Skip to content

Commit

Permalink
add --target-dir option
Browse files Browse the repository at this point in the history
  • Loading branch information
ayoisaiah committed Oct 17, 2024
1 parent 91303c0 commit ed894d1
Show file tree
Hide file tree
Showing 18 changed files with 161 additions and 36 deletions.
1 change: 1 addition & 0 deletions app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,7 @@ offers several options for fine-grained control over the renaming process.`,
flagSortr,
flagSortPerDir,
flagStringMode,
flagTargetDir,
flagVerbose,
},
UseShortOptionHandling: true,
Expand Down
5 changes: 4 additions & 1 deletion app/app_test/testdata/help_stdout.golden
Original file line number Diff line number Diff line change
Expand Up @@ -179,9 +179,12 @@ Project repository: https://github.com/ayoisaiah/f2
Treats the search pattern (specified by -f/--find) as a literal string
instead of a regular expression.

-t, --target-dir
Specify a target directory to move renamed files and reorganize your
filesystem.

-V, --verbose
Enables verbose output during the renaming operation.

ENVIRONMENTAL VARIABLES
F2_DEFAULT_OPTS
Override the default options according to your preferences. For example,
Expand Down
8 changes: 8 additions & 0 deletions app/flag.go
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,14 @@ var (
instead of a regular expression.`,
}

flagTargetDir = &cli.StringFlag{
Name: "target-dir",
Aliases: []string{"t"},
Usage: `
Specify a target directory to move renamed files and reorganize your
filesystem.`,
}

flagVerbose = &cli.BoolFlag{
Name: "verbose",
Aliases: []string{"V"},
Expand Down
9 changes: 9 additions & 0 deletions app/help.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,13 @@ func helpText(app *cli.App) string {
flagStringMode.GetUsage(),
)

flagTargetDirHelp := fmt.Sprintf(
`%s, %s %s`,
pterm.Green("-", flagTargetDir.Aliases[0]),
pterm.Green("--", flagTargetDir.Name),
flagTargetDir.GetUsage(),
)

flagVerboseHelp := fmt.Sprintf(
`%s, %s %s`,
pterm.Green("-", flagVerbose.Aliases[0]),
Expand Down Expand Up @@ -286,6 +293,7 @@ Project repository: https://github.com/ayoisaiah/f2
%s
%s
%s
%s
Expand Down Expand Up @@ -331,6 +339,7 @@ Project repository: https://github.com/ayoisaiah/f2
flagSortrHelp,
flagSortPerDirHelp,
flagStringModeHelp,
flagTargetDirHelp,
flagVerboseHelp,
pterm.Bold.Sprintf("ENVIRONMENTAL VARIABLES"),
envHelp(),
Expand Down
2 changes: 1 addition & 1 deletion f2.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ func execute(_ *cli.Context) error {
return nil
}

err = rename.Rename(changes)
err = rename.Rename(appConfig, changes)

rename.PostRename(appConfig, changes, err)

Expand Down
12 changes: 11 additions & 1 deletion find/csv.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ func handleCSV(conf *config.Config) (file.Changes, error) {

match := &file.Change{
BaseDir: sourceDir,
TargetDir: sourceDir,
IsDir: fileInfo.IsDir(),
Source: fileName,
Target: fileName,
Expand All @@ -100,11 +101,20 @@ func handleCSV(conf *config.Config) (file.Changes, error) {
Position: i,
}

changes = append(changes, match)
if conf.TargetDir != "" {
match.TargetDir = conf.TargetDir
}

if len(record) > 1 {
match.Target = strings.TrimSpace(record[1])

if filepath.IsAbs(match.Target) {
match.TargetDir = ""
continue
}
}

changes = append(changes, match)
}

return changes, nil
Expand Down
17 changes: 13 additions & 4 deletions find/find.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,18 +99,27 @@ func isMaxDepth(rootPath, currentPath string, maxDepth int) bool {
return depthCount > maxDepth
}

func createFileChange(dirPath string, fileInfo fs.FileInfo) *file.Change {
func createFileChange(
conf *config.Config,
dirPath string,
fileInfo fs.FileInfo,
) *file.Change {
baseDir := filepath.Dir(dirPath)
fileName := fileInfo.Name()

match := &file.Change{
BaseDir: baseDir,
TargetDir: baseDir,
IsDir: fileInfo.IsDir(),
Source: fileName,
OriginalName: fileName,
SourcePath: filepath.Join(baseDir, fileName),
}

if conf.TargetDir != "" {
match.TargetDir = conf.TargetDir
}

return match
}

Expand All @@ -135,7 +144,7 @@ func searchPaths(conf *config.Config) (file.Changes, error) {
}

if conf.Search.Regex.MatchString(fileInfo.Name()) {
match := createFileChange(rootPath, fileInfo)
match := createFileChange(conf, rootPath, fileInfo)

if !shouldFilter(conf, match) {
matches = append(matches, match)
Expand Down Expand Up @@ -203,7 +212,7 @@ func searchPaths(conf *config.Config) (file.Changes, error) {
return infoErr
}

match := createFileChange(currentPath, fileInfo)
match := createFileChange(conf, currentPath, fileInfo)

if !shouldFilter(conf, match) {
matches = append(matches, match)
Expand Down Expand Up @@ -250,7 +259,7 @@ func loadFromBackup(conf *config.Config) (file.Changes, error) {
for i := range changes {
ch := changes[i]
ch.Source, ch.Target = ch.Target, ch.Source
ch.SourcePath = filepath.Join(ch.BaseDir, ch.Source)
ch.SourcePath = filepath.Join(ch.TargetDir, ch.Source)
ch.TargetPath = filepath.Join(ch.BaseDir, ch.Target)
ch.Status = status.OK

Expand Down
13 changes: 13 additions & 0 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ type Config struct {
FixConflictsPattern string `json:"fix_conflicts_pattern"`
CSVFilename string `json:"csv_filename"`
BackupFilename string `json:"backup_filename"`
TargetDir string `json:"target_dir"`
ExiftoolOpts ExiftoolOpts `json:"exiftool_opts"`
PairOrder []string `json:"pair_order"`
FindSlice []string `json:"find_slice"`
Expand Down Expand Up @@ -158,6 +159,18 @@ func (c *Config) setOptions(ctx *cli.Context) error {
c.Revert = ctx.Bool("undo")
c.Debug = ctx.Bool("debug")
c.FilesAndDirPaths = ctx.Args().Slice()
c.TargetDir = ctx.String("target-dir")

if c.TargetDir != "" {
info, err := os.Stat(c.TargetDir)
if err == nil && !info.IsDir() {
return errInvalidTargetDir.Fmt(c.TargetDir)
}

if err != nil && os.IsExist(err) {
return err
}
}

if c.CSVFilename != "" {
absPath, err := filepath.Abs(filepath.Dir(c.CSVFilename))
Expand Down
4 changes: 4 additions & 0 deletions internal/config/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,8 @@ var (
errInvalidSort = &apperr.Error{
Message: "the provided sort '%s' is invalid",
}

errInvalidTargetDir = &apperr.Error{
Message: "target path '%s' exists but is not a directory",
}
)
12 changes: 7 additions & 5 deletions internal/file/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,18 @@ import (
// Change represents a single renaming change.
type Change struct {
Error error `json:"error,omitempty"`
// The original filename which can be different from Source in
// The original filename can be different from the `Source` in
// a multi-step renaming operation
OriginalName string `json:"-"`
Status status.Status `json:"status"`
BaseDir string `json:"base_dir"`
Source string `json:"source"`
Target string `json:"target"`
// TargetDir is the same as BaseDir unless `--target-dir` is provided
TargetDir string `json:"target_dir"`
Source string `json:"source"`
Target string `json:"target"`
// SourcePath is BaseDir + Source
SourcePath string `json:"-"`
// TargetPath is BaseDir + Target
// TargetPath is TargetDir + Target
TargetPath string `json:"-"`
CSVRow []string `json:"-"`
Position int `json:"-"`
Expand All @@ -36,7 +38,7 @@ type Change struct {
// AutoFixTarget sets the new target name.
func (c *Change) AutoFixTarget(newTarget string) {
c.Target = newTarget
c.TargetPath = filepath.Join(c.BaseDir, c.Target)
c.TargetPath = filepath.Join(c.TargetDir, c.Target)

// Ensure empty targets is reported as empty instead of as a dot
if c.TargetPath == "." {
Expand Down
16 changes: 14 additions & 2 deletions internal/testutil/testutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,14 +182,18 @@ func UpdateFileChanges(files file.Changes) {
for i := range files {
ch := files[i]

if ch.TargetDir == "" {
ch.TargetDir = ch.BaseDir
}

files[i].OriginalName = ch.Source
files[i].Position = i
files[i].SourcePath = filepath.Join(
ch.BaseDir,
ch.Source,
)
files[i].TargetPath = filepath.Join(
ch.BaseDir,
ch.TargetDir,
ch.Target,
)
}
Expand Down Expand Up @@ -219,6 +223,10 @@ func ProcessTestCaseChanges(t *testing.T, cases []TestCase) {
for j := range tc.Changes {
ch := tc.Changes[j]

if ch.TargetDir == "" {
ch.TargetDir = ch.BaseDir
}

if ch.Status == "" {
cases[i].Changes[j].Status = status.OK
}
Expand All @@ -234,7 +242,7 @@ func ProcessTestCaseChanges(t *testing.T, cases []TestCase) {

if cases[i].Changes[j].TargetPath == "" {
cases[i].Changes[j].TargetPath = filepath.Join(
ch.BaseDir,
ch.TargetDir,
ch.Target,
)
}
Expand All @@ -250,6 +258,10 @@ func GetConfig(t *testing.T, tc *TestCase, testDir string) *config.Config {
t.Setenv(k, v)
}

if len(tc.Args) == 0 {
tc.Args = []string{"-f", "", "-r", ""}
}

// add fake binary name as first argument
args := append([]string{"f2_test"}, tc.Args...)

Expand Down
12 changes: 10 additions & 2 deletions rename/rename.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ func commit(fileChanges file.Changes) []int {
isCaseChangeOnly = true
timeStr := fmt.Sprintf("%d", time.Now().UnixNano())
targetPath = filepath.Join(
change.BaseDir,
change.TargetDir,
"__"+timeStr+"__"+change.Target+"__"+timeStr+"__", // step 1
)
}
Expand All @@ -69,7 +69,7 @@ func commit(fileChanges file.Changes) []int {
dir := filepath.Dir(change.Target)

err := os.MkdirAll(
filepath.Join(change.BaseDir, dir),
filepath.Join(change.TargetDir, dir),
osutil.DirPermission,
)
if err != nil {
Expand Down Expand Up @@ -99,8 +99,16 @@ func commit(fileChanges file.Changes) []int {
// Rename renames files according to the provided changes and configuration
// handling conflicts and backups.
func Rename(
conf *config.Config,
fileChanges file.Changes,
) error {
if conf.TargetDir != "" {
err := os.MkdirAll(conf.TargetDir, osutil.DirPermission)
if err != nil {
return err
}
}

renameErrs := commit(fileChanges)
if len(renameErrs) > 0 {
return errRenameFailed.WithCtx(renameErrs)
Expand Down
Loading

0 comments on commit ed894d1

Please sign in to comment.