From f21ae2e502d89acb488232ae4b4f327a33639533 Mon Sep 17 00:00:00 2001 From: Nick Krichevsky Date: Thu, 9 Sep 2021 23:19:09 -0400 Subject: [PATCH] Refactor categorization to use a callback instead of aggregating the results This will prepare for taking user input, as we don't want to continue scanning before we can find out if a user wants to back out --- cmd/categorize.go | 69 ++++++++++++++++++++++++++++++----------------- cmd/main.go | 17 +++++++++--- 2 files changed, 58 insertions(+), 28 deletions(-) diff --git a/cmd/categorize.go b/cmd/categorize.go index ae3af4b..dd24d5a 100644 --- a/cmd/categorize.go +++ b/cmd/categorize.go @@ -16,16 +16,23 @@ type fileLocation struct { location tv.Location } +// categorizationResult is the result of an individual categorization; it's effectively what a return value would be +// for a single operation, but it's returned through a callback type categorizationResult struct { - locations []fileLocation + // the location of the categorized file. This should be ignored if the error is non-nil, or "skipped" is true + location fileLocation + err error +} + +type categorizationStats struct { filesScanned int filesSkipped int } // Performs the categorization process for the given source (i.e. where the downloaded files are stored) -// and library (i.e. where they will be sorted into). Errors that halt the program proceeding will be returned. Those -// that do not will be printed to stderr. -func categorizeFolder(sourceFolder, libraryFolder string) (categorizationResult, error) { +// and library (i.e. where they will be sorted into). Errors that halt the program proceeding will be returned. Results +// and non-fatal errors will be returned via resultCallback. If this callback returns false, categorization stops. +func categorizeFolder(sourceFolder, libraryFolder string, resultCallback func(categorizationResult) bool) (categorizationStats, error) { sourceFolderContents, err := getDirEntries(sourceFolder, func(entries []os.FileInfo) { // Sort from newest to oldest sort.Slice(entries, func(i, j int) bool { @@ -37,40 +44,54 @@ func categorizeFolder(sourceFolder, libraryFolder string) (categorizationResult, }) if err != nil { - return categorizationResult{}, fmt.Errorf("failed to read source folder: %w", err) + return categorizationStats{}, fmt.Errorf("failed to read source folder: %w", err) } categorizer, err := categorize.NewCategorizer(libraryFolder) if err != nil { - return categorizationResult{}, fmt.Errorf("failed to make categorizer: %w", err) + return categorizationStats{}, fmt.Errorf("failed to make categorizer: %w", err) } - locations := performCategorization(categorizer, sourceFolderContents) - return categorizationResult{ - locations: locations, - filesScanned: len(locations), - filesSkipped: len(sourceFolderContents) - len(locations), - }, nil + stats := categorizationStats{} + performCategorization(categorizer, sourceFolderContents, func(res categorizationResult) bool { + stats.filesScanned++ + if errors.Is(err, match.ErrNoMatches) { + stats.filesSkipped++ + return true + } else if res.err != nil { + stats.filesSkipped++ + return true + } + + return resultCallback(res) + }) + + return stats, nil } // performCategorization categorizes the given files through the given categorizer, converting them to a -// slice of FileLocations, ordered as they were provided in sourceFolderContents. -// When a file fails to be read, an error will be printed to stderr. -func performCategorization(categorizer categorize.Categorizer, sourceFolderContents []string) []fileLocation { - locations := []fileLocation{} +// slice of FileLocations, ordered as they were provided in sourceFolderContents. Results and non-fatal errors will be +// returned via resultCallback. If this callback returns false, categorization stops. +func performCategorization(categorizer categorize.Categorizer, sourceFolderContents []string, resultCallback func(categorizationResult) bool) { for _, entry := range sourceFolderContents { location, err := categorizer.CategorizeFile(entry) - if errors.Is(err, match.ErrNoMatches) { - continue - } else if err != nil { - fmt.Fprintf(os.Stderr, "Failed to categorize '%s':\n\t%s\n", entry, err) - continue + + var wrappedErr error + if err != nil { + wrappedErr = fmt.Errorf("failed to categorize '%s': %w", entry, err) } - locations = append(locations, fileLocation{entry, location}) - } + shouldContinue := resultCallback(categorizationResult{ + location: fileLocation{entry, location}, + err: wrappedErr, + }) - return locations + if shouldContinue { + continue + } else { + break + } + } } // getDirEntries gets filenames in a directory, ordered by the given sort function. diff --git a/cmd/main.go b/cmd/main.go index 03a388e..b291f67 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -17,16 +17,25 @@ func main() { sourceFolder, libraryFolder := os.Args[1], os.Args[2] - categorizationRes, err := categorizeFolder(sourceFolder, libraryFolder) + locations := []fileLocation{} + categorizationStats, err := categorizeFolder(sourceFolder, libraryFolder, func(res categorizationResult) bool { + if res.err != nil { + fmt.Fprintln(os.Stderr, res.err) + } else { + locations = append(locations, res.location) + } + + return true + }) + if err != nil { panic(err) } - prettyPrintLocations(categorizationRes.locations) - fmt.Fprintf(os.Stderr, "%d items not categorized\n", categorizationRes.filesSkipped) + prettyPrintLocations(locations) + fmt.Fprintf(os.Stderr, "%d items not categorized\n", categorizationStats.filesSkipped) } - func prettyPrintLocations(fileLocations []fileLocation) { for _, fl := range fileLocations { filename, location := fl.filename, fl.location