package engine import ( "fmt" "os" "path/filepath" "github.com/spf13/viper" ) type Config struct { DefaultFilter string `mapstructure:"default_filter"` DefaultSort string `mapstructure:"default_sort"` ColorOutput bool `mapstructure:"color_output"` } var globalConfig *Config // GetConfigDir returns the configuration directory path func GetConfigDir() (string, error) { home, err := os.UserHomeDir() if err != nil { return "", fmt.Errorf("failed to get home directory: %w", err) } return filepath.Join(home, ".config", "jade"), nil } // GetDBPath returns the path to the SQLite database func GetDBPath() (string, error) { configDir, err := GetConfigDir() if err != nil { return "", err } return filepath.Join(configDir, "opal.db"), nil } // GetConfigPath returns the path to the config file func GetConfigPath() (string, error) { configDir, err := GetConfigDir() if err != nil { return "", err } return filepath.Join(configDir, "opal.yml"), nil } // LoadConfig loads the configuration from file or creates default func LoadConfig() (*Config, error) { if globalConfig != nil { return globalConfig, nil } configDir, err := GetConfigDir() if err != nil { return nil, err } // Ensure config directory exists if err := os.MkdirAll(configDir, 0755); err != nil { return nil, fmt.Errorf("failed to create config directory: %w", err) } configPath, err := GetConfigPath() if err != nil { return nil, err } v := viper.New() v.SetConfigFile(configPath) v.SetConfigType("yaml") // Set defaults v.SetDefault("default_filter", "status:pending") v.SetDefault("default_sort", "due,priority") v.SetDefault("color_output", true) // Try to read existing config err = v.ReadInConfig() if err != nil { // Config doesn't exist, create it with defaults // Write config with defaults (ignoring the error type check for now) if err := v.WriteConfigAs(configPath); err != nil { return nil, fmt.Errorf("failed to create config file: %w", err) } // Try reading again if err := v.ReadInConfig(); err != nil { return nil, fmt.Errorf("failed to read newly created config: %w", err) } } cfg := &Config{} if err := v.Unmarshal(cfg); err != nil { return nil, fmt.Errorf("failed to unmarshal config: %w", err) } globalConfig = cfg return cfg, nil } // SaveConfig saves the configuration to file func SaveConfig(cfg *Config) error { configPath, err := GetConfigPath() if err != nil { return err } v := viper.New() v.SetConfigFile(configPath) v.SetConfigType("yaml") v.Set("default_filter", cfg.DefaultFilter) v.Set("default_sort", cfg.DefaultSort) v.Set("color_output", cfg.ColorOutput) return v.WriteConfig() } // GetConfig returns the loaded config or loads it if not already loaded func GetConfig() (*Config, error) { if globalConfig != nil { return globalConfig, nil } return LoadConfig() }