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 outmaster
parent
08089c4bd9
commit
f21ae2e502
|
@ -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.
|
||||
|
|
17
cmd/main.go
17
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
|
||||
|
|
Loading…
Reference in New Issue