package utils import ( "fmt" "os" "path/filepath" "strconv" "strings" "github.com/spf13/viper" ) var ConfigVar *Config var ( defaultConfigLocations = []string{ "", os.Getenv("COCOMMIT_CONFIG"), os.Getenv("HOME") + "/.config/cocommit", os.Getenv("HOME") + "/cocommit", "/etc/cocommit", "/usr/local/etc/cocommit", } configName = "config" configType = "toml" ) type Config struct { Settings struct { AuthorFile string `mapstructure:"author_file"` StartingScope string `mapstructure:"starting_scope"` Editor string `mapstructure:"editor"` } `mapstructure:"settings"` } func (c *Config) String() string { return fmt.Sprintf("Author File: %s\nStarting Scope: %s\nEditor: %s", c.Settings.AuthorFile, c.Settings.StartingScope, c.Settings.Editor) } func init() { configDir, err := os.UserConfigDir() if err == nil { defaultConfigLocations[0] = filepath.Join(configDir, "cocommit") } } var v *viper.Viper func LoadConfig() (*Config, error) { // TODO: create if and give param as default config location v = viper.New() v.SetConfigName(configName) v.SetConfigType(configType) // Set default values v.SetDefault("settings.author_file", defaultConfigLocations[0]+"/authors.json") v.SetDefault("settings.starting_scope", "git") v.SetDefault("settings.editor", "built-in") // Add search paths for _, path := range defaultConfigLocations { if path != "" { v.AddConfigPath(path) } } // Try to read config if err := v.ReadInConfig(); err != nil { if _, ok := err.(viper.ConfigFileNotFoundError); ok { return nil, nil } // if _, ok := err.(viper.ConfigFileNotFoundError); ok { // if err := handleMissingConfig(v); err != nil { // return nil, err // } // } else { // return nil, fmt.Errorf("config error: %w", err) // } } var cfg Config if err := v.Unmarshal(&cfg); err != nil { return nil, fmt.Errorf("config unmarshal error: %w", err) } if cfg.Settings.AuthorFile == "" { cfg.Settings.AuthorFile = defaultConfigLocations[0] + "/authors.json" } return &cfg, nil } func (c *Config) SetGlobalConfig() { if ConfigVar == nil { ConfigVar = c // This doesnt really do much right now but might be useful later viper.WatchConfig() } } func HandleMissingConfig() error { fmt.Println("Config file not found. Would you like to create one? (y/n)") var response string if _, err := fmt.Scanln(&response); err != nil { return fmt.Errorf("error reading response: %w", err) } yesResponses := map[string]bool{"y": true, "Y": true, "yes": true, "Yes": true, "YES": true} if !yesResponses[strings.TrimSpace(response)] { return fmt.Errorf("config file not found") } if v == nil { v = viper.New() v.SetConfigName(configName) v.SetConfigType(configType) } return CreateConfig() } func CheckConfig() bool { if v == nil { return false } else if v.ConfigFileUsed() == "" { return false } else { return true } } func GetConfigFilePath() string { if v == nil || v.ConfigFileUsed() == "" { return "" } return v.ConfigFileUsed() } func RemoveConfig() error { if v == nil || v.ConfigFileUsed() == "" { return fmt.Errorf("no config file to remove") } configPath := v.ConfigFileUsed() if err := os.Remove(configPath); err != nil { return fmt.Errorf("failed to remove config file: %w", err) } fmt.Printf("Config file removed: %s\n", configPath) return nil } func CreateConfig() error { fmt.Println("Where would you like to create the config file?") for i, path := range defaultConfigLocations { fmt.Printf("%d. %s\n", i, path) } fmt.Println("Please enter the number of the location or a custom path:") var response string if _, err := fmt.Scanln(&response); err != nil { return fmt.Errorf("error reading response: %w", err) } var configPath string if num, err := strconv.Atoi(response); err == nil && num >= 0 && num < len(defaultConfigLocations) { configPath = defaultConfigLocations[num] } else { configPath = response } // Ensure directory exists if err := os.MkdirAll(configPath, 0755); err != nil { return fmt.Errorf("failed to create config directory: %w", err) } // Set the config file path fullPath := filepath.Join(configPath, fmt.Sprintf("%s.%s", configName, configType)) v.SetConfigFile(fullPath) // Write default config if err := v.SafeWriteConfig(); err != nil { return fmt.Errorf("failed to write config: %w", err) } fmt.Printf("Config file created at: %s\n", fullPath) return nil } func (c *Config) Save() error { v := viper.New() // Set all configuration values from the struct v.Set("settings.author_file", c.Settings.AuthorFile) v.Set("settings.starting_scope", c.Settings.StartingScope) v.Set("settings.editor", c.Settings.Editor) v.SetConfigName(configName) v.SetConfigType(configType) // Try to determine the original config file location if viper.ConfigFileUsed() != "" { v.SetConfigFile(viper.ConfigFileUsed()) } else { // Fall back to first default location if no existing config if len(defaultConfigLocations) > 0 && defaultConfigLocations[0] != "" { v.SetConfigFile(filepath.Join(defaultConfigLocations[0], fmt.Sprintf("%s.%s", configName, configType))) } else { return fmt.Errorf("no config file location available") } } // Ensure the directory exists configDir := filepath.Dir(v.ConfigFileUsed()) if err := os.MkdirAll(configDir, 0755); err != nil { return fmt.Errorf("failed to create config directory: %w", err) } // Write the config file if err := v.WriteConfig(); err != nil { return fmt.Errorf("failed to save config: %w", err) } return nil }