From 2e8f15d91bb2516bb4af70eb8ae28f22b7484a11 Mon Sep 17 00:00:00 2001 From: Logan Jones Date: Wed, 19 Mar 2025 23:37:23 -0500 Subject: [PATCH] added config --- app.go | 10 +++++- backend/config.go | 77 ++++++++++++++++++++++++++++++++++++++++++ backend/userdata.go | 82 +++++++++++++++++++++++++++++++++++++++++++++ go.mod | 5 ++- go.sum | 2 ++ 5 files changed, 174 insertions(+), 2 deletions(-) create mode 100644 backend/config.go create mode 100644 backend/userdata.go diff --git a/app.go b/app.go index 0fbe15d..dfca388 100644 --- a/app.go +++ b/app.go @@ -3,11 +3,13 @@ package main import ( "context" "fmt" + "yellowjacket/backend" ) // App struct type App struct { - ctx context.Context + ctx context.Context + config *backend.Config } // NewApp creates a new App application struct @@ -19,6 +21,12 @@ func NewApp() *App { // so we can call the runtime methods func (a *App) startup(ctx context.Context) { a.ctx = ctx + + conf, err := backend.GetConfig() + if err != nil { + panic(fmt.Errorf("could not get config: %w", err)) + } + a.config = conf } // Greet returns a greeting for the given name diff --git a/backend/config.go b/backend/config.go new file mode 100644 index 0000000..7d34cd1 --- /dev/null +++ b/backend/config.go @@ -0,0 +1,77 @@ +package backend + +import ( + "fmt" + "os" + "path" + + "github.com/BurntSushi/toml" +) + +type Config struct { + LibraryDirPath string + MyConfigValue string +} + +var defaultConfig *Config = &Config{ + LibraryDirPath: "", + MyConfigValue: "testtesttest", +} + +func GetConfig() (*Config, error) { + + configDir, err := GetUserConfigDirPath() + if err != nil { + return nil, fmt.Errorf("could not get user config directory path: %w", err) + } + configFilePath := path.Join(configDir, "config.toml") + // create the config file and load defaults if it doesn't exist + _, err = os.Stat(configFilePath) + if os.IsNotExist(err) { + defaultConfig.WriteConfig(configFilePath) + } + conf, err := LoadConfig(configFilePath) + if err != nil { + return nil, fmt.Errorf("could not load config file %s: %w", configFilePath, err) + } + return conf, nil +} + +func LoadConfig(configFilePath string) (*Config, error) { + // read in the file + confFileData, err := os.ReadFile(configFilePath) + if err != nil { + return nil, fmt.Errorf("problem reading config file %s: %w", configFilePath, err) + } + + // parse it into the config struct + var conf Config + _, err = toml.Decode(string(confFileData), &conf) + if err != nil { + return nil, fmt.Errorf("problem parsing config file %s: %w", configFilePath, err) + } + + // validate the config + if err = conf.validate(); err != nil { + return nil, fmt.Errorf("invalid config file at %s: %w", configFilePath, err) + } + + return &conf, nil +} + +func (c *Config) WriteConfig(configFileWritePath string) error { + confFileData, err := toml.Marshal(c) + if err != nil { + return fmt.Errorf("could not marshal config struct: %w", err) + } + err = os.WriteFile(configFileWritePath, confFileData, os.FileMode(int(0666))) + if err != nil { + return fmt.Errorf("could not write config file: %w", err) + } + return nil +} + +// return errors if there is a *breaking* issue with the config +func (c *Config) validate() error { + return nil +} diff --git a/backend/userdata.go b/backend/userdata.go new file mode 100644 index 0000000..320634f --- /dev/null +++ b/backend/userdata.go @@ -0,0 +1,82 @@ +package backend + +import ( + "fmt" + "os" + "os/user" + "runtime" +) + +// user config directories vary by operating system and purpose +// Linux/Mac: ~/.config +// Windows: C:\Users\\AppData +func GetUserConfigDirPath() (string, error) { + path := "" + currentUser, err := user.Current() + if err != nil { + return "", fmt.Errorf("could not get current user: %w", err) + } + switch currentOS := runtime.GOOS; currentOS { + case "darwin": + fallthrough + case "linux": + path = fmt.Sprintf("/home/%s/.config/yellowjacket", currentUser.Username) + case "windows": + path = fmt.Sprintf(`C:\Users\%s\AppData\local\yellowjacket\config`, currentUser.Username) + default: + return "", fmt.Errorf("unsupported OS: %s", currentOS) + } + err = os.MkdirAll(path, os.ModePerm) + if err != nil { + return "", fmt.Errorf("could not make user config directory: %w", err) + } + + // final check + dirInfo, err := os.Stat(path) + if err != nil { + return "", fmt.Errorf("could not stat the user config directory %s: %w", path, err) + } + + if !dirInfo.IsDir() { + return "", fmt.Errorf("not a directory: %s", path) + } + + return path, nil +} + +// user data directories vary by operating system and purpose +// Linux/Mac: ~/.local/share +// Windows: C:\Users\\AppData\Local +func getUserDataDirPath() (string, error) { + path := "" + currentUser, err := user.Current() + if err != nil { + return "", fmt.Errorf("could not get current user: %w", err) + } + switch currentOS := runtime.GOOS; currentOS { + case "darwin": + fallthrough + case "linux": + path = fmt.Sprintf("/home/%s/.local/share/yellowjacket", currentUser.Username) + case "windows": + path = fmt.Sprintf(`C:\Users\%s\AppData\local\yellowjacket\config`, currentUser.Username) + default: + return "", fmt.Errorf("unsupported OS: %s", currentOS) + } + err = os.MkdirAll(path, os.ModePerm) + if err != nil { + return "", fmt.Errorf("could not make user data directory: %w", err) + } + + // final check + dirInfo, err := os.Stat(path) + if err != nil { + return "", fmt.Errorf("could not stat the user data directory %s: %w", path, err) + } + + if !dirInfo.IsDir() { + return "", fmt.Errorf("not a directory: %s", path) + } + + return path, nil +} diff --git a/go.mod b/go.mod index 94d2204..2faa21f 100644 --- a/go.mod +++ b/go.mod @@ -2,7 +2,10 @@ module yellowjacket go 1.23 -require github.com/wailsapp/wails/v2 v2.10.1 +require ( + github.com/BurntSushi/toml v1.5.0 + github.com/wailsapp/wails/v2 v2.10.1 +) require ( github.com/bep/debounce v1.2.1 // indirect diff --git a/go.sum b/go.sum index 0ad6722..0c5c92e 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg= +github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/bep/debounce v1.2.1 h1:v67fRdBA9UQu2NhLFXrSg0Brw7CexQekrBwDMM8bzeY= github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=