From e3f47c602f9991f39f8cffae8dddb7ab85a67dc1 Mon Sep 17 00:00:00 2001 From: Logan Jones Date: Sat, 22 Mar 2025 16:33:04 -0500 Subject: [PATCH] logging, config file managment, writing library dir --- app.go | 44 +++++++++---- backend/config.go | 77 ---------------------- backend/config/config.go | 106 +++++++++++++++++++++++++++++++ backend/{ => config}/userdata.go | 2 +- backend/library/library.go | 34 ++++++++++ backend/logging/logging.go | 11 ++++ main.go | 25 ++++++-- 7 files changed, 201 insertions(+), 98 deletions(-) delete mode 100644 backend/config.go create mode 100644 backend/config/config.go rename backend/{ => config}/userdata.go (99%) create mode 100644 backend/library/library.go create mode 100644 backend/logging/logging.go diff --git a/app.go b/app.go index 323ac50..dcae8dc 100644 --- a/app.go +++ b/app.go @@ -3,48 +3,66 @@ package main import ( "context" "fmt" - "yellowjacket/backend" + "log/slog" + "yellowjacket/backend/config" + "yellowjacket/backend/library" + "yellowjacket/backend/logging" "github.com/wailsapp/wails/v2/pkg/runtime" ) // App struct type App struct { - ctx context.Context - config *backend.Config + ctx context.Context + config *config.Config + library *library.Library + logger *slog.Logger } // NewApp creates a new App application struct -func NewApp() *App { - return &App{} +func NewApp(conf *config.Config) *App { + if conf == nil { + // TODO proper error handling + panic(fmt.Errorf("conf was nil")) + } + return &App{ + config: conf, + } } // startup is called when the app starts. The context is saved // so we can call the runtime methods func (a *App) startup(ctx context.Context) { a.ctx = ctx + runtime.LogInfo(a.ctx, fmt.Sprintf("starting app with config %s", logging.PrettyJSON(*a.config))) - conf, err := backend.GetConfig() + musicLib, err := library.GetNewLibrary(ctx, a.config.Library) if err != nil { - panic(fmt.Errorf("could not get config: %w", err)) + panic(fmt.Errorf("could not initialize library: %w", err)) } - a.config = conf + // config might have been updated, so lets propogate that to the app + a.config.Library = musicLib.Conf + + runtime.LogInfo(a.ctx, fmt.Sprintf("using library %s", logging.PrettyJSON(musicLib))) + a.library = musicLib } // trying to make a function func (a *App) DirectoryPicker() (string, error) { + runtime.LogInfo(a.ctx, "selecting a directory") dir, err := runtime.OpenDirectoryDialog( a.ctx, - runtime.OpenDialogOptions{ - ShowHiddenFiles: true, - }) - + runtime.OpenDialogOptions{}) if err != nil { return "", fmt.Errorf("could not open directory dialog\n%w", err) } if dir == "" { return "No Library Directory Selected", nil } - return fmt.Sprintf("Library Directory: %s", dir), nil + // we got a directory, lets do something with it + runtime.LogInfo(a.ctx, fmt.Sprintf("got directory %s assigning to library directory config %s", dir, logging.PrettyJSON(*a.config))) + a.config.Library.DirectoryPath = dir + a.config.WriteConfig() + return dir, nil } diff --git a/backend/config.go b/backend/config.go deleted file mode 100644 index 7d34cd1..0000000 --- a/backend/config.go +++ /dev/null @@ -1,77 +0,0 @@ -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/config/config.go b/backend/config/config.go new file mode 100644 index 0000000..16d5fc0 --- /dev/null +++ b/backend/config/config.go @@ -0,0 +1,106 @@ +package config + +import ( + "fmt" + "os" + "path" + "yellowjacket/backend/library" + + "github.com/BurntSushi/toml" +) + +type Config struct { + filePath string // required + *configData +} + +func newConfig(filePath string, data *configData) (*Config, error) { + // TODO make sure required fields have SOMETHING in them + // TODO Merge existing config with read in config + if data == nil { + data = defaultConfigData + } + return &Config{ + filePath: filePath, + configData: data, + }, nil +} + +type configData struct { + Library *library.Config +} + +// return errors if there is a *breaking* issue with the config +func (d *configData) validate() error { + return nil +} + +var defaultConfigData *configData = &configData{ + Library: library.DefaultConfig, +} + +// GetCurrentConfig will load and return the config +// reading the config file in the user's config directory +func GetCurrentConfig() (*Config, error) { + // get the config file location + 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") + config, err := newConfig(configFilePath, nil) + if err != nil { + return nil, fmt.Errorf("could not create new config: %w", err) + } + config.filePath = configFilePath + + _, err = os.Stat(configFilePath) + if os.IsNotExist(err) { + config.WriteConfig() + } + config, err = config.loadConfig() + if err != nil { + return nil, fmt.Errorf("could not load config file %s: %w", configFilePath, err) + } + return config, nil +} + +func (c *Config) loadConfig() (*Config, error) { + // read in the file + confFileData, err := os.ReadFile(c.filePath) + if err != nil { + return nil, fmt.Errorf("problem reading config file %s: %w", c.filePath, err) + } + + // parse it into the config struct + var confData configData + _, err = toml.Decode(string(confFileData), &confData) + if err != nil { + return nil, fmt.Errorf("problem parsing config file %s: %w", c.filePath, err) + } + + // validate the config + if err = confData.validate(); err != nil { + return nil, fmt.Errorf("invalid config file at %s: %w", c.filePath, err) + } + + config, err := newConfig(c.filePath, &confData) + if err != nil { + return nil, fmt.Errorf("could not create config from config file data at %s: %w", c.filePath, err) + } + + return config, nil +} + +func (c *Config) WriteConfig() error { + confFileData, err := toml.Marshal(c) + if err != nil { + return fmt.Errorf("could not marshal config struct: %w", err) + } + + err = os.WriteFile(c.filePath, confFileData, os.FileMode(int(0666))) + if err != nil { + return fmt.Errorf("could not write config file: %w", err) + } + return nil +} diff --git a/backend/userdata.go b/backend/config/userdata.go similarity index 99% rename from backend/userdata.go rename to backend/config/userdata.go index 320634f..3ae2eb1 100644 --- a/backend/userdata.go +++ b/backend/config/userdata.go @@ -1,4 +1,4 @@ -package backend +package config import ( "fmt" diff --git a/backend/library/library.go b/backend/library/library.go new file mode 100644 index 0000000..a1a04f9 --- /dev/null +++ b/backend/library/library.go @@ -0,0 +1,34 @@ +package library + +import ( + "context" + "fmt" + "yellowjacket/backend/logging" + + "github.com/wailsapp/wails/v2/pkg/runtime" +) + +type Config struct { + DirectoryPath string +} + +var DefaultConfig *Config = &Config{ + DirectoryPath: "", +} + +type Library struct { + ctx context.Context + Conf *Config +} + +func GetNewLibrary(ctx context.Context, conf *Config) (*Library, error) { + if conf == nil { + runtime.LogInfo(ctx, fmt.Sprintf("library config is nil, using default config: %s", logging.PrettyJSON(DefaultConfig))) + conf = DefaultConfig + } + runtime.LogInfo(ctx, fmt.Sprintf("creating new library with config: %#v", conf)) + return &Library{ + ctx: ctx, + Conf: conf, + }, nil +} diff --git a/backend/logging/logging.go b/backend/logging/logging.go new file mode 100644 index 0000000..23cb975 --- /dev/null +++ b/backend/logging/logging.go @@ -0,0 +1,11 @@ +package logging + +import ( + "encoding/json" +) + +// TODO check that error +func PrettyJSON(obj interface{}) string { + bytes, _ := json.MarshalIndent(obj, "\t", "\t") + return string(bytes) +} diff --git a/main.go b/main.go index f0ef9c5..75f91ba 100644 --- a/main.go +++ b/main.go @@ -1,9 +1,14 @@ package main import ( + "context" "embed" + "fmt" + + "yellowjacket/backend/config" "github.com/wailsapp/wails/v2" + "github.com/wailsapp/wails/v2/pkg/logger" "github.com/wailsapp/wails/v2/pkg/options" "github.com/wailsapp/wails/v2/pkg/options/assetserver" ) @@ -12,24 +17,30 @@ import ( var assets embed.FS func main() { - // Create an instance of the app structure - app := NewApp() + config, err := config.GetCurrentConfig() + if err != nil { + panic(fmt.Errorf("could not get config: %w", err)) + } + + app := NewApp(config) // Create application with options - err := wails.Run(&options.App{ + err = wails.Run(&options.App{ Title: "yellowjacket", - Width: 1024, - Height: 768, + Width: 512, + Height: 384, AssetServer: &assetserver.Options{ Assets: assets, }, + LogLevel: logger.INFO, BackgroundColour: &options.RGBA{R: 27, G: 38, B: 54, A: 1}, - OnStartup: app.startup, + OnStartup: func(ctx context.Context) { + app.startup(ctx) + }, Bind: []interface{}{ app, }, }) - if err != nil { println("Error:", err.Error()) }