package tools
import (
"fmt"
"os"
"path/filepath"
"regexp"
"strings"
)
type PrependInput struct {
Folder string
Prefix string
Recursive bool
DryRun bool
}
type PrependResult struct {
Old string `json:"old"`
New string `json:"new"`
}
type PrependSkipped struct {
File string `json:"file"`
Reason string `json:"reason"`
}
type PrependError struct {
File string `json:"file"`
Error string `json:"error"`
}
type PrependOutput struct {
Folder string `json:"folder"`
Prefix string `json:"prefix"`
Recursive bool `json:"recursive"`
DryRun bool `json:"dry_run"`
Renamed []PrependResult `json:"renamed"`
Skipped []PrependSkipped `json:"skipped"`
Errors []PrependError `json:"errors"`
}
var datestringRegex = regexp.MustCompile(`^\d{8}_\d{6}\.`)
func Prepend(input PrependInput) (*PrependOutput, error) {
output := &PrependOutput{
Folder: input.Folder,
Prefix: input.Prefix,
Recursive: input.Recursive,
DryRun: input.DryRun,
Renamed: []PrependResult{},
Skipped: []PrependSkipped{},
Errors: []PrependError{},
}
folders := []string{input.Folder}
if input.Recursive {
entries, err := os.ReadDir(input.Folder)
if err != nil {
return nil, fmt.Errorf("failed to read folder: %w", err)
}
for _, entry := range entries {
if entry.IsDir() {
folders = append(folders, filepath.Join(input.Folder, entry.Name()))
}
}
}
for _, folder := range folders {
entries, err := os.ReadDir(folder)
if err != nil {
return nil, fmt.Errorf("failed to read folder %s: %w", folder, err)
}
for _, entry := range entries {
if entry.IsDir() {
continue
}
filename := entry.Name()
oldPath := filepath.Join(folder, filename)
shouldRename, skipReason := shouldPrependFile(filename, input.Prefix)
if !shouldRename {
if skipReason != "" {
output.Skipped = append(output.Skipped, PrependSkipped{
File: oldPath,
Reason: skipReason,
})
}
continue
}
newFilename := input.Prefix + "_" + filename
newPath := filepath.Join(folder, newFilename)
if input.DryRun {
output.Renamed = append(output.Renamed, PrependResult{
Old: oldPath,
New: newPath,
})
continue
}
if err := os.Rename(oldPath, newPath); err != nil {
output.Errors = append(output.Errors, PrependError{
File: oldPath,
Error: err.Error(),
})
continue
}
output.Renamed = append(output.Renamed, PrependResult{
Old: oldPath,
New: newPath,
})
}
}
return output, nil
}
func shouldPrependFile(filename, prefix string) (bool, string) {
lowerName := strings.ToLower(filename)
if strings.HasPrefix(filename, prefix+"_") {
if filename == prefix+"_log.txt" || isWavOrData(lowerName) {
return false, "already prefixed"
}
return false, ""
}
if filename == "log.txt" {
return true, ""
}
if !isWavOrData(lowerName) {
return false, "" }
if !datestringRegex.MatchString(filename) {
return false, "no datestring prefix"
}
return true, ""
}
func isWavOrData(lowerName string) bool {
return strings.HasSuffix(lowerName, ".wav") || strings.HasSuffix(lowerName, ".wav.data")
}