Files
MUTN/src/cli/entryList.go
T

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)
}