Initial project

This commit is contained in:
bwilliams committed 2026-09-23 14:32:50 +01:00
commit e715024cbd
23 files changed
+1547

No files matched your search

+41
View File
@@ -0,0 +1,41 @@
/*
Copyright © 2026 Craig Williams <craig@avsdev.uk>
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 <http://www.gnu.org/licenses/>.
*/
package config
import (
"log/slog"
"github.com/spf13/cobra"
)
func NewCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "config",
Short: "Configuration management",
PersistentPreRun: func(cmd *cobra.Command, args []string) {
slog.Debug("config.PersistentPreRun")
},
PersistentPostRun: func(cmd *cobra.Command, args []string) {
slog.Debug("config.PersistentPostRun")
},
}
cmd.AddCommand(commandShow())
cmd.AddCommand(commandGet())
cmd.AddCommand(commandSet())
cmd.AddCommand(commandInit())
return cmd
}
+83
View File
@@ -0,0 +1,83 @@
/*
Copyright © 2026 Craig Williams <craig@avsdev.uk>
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 <http://www.gnu.org/licenses/>.
*/
package config
import (
"errors"
"fmt"
"log/slog"
"git.avsdev.uk/Go/job-search/internal/cli"
"github.com/spf13/cobra"
)
func commandGet() *cobra.Command {
cmd := &cobra.Command{
Use: "get [config_key]",
Args: cobra.ExactArgs(1),
// Args: func(cmd *cobra.Command, args []string) error {
// // Optionally run one of the validators provided by cobra
// if err := cobra.ExactArgs(1)(cmd, args); err != nil {
// return err
// }
// conf, ok := cli.ConfigFromContext(cmd.Context())
// if !ok {
// return errors.New("no configuration loaded")
// }
// flatConf := config.FlattenConfig(conf)
// if !util.HasKey(flatConf, args[0]) {
// return fmt.Errorf("invalid config key specified: %s", args[0])
// }
// return nil
// },
Short: "Get a value from configuration",
PersistentPreRun: func(cmd *cobra.Command, args []string) {
slog.Debug("config.get.PersistentPreRun")
},
PersistentPostRun: func(cmd *cobra.Command, args []string) {
slog.Debug("config.get.PersistentPostRun")
},
RunE: func(cmd *cobra.Command, args []string) error {
slog.Debug("config.get.Run")
if err := cobra.ExactArgs(1)(cmd, args); err != nil {
return err
}
conf, ok := cli.ConfigFromContext(cmd.Context())
if !ok {
return errors.New("no configuration loaded")
}
v, err := conf.ValueOf(args[0])
if err != nil {
return err
}
t, _ := conf.TypeOf(args[0])
fmt.Println(v, "<"+t.String()+">")
return nil
},
Annotations: map[string]string{
"terminal_log": "1",
},
}
return cmd
}
+96
View File
@@ -0,0 +1,96 @@
/*
Copyright © 2026 Craig Williams <craig@avsdev.uk>
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 <http://www.gnu.org/licenses/>.
*/
package config
import (
"errors"
"fmt"
"log/slog"
"os"
"git.avsdev.uk/Go/job-search/internal/config"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
func commandInit() *cobra.Command {
cmd := &cobra.Command{
Use: "init [config_path]",
Args: cobra.MatchAll(cobra.MinimumNArgs(0), cobra.MaximumNArgs(1)),
Short: "Create an empty configuration file",
PersistentPreRun: func(cmd *cobra.Command, args []string) {
slog.Debug("config.init.PersistentPreRun")
},
PersistentPostRun: func(cmd *cobra.Command, args []string) {
slog.Debug("config.init.PersistentPostRun")
},
RunE: func(cmd *cobra.Command, args []string) error {
slog.Debug("config.init.Run")
if err := cobra.MinimumNArgs(0)(cmd, args); err != nil {
return err
}
if err := cobra.MaximumNArgs(1)(cmd, args); err != nil {
return err
}
cfgFile, _ := cmd.Flags().GetString("config")
if cfgFile != "" {
// Use config file from the flag.
viper.SetConfigFile(cfgFile)
} else {
// Search config in /etc or local directory.
viper.AddConfigPath(config.EtcConfigPath)
viper.AddConfigPath(".")
viper.SetConfigName("config")
}
err := viper.ReadInConfig()
if err == nil {
return errors.New("configuration file already exists")
}
_, ok := err.(viper.ConfigFileNotFoundError)
if !errors.Is(err, os.ErrNotExist) && !ok {
// Config file was found but another error was produced
return err
}
// Load default configuration
config.LoadDefaultConfig()
if viper.ConfigFileUsed() == "" {
viper.SetConfigFile(config.EtcConfigPath + "/config.yaml")
}
fmt.Print(
"writing default configuration to file: " +
viper.ConfigFileUsed() +
"\n")
if err := viper.WriteConfig(); err != nil {
return err
}
return nil
},
Annotations: map[string]string{
"no_config": "1",
"terminal_log": "1",
},
}
return cmd
}
+141
View File
@@ -0,0 +1,141 @@
/*
Copyright © 2026 Craig Williams <craig@avsdev.uk>
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 <http://www.gnu.org/licenses/>.
*/
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
}
+62
View File
@@ -0,0 +1,62 @@
/*
Copyright © 2026 Craig Williams <craig@avsdev.uk>
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 <http://www.gnu.org/licenses/>.
*/
package config
import (
"errors"
"log/slog"
"git.avsdev.uk/Go/job-search/internal/cli"
"github.com/spf13/cobra"
)
func commandShow() *cobra.Command {
cmd := &cobra.Command{
Use: "show",
Short: "Print config information",
PersistentPreRun: func(cmd *cobra.Command, args []string) {
slog.Debug("config.show.PersistentPreRun")
},
PersistentPostRun: func(cmd *cobra.Command, args []string) {
slog.Debug("config.show.PersistentPostRun")
},
RunE: func(cmd *cobra.Command, args []string) error {
slog.Debug("config.show.Run")
conf, ok := cli.ConfigFromContext(cmd.Context())
if !ok {
return errors.New("no configuration loaded")
}
if yaml, _ := cmd.Flags().GetBool("yaml"); yaml {
conf.PrintYaml()
} else if json, _ := cmd.Flags().GetBool("json"); json {
conf.PrintJson()
} else {
conf.PrintTerminal()
}
return nil
},
Annotations: map[string]string{
"terminal_log": "1",
},
}
cmd.Flags().Bool("json", false, "format configuration as json")
cmd.Flags().Bool("yaml", false, "format configuration as yaml")
return cmd
}