/* Copyright © 2026 Craig Williams This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . */ package config import ( "errors" "fmt" "log/slog" "reflect" "strconv" "git.avsdev.uk/Go/job-search/internal/cli" "git.avsdev.uk/Go/job-search/internal/config" "github.com/spf13/cobra" "github.com/spf13/viper" ) func setConfig(conf *config.JobSearchConfig, key string, value string) error { var confValue any = nil var confType reflect.Kind var err error confType, err = conf.TypeOf(key) if err != nil { return err } switch confType { case reflect.Bool: confValue, err = strconv.ParseBool(value) case reflect.String: confValue = value case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: confValue, err = strconv.ParseInt(value, 10, 64) case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: confValue, err = strconv.ParseUint(value, 10, 64) case reflect.Float32, reflect.Float64: confValue, err = strconv.ParseFloat(value, 64) default: err = errors.ErrUnsupported } if err != nil { return fmt.Errorf("config value type not accepted, expected <%s>", confType) } viper.Set(key, confValue) if err := viper.WriteConfig(); err != nil { return err } fmt.Print(key, " = ", confValue, " <", confType, ">\n") return nil } func unsetConfig(conf *config.JobSearchConfig, key string) error { if _, err := conf.TypeOf(key); err != nil { return err } v := viper.New() v.SetConfigFile(viper.ConfigFileUsed()) keys := viper.AllKeys() for i := range keys { k := keys[i] if k != key { v.Set(k, viper.Get(k)) } } if err := v.WriteConfig(); err != nil { return err } return nil } func commandSet(opts *cli.Options) *cobra.Command { cmd := &cobra.Command{ Use: "set [config_key] [config_value]", Args: cobra.MatchAll(cobra.MinimumNArgs(1), cobra.MaximumNArgs(2)), Short: "Set a value to configuration", PersistentPreRun: func(cmd *cobra.Command, args []string) { slog.Debug("config.set.PersistentPreRun") }, PersistentPostRun: func(cmd *cobra.Command, args []string) { slog.Debug("config.set.PersistentPostRun") }, RunE: func(cmd *cobra.Command, args []string) error { slog.Debug("config.set.Run") if err := cobra.MinimumNArgs(1)(cmd, args); err != nil { return err } if err := cobra.MaximumNArgs(2)(cmd, args); err != nil { return err } unset, unsetFlagErr := cmd.Flags().GetBool("unset") if unsetFlagErr != nil { return unsetFlagErr } if err := cobra.ExactArgs(2)(cmd, args); err != nil && !unset { return err } conf, err := opts.RunSetup(cmd, true, true) if err != nil { return fmt.Errorf("no configuration loaded (%w)", err) } if viper.ConfigFileUsed() == "" { return errors.New("no configuration file exists") } if unset { return unsetConfig(conf, args[0]) } else { return setConfig(conf, args[0], args[1]) } }, } cmd.Flags().BoolP("unset", "x", false, "clear configuration setting") return cmd }