summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeff Carr <[email protected]>2025-10-17 01:38:38 -0500
committerJeff Carr <[email protected]>2025-10-17 01:38:38 -0500
commit5579498720c5c1e0cdf31f97f7ad31dfe9dbf0aa (patch)
treef5dea6f7e4bc605358b5bb6ee22ef7c2cffafe37
parentc14367731106c50ff790c5eb4e0cdedc32f0ddd1 (diff)
tweaks on config load. more smarters than befores
-rw-r--r--README.md25
-rw-r--r--load.go77
-rw-r--r--save.go5
3 files changed, 84 insertions, 23 deletions
diff --git a/README.md b/README.md
index 29264c2..67dbb17 100644
--- a/README.md
+++ b/README.md
@@ -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)
+}
diff --git a/load.go b/load.go
index 48b6d4e..fc9a5a0 100644
--- a/load.go
+++ b/load.go
@@ -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)
diff --git a/save.go b/save.go
index f926a31..d257649 100644
--- a/save.go
+++ b/save.go
@@ -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)