package files import ( "os" "path" "path/filepath" "sort" "strings" "ddc/internal/extract" ) const ( maxTreeEntries = 20000 maxTreeDepth = 32 ) type Node struct { Name string `json:"name"` Path string `json:"path"` IsDir bool `json:"isDir"` IsArchive bool `json:"isArchive"` Size int64 `json:"size"` Children []Node `json:"children,omitempty"` } func BuildTree(root string) ([]Node, error) { entryCount := 0 return readLevel(root, "", 0, &entryCount) } func readLevel(root, relative string, depth int, entryCount *int) ([]Node, error) { entries, err := os.ReadDir(filepath.Join(root, relative)) if err != nil { return nil, err } nodes := make([]Node, 0, len(entries)) for _, entry := range entries { if *entryCount >= maxTreeEntries { break } entryType := entry.Type() if entry.Name() == PartFileName || (!entryType.IsDir() && !entryType.IsRegular()) { continue } *entryCount++ node := Node{ Name: entry.Name(), Path: path.Join(filepath.ToSlash(relative), entry.Name()), IsDir: entry.IsDir(), } if node.IsDir { if depth < maxTreeDepth { children, err := readLevel(root, filepath.Join(relative, entry.Name()), depth+1, entryCount) if err != nil { return nil, err } node.Children = children for _, child := range children { node.Size += child.Size } } } else { info, err := entry.Info() if err != nil { continue } node.Size = info.Size() node.IsArchive = extract.IsArchive(entry.Name()) } nodes = append(nodes, node) } sort.SliceStable(nodes, func(first, second int) bool { if nodes[first].IsDir != nodes[second].IsDir { return nodes[first].IsDir } return strings.ToLower(nodes[first].Name) < strings.ToLower(nodes[second].Name) }) return nodes, nil }