>> write function to parse files & directories listed at command line

> remove sorted paths function
> use the comparator directly
> simplify warn function
This commit is contained in:
Thomas Lindop 2025-05-24 00:14:20 +01:00
parent 18fede0d0d
commit 9eec80ec79

70
main.go
View file

@ -21,6 +21,7 @@ package main
import ( import (
"errors" "errors"
"fmt" "fmt"
"io/fs"
"os" "os"
fpath "path/filepath" fpath "path/filepath"
"slices" "slices"
@ -36,10 +37,10 @@ import (
* (in that order) of a range of Code Points which are valid. Ranges are * (in that order) of a range of Code Points which are valid. Ranges are
* inclusive. * inclusive.
*/ */
type runeset = [][2]rune type runeset [][2]rune
// Unrelated to the standard library's "context". // Unrelated to the standard library's "context".
type context = struct { type context struct {
FileList []string FileList []string
RecurseDirs []string RecurseDirs []string
DryRun bool DryRun bool
@ -72,12 +73,11 @@ var FAT_RUNESET = runeset{
/// MAIN FUNCTIONS /// MAIN FUNCTIONS
/** /**
* Sort a list of paths so that all files are listed before the directories * Compares paths so that all files come before the directories that contain
* that contain them. Files in the same directory are listed in alphanumeric * them. Files in the same directory come in alphanumeric
* order. * order.
*/ */
func sortedPaths(paths []string) []string { func comparePaths(a, b string) int {
sorter := func(a, b string) int {
sep := string(fpath.Separator) sep := string(fpath.Separator)
aNumComponents := len(strings.Split(a, sep)) aNumComponents := len(strings.Split(a, sep))
bNumComponents := len(strings.Split(b, sep)) bNumComponents := len(strings.Split(b, sep))
@ -85,12 +85,44 @@ func sortedPaths(paths []string) []string {
return strings.Compare(fpath.Base(a), fpath.Base(b)) return strings.Compare(fpath.Base(a), fpath.Base(b))
} }
return bNumComponents - aNumComponents return bNumComponents - aNumComponents
}
result := slices.Clone(paths)
slices.SortFunc(result, sorter)
return result
} }
func (ctx *context) parseFileList() ([]string, error) {
errMsgs := []string{}
result := slices.Clone(ctx.FileList)
slices.SortFunc(result, comparePaths)
for _, path := range result {
if _, err := os.Stat(path); err != nil {
msg := fmt.Sprintf( "File <<%s>> does not exist", path)
errMsgs = append(errMsgs, msg)
}
}
addFiles := func(fullPath string, fileInfo fs.DirEntry, err error) error {
if err != nil {
msg := fmt.Sprintf(
"Directory <<%s>> does not exist or is not searchable",
fullPath,
)
errMsgs = append(errMsgs,msg)
return err
}
loc, isDup := slices.BinarySearchFunc(result, fullPath, comparePaths)
if !isDup {
result = slices.Insert(result, loc, fullPath)
}
return nil
}
for _, dir := range ctx.RecurseDirs {
fpath.WalkDir(dir, addFiles)
}
if len(errMsgs) == 0 {
return result, nil
} else {
return result, errors.New(strings.Join(errMsgs, "\n"))
}
}
func isFatValid(r rune) bool { func isFatValid(r rune) bool {
for _, runeRange := range FAT_RUNESET { for _, runeRange := range FAT_RUNESET {
if runeRange[0] <= r && r <= runeRange[1] { if runeRange[0] <= r && r <= runeRange[1] {
@ -125,8 +157,7 @@ func restrictRuneset(s, strategy string) string {
return result return result
} }
func warn(msgFmt, detail string) { func warn(msg string) {
msg := fmt.Sprintf(msgFmt, detail)
fmt.Fprintf(os.Stderr, "\x1b[33m%s\x1b[0m\n", msg) fmt.Fprintf(os.Stderr, "\x1b[33m%s\x1b[0m\n", msg)
} }
@ -155,13 +186,13 @@ func parseCLIArgs(args []string) (context, error) {
for index < len(args) { for index < len(args) {
switch arg := args[index]; arg { switch arg := args[index]; arg {
case "-d", "--directory": case "-d", "--directory":
result.RecurseDirs = append(result.RecurseDirs, arg)
index++ index++
result.RecurseDirs = append(result.RecurseDirs, args[index])
case "-n", "--dry-run": case "-n", "--dry-run":
result.DryRun = true result.DryRun = true
case "-s", "--strategy": case "-s", "--strategy":
result.Strategy = args[index]
index++ index++
result.Strategy = args[index]
case "-h", "--help": case "-h", "--help":
result.DoHelp = true result.DoHelp = true
case "-v", "--version": case "-v", "--version":
@ -212,14 +243,17 @@ func main() {
OwlVersion, OwlVersion,
) )
} else { } else {
for _, file := range ctx.FileList { ctx.FileList, err = ctx.parseFileList()
if _, err := os.Stat(file); err != nil { if err != nil {
warn("File <<%s>> does not exist!", file) warn(err.Error())
continue
} }
for _, file := range ctx.FileList {
oldName := fpath.Base(file) oldName := fpath.Base(file)
dirName := fpath.Dir(file) dirName := fpath.Dir(file)
newPath := fpath.Join(dirName, restrictRuneset(oldName, ctx.Strategy)) newPath := fpath.Join(dirName, restrictRuneset(oldName, ctx.Strategy))
if newPath == file {
continue
}
if ctx.DryRun { if ctx.DryRun {
fmt.Printf( fmt.Printf(
"%s -> <<%s>>\n", "%s -> <<%s>>\n",