package cmd import ( "fmt" "os" "path/filepath" "sort" "strings" "github.com/spf13/cobra" "gopkg.in/yaml.v3" ) func configPath() (string, error) { home, err := os.UserHomeDir() if err != nil { return "", err } return filepath.Join(home, ".typecast", "config.yaml"), nil } func readConfig() (map[string]any, error) { path, err := configPath() if err != nil { return nil, err } config := map[string]any{} data, err := os.ReadFile(path) if err != nil && !os.IsNotExist(err) { return nil, err } if err := yaml.Unmarshal(data, &config); err != nil { return nil, fmt.Errorf("failed to parse config file: %w", err) } return config, nil } func writeConfig(config map[string]any) error { path, err := configPath() if err != nil { return err } if err := os.MkdirAll(filepath.Dir(path), 0700); err != nil { return err } data, err := yaml.Marshal(config) if err != nil { return err } return os.WriteFile(path, data, 0600) } func saveConfig(apiKey string) error { config, err := readConfig() if err != nil { return err } config["api_key"] = apiKey return writeConfig(config) } // configKeys maps CLI flag names to config file keys var configKeys = map[string]string{ "voice-id": "voice_id", "model": "model", "language": "language", "emotion": "emotion", "emotion-preset": "emotion_preset", "emotion-intensity": "emotion_intensity", "volume": "volume", "pitch": "pitch", "tempo": "tempo", "format": "format", "base-url": "base_url", } var configCmd = &cobra.Command{ Use: "config", Short: "Manage default settings", } var configSetCmd = &cobra.Command{ Use: "set ", Short: "Set a default value", Long: "Set a default value in ~/.typecast/config.yaml\n\nAvailable keys: " + "voice-id, model, language, emotion, emotion-preset, emotion-intensity, volume, pitch, tempo, format, base-url", Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { key, value := args[0], args[1] configKey, ok := configKeys[key] if !ok { return fmt.Errorf("unknown key %q, available: %s", key, strings.Join(availableKeys(), ", ")) } config, err := readConfig() if err != nil { return err } config[configKey] = value if err := writeConfig(config); err != nil { return err } fmt.Printf("%s = %s\n", configKey, value) return nil }, } var configUnsetCmd = &cobra.Command{ Use: "unset ", Short: "Remove a default value", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { key := args[0] configKey, ok := configKeys[key] if !ok { return fmt.Errorf("unknown key %q, available: %s", key, strings.Join(availableKeys(), ", ")) } config, err := readConfig() if err != nil { return err } delete(config, configKey) if err := writeConfig(config); err != nil { return err } fmt.Printf("unset %s\n", configKey) return nil }, } var configListCmd = &cobra.Command{ Use: "list", Short: "Show current config", RunE: func(cmd *cobra.Command, args []string) error { config, err := readConfig() if err != nil { return err } if len(config) == 0 { fmt.Println("(empty)") return nil } keys := make([]string, 0, len(config)) for k := range config { keys = append(keys, k) } sort.Strings(keys) for _, k := range keys { v := config[k] if k == "api_key" { s := fmt.Sprintf("%v", v) if len(s) > 8 { v = s[:8] + "..." } } fmt.Printf("%s = %v\n", k, v) } return nil }, } func availableKeys() []string { keys := make([]string, 0, len(configKeys)) for k := range configKeys { keys = append(keys, k) } sort.Strings(keys) return keys } func init() { configCmd.AddCommand(configSetCmd) configCmd.AddCommand(configUnsetCmd) configCmd.AddCommand(configListCmd) rootCmd.AddCommand(configCmd) }