diff --git a/main.go b/main.go index 5007ae0..b8478bb 100644 --- a/main.go +++ b/main.go @@ -2,11 +2,11 @@ package main import ( "bufio" + "flag" "fmt" "log" "os" "regexp" - "strconv" "strings" ) @@ -26,63 +26,164 @@ const TargetColor = Yellow const ErrorColor = Red const DefaultColor = White +var programName = os.Args[0] + type LinkInstruction struct { Source string Target string Force bool } -func InstructionToString(instruction LinkInstruction) string { - return fmt.Sprintf("%s%s%s%s%s%s%s%s%s", SourceColor, instruction.Source, DefaultColor, deliminer, TargetColor, instruction.Target, DefaultColor, deliminer, strconv.FormatBool(instruction.Force)) -} - func main() { // Format: - // source|target|force? + // source,target,force? log.SetFlags(log.Lmicroseconds) - var inputs []string - // os.Chdir("C:/Users/Administrator/Seafile/Last-Epoch-Backup") - if len(os.Args) > 1 { - inputs = append(inputs, os.Args[1:]...) - } else { - info, err := os.Stdin.Stat() + recurse := flag.String("r", "", "recurse into directories") + file := flag.String("f", "", "file to read instructions from") + flag.Parse() + + log.Printf("Recurse: %s", *recurse) + log.Printf("File: %s", *file) + // The plan: + // As input take file or list of files (via -f) + // For every file: ch work dir to file dir + // Read file + // Parse instructions + // Run instructions + // If not -f then if -r: + // Recurse into directories + // Find all sync files + // Repeat the above for every file + // If not -f and not -r and args: + // Read from args + // Parse instructions + // Run instructions + // If not -f and not -r and no args: + // Read from stdin + // Parse instructions + // Run instructions + + if *recurse != "" { + startingDir, _ := os.Getwd() + log.Println("Workdir:", startingDir) + var targetDir = os.Args[1] + if targetDir == "" { + targetDir, _ = os.Getwd() + } + log.Printf("Recursively finding sync files in workdir %s...", targetDir) + + files, err := GetSyncFilesRecursively(targetDir) if err != nil { - log.Fatalf("Failed to stat stdin: %s%+v%s", ErrorColor, err, DefaultColor) + log.Fatalf("Failed to get sync files recursively: %s%+v%s", ErrorColor, err, DefaultColor) } - if info.Mode()&os.ModeNamedPipe != 0 { - scanner := bufio.NewScanner(os.Stdin) - for scanner.Scan() { - inputs = append(inputs, scanner.Text()) + + dirRegex, _ := regexp.Compile("^(.+?)sync$") + for _, file := range files { + file = NormalizePath(file) + fileDir := dirRegex.FindStringSubmatch(file) + err := os.Chdir(fileDir[1]) + if err != nil { + log.Fatalf("Failed to change directory to %s%s%s: %s%+v%s", SourceColor, fileDir[1], DefaultColor, ErrorColor, err, DefaultColor) } - if err := scanner.Err(); err != nil { - log.Fatalf("Error reading from stdin: %s%+v%s", ErrorColor, err, DefaultColor) + + } + } else { + var instructions []LinkInstruction + if *file != "" { + instructions, _ = ReadFromFile(*file) + } else if len(os.Args) > 1 { + instructions, _ = ReadFromArgs() + } else { + instructions, _ = ReadFromStdin() + } + + if len(instructions) == 0 { + log.Printf("No input provided") + log.Println("Supply input as: ") + log.Printf("Arguments - %s ,,", programName) + log.Printf("File - %s -f ", programName) + log.Printf("Folder (finding sync files in folder recursively) - %s -r ", programName) + log.Printf("stdin - (cat | %s)", programName) + os.Exit(1) + } + for _, instruction := range instructions { + log.Printf("Processing: %s", InstructionToString(instruction)) + err := RunInstruction(instruction) + if err != nil { + log.Printf("Failed parsing instruction %s%s%s due to %s%+v%s", SourceColor, InstructionToString(instruction), DefaultColor, ErrorColor, err, DefaultColor) + continue } } } - if len(inputs) == 0 { - log.Printf("No input provided") - log.Printf("Supply input as either arguments (source,target,force?)") - log.Printf("or via stdin (cat | %s)", os.Args[0]) - os.Exit(1) - } +} - for _, line := range inputs { - instruction, err := parseLine(line) +func ReadFromFile(input string) ([]LinkInstruction, error) { + log.Printf("Reading input from file: %s", input) + var instructions []LinkInstruction + file, err := os.Open(input) + if err != nil { + log.Fatalf("Failed to open file %s%s%s: %s%+v%s", SourceColor, input, DefaultColor, ErrorColor, err, DefaultColor) + } + defer file.Close() + + scanner := bufio.NewScanner(file) + for scanner.Scan() { + line := scanner.Text() + instruction, err := ParseInstruction(line) if err != nil { log.Printf("Error parsing line: %s'%s'%s, error: %s%+v%s", SourceColor, line, DefaultColor, ErrorColor, err, DefaultColor) continue } - log.Printf("Processing: %s", InstructionToString(instruction)) - err = processInstruction(instruction) + instructions = append(instructions, instruction) + } + log.Printf("Read %d instructions from file", len(instructions)) + return instructions, nil +} +func ReadFromArgs() ([]LinkInstruction, error) { + log.Printf("Reading input from args") + var instructions []LinkInstruction + for _, arg := range os.Args[1:] { + instruction, err := ParseInstruction(arg) if err != nil { - log.Printf("Failed parsing instruction %s%s%s due to %s%+v%s", SourceColor, InstructionToString(instruction), DefaultColor, ErrorColor, err, DefaultColor) + log.Printf("Error parsing arg: %s'%s'%s, error: %s%+v%s", SourceColor, arg, DefaultColor, ErrorColor, err, DefaultColor) continue } + instructions = append(instructions, instruction) } + log.Printf("Read %d instructions from args", len(instructions)) + return instructions, nil } -func parseLine(line string) (LinkInstruction, error) { +func ReadFromStdin() ([]LinkInstruction, error) { + log.Printf("Reading input from stdin") + var instructions []LinkInstruction + + info, err := os.Stdin.Stat() + if err != nil { + log.Fatalf("Failed to stat stdin: %s%+v%s", ErrorColor, err, DefaultColor) + } + if info.Mode()&os.ModeNamedPipe != 0 { + scanner := bufio.NewScanner(os.Stdin) + for scanner.Scan() { + line := scanner.Text() + instruction, err := ParseInstruction(line) + if err != nil { + log.Printf("Error parsing line: %s'%s'%s, error: %s%+v%s", SourceColor, line, DefaultColor, ErrorColor, err, DefaultColor) + continue + } + instructions = append(instructions, instruction) + } + if err := scanner.Err(); err != nil { + log.Fatalf("Error reading from stdin: %s%+v%s", ErrorColor, err, DefaultColor) + } + } + + log.Printf("Read %d instructions from stdin", len(instructions)) + return instructions, nil +} + +func ParseInstruction(line string) (LinkInstruction, error) { parts := strings.Split(line, deliminer) instruction := LinkInstruction{} @@ -107,7 +208,7 @@ func parseLine(line string) (LinkInstruction, error) { return instruction, nil } -func processInstruction(instruction LinkInstruction) error { +func RunInstruction(instruction LinkInstruction) error { if !FileExists(instruction.Source) { return fmt.Errorf("instruction source %s%s%s does not exist", SourceColor, instruction.Source, DefaultColor) } diff --git a/sync b/sync index db97489..7a2ce47 100644 --- a/sync +++ b/sync @@ -1 +1 @@ -"Last Epoch","C:\Users\Administrator\AppData\LocalLow\Eleventh Hour Games\Last Epoch" +test,testdir/test3 \ No newline at end of file diff --git a/util.go b/util.go index 4445be1..a878258 100644 --- a/util.go +++ b/util.go @@ -2,8 +2,10 @@ package main import ( "fmt" + "io/fs" "os" "path/filepath" + "strconv" "strings" ) @@ -20,10 +22,7 @@ func IsSymlink(path string) (bool, error) { func FileExists(path string) bool { _, err := os.Lstat(path) - if err != nil { - return false - } - return true + return err == nil } func NormalizePath(input string) string { @@ -62,3 +61,28 @@ func ConvertHome(input string) (string, error) { } return input, nil } + +func InstructionToString(instruction LinkInstruction) string { + return fmt.Sprintf("%s%s%s%s%s%s%s%s%s", SourceColor, instruction.Source, DefaultColor, deliminer, TargetColor, instruction.Target, DefaultColor, deliminer, strconv.FormatBool(instruction.Force)) +} + +func GetSyncFilesRecursively(input string) ([]string, error) { + var files []string + err := filepath.WalkDir(input, func(path string, file fs.DirEntry, err error) error { + if err != nil { + return err + } + + // Effectively only find files named "sync" (with no extension!!) + if !file.IsDir() && strings.HasSuffix(path, "sync") { + files = append(files, path) + } + + return nil + }) + if err != nil { + return nil, err + } + + return files, nil +}