mirror of
https://github.com/rwinkhart/MUTN.git
synced 2026-08-28 04:46:41 -04:00
182 lines
5.0 KiB
Go
182 lines
5.0 KiB
Go
package cli
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"sort"
|
|
"strings"
|
|
|
|
"github.com/rwinkhart/go-boilerplate/back"
|
|
"github.com/rwinkhart/go-boilerplate/other"
|
|
"github.com/rwinkhart/libmutton/age"
|
|
"github.com/rwinkhart/libmutton/synccommon"
|
|
)
|
|
|
|
// ANSI color constants used only in this file
|
|
const (
|
|
ansiAlternateEntryColor = "\033[38;5;8m"
|
|
ansiDirectoryHeader = "\033[38;5;7;48;5;8m"
|
|
)
|
|
|
|
func writeIndent(sb *strings.Builder, indent int) { sb.WriteString(strings.Repeat(" ", indent*2)) }
|
|
|
|
// determineIndentation calculates the final visual indentation multiplier (adjusts for skipped parents)
|
|
// and trims the parent directory name from the directory header (if applicable).
|
|
func determineIndentation(skippedDirList []bool, dirList []string, currentDirIndex int) (int, string) {
|
|
trimmed := dirList[currentDirIndex]
|
|
indent := strings.Count(trimmed, "/") - 1 // avoid indenting root-level dirs
|
|
|
|
subtractor, lastPrefixIndex := 0, 0
|
|
for i, skipped := range skippedDirList[:currentDirIndex] {
|
|
if strings.HasPrefix(trimmed, dirList[i]+"/") {
|
|
if skipped {
|
|
subtractor++
|
|
} else {
|
|
lastPrefixIndex = i
|
|
}
|
|
}
|
|
}
|
|
if indent -= subtractor; indent < 0 {
|
|
return 0, trimmed
|
|
}
|
|
if indent > 0 {
|
|
trimmed = strings.TrimPrefix(trimmed, dirList[lastPrefixIndex])
|
|
}
|
|
return indent, trimmed
|
|
}
|
|
|
|
// printFileEntry handles processing for printing file entries (determines color, adds aging indicator, wraps lines, and prints).
|
|
func printFileEntry(sb *strings.Builder, entry string, lastSlash int, charCounter *int, indent int, colorAlternator int8, agingTimestamp *int64) int8 {
|
|
colorCode := ""
|
|
if colorAlternator <= 0 {
|
|
colorCode = ansiAlternateEntryColor
|
|
}
|
|
colorAlternator = -colorAlternator
|
|
|
|
agingDot := ""
|
|
switch age.TranslateAgeTimestamp(agingTimestamp) {
|
|
case 1:
|
|
agingDot = back.AnsiGreen + "⁍" + back.AnsiReset
|
|
case 2:
|
|
agingDot = back.AnsiWarning + "⁍" + back.AnsiReset
|
|
case 3:
|
|
agingDot = back.AnsiError + "⁍" + back.AnsiReset
|
|
}
|
|
|
|
name := entry[lastSlash:]
|
|
if *charCounter == 0 {
|
|
writeIndent(sb, indent)
|
|
}
|
|
|
|
// wrap if needed; +1 accounts for trailing space, +1 more if aging dot is present
|
|
inc := len(name) + 1
|
|
if agingDot != "" {
|
|
inc++
|
|
}
|
|
if *charCounter += inc; *charCounter+indent*2 >= width {
|
|
*charCounter = len(name) + 1
|
|
if agingDot != "" {
|
|
*charCounter++
|
|
}
|
|
sb.WriteString("\n")
|
|
writeIndent(sb, indent)
|
|
}
|
|
|
|
sb.WriteString(agingDot)
|
|
sb.WriteString(colorCode)
|
|
sb.WriteString(name)
|
|
sb.WriteString(back.AnsiReset)
|
|
sb.WriteString(" ")
|
|
return colorAlternator
|
|
}
|
|
|
|
// EntryListGen generates and displays the full libmutton entry list.
|
|
func EntryListGen() {
|
|
_, dirList, err := synccommon.WalkEntryDir()
|
|
if err != nil {
|
|
other.PrintError("Failed to generate entry list: "+err.Error(), back.ErrorRead)
|
|
}
|
|
entryMap, err := synccommon.GetAllEntryData()
|
|
if err != nil {
|
|
other.PrintError("Failed to retrieve entry aging data: "+err.Error(), back.ErrorRead)
|
|
}
|
|
|
|
var sb strings.Builder
|
|
|
|
// print header bar w/total entry count
|
|
sb.WriteString("\n")
|
|
sb.WriteString(ansiBlackOnWhite)
|
|
fmt.Fprint(&sb, len(entryMap))
|
|
sb.WriteString(" libmutton entries:")
|
|
sb.WriteString(back.AnsiReset)
|
|
|
|
entriesByDir := make(map[string][]string, len(dirList))
|
|
for vanityPath := range entryMap {
|
|
if lastSlash := strings.LastIndex(vanityPath, "/"); lastSlash >= 0 {
|
|
entriesByDir[vanityPath[:lastSlash]] = append(entriesByDir[vanityPath[:lastSlash]], vanityPath)
|
|
}
|
|
}
|
|
for _, entries := range entriesByDir {
|
|
sort.Strings(entries)
|
|
}
|
|
|
|
// precompute whether each directory contains subdirectories;
|
|
// handle root-level child dirs by treating "" as their parent.
|
|
hasChildDir := make(map[string]bool, len(dirList))
|
|
for _, d := range dirList {
|
|
switch parentEnd := strings.LastIndex(d, "/"); {
|
|
case parentEnd > 0:
|
|
hasChildDir[d[:parentEnd]] = true
|
|
case parentEnd == -1:
|
|
hasChildDir[""] = true
|
|
}
|
|
}
|
|
|
|
dirListLength := len(dirList)
|
|
skippedDirList := make([]bool, dirListLength)
|
|
charCounter := 0
|
|
var colorAlternator int8 = 1
|
|
|
|
printDirHeader := func(i int) int {
|
|
skippedDirList[i] = false
|
|
indent, vanityDirectory := determineIndentation(skippedDirList, dirList, i)
|
|
sb.WriteString("\n\n")
|
|
writeIndent(&sb, indent)
|
|
sb.WriteString(ansiDirectoryHeader)
|
|
sb.WriteString(vanityDirectory)
|
|
sb.WriteString("/" + back.AnsiReset + "\n")
|
|
return indent
|
|
}
|
|
|
|
for i, directory := range dirList {
|
|
charCounter, colorAlternator = 0, 1
|
|
skippedDirList[i] = i != 0
|
|
|
|
if entries := entriesByDir[directory]; len(entries) > 0 {
|
|
indent := printDirHeader(i)
|
|
for _, vanityPath := range entries {
|
|
lastSlash := strings.LastIndex(vanityPath, "/") + 1
|
|
colorAlternator = printFileEntry(&sb, vanityPath, lastSlash, &charCounter, indent, colorAlternator, entryMap[vanityPath].AgeTimestamp)
|
|
}
|
|
continue
|
|
}
|
|
|
|
if hasChildDir[directory] {
|
|
continue
|
|
}
|
|
|
|
if dirListLength > 1 {
|
|
indent := printDirHeader(i)
|
|
writeIndent(&sb, indent)
|
|
sb.WriteString(back.AnsiWarning + "-empty directory-" + back.AnsiReset)
|
|
continue
|
|
}
|
|
|
|
sb.WriteString("\n\nNothing's here! For help creating your first entry, run \"mutn help\".")
|
|
}
|
|
|
|
sb.WriteString("\n\n")
|
|
fmt.Print(sb.String())
|
|
os.Exit(0)
|
|
}
|