diff options
| author | Jeff Carr <[email protected]> | 2025-10-17 01:38:38 -0500 |
|---|---|---|
| committer | Jeff Carr <[email protected]> | 2025-10-17 01:38:38 -0500 |
| commit | 5579498720c5c1e0cdf31f97f7ad31dfe9dbf0aa (patch) | |
| tree | f5dea6f7e4bc605358b5bb6ee22ef7c2cffafe37 | |
| parent | c14367731106c50ff790c5eb4e0cdedc32f0ddd1 (diff) | |
tweaks on config load. more smarters than befores
| -rw-r--r-- | README.md | 25 | ||||
| -rw-r--r-- | load.go | 77 | ||||
| -rw-r--r-- | save.go | 5 |
3 files changed, 84 insertions, 23 deletions
@@ -1,15 +1,26 @@ // Copyright 2025 WIT.COM Inc Licensed GPL 3.0 common config file handling for protobuf defined config files +intended to be super simple so the code you need to write is simple. -By default, the config files are stored as: +Enables Load functions: -~/.config/<argname>/<protoname>.text +// loads ~/.config/myapp/trees.text +cfg := new(MyPB) +err := config.ConfigLoad(cfg, "myapp", "trees") -assumes config files are simple, intended to be edited by hand -by the user and can be exported by protobuf FormatTEXT() +Enables Save functions: -intended to be called by functions that are automatically -generated by 'autogenpb' for protobuf defined config files. +err := cfg.Save() // it automatically knows where to save -If you aren't using .proto defined config files, this package is not for you +### Errors #### + +if errors.Is(err, config.VersionMismatch) { + // protobuf structure changed +} +if errors.Is(err, config.ErrEmpty) { + // config file was empty +} +if errors.Is(err, config.ErrNotExist) { + // config file didn't exist (yes, this is the os.ExistErr) +} @@ -10,14 +10,15 @@ import ( "path/filepath" "strings" + "go.wit.com/log" "google.golang.org/protobuf/encoding/protojson" "google.golang.org/protobuf/encoding/prototext" "google.golang.org/protobuf/proto" ) /* -// loads a file from ~/.config/<argname>/ -func Load(argname string) ([]byte, string) { +// loads a file from ~/.config/<appname>/ +func Load(appname string) ([]byte, string) { } */ @@ -25,20 +26,30 @@ var ErrEmpty error = fmt.Errorf("config file was empty") var ErrMarshal error = fmt.Errorf("protobuf parse error") // returns: -// - Full path to the config file. usually: ~/.config/<argname> +// - Full path to the config file. usually: ~/.config/<appname> // - []byte : the contents of the file // - error on read -func ConfigLoad(pb proto.Message, argname string, protoname string) error { - var fullname string +func ConfigLoad(pb proto.Message, appname string, protoname string) error { + // Get ~/.config/appname/protoname.text + fullname := GetConfigFilename(appname, protoname) + + var pbFilenameSupport bool var err error - configDir, err := os.UserConfigDir() + curfilename, err := GetFilename(pb) if err != nil { - return err + log.Info("This protobuf doesn't support pb.Filename") + // make note this protobuf doesn't support Filenames + } else { + pbFilenameSupport = true } + // panic(curfilename) - fullname = filepath.Join(configDir, argname, protoname+".text") - SetFilename(pb, fullname) - + // potential syntax with this GO library is starting to look like: + // + // if errors.Is(err, config.ErrEmpty) + // + // if errors.Is(err, config.VersionMismatch) + // // if both don't exist or both are empty, return known errors // these can be used to detect if the user is new to the application if err := missingConfig(fullname); err != nil { @@ -52,11 +63,33 @@ func ConfigLoad(pb proto.Message, argname string, protoname string) error { } if err = loadTEXT(pb, fullname); err == nil { + if pbFilenameSupport { + // If the config is old or broken, this sets the filename + if curfilename != fullname { + _, err := SetFilename(pb, fullname) + if err != nil { + log.Info("FILENAME COULD NOT BE SET old=", curfilename) + log.Info("FILENAME COULD NOT BE SET new=", fullname) + panic("something is wrong in lib/config") + } + } + } return nil } else { if strings.HasSuffix(fullname, ".text") { fulljson := fullname + ".json" + // If the config is old or broken, this sets the filename if err := loadJSON(pb, fulljson); err == nil { + if pbFilenameSupport { + if curfilename != fullname { + _, err := SetFilename(pb, fullname) + if err != nil { + log.Info("FILENAME COULD NOT BE SET old=", curfilename) + log.Info("FILENAME COULD NOT BE SET new=", fullname) + panic("something is wrong in lib/config") + } + } + } return nil } } @@ -64,14 +97,26 @@ func ConfigLoad(pb proto.Message, argname string, protoname string) error { return ErrMarshal } +// returns the default constructed filename: +// ~/.config/appname/protoname.text +func GetConfigFilename(appname string, protoname string) string { + var err error + configDir, err := os.UserConfigDir() + if err != nil { + // todo: get something better than /tmp/ if anyone cares + return filepath.Join("/tmp", appname, protoname+".text") + } + return filepath.Join(configDir, appname, protoname+".text") +} + // loads from the users .cache dir // if the .proto file version changes, automatically delete the .pb // file. This is important to avoid marshalling garbage data // .cache files are treated as such, a "cache" file. don't keep important // things in here. argv stores the information here for autodelete -func LoadCache(pb proto.Message, argname string, protoname string) error { +func LoadCache(pb proto.Message, appname string, protoname string) error { cacheDir, _ := os.UserCacheDir() - fullpath := filepath.Join(cacheDir, argname) + fullpath := filepath.Join(cacheDir, appname) os.MkdirAll(fullpath, os.ModePerm) fullname := filepath.Join(fullpath, protoname+".pb") _, err := SetFilename(pb, fullname) @@ -198,17 +243,17 @@ func loadPB(pb proto.Message, fullname string) error { return nil } -func LoadConfigPB(pb proto.Message, argname string, protoname string) (string, error) { +func LoadConfigPB(pb proto.Message, appname string, protoname string) (string, error) { var fullname string - if strings.HasPrefix(argname, "/") { - fullname = filepath.Join(argname, protoname+".pb") + if strings.HasPrefix(appname, "/") { + fullname = filepath.Join(appname, protoname+".pb") } else { configDir, err := os.UserConfigDir() if err != nil { return "", err } - fullname = filepath.Join(configDir, argname, protoname+".pb") + fullname = filepath.Join(configDir, appname, protoname+".pb") } data, err := loadFile(fullname) @@ -94,6 +94,11 @@ func saveTEXT(pb proto.Message, header string) error { if err != nil { return err } + fullname = strings.TrimSpace(fullname) + if fullname == "" { + return fmt.Errorf("saveTEXT() pb.Filename was blank") + } + if !strings.HasSuffix(fullname, ".text") { // todo: append .text here? return fmt.Errorf("not .text file: %s", fullname) |
