-
Notifications
You must be signed in to change notification settings - Fork 0
/
file-handling.go
101 lines (87 loc) · 2.94 KB
/
file-handling.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
// Copyright (c) 2022-2023 David Vogel
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
package main
import (
"bufio"
"fmt"
"log"
"os"
"strconv"
"strings"
)
// createEntriesFile creates a new file with all the fileEntries and returns its file path.
func createEntriesFile(rootDir string, fileEntries []fileEntry, numbering bool) (filePath string, err error) {
// Create temporary file with paths.
file, err := os.CreateTemp(".", "*.batch-rename")
if err != nil {
log.Fatal(err)
}
if numbering {
decimalWidth := getDecimalWidth(uint(len(fileEntries)))
for i, fileEntry := range fileEntries {
fmt.Fprintf(file, "%0"+fmt.Sprint(decimalWidth)+"d\t%s\n", i+1, fileEntry.originalPath)
}
} else {
for _, fileEntry := range fileEntries {
fmt.Fprintf(file, "%s\n", fileEntry.originalPath)
}
}
if err := file.Close(); err != nil {
return "", err
}
return file.Name(), nil
}
// readEntriesFile reads the (edited) temporary file back and modifies the fileEntries with the new file paths.
func readEntriesFile(filePath string, fileEntries []fileEntry, numbering bool) error {
file, err := os.Open(filePath)
if err != nil {
return err
}
defer file.Close()
scanner := bufio.NewScanner(file)
i := 0
for scanner.Scan() {
if numbering {
substrings := strings.SplitN(scanner.Text(), "\t", 2)
entry, err := strconv.ParseUint(substrings[0], 10, 64)
if err != nil {
return fmt.Errorf("failed to parse element number: %w", err)
}
if entry <= 0 || entry > uint64(len(fileEntries)) {
return fmt.Errorf("element number outside of valid range: Got %d, but there are only %d entries", entry, len(fileEntries))
}
fileEntries[entry-1].newPath = substrings[1]
} else {
if i >= len(fileEntries) {
return fmt.Errorf("there are more lines than files: Got %d, but expected %d", i+1, len(fileEntries))
}
fileEntries[i].newPath = scanner.Text()
i++
}
}
// In case we don't do numbering, we should make sure that the number of lines didn't change.
if !numbering && i != len(fileEntries) {
return fmt.Errorf("the number of lines don't match the number of files: Got %d, but expected %d", i, len(fileEntries))
}
if err := scanner.Err(); err != nil {
return fmt.Errorf("failed to scan file: %w", err)
}
return nil
}
// moveFiles renames the files according to the list of file entries.
func moveFiles(fileEntries []fileEntry) error {
for _, fileEntry := range fileEntries {
if fileEntry.originalPath != fileEntry.newPath {
if err := os.Rename(fileEntry.originalPath, fileEntry.newPath); err != nil {
return err
}
log.Printf("Renamed %q to %q", fileEntry.originalPath, fileEntry.newPath)
// This is important, because if there is an error later on, we know which files were already moved.
// So we can query the user to correct the list, and just call moveFiles again, without files getting messed up.
fileEntry.originalPath = fileEntry.newPath
}
}
return nil
}