From 3e1ba8d965bc8b50cb60eba123a193da646b5009 Mon Sep 17 00:00:00 2001 From: Slug-Boi Date: Sun, 20 Apr 2025 22:07:12 +0200 Subject: [PATCH] feat: add config utils functions to handle config fetching and updating --- src/cmd/utils/config.go | 176 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 176 insertions(+) create mode 100644 src/cmd/utils/config.go diff --git a/src/cmd/utils/config.go b/src/cmd/utils/config.go new file mode 100644 index 0000000..8e3b4d3 --- /dev/null +++ b/src/cmd/utils/config.go @@ -0,0 +1,176 @@ +package utils + +import ( + "fmt" + "os" + "path/filepath" + "strconv" + "strings" + + "github.com/spf13/viper" +) + +var ConfigVar *Config + +var ( + defaultConfigLocations = []string{ + "", + 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 init() { + configDir, err := os.UserConfigDir() + if err == nil { + defaultConfigLocations[0] = filepath.Join(configDir, "cocommit") + } +} + +func LoadConfig() (*Config, error) { + 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 { + 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(v *viper.Viper) 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") + } + + return createConfig(v) +} + +func createConfig(v *viper.Viper) 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 +} \ No newline at end of file