diff options
| author | Paul Buetow <paul@buetow.org> | 2026-04-03 08:03:05 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-04-03 08:03:05 +0300 |
| commit | 63bd86d8046949e80e5b1122a1fcf717f51915c6 (patch) | |
| tree | 10bc72c97d1ba597bc82fb273987716eabf2e989 /internal/utils.go | |
| parent | 5a1a6b863c3adaa8ec9d087ba130f7676fc9575b (diff) | |
tasks 00d, 006, 007: move test seams to struct fields, centralize Viper config, extract shared card-dir logic
task 00d: remove package-level var test seams from processor and gui packages;
factory functions (newAudioProvider, newOpenAIImageClient, newNanoBananaImageClient)
are now struct fields on Processor and Application, initialized with production
defaults in constructors and replaced in tests without global mutation.
task 006: add viperConfig struct captured once in NewProcessor; no method body
calls viper.GetString/IsSet/GetFloat64 directly any more — all config-file values
are accessed via p.viperCfg fields.
task 007: extract FindCardDirectory and FindOrCreateCardDirectory into
internal/utils.go; both Processor.findCardDirectory and
Application.findCardDirectory now delegate to the shared implementation,
which also handles the legacy _word.txt backward-compat fallback.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat (limited to 'internal/utils.go')
| -rw-r--r-- | internal/utils.go | 59 |
1 files changed, 59 insertions, 0 deletions
diff --git a/internal/utils.go b/internal/utils.go index 47513ae..b83d46a 100644 --- a/internal/utils.go +++ b/internal/utils.go @@ -4,6 +4,8 @@ import ( "crypto/md5" "encoding/hex" "fmt" + "os" + "path/filepath" "strings" "time" ) @@ -23,6 +25,63 @@ func GenerateCardID(bulgarianWord string) string { return fmt.Sprintf("%d_%s", epochMillis, hashStr) } +// FindCardDirectory searches outputDir for a subdirectory whose word.txt +// (or legacy _word.txt) matches the given word. Returns the directory path +// or an empty string if not found. +func FindCardDirectory(outputDir, word string) string { + entries, err := os.ReadDir(outputDir) + if err != nil { + return "" + } + + for _, entry := range entries { + if !entry.IsDir() || strings.HasPrefix(entry.Name(), ".") { + continue + } + + dirPath := filepath.Join(outputDir, entry.Name()) + wordFile := filepath.Join(dirPath, "word.txt") + + if data, err := os.ReadFile(wordFile); err == nil { + if strings.TrimSpace(string(data)) == word { + return dirPath + } + } else { + // Backward-compatible fallback: old format used _word.txt + wordFile = filepath.Join(dirPath, "_word.txt") + if data, err := os.ReadFile(wordFile); err == nil { + if strings.TrimSpace(string(data)) == word { + return dirPath + } + } + } + } + + return "" +} + +// FindOrCreateCardDirectory returns the existing card directory for word inside +// outputDir, or creates a new one with a generated card ID. It also writes +// word.txt so subsequent calls can find the directory. +func FindOrCreateCardDirectory(outputDir, word string) string { + if dir := FindCardDirectory(outputDir, word); dir != "" { + return dir + } + + cardID := GenerateCardID(word) + wordDir := filepath.Join(outputDir, cardID) + if err := os.MkdirAll(wordDir, 0755); err != nil { + fmt.Printf("Warning: failed to create word directory: %v\n", err) + return outputDir + } + + if err := os.WriteFile(filepath.Join(wordDir, "word.txt"), []byte(word), 0644); err != nil { + fmt.Printf("Warning: failed to save word metadata: %v\n", err) + } + + return wordDir +} + // SanitizeFilename creates a safe filename from a string. // Uses strings.Builder to avoid per-rune heap allocations. func SanitizeFilename(s string) string { |
