From b3f9f44eea88ea8788c69e991ab8093bceb253f0 Mon Sep 17 00:00:00 2001 From: Pruthvi Kumar Date: Wed, 30 Oct 2024 14:38:21 +1100 Subject: [PATCH] feat: adds validators and updates readme --- config/config.go | 63 +++++++++++++++++++++++---------------- config/validation.go | 71 ++++++++++++++++++++++++++++++++++++++++++++ readme.md | 1 + 3 files changed, 110 insertions(+), 25 deletions(-) create mode 100644 config/validation.go diff --git a/config/config.go b/config/config.go index 6cbeabc..a3e1888 100644 --- a/config/config.go +++ b/config/config.go @@ -2,7 +2,6 @@ package config import ( "fmt" - "os" "strconv" "time" ) @@ -18,33 +17,31 @@ type Config struct { } func LoadConfig() (*Config, error) { - pomoDuration, _ := time.ParseDuration("25m") - shortBreak, _ := time.ParseDuration("5m") - longBreak, _ := time.ParseDuration("15m") - cyclesBeforeLongBreak := 4 + if err := validateEnvVars(); err != nil { + return nil, err + } - if val, exists := os.LookupEnv("POMODORO_DURATION"); exists { - if d, err := time.ParseDuration(val); err == nil { - pomoDuration = d - } + pomoDuration, err := parseDurationEnv("POMODORO_DURATION", "25m") + if err != nil { + return nil, fmt.Errorf("invalid POMODORO_DURATION: %w", err) } - if val, exists := os.LookupEnv("SHORT_BREAK_DURATION"); exists { - if d, err := time.ParseDuration(val); err == nil { - shortBreak = d - } + + shortBreak, err := parseDurationEnv("SHORT_BREAK_DURATION", "5m") + if err != nil { + return nil, fmt.Errorf("invalid SHORT_BREAK_DURATION: %w", err) } - if val, exists := os.LookupEnv("LONG_BREAK_DURATION"); exists { - if d, err := time.ParseDuration(val); err == nil { - longBreak = d - } + + longBreak, err := parseDurationEnv("LONG_BREAK_DURATION", "15m") + if err != nil { + return nil, fmt.Errorf("invalid LONG_BREAK_DURATION: %w", err) } - if val, exists := os.LookupEnv("CYCLES_BEFORE_LONGBREAK"); exists { - if n, err := strconv.Atoi(val); err == nil { - cyclesBeforeLongBreak = n - } + + cycles, err := parseIntEnv("CYCLES_BEFORE_LONGBREAK", 4) + if err != nil { + return nil, fmt.Errorf("invalid CYCLES_BEFORE_LONGBREAK: %w", err) } - contextDir := os.Getenv("TOMATICK_CONTEXT_DIR") + contextDir := getEnvVar("TOMATICK_CONTEXT_DIR") if contextDir == "" { contextDir = getDefaultContextDir() } @@ -57,9 +54,25 @@ func LoadConfig() (*Config, error) { TomatickMementoDuration: pomoDuration, ShortBreakDuration: shortBreak, LongBreakDuration: longBreak, - CyclesBeforeLongBreak: cyclesBeforeLongBreak, - MEMAIAPIToken: os.Getenv("MEM_AI_API_TOKEN"), + CyclesBeforeLongBreak: cycles, + MEMAIAPIToken: getEnvVar("MEM_AI_API_TOKEN"), ContextDir: contextDir, - PerplexityAPIToken: os.Getenv("PERPLEXITY_API_TOKEN"), + PerplexityAPIToken: getEnvVar("PERPLEXITY_API_TOKEN"), }, nil } + +func parseDurationEnv(key, defaultValue string) (time.Duration, error) { + value := getEnvVar(key) + if value == "" { + value = defaultValue + } + return time.ParseDuration(value) +} + +func parseIntEnv(key string, defaultValue int) (int, error) { + value := getEnvVar(key) + if value == "" { + return defaultValue, nil + } + return strconv.Atoi(value) +} diff --git a/config/validation.go b/config/validation.go new file mode 100644 index 0000000..0001415 --- /dev/null +++ b/config/validation.go @@ -0,0 +1,71 @@ +package config + +import ( + "fmt" + "os" + "strings" +) + +type EnvVar struct { + Name string + Description string + Required bool +} + +var requiredEnvVars = []EnvVar{ + { + Name: "MEM_AI_API_TOKEN", + Description: "API token for Mem.ai integration", + Required: true, + }, + { + Name: "PERPLEXITY_API_TOKEN", + Description: "API token for Perplexity AI integration", + Required: true, + }, + { + Name: "TOMATICK_CONTEXT_DIR", + Description: "Directory for storing context files", + Required: false, // We have a default value + }, + { + Name: "POMODORO_DURATION", + Description: "Duration of each Pomodoro cycle (e.g., 25m)", + Required: false, // We have a default value + }, + { + Name: "SHORT_BREAK_DURATION", + Description: "Duration of short breaks (e.g., 5m)", + Required: false, // We have a default value + }, + { + Name: "LONG_BREAK_DURATION", + Description: "Duration of long breaks (e.g., 15m)", + Required: false, // We have a default value + }, + { + Name: "CYCLES_BEFORE_LONGBREAK", + Description: "Number of cycles before a long break", + Required: false, // We have a default value + }, +} + +func validateEnvVars() error { + var missingVars []string + + for _, env := range requiredEnvVars { + if env.Required && strings.TrimSpace(getEnvVar(env.Name)) == "" { + missingVars = append(missingVars, fmt.Sprintf("- %s: %s", env.Name, env.Description)) + } + } + + if len(missingVars) > 0 { + return fmt.Errorf("missing required environment variables:\n%s\n\nPlease set these environment variables and try again", strings.Join(missingVars, "\n")) + } + + return nil +} + +func getEnvVar(name string) string { + return strings.TrimSpace(os.Getenv(name)) +} diff --git a/readme.md b/readme.md index 7429006..8e1a1cd 100644 --- a/readme.md +++ b/readme.md @@ -2,6 +2,7 @@ Yet another pomodoro timer (yes, really) – but this one's your accountability buddy with just enough cognitive wizardry to keep perfectionists from disappearing down the rabbit hole of endless optimizations. Because sometimes "good enough" today beats "perfect" never. :brain: :rocket: +(& Tomatick ?? What the heck is that? Well, it's a combination of "tomato" and "tick" (as in a timer) 🤷 Dad joke? Maybe.) ## Core Features - **Interactive CLI Interface**: A terminal interface that doesn't suck, with colors and all that jazz