Skip to content

Commit

Permalink
Merge pull request #24 from asdf-vm/tb/config-api
Browse files Browse the repository at this point in the history
feat(golang-rewrite): add config methods
  • Loading branch information
Stratus3D authored Feb 21, 2024
2 parents edde355 + f0bbe8f commit da9f842
Show file tree
Hide file tree
Showing 2 changed files with 170 additions and 35 deletions.
153 changes: 123 additions & 30 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,36 +2,65 @@ package main

import (
"context"
"strconv"
"strings"

"github.com/mitchellh/go-homedir"
"github.com/sethvargo/go-envconfig"
"gopkg.in/ini.v1"
)

// Not sure how best to represent this enum type
//type PluginRepositoryLastCheckDuration struct {
// Never bool
// Every int
//}
const LegacyVersionFileDefault = false
const AlwaysKeepDownloadDefault = false
const DisablePluginShortNameRepositoryDefault = false
const ForcePrependDefault = false
const DataDirDefault = "~/.asdf"
const ConfigFileDefault = "~/.asdfrc"
const DefaultToolVersionsFilenameDefault = ".tool-versions"

/* Struct to represent the remote plugin repo check duration (never or every N
* seconds). It's not clear to me how this should be represented in Golang so
* using a struct for maximum flexibility. */
type PluginRepoCheckDuration struct {
Never bool
Every int
}

var PluginRepoCheckDurationDefault = PluginRepoCheckDuration{Every: 60}

type Settings struct {
Loaded bool
LegacyVersionFile bool
// I don't think this setting should be supported in the Golang implementation
//UseReleaseCandidates bool
AlwaysKeepDownload bool
PluginRepositoryLastCheckDuration string
PluginRepositoryLastCheckDuration PluginRepoCheckDuration
DisablePluginShortNameRepository bool
}

type Config struct {
Home string
ConfigFile string `env:"ASDF_CONFIG_FILE"`
DefaultToolVersionsFilename string `env:"ASDF_DEFAULT_TOOL_VERSIONS_FILENAME"`
ConfigFile string `env:"ASDF_CONFIG_FILE, overwrite"`
DefaultToolVersionsFilename string `env:"ASDF_DEFAULT_TOOL_VERSIONS_FILENAME, overwrite"`
// Unclear if this value will be needed with the golang implementation.
//AsdfDir string
DataDir string `env:"ASDF_DATA_DIR"`
ForcePrepend bool `env:"ASDF_FORCE_PREPEND"`
DataDir string `env:"ASDF_DATA_DIR, overwrite"`
ForcePrepend bool `env:"ASDF_FORCE_PREPEND, overwrite"`
// Field that stores the settings struct if it is loaded
Settings Settings
}

func NewPluginRepoCheckDuration(checkDuration string) PluginRepoCheckDuration {
if strings.ToLower(checkDuration) == "never" {
return PluginRepoCheckDuration{Never: true}
} else {
every, err := strconv.Atoi(checkDuration)
if err != nil {
// if error parsing config use default value
return PluginRepoCheckDurationDefault
}
return PluginRepoCheckDuration{Every: every}
}
}

func LoadConfig() (Config, error) {
Expand All @@ -52,16 +81,90 @@ func LoadConfig() (Config, error) {
return config, nil
}

// Methods on the Config struct that allow it to load and cache values from the
// Settings struct, which is loaded from file on disk and therefor somewhat
// "expensive".
func (c *Config) LegacyVersionFile() (bool, error) {
err := c.loadSettings()

if err != nil {
return false, err
}

return c.Settings.LegacyVersionFile, nil
}

func (c *Config) AlwaysKeepDownload() (bool, error) {
err := c.loadSettings()

if err != nil {
return false, err
}

return c.Settings.AlwaysKeepDownload, nil
}

func (c *Config) PluginRepositoryLastCheckDuration() (PluginRepoCheckDuration, error) {
err := c.loadSettings()

if err != nil {
return NewPluginRepoCheckDuration(""), err
}

return c.Settings.PluginRepositoryLastCheckDuration, nil
}

func (c *Config) DisablePluginShortNameRepository() (bool, error) {
err := c.loadSettings()

if err != nil {
return false, err
}

return c.Settings.DisablePluginShortNameRepository, nil
}

func (c *Config) loadSettings() error {
if c.Settings.Loaded {
return nil
}

settings, err := loadSettings(c.ConfigFile)

if err != nil {
return err
}

c.Settings = settings

return nil
}

func loadConfigEnv() (Config, error) {
context := context.Background()
config := Config{}
dataDir, err := homedir.Expand(DataDirDefault)
if err != nil {
return Config{}, err
}

err := envconfig.Process(context, &config)
configFile, err := homedir.Expand(ConfigFileDefault)
if err != nil {
return Config{}, err
}

config := Config{
ForcePrepend: ForcePrependDefault,
DataDir: dataDir,
ConfigFile: configFile,
DefaultToolVersionsFilename: DefaultToolVersionsFilenameDefault,
}

context := context.Background()
err = envconfig.Process(context, &config)

return config, err
}

func LoadSettings(asdfrcPath string) (Settings, error) {
func loadSettings(asdfrcPath string) (Settings, error) {
// asdfrc is effectively formatted as ini
config, err := ini.Load(asdfrcPath)

Expand All @@ -70,17 +173,18 @@ func LoadSettings(asdfrcPath string) (Settings, error) {
}

mainConf := config.Section("")
checkDuration := stringToCheckDuration(mainConf.Key("plugin_repository_last_check_duration").String(), "60")
checkDuration := NewPluginRepoCheckDuration(mainConf.Key("plugin_repository_last_check_duration").String())

return Settings{
LegacyVersionFile: YesNoToBool(mainConf, "legacy_version_file", false),
AlwaysKeepDownload: YesNoToBool(mainConf, "use_release_candidates", false),
Loaded: true,
LegacyVersionFile: yesNoToBool(mainConf, "legacy_version_file", LegacyVersionFileDefault),
AlwaysKeepDownload: yesNoToBool(mainConf, "use_release_candidates", AlwaysKeepDownloadDefault),
PluginRepositoryLastCheckDuration: checkDuration,
DisablePluginShortNameRepository: YesNoToBool(mainConf, "disable_plugin_short_name_repository", false),
DisablePluginShortNameRepository: yesNoToBool(mainConf, "disable_plugin_short_name_repository", DisablePluginShortNameRepositoryDefault),
}, nil
}

func YesNoToBool(section *ini.Section, key string, defaultValue bool) bool {
func yesNoToBool(section *ini.Section, key string, defaultValue bool) bool {
yesOrNo := section.Key(key).String()
lcYesOrNo := strings.ToLower(yesOrNo)
if lcYesOrNo == "yes" {
Expand All @@ -92,14 +196,3 @@ func YesNoToBool(section *ini.Section, key string, defaultValue bool) bool {

return defaultValue
}

// Eventually this should return a custom type but I need to figure out how to
// represent the (never string, duration int) type. For now it just returns a
// string.
func stringToCheckDuration(checkDuration string, defaultValue string) string {
if checkDuration != "" {
return checkDuration
}

return defaultValue
}
52 changes: 47 additions & 5 deletions config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,37 +22,79 @@ func TestLoadConfigEnv(t *testing.T) {

func TestLoadSettings(t *testing.T) {
t.Run("When given invalid path returns error", func(t *testing.T) {
_, err := LoadSettings("./foobar")
settings, err := loadSettings("./foobar")

if err == nil {
t.Fatal("Didn't get an error")
}

if settings.Loaded {
t.Fatal("Didn't expect settings to be loaded")
}
})

t.Run("When given path to populated asdfrc returns populated settings struct", func(t *testing.T) {
settings, err := LoadSettings("testdata/asdfrc")
settings, err := loadSettings("testdata/asdfrc")

refuteError(t, err)

assert(t, settings.Loaded, "Expected Loaded field to be set to true")
assert(t, settings.LegacyVersionFile == true, "LegacyVersionFile field has wrong value")
assert(t, settings.AlwaysKeepDownload == true, "AlwaysKeepDownload field has wrong value")
assert(t, settings.PluginRepositoryLastCheckDuration == "never", "PluginRepositoryLastCheckDuration field has wrong value")
assert(t, settings.PluginRepositoryLastCheckDuration.Never, "PluginRepositoryLastCheckDuration field has wrong value")
assert(t, settings.PluginRepositoryLastCheckDuration.Every == 0, "PluginRepositoryLastCheckDuration field has wrong value")
assert(t, settings.DisablePluginShortNameRepository == true, "DisablePluginShortNameRepository field has wrong value")
})

t.Run("When given path to empty file returns settings struct with defaults", func(t *testing.T) {
settings, err := LoadSettings("testdata/empty-asdfrc")
settings, err := loadSettings("testdata/empty-asdfrc")

refuteError(t, err)

assert(t, settings.LegacyVersionFile == false, "LegacyVersionFile field has wrong value")
assert(t, settings.AlwaysKeepDownload == false, "AlwaysKeepDownload field has wrong value")
assert(t, settings.PluginRepositoryLastCheckDuration == "60", "PluginRepositoryLastCheckDuration field has wrong value")
assert(t, settings.PluginRepositoryLastCheckDuration.Never == false, "PluginRepositoryLastCheckDuration field has wrong value")
assert(t, settings.PluginRepositoryLastCheckDuration.Every == 60, "PluginRepositoryLastCheckDuration field has wrong value")
assert(t, settings.DisablePluginShortNameRepository == false, "DisablePluginShortNameRepository field has wrong value")
})
}

func TestConfigMethods(t *testing.T) {
// Set the asdf config file location to the test file
t.Setenv("ASDF_CONFIG_FILE", "testdata/asdfrc")

config, err := LoadConfig()
assert(t, err == nil, "Returned error when building config")

t.Run("Returns LegacyVersionFile from asdfrc file", func(t *testing.T) {
legacyFile, err := config.LegacyVersionFile()
assert(t, err == nil, "Returned error when loading settings")
assert(t, legacyFile == true, "Expected LegacyVersionFile to be set")
})

t.Run("Returns AlwaysKeepDownload from asdfrc file", func(t *testing.T) {
alwaysKeepDownload, err := config.AlwaysKeepDownload()
assert(t, err == nil, "Returned error when loading settings")
assert(t, alwaysKeepDownload == true, "Expected AlwaysKeepDownload to be set")
})

t.Run("Returns PluginRepositoryLastCheckDuration from asdfrc file", func(t *testing.T) {
checkDuration, err := config.PluginRepositoryLastCheckDuration()
assert(t, err == nil, "Returned error when loading settings")
assert(t, checkDuration.Never == true, "Expected PluginRepositoryLastCheckDuration to be set")
assert(t, checkDuration.Every == 0, "Expected PluginRepositoryLastCheckDuration to be set")
})

t.Run("Returns DisablePluginShortNameRepository from asdfrc file", func(t *testing.T) {
DisablePluginShortNameRepository, err := config.DisablePluginShortNameRepository()
assert(t, err == nil, "Returned error when loading settings")
assert(t, DisablePluginShortNameRepository == true, "Expected DisablePluginShortNameRepository to be set")
})
}

func assert(t *testing.T, expr bool, message string) {
t.Helper()

if !expr {
t.Error(message)
}
Expand Down

0 comments on commit da9f842

Please sign in to comment.