/* 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() *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, ok := cli.ConfigFromContext(cmd.Context()) if !ok { return errors.New("no configuration loaded") } 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]) } }, Annotations: map[string]string{ "terminal_log": "1", }, } cmd.Flags().BoolP("unset", "x", false, "clear configuration setting") return cmd }