github.com/jfrog/jfrog-cli-core/v2@v2.52.0/artifactory/utils/filetree.go (about)

     1  package utils
     2  
     3  import (
     4  	"strings"
     5  )
     6  
     7  var maxFilesInTree = 200
     8  
     9  // FileTree is a UI components that displays a file-system tree view in the terminal.
    10  type FileTree struct {
    11  	repos      map[string]*dirNode
    12  	size       int
    13  	exceedsMax bool
    14  }
    15  
    16  func NewFileTree() *FileTree {
    17  	return &FileTree{repos: map[string]*dirNode{}, size: 0}
    18  }
    19  
    20  func (ft *FileTree) AddFile(path string) {
    21  	if ft.size >= maxFilesInTree {
    22  		ft.exceedsMax = true
    23  		return
    24  	}
    25  	splitPath := strings.Split(path, "/")
    26  	if _, exist := ft.repos[splitPath[0]]; !exist {
    27  		ft.repos[splitPath[0]] = &dirNode{name: splitPath[0], prefix: "📦 ", subDirNodes: map[string]*dirNode{}, fileNames: map[string]bool{}}
    28  	}
    29  	if ft.repos[splitPath[0]].addArtifact(splitPath[1:]) {
    30  		ft.size++
    31  	}
    32  }
    33  
    34  // Returns a string representation of the tree. If the number of files exceeded the maximum, an empty string will be returned.
    35  func (ft *FileTree) String() string {
    36  	if ft.exceedsMax {
    37  		return ""
    38  	}
    39  	treeStr := ""
    40  	for _, repo := range ft.repos {
    41  		treeStr += strings.Join(repo.strings(), "\n") + "\n"
    42  	}
    43  	return treeStr
    44  }
    45  
    46  type dirNode struct {
    47  	name        string
    48  	prefix      string
    49  	subDirNodes map[string]*dirNode
    50  	fileNames   map[string]bool
    51  }
    52  
    53  func (dn *dirNode) addArtifact(pathInDir []string) bool {
    54  	if len(pathInDir) == 1 {
    55  		if _, exist := dn.fileNames[pathInDir[0]]; exist {
    56  			return false
    57  		}
    58  		dn.fileNames[pathInDir[0]] = true
    59  	} else {
    60  		if _, exist := dn.subDirNodes[pathInDir[0]]; !exist {
    61  			dn.subDirNodes[pathInDir[0]] = &dirNode{name: pathInDir[0], prefix: "📁 ", subDirNodes: map[string]*dirNode{}, fileNames: map[string]bool{}}
    62  		}
    63  		return dn.subDirNodes[pathInDir[0]].addArtifact(pathInDir[1:])
    64  	}
    65  	return true
    66  }
    67  
    68  func (dn *dirNode) strings() []string {
    69  	strs := []string{dn.prefix + dn.name}
    70  	subDirIndex := 0
    71  	for subDirName := range dn.subDirNodes {
    72  		var subDirPrefix string
    73  		var innerStrPrefix string
    74  		if subDirIndex == len(dn.subDirNodes)-1 && len(dn.fileNames) == 0 {
    75  			subDirPrefix = "└── "
    76  			innerStrPrefix = "    "
    77  		} else {
    78  			subDirPrefix = "├── "
    79  			innerStrPrefix = "│   "
    80  		}
    81  		subDirStrs := dn.subDirNodes[subDirName].strings()
    82  		strs = append(strs, subDirPrefix+subDirStrs[0])
    83  		for subDirStrIndex := 1; subDirStrIndex < len(subDirStrs); subDirStrIndex++ {
    84  			strs = append(strs, innerStrPrefix+subDirStrs[subDirStrIndex])
    85  		}
    86  		subDirIndex++
    87  	}
    88  	fileIndex := 0
    89  	for fileName := range dn.fileNames {
    90  		var filePrefix string
    91  		if fileIndex == len(dn.fileNames)-1 {
    92  			filePrefix = "└── 📄 "
    93  		} else {
    94  			filePrefix = "├── 📄 "
    95  			fileIndex++
    96  		}
    97  		strs = append(strs, filePrefix+fileName)
    98  	}
    99  	return strs
   100  }