commit 2a0db8026274cf75eae1dc128cb8cc6ff9e162ca Author: restitux Date: Wed Sep 14 17:20:21 2022 -0600 Initial commit diff --git a/config.go b/config.go new file mode 100644 index 0000000..845d3f6 --- /dev/null +++ b/config.go @@ -0,0 +1,78 @@ +package config + +import ( + "fmt" + "github.com/pelletier/go-toml/v2" + "io/ioutil" + "os" + "path/filepath" +) + +type Config[T any] struct { + Name string + Filename string + Config T +} + +func getConfigDir(n string) string { + // Find config directory + + // Linux (XDG base directory specification compliant) + xdg_config_home := os.Getenv("XDG_CONFIG_HOME") + + var base string + if xdg_config_home != "" { + base = xdg_config_home + } else { + home := os.Getenv("HOME") + base = filepath.Join(home, ".config") + } + return filepath.Join(base, n) +} + +func (c Config[T]) Get() (bool, error) { + config_dir := getConfigDir(c.Name) + + err := os.MkdirAll(config_dir, 0755) + if err != nil { + return false, fmt.Errorf("Could not create config directory: %v", err) + } + + config_path := filepath.Join(config_dir, c.Filename+".toml") + + // open file, creating it if empty + config_file, err := os.OpenFile(config_path, os.O_RDWR|os.O_CREATE, 0755) + defer config_file.Close() + + if err != nil { + return false, fmt.Errorf("Could not open config file: %v", err) + } + + fi, err := config_file.Stat() + if err != nil { + return false, fmt.Errorf("Could not get config file size: %v", err) + } + + if fi.Size() == 0 { + // if file is empty, write default config + data, err := toml.Marshal(c.Config) + if err != nil { + return false, fmt.Errorf("Could not write default config to file: %v", err) + } + config_file.Write(data) + // return nil as the user must edit the config file + return true, nil + } else { + // try to parse config file + data, err := ioutil.ReadAll(config_file) + if err != nil { + return false, fmt.Errorf("Could not read data from config file: %v", err) + } + err = toml.Unmarshal(data, &c.Config) + if err != nil { + return false, fmt.Errorf("Could not parse config file contents: %v", err) + } + return false, nil + } + +} diff --git a/config_test.go b/config_test.go new file mode 100644 index 0000000..29f359d --- /dev/null +++ b/config_test.go @@ -0,0 +1,125 @@ +package config + +import ( + "os" + "testing" +) + +type LocalConfig struct { + Address string + Port int +} + +func Test_getConfigDir_HOME(t *testing.T) { + os.Unsetenv("XDG_CONFIG_HOME") + os.Setenv("HOME", "/test") + + expected_config_dir := "/test/.config/test" + config_dir := getConfigDir("test") + + if config_dir != expected_config_dir { + t.Errorf("getConfigDir() returned %v, %v expected", config_dir, expected_config_dir) + } +} + +func Test_getConfigDir_XDG_CONFIG_HOME(t *testing.T) { + os.Setenv("XDG_CONFIG_HOME", "/test/") + + expected_config_dir := "/test/test" + config_dir := getConfigDir("test") + + if config_dir != "/test/test" { + t.Errorf("getConfigDir() returned %v, %v expected", config_dir, expected_config_dir) + } else { + t.Log("Success!") + } +} + +func Test_ConfigGetReturnVal_NoConfigFile(t *testing.T) { + os.Setenv("XDG_CONFIG_HOME", "/tmp") + + config := Config[LocalConfig]{ + Name: "ohea-config", + Filename: "config", + Config: LocalConfig{}, + } + + os.RemoveAll(getConfigDir(config.Name)) + + ret, err := config.Get() + if err != nil { + t.Errorf("Failed to get config: %v", err) + } else if ret { + t.Log("Success!") + } else if !ret { + t.Errorf("Config.Get() returned false despite config file not existing") + } +} + +func Test_ConfigGetConfig_NoConfigFile(t *testing.T) { + os.Setenv("XDG_CONFIG_HOME", "/tmp") + + expected_address := "127.0.0.1" + experted_port := 9999 + + config := Config[LocalConfig]{ + Name: "ohea-config", + Filename: "config", + Config: LocalConfig{ + Address: expected_address, + Port: experted_port, + }, + } + + os.RemoveAll(getConfigDir(config.Name)) + + _, err := config.Get() + if err != nil { + t.Errorf("Failed to get config: %v", err) + } + + if config.Config.Address != expected_address { + t.Errorf("Returned value %v did not match expected value %v", config.Config.Address, expected_address) + } else if config.Config.Port != experted_port { + t.Errorf("Returned value %v did not match expected value %v", config.Config.Port, experted_port) + } else { + t.Log("Success!") + } +} + +func Test_ConfigGetConfig_ConfigFile(t *testing.T) { + os.Setenv("XDG_CONFIG_HOME", "/tmp") + + expected_address := "127.0.0.1" + experted_port := 9999 + + config := Config[LocalConfig]{ + Name: "ohea-config", + Filename: "config", + Config: LocalConfig{ + Address: expected_address, + Port: experted_port, + }, + } + + os.RemoveAll(getConfigDir(config.Name)) + + _, err := config.Get() + if err != nil { + t.Errorf("Failed to get config: %v", err) + } + + _, err = config.Get() + if err != nil { + t.Errorf("Failed to get config: %v", err) + } + + if config.Config.Address != expected_address { + t.Errorf("Returned value %v did not match expected value %v", config.Config.Address, expected_address) + } else if config.Config.Port != experted_port { + t.Errorf("Returned value %v did not match expected value %v", config.Config.Port, experted_port) + } else { + t.Log("Success!") + } + +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..45a2061 --- /dev/null +++ b/go.mod @@ -0,0 +1,5 @@ +module gitlab.com/ohea/golang/config + +go 1.19 + +require github.com/pelletier/go-toml/v2 v2.0.5 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..5edb4c0 --- /dev/null +++ b/go.sum @@ -0,0 +1,16 @@ +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pelletier/go-toml/v2 v2.0.5 h1:ipoSadvV8oGUjnUbMub59IDPPwfxF694nG/jwbMiyQg= +github.com/pelletier/go-toml/v2 v2.0.5/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=