From 23160bce9a18a70080a85dda6e9c654499aba7f7 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Mon, 6 Apr 2026 10:58:58 +0300 Subject: refactor: remove Viper coupling from processor package (DIP) Resolve all Viper config values once at startup in cmd/totalrecall/main.go via the new newProcessorConfig() helper. The exported processor.Config struct replaces the internal viperConfig type, and NewProcessor now accepts *Config instead of querying Viper internally. The processor package no longer imports Viper at all. Tests are updated to pass Config values directly, eliminating the viper.Set/Reset boilerplate that coupled tests to the global singleton. Co-Authored-By: Claude Sonnet 4.6 --- cmd/totalrecall/main.go | 49 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 2 deletions(-) (limited to 'cmd') diff --git a/cmd/totalrecall/main.go b/cmd/totalrecall/main.go index a3523ce..9e64897 100644 --- a/cmd/totalrecall/main.go +++ b/cmd/totalrecall/main.go @@ -4,8 +4,10 @@ import ( "fmt" "os" "path/filepath" + "strings" "github.com/spf13/cobra" + "github.com/spf13/viper" "codeberg.org/snonux/totalrecall/internal/archive" "codeberg.org/snonux/totalrecall/internal/cli" @@ -16,6 +18,48 @@ import ( "codeberg.org/snonux/totalrecall/internal/story" ) +// newProcessorConfig reads all Viper-sourced settings in a single pass and +// returns a fully-resolved processor.Config. Centralising all Viper access +// here means the processor package is free of any Viper dependency, which +// improves testability and removes tight coupling to the global config singleton. +func newProcessorConfig() *processor.Config { + return &processor.Config{ + // Translation & phonetic + TranslationProvider: strings.TrimSpace(viper.GetString("translation.provider")), + PhoneticProvider: strings.TrimSpace(viper.GetString("phonetic.provider")), + TranslationGeminiModel: viper.GetString("translation.gemini_model"), + + // Audio + AudioProvider: strings.ToLower(strings.TrimSpace(viper.GetString("audio.provider"))), + AudioFormat: strings.ToLower(strings.TrimSpace(viper.GetString("audio.format"))), + AudioFormatSet: viper.IsSet("audio.format"), + GeminiTTSModel: strings.TrimSpace(viper.GetString("audio.gemini_tts_model")), + GeminiVoice: strings.TrimSpace(viper.GetString("audio.gemini_voice")), + OpenAIVoice: strings.TrimSpace(viper.GetString("audio.openai_voice")), + OpenAIModel: viper.GetString("audio.openai_model"), + OpenAIModelSet: viper.IsSet("audio.openai_model"), + OpenAISpeed: viper.GetFloat64("audio.openai_speed"), + OpenAISpeedSet: viper.IsSet("audio.openai_speed"), + OpenAIInstruction: viper.GetString("audio.openai_instruction"), + OpenAIInstructionSet: viper.IsSet("audio.openai_instruction"), + + // Image + ImageProvider: strings.ToLower(strings.TrimSpace(viper.GetString("image.provider"))), + ImageOpenAIModel: viper.GetString("image.openai_model"), + ImageOpenAIModelSet: viper.IsSet("image.openai_model"), + ImageOpenAISize: viper.GetString("image.openai_size"), + ImageOpenAISizeSet: viper.IsSet("image.openai_size"), + ImageOpenAIQuality: viper.GetString("image.openai_quality"), + ImageOpenAIQualitySet: viper.IsSet("image.openai_quality"), + ImageOpenAIStyle: viper.GetString("image.openai_style"), + ImageOpenAIStyleSet: viper.IsSet("image.openai_style"), + ImageNanoBananaModel: strings.TrimSpace(viper.GetString("image.nanobanana_model")), + ImageNanoBananaModelSet: viper.IsSet("image.nanobanana_model"), + ImageNanoBananaTextModel: strings.TrimSpace(viper.GetString("image.nanobanana_text_model")), + ImageNanoBananaTextModelSet: viper.IsSet("image.nanobanana_text_model"), + } +} + func main() { // Create flags instance flags := cli.NewFlags() @@ -83,8 +127,9 @@ func runCommand(cmd *cobra.Command, args []string, flags *cli.Flags) error { fmt.Printf("Note: Using image size 1024x1024 for DALL-E 3 (use --openai-image-size to override)\n") } - // Create processor - proc := processor.NewProcessor(flags) + // Resolve all Viper config values once here so the processor never touches + // the global Viper singleton directly (Dependency Inversion Principle). + proc := processor.NewProcessor(flags, newProcessorConfig()) // Handle batch processing if flags.BatchFile != "" { -- cgit v1.2.3