mirror of
https://github.com/rwinkhart/MUTN.git
synced 2026-09-06 00:57:15 -04:00
Refactor all of entryList.go for efficiency
This commit is contained in:
@@ -5,7 +5,7 @@ go 1.25.6
|
|||||||
require (
|
require (
|
||||||
github.com/charmbracelet/glamour v0.7.0
|
github.com/charmbracelet/glamour v0.7.0
|
||||||
github.com/rwinkhart/go-boilerplate v0.2.2
|
github.com/rwinkhart/go-boilerplate v0.2.2
|
||||||
github.com/rwinkhart/libmutton v0.4.3-0.20260124203434-cb3a554676ff
|
github.com/rwinkhart/libmutton v0.4.3-0.20260124213405-e1c64695062c
|
||||||
golang.org/x/term v0.39.0
|
golang.org/x/term v0.39.0
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -50,8 +50,8 @@ github.com/rwinkhart/go-highlite v0.1.1 h1:9TxbRhYVfD/3YaEgNk1BtEiZ0t8/5mxDUZqNM
|
|||||||
github.com/rwinkhart/go-highlite v0.1.1/go.mod h1:mWLMtCWcyV0BG4NeyPyAUGMjPvI0ds6vXmUq89QwBFA=
|
github.com/rwinkhart/go-highlite v0.1.1/go.mod h1:mWLMtCWcyV0BG4NeyPyAUGMjPvI0ds6vXmUq89QwBFA=
|
||||||
github.com/rwinkhart/go-winio v0.1.0 h1:b72agLW+dETGmhR3VbcbwnStfgKfc5AfgJOXBJDkaHg=
|
github.com/rwinkhart/go-winio v0.1.0 h1:b72agLW+dETGmhR3VbcbwnStfgKfc5AfgJOXBJDkaHg=
|
||||||
github.com/rwinkhart/go-winio v0.1.0/go.mod h1:ZWa7ssZJT30CCDGJ7fk/2SBTq9BIQrrVjrcss0UW2s0=
|
github.com/rwinkhart/go-winio v0.1.0/go.mod h1:ZWa7ssZJT30CCDGJ7fk/2SBTq9BIQrrVjrcss0UW2s0=
|
||||||
github.com/rwinkhart/libmutton v0.4.3-0.20260124203434-cb3a554676ff h1:dCxXlKGeTr4EYH+6ps6VC6LMDXlAAuDpyEHMUPCSZEw=
|
github.com/rwinkhart/libmutton v0.4.3-0.20260124213405-e1c64695062c h1:W86sc4w/nX1cieQCLjgdQvu6G/6Gooc+zkyXBiEAV1A=
|
||||||
github.com/rwinkhart/libmutton v0.4.3-0.20260124203434-cb3a554676ff/go.mod h1:4/Ry5uDUaWTdNqVR/URhqM6NveB3dFfxCmO+LyisBcQ=
|
github.com/rwinkhart/libmutton v0.4.3-0.20260124213405-e1c64695062c/go.mod h1:4/Ry5uDUaWTdNqVR/URhqM6NveB3dFfxCmO+LyisBcQ=
|
||||||
github.com/rwinkhart/peercred-mini v0.1.2 h1:4cGWDbv0whvLeVvbUdx84V/9p+2fS+DEXgrA1KxlRFo=
|
github.com/rwinkhart/peercred-mini v0.1.2 h1:4cGWDbv0whvLeVvbUdx84V/9p+2fS+DEXgrA1KxlRFo=
|
||||||
github.com/rwinkhart/peercred-mini v0.1.2/go.mod h1:LLHG7YshHEpbpJJP+Il9nx2dnGj5O3VGE32rWmflj0c=
|
github.com/rwinkhart/peercred-mini v0.1.2/go.mod h1:LLHG7YshHEpbpJJP+Il9nx2dnGj5O3VGE32rWmflj0c=
|
||||||
github.com/rwinkhart/rcw v0.2.4 h1:FrRSsl+ui1WLmDwqjLSROtFPKqSeGLEfF2SPENt4qP0=
|
github.com/rwinkhart/rcw v0.2.4 h1:FrRSsl+ui1WLmDwqjLSROtFPKqSeGLEfF2SPENt4qP0=
|
||||||
|
|||||||
@@ -234,7 +234,7 @@ func main() {
|
|||||||
case 5:
|
case 5:
|
||||||
oldPassword := confirmRCWPassword("old")
|
oldPassword := confirmRCWPassword("old")
|
||||||
newPassword := confirmRCWPassword("new")
|
newPassword := confirmRCWPassword("new")
|
||||||
fmt.Print("\nRe-encrypting entries...\nPlease wait; do not force close this process.\n\n")
|
fmt.Print("Re-encrypting entries...\nPlease wait; do not force close this process.\n\n")
|
||||||
if err := core.EntryRefresh(oldPassword, newPassword, false); err != nil {
|
if err := core.EntryRefresh(oldPassword, newPassword, false); err != nil {
|
||||||
other.PrintError("Re-encryption failed: "+err.Error(), global.ErrorEncryption)
|
other.PrintError("Re-encryption failed: "+err.Error(), global.ErrorEncryption)
|
||||||
}
|
}
|
||||||
|
|||||||
+101
-98
@@ -3,6 +3,7 @@ package cli
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/rwinkhart/go-boilerplate/back"
|
"github.com/rwinkhart/go-boilerplate/back"
|
||||||
@@ -17,49 +18,42 @@ const (
|
|||||||
ansiDirectoryHeader = "\033[38;5;7;48;5;8m"
|
ansiDirectoryHeader = "\033[38;5;7;48;5;8m"
|
||||||
)
|
)
|
||||||
|
|
||||||
// determineIndentation calculates and returns the final visual indentation multiplier (needed to adjust indentation for skipped parent directories); also subtracts "old" text from directory header.
|
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) {
|
func determineIndentation(skippedDirList []bool, dirList []string, currentDirIndex int) (int, string) {
|
||||||
var subtractor int // tracks how much to subtract from expected indentation multiplier
|
trimmed := dirList[currentDirIndex]
|
||||||
var lastPrefixIndex int // tracks the index (in both skippedDirList and dirList) of the last displayed parent directory
|
indent := strings.Count(trimmed, "/") - 1 // avoid indenting root-level dirs
|
||||||
var trimmedDirectory = dirList[currentDirIndex]
|
|
||||||
|
|
||||||
// determine initial indentation multiplier based on "/" occurrences
|
subtractor, lastPrefixIndex := 0, 0
|
||||||
indent := strings.Count(trimmedDirectory, "/") - 1 // subtract 1 to avoid indenting root-level directories
|
for i, skipped := range skippedDirList[:currentDirIndex] {
|
||||||
|
if strings.HasPrefix(trimmed, dirList[i]+"/") {
|
||||||
for i, skipped := range skippedDirList[:currentDirIndex] { // checks each skipped directory to determine if it is a parent to the current directory
|
if skipped {
|
||||||
if strings.HasPrefix(trimmedDirectory, dirList[i]+"/") { // if the current directory is the child of this iteration's directory...
|
subtractor++
|
||||||
if skipped { // ...and this iteration's directory was skipped...
|
|
||||||
subtractor++ // increment the subtractor to indicate that the visual indentation should be reduced
|
|
||||||
} else {
|
} else {
|
||||||
lastPrefixIndex = i
|
lastPrefixIndex = i
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if indent -= subtractor; indent < 0 {
|
||||||
indent = indent - subtractor // calculates final visual indentation multiplier
|
return 0, trimmed
|
||||||
|
|
||||||
if indent < 0 { // disallow negative indentation multipliers
|
|
||||||
indent = 0
|
|
||||||
} else if indent > 0 { // trim the most recently displayed parent directory from the directory header to avoid displaying redundant information
|
|
||||||
trimmedDirectory = strings.Replace(trimmedDirectory, dirList[lastPrefixIndex], "", 1)
|
|
||||||
}
|
}
|
||||||
|
if indent > 0 {
|
||||||
return indent, trimmedDirectory
|
trimmed = strings.TrimPrefix(trimmed, dirList[lastPrefixIndex])
|
||||||
|
}
|
||||||
|
return indent, trimmed
|
||||||
}
|
}
|
||||||
|
|
||||||
// printFileEntry handles processing for printing file entries (determines color, wraps lines, and prints).
|
// printFileEntry handles processing for printing file entries (determines color, adds aging indicator, wraps lines, and prints).
|
||||||
func printFileEntry(entry string, lastSlash int, charCounter *int, indent int, colorAlternator int8, agingTimestamp *int64) int8 {
|
func printFileEntry(sb *strings.Builder, entry string, lastSlash int, charCounter *int, indent int, colorAlternator int8, agingTimestamp *int64) int8 {
|
||||||
// determine color to print fileEntryName (alternate each time function is run)
|
colorCode := ""
|
||||||
var colorCode string
|
if colorAlternator <= 0 {
|
||||||
if colorAlternator > 0 {
|
|
||||||
colorCode = ""
|
|
||||||
} else {
|
|
||||||
colorCode = ansiAlternateEntryColor
|
colorCode = ansiAlternateEntryColor
|
||||||
}
|
}
|
||||||
colorAlternator = -colorAlternator
|
colorAlternator = -colorAlternator
|
||||||
|
|
||||||
// determine password aging dot
|
agingDot := ""
|
||||||
var agingDot string
|
|
||||||
switch age.TranslateAgeTimestamp(agingTimestamp) {
|
switch age.TranslateAgeTimestamp(agingTimestamp) {
|
||||||
case 1:
|
case 1:
|
||||||
agingDot = back.AnsiGreen + "⁍" + back.AnsiReset
|
agingDot = back.AnsiGreen + "⁍" + back.AnsiReset
|
||||||
@@ -69,32 +63,36 @@ func printFileEntry(entry string, lastSlash int, charCounter *int, indent int, c
|
|||||||
agingDot = back.AnsiError + "⁍" + back.AnsiReset
|
agingDot = back.AnsiError + "⁍" + back.AnsiReset
|
||||||
}
|
}
|
||||||
|
|
||||||
// trim the containing directory from the entry to determine fileEntryName
|
name := entry[lastSlash:]
|
||||||
fileEntryName := entry[lastSlash:]
|
if *charCounter == 0 {
|
||||||
|
writeIndent(sb, indent)
|
||||||
if *charCounter == 0 { // indent first line of entries for each directory header
|
|
||||||
fmt.Print(strings.Repeat(" ", indent*2))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// determine whether to wrap to a new line (+1 is to account for trailing spaces)
|
// wrap if needed; +1 accounts for trailing space, +1 more if aging dot is present
|
||||||
*charCounter += len(fileEntryName) + 1
|
inc := len(name) + 1
|
||||||
|
if agingDot != "" {
|
||||||
|
inc++
|
||||||
|
}
|
||||||
|
if *charCounter += inc; *charCounter+indent*2 >= width {
|
||||||
|
*charCounter = len(name) + 1
|
||||||
if agingDot != "" {
|
if agingDot != "" {
|
||||||
*charCounter++
|
*charCounter++
|
||||||
}
|
}
|
||||||
if indentation := indent * 2; *charCounter+(indentation) >= width {
|
sb.WriteString("\n")
|
||||||
*charCounter = len(fileEntryName) + 1
|
writeIndent(sb, indent)
|
||||||
fmt.Print("\n" + strings.Repeat(" ", indentation)) // indent each line
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// print fileEntryName to screen
|
sb.WriteString(agingDot)
|
||||||
fmt.Printf("%s%s%s%s ", agingDot, colorCode, fileEntryName, back.AnsiReset)
|
sb.WriteString(colorCode)
|
||||||
|
sb.WriteString(name)
|
||||||
|
sb.WriteString(back.AnsiReset)
|
||||||
|
sb.WriteString(" ")
|
||||||
return colorAlternator
|
return colorAlternator
|
||||||
}
|
}
|
||||||
|
|
||||||
// EntryListGen generates and displays the full libmutton entry list.
|
// EntryListGen generates and displays the full libmutton entry list.
|
||||||
func EntryListGen() {
|
func EntryListGen() {
|
||||||
fileList, dirList, err := synccommon.WalkEntryDir()
|
_, dirList, err := synccommon.WalkEntryDir()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
other.PrintError("Failed to generate entry list: "+err.Error(), back.ErrorRead)
|
other.PrintError("Failed to generate entry list: "+err.Error(), back.ErrorRead)
|
||||||
}
|
}
|
||||||
@@ -103,76 +101,81 @@ func EntryListGen() {
|
|||||||
other.PrintError("Failed to retrieve entry aging data: "+err.Error(), back.ErrorRead)
|
other.PrintError("Failed to retrieve entry aging data: "+err.Error(), back.ErrorRead)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var sb strings.Builder
|
||||||
|
|
||||||
// print header bar w/total entry count
|
// print header bar w/total entry count
|
||||||
fmt.Print("\n"+ansiBlackOnWhite, len(fileList), " libmutton entries:"+back.AnsiReset)
|
sb.WriteString("\n")
|
||||||
|
sb.WriteString(ansiBlackOnWhite)
|
||||||
|
fmt.Fprint(&sb, len(entryMap))
|
||||||
|
sb.WriteString(" libmutton entries:")
|
||||||
|
sb.WriteString(back.AnsiReset)
|
||||||
|
|
||||||
// dirList iteration
|
entriesByDir := make(map[string][]string, len(dirList))
|
||||||
dirListLength := len(dirList) // save length for multiple references below
|
for vanityPath := range entryMap {
|
||||||
var skippedDirList = make([]bool, dirListLength) // stores whether each directory was skipped during printout (later used to determine appropriate visual indentation)
|
if lastSlash := strings.LastIndex(vanityPath, "/"); lastSlash >= 0 {
|
||||||
charCounter := 0 // track whether to line-wrap based on character count in line
|
entriesByDir[vanityPath[:lastSlash]] = append(entriesByDir[vanityPath[:lastSlash]], vanityPath)
|
||||||
var colorAlternator int8 = 1 // track alternating colors for each printed entry name
|
}
|
||||||
var containsSubdirectory bool // indicates whether the current directory contains a subdirectory
|
}
|
||||||
var indent int // visual indentation multiplier
|
for _, entries := range entriesByDir {
|
||||||
var vanityDirectory string // directory header printed to end-user - visual only, not used in any processing
|
sort.Strings(entries)
|
||||||
for i, directory := range dirList {
|
}
|
||||||
|
|
||||||
// reset formatting variables for new directory
|
// precompute whether each directory contains subdirectories;
|
||||||
charCounter = 0
|
// handle root-level child dirs by treating "" as their parent.
|
||||||
colorAlternator = 1
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// default to assuming this directory will be skipped (unless it is the root)
|
dirListLength := len(dirList)
|
||||||
if i == 0 {
|
skippedDirList := make([]bool, dirListLength)
|
||||||
|
charCounter := 0
|
||||||
|
var colorAlternator int8 = 1
|
||||||
|
|
||||||
|
printDirHeader := func(i int) int {
|
||||||
skippedDirList[i] = false
|
skippedDirList[i] = false
|
||||||
} else {
|
indent, vanityDirectory := determineIndentation(skippedDirList, dirList, i)
|
||||||
skippedDirList[i] = true
|
sb.WriteString("\n\n")
|
||||||
|
writeIndent(&sb, indent)
|
||||||
|
sb.WriteString(ansiDirectoryHeader)
|
||||||
|
sb.WriteString(vanityDirectory)
|
||||||
|
sb.WriteString("/" + back.AnsiReset + "\n")
|
||||||
|
return indent
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if next directory is within the current one
|
for i, directory := range dirList {
|
||||||
if dirListLength > i+1 {
|
charCounter, colorAlternator = 0, 1
|
||||||
if nextDir := dirList[i+1]; directory == nextDir[:strings.LastIndex(nextDir, "/")] {
|
skippedDirList[i] = i != 0
|
||||||
containsSubdirectory = true
|
|
||||||
} else {
|
if entries := entriesByDir[directory]; len(entries) > 0 {
|
||||||
containsSubdirectory = false
|
indent := printDirHeader(i)
|
||||||
|
for _, vanityPath := range entries {
|
||||||
|
lastSlash := strings.LastIndex(vanityPath, "/") + 1
|
||||||
|
colorAlternator = printFileEntry(&sb, vanityPath, lastSlash, &charCounter, indent, colorAlternator, entryMap[vanityPath].AgeTimestamp)
|
||||||
}
|
}
|
||||||
} else {
|
continue
|
||||||
containsSubdirectory = false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// fileList iteration
|
if hasChildDir[directory] {
|
||||||
containsFiles := false // indicates whether the current directory contains files (entries)
|
continue
|
||||||
for _, vanityPath := range fileList {
|
|
||||||
|
|
||||||
// print the current file if it belongs in the current directory - otherwise, break the loop and move on to the next directory
|
|
||||||
if lastSlash := strings.LastIndex(vanityPath, "/") + 1; vanityPath[:lastSlash-1] == directory {
|
|
||||||
|
|
||||||
// print directory header if this is the first run of the loop
|
|
||||||
if !containsFiles {
|
|
||||||
containsFiles = true
|
|
||||||
skippedDirList[i] = false // the directory header is being printed, indicate that it is not being skipped
|
|
||||||
indent, vanityDirectory = determineIndentation(skippedDirList, dirList, i) // calculate the final indentation multiplier
|
|
||||||
fmt.Printf("\n\n"+strings.Repeat(" ", indent*2)+ansiDirectoryHeader+"%s/"+back.AnsiReset+"\n", vanityDirectory)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
colorAlternator = printFileEntry(vanityPath, lastSlash, &charCounter, indent, colorAlternator, entryMap[vanityPath].AgeTimestamp)
|
if dirListLength > 1 {
|
||||||
}
|
indent := printDirHeader(i)
|
||||||
|
writeIndent(&sb, indent)
|
||||||
|
sb.WriteString(back.AnsiWarning + "-empty directory-" + back.AnsiReset)
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if !containsFiles { // if the current directory contains no files...
|
sb.WriteString("\n\nNothing's here! For help creating your first entry, run \"mutn help\".")
|
||||||
if !containsSubdirectory { // nor does it contain any subdirectories...
|
|
||||||
if dirListLength > 1 { // and directories besides the root-level exist... display directory header and empty directory warning
|
|
||||||
skippedDirList[i] = false // the directory header is being printed, indicate that it is not being skipped
|
|
||||||
indent, vanityDirectory = determineIndentation(skippedDirList, dirList, i) // calculate the final indentation multiplier
|
|
||||||
fmt.Printf("\n\n"+strings.Repeat(" ", indent*2)+ansiDirectoryHeader+"%s/"+back.AnsiReset+"\n", vanityDirectory)
|
|
||||||
fmt.Print(strings.Repeat(" ", indent*2) + back.AnsiWarning + "-empty directory-" + back.AnsiReset)
|
|
||||||
} else { // warn if the only thing that exists is the root-level directory
|
|
||||||
fmt.Print("\n\nNothing's here! For help creating your first entry, run \"mutn help\".")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// print trailing new lines for proper spacing after entry list is complete
|
sb.WriteString("\n\n")
|
||||||
fmt.Print("\n\n")
|
fmt.Print(sb.String())
|
||||||
|
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user