From 23b2d19bc03b4db253b15cc6fef7176ab31d4f18 Mon Sep 17 00:00:00 2001 From: Jeff Carr Date: Wed, 22 Oct 2025 09:19:26 -0500 Subject: finally can fix the Load() and Save() names --- cacheDir.go | 68 ++++++++++++ configDir.go | 88 +++++++++++++++ load.go | 170 +++++++++++++++++++++++++++++ loadByAppname.go | 51 --------- loadCache.go | 318 ------------------------------------------------------- loadConfig.go | 176 ------------------------------ makeFilenames.go | 4 +- panic.go | 19 +++- save.go | 17 +-- versionCheck.go | 53 ++++++++++ 10 files changed, 404 insertions(+), 560 deletions(-) create mode 100644 cacheDir.go create mode 100644 configDir.go create mode 100644 load.go delete mode 100644 loadByAppname.go delete mode 100644 loadCache.go delete mode 100644 loadConfig.go create mode 100644 versionCheck.go diff --git a/cacheDir.go b/cacheDir.go new file mode 100644 index 0000000..370b619 --- /dev/null +++ b/cacheDir.go @@ -0,0 +1,68 @@ +package config + +// loads from the users .cache/ dir +// +// GOAL: always work. When in doubt, delete things in ~/.cache/ +// RULE: always use raw .pb files, never human readable .text or .json files +// +// deletes files when versions don't match +// deletes files when filenames can't be set +// deletes anything that doesn't work +// +// This is important to avoid unmarshalling garbage protobuf data + +import ( + "errors" + "os" + + "go.wit.com/lib/ENV" + "go.wit.com/log" + "google.golang.org/protobuf/proto" +) + +// loads foo.proto from ~/.cache//foo.pb +func LoadCacheDir(pb proto.Message) error { + appname, err := ENV.GetAppname() // already configured by your application + if err != nil { + return err + } + + err = LoadCacheDirByAppname(pb, appname) + return err +} + +// loads foo.proto from ~/.cache//foo.pb +func LoadCacheDirByAppname(pb proto.Message, appname string) error { + protoname, err := GetProtobufName(pb) // defined in the foo.proto file + if err != nil { + return err + } + + err = CreateCacheDirPB(pb, appname, protoname) + return err +} + +// checks the UUID and Version of the .pb file +func CreateCacheDirPB(pb proto.Message, appname string, protoname string) error { + // Get ~/.cache/appname/protoname.text + fullname := MakeCacheFilename(appname, protoname) + + _, err := SetFilename(pb, fullname) + if err != nil { + log.Info("lib/config PB file does not support Filename") + } + newver, curver, err := VersionCheckFile(pb, fullname) + _, _ = newver, curver + if err == nil { + // everything is fine. Versions match. load file + err = LoadFromFilename(pb, fullname) + return err + } + if errors.Is(err, os.ErrNotExist) { + // file is new, create the file + err = SaveToFilename(pb, fullname) + return err + } + // some other bad error + return err +} diff --git a/configDir.go b/configDir.go new file mode 100644 index 0000000..2f034f3 --- /dev/null +++ b/configDir.go @@ -0,0 +1,88 @@ +package config + +import ( + "errors" + "os" + "path/filepath" + "strings" + + "go.wit.com/lib/ENV" + "go.wit.com/log" + "google.golang.org/protobuf/proto" +) + +// loads foo.proto from ~/.config//foo.text +func LoadConfigDir(pb proto.Message) error { + appname, err := ENV.GetAppname() // already configured by your application + if err != nil { + return err + } + protoname, err := GetProtobufName(pb) // defined in the foo.proto file + if err != nil { + return err + } + + curfilename, err := GetFilename(pb) + if err == nil { + return err + } + + // Get ~/.config/appname/protoname.text + fullname := MakeConfigFilename(appname, protoname) + + if err = loadTEXT(pb, fullname); err == nil { + // 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) + return errors.Join(err, errors.New("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 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) + return errors.Join(err, errors.New("something is wrong in lib/config")) + } + } + return nil + } + } + } + return ErrMarshal +} + +func LoadConfigPB(pb proto.Message, appname string, protoname string) (string, error) { + var fullname string + if strings.HasPrefix(appname, "/") { + fullname = filepath.Join(appname, protoname+".pb") + } else { + configDir, err := os.UserConfigDir() + if err != nil { + return "", err + } + + fullname = filepath.Join(configDir, appname, protoname+".pb") + } + + data, err := loadFile(fullname) + if err != nil { + return fullname, err + } + + // Unmarshal() + if err = proto.Unmarshal(data, pb); err != nil { + return fullname, err + } + + return fullname, nil +} diff --git a/load.go b/load.go new file mode 100644 index 0000000..1fbd2ca --- /dev/null +++ b/load.go @@ -0,0 +1,170 @@ +package config + +import ( + "errors" + "fmt" + "os" + "strings" + + "google.golang.org/protobuf/encoding/protojson" + "google.golang.org/protobuf/encoding/prototext" + "google.golang.org/protobuf/proto" +) + +func LoadPB(pb proto.Message) error { + return ReLoad(pb) +} + +// uses the version to die. This is needed because loading binary +// protobuf files with rearranged messages is indeterminate +func ReLoad(pb proto.Message) error { + fullname, err := GetFilename(pb) + if (fullname == "") || (err != nil) { + panic("config.LoadPB() got blank filename = ''") + } + // this code needs work + newver, pbver, err := VersionCheckFile(pb, fullname) + if errors.Is(err, os.ErrNotExist) { + // + err = SaveToFilename(pb, fullname) + return err + } + if errors.Is(err, VersionMismatch) || (newver != pbver) { + fmt.Println("") + fmt.Printf("VERSION new '%s' != cur PB '%s'\n", newver, pbver) + fmt.Println("") + fmt.Println("Your protobuf file is old and can not be loaded") + fmt.Println("your application must decide how to handle this (delete or fix)") + fmt.Println("always die here. application is broken") + fmt.Println("You must delete or convert the file", fullname) + fmt.Println("") + // probably should ALWAYS PANIC HERE + // upon further study, always die here is better than not + s := fmt.Sprintf("protobuf version wrong. delete or fix %s", fullname) + panic(s) + } + if err != nil { + // return to let the application figure this out + return err + } + return nil +} + +func LoadFromFilename(pb proto.Message, fullname string) error { + if strings.HasSuffix(fullname, ".text") { + return loadTEXT(pb, fullname) + } + if strings.HasSuffix(fullname, ".json") { + return loadJSON(pb, fullname) + } + if strings.HasSuffix(fullname, ".pb") { + return loadPB(pb, fullname) + } + + return fmt.Errorf("unknown filetype '%s'", fullname) +} + +func loadPB(pb proto.Message, fullname string) error { + data, err := loadFile(fullname) + if err != nil { + // set pb.Filename that was attempted + return err + } + + if err = proto.Unmarshal(data, pb); err != nil { + return err + } + + return nil +} + +func loadTEXT(pb proto.Message, fullname string) error { + var data []byte + var err error + SetFilename(pb, fullname) + if data, err = loadFile(fullname); err != nil { + return err + } + + // don't even bother with Marshal() + if data == nil { + return ErrEmpty // file is empty + } + + // Unmarshal() + if err = prototext.Unmarshal(data, pb); err != nil { + return ErrMarshal + } + + if fn, err := GetFilename(pb); err != nil { + if fn != fullname { + SetFilename(pb, fullname) + } + } + return nil +} + +// json files are backup Marshal() data in case .text Unmarshal() fails +// they always should have the ".text" filename in them +func loadJSON(pb proto.Message, fullname string) error { + var data []byte + var err error + if data, err = loadFile(fullname); err != nil { + return err + } + + // don't even bother with Marshal() + if data == nil { + return ErrEmpty // file is empty + } + + // Unmarshal() + if err = protojson.Unmarshal(data, pb); err != nil { + return ErrMarshal + } + + if fn, err := GetFilename(pb); err != nil { + if fn != fullname { + SetFilename(pb, fullname) + } + } + return nil +} + +func loadFile(fullname string) ([]byte, error) { + data, err := os.ReadFile(fullname) + if errors.Is(err, os.ErrNotExist) { + // if file does not exist, just return nil. this + return nil, err + } + if err != nil { + return nil, err + } + if len(data) == 0 { + return data, ErrEmpty + } + return data, nil +} + +/* left this here to remind myself just how dumb I can be + +// dumb but simple to read logic +func missingConfig(fullname string) error { + data1, err1 := os.ReadFile(fullname) + if !errors.Is(err1, os.ErrNotExist) { + return err1 + } + + data2, err2 := os.ReadFile(fullname + ".json") + if !errors.Is(err2, os.ErrNotExist) { + return err2 + } + if errors.Is(err1, os.ErrNotExist) && errors.Is(err2, os.ErrNotExist) { + return os.ErrNotExist + } + if (len(data1) == 0) && (len(data2) == 0) { + return ErrEmpty + } + return nil +} +*/ diff --git a/loadByAppname.go b/loadByAppname.go deleted file mode 100644 index 40ae9a4..0000000 --- a/loadByAppname.go +++ /dev/null @@ -1,51 +0,0 @@ -package config - -import ( - "errors" - "os" - - "go.wit.com/log" - "google.golang.org/protobuf/proto" -) - -// loads foo.proto from ~/.cache//foo.pb -func LoadAppnameCache(pb proto.Message, appname string) error { - protoname, err := GetProtobufName(pb) // defined in the foo.proto file - if err != nil { - return err - } - - // Get ~/.cache/appname/protoname.text - fullname := makeCacheFilename(appname, protoname) - - // get the current filename in the protobuf file - curfilename, err := GetFilename(pb) - if err != nil { - return err - } - if curfilename == "" { - // log.Printf("ConfigLoad() read in %s\n", fullname) - } - - if !Exists(fullname) { - SetFilename(pb, fullname) - return os.ErrNotExist - } - - err = loadPB(pb, fullname) - if err != nil { - return ErrMarshal - } - - // If the cache file is new or has moved, this updates it to correct filename - // (the filename is what is used by pb.Save() - 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) - return errors.Join(err, errors.New("something is wrong in lib/config")) - } - } - return nil -} diff --git a/loadCache.go b/loadCache.go deleted file mode 100644 index 2c8b9a9..0000000 --- a/loadCache.go +++ /dev/null @@ -1,318 +0,0 @@ -package config - -import ( - "errors" - "fmt" - "os" - "path/filepath" - "strings" - - "go.wit.com/lib/ENV" - "go.wit.com/log" - "google.golang.org/protobuf/encoding/protojson" - "google.golang.org/protobuf/encoding/prototext" - "google.golang.org/protobuf/proto" -) - -// loads foo.proto from ~/.cache//foo.text -func CacheLoad(pb proto.Message) error { - appname, err := ENV.GetAppname() // already configured by your application - if err != nil { - return err - } - protoname, err := GetProtobufName(pb) // defined in the foo.proto file - if err != nil { - return err - } - - // Get ~/.cache/appname/protoname.text - fullname := makeCacheFilename(appname, protoname) - - // get the current filename in the protobuf file - curfilename, err := GetFilename(pb) - if err != nil { - return err - } - if curfilename == "" { - // log.Printf("ConfigLoad() read in %s\n", fullname) - } - - err = loadPB(pb, fullname) - if err != nil { - return ErrMarshal - } - - // If the cache file is new or has moved, this updates it to correct filename - // (the filename is what is used by pb.Save() - 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) - return errors.Join(err, errors.New("something is wrong in lib/config")) - } - } - return nil -} - -// 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, appname string, protoname string) error { - cacheDir, _ := os.UserCacheDir() - fullpath := filepath.Join(cacheDir, appname) - os.MkdirAll(fullpath, os.ModePerm) - fullname := filepath.Join(fullpath, protoname+".pb") - _, err := SetFilename(pb, fullname) - if err != nil { - pb = nil - os.Remove(fullname) - return err - } - newver, curver, err := LoadVersionCheckPB(pb) - if err != nil { - pb = nil - os.Remove(fullname) - return err - } - _, _ = newver, curver - return nil -} - -func LoadVersionCheckPB(pb proto.Message) (string, string, error) { - var newver string - var pbver string - var err error - - fullname, err := GetFilename(pb) - if err != nil { - return newver, pbver, err - } - // text is supposed to be "easy". Don't verify 'version' - if strings.HasSuffix(fullname, ".text") { - err = loadTEXT(pb, fullname) - return newver, pbver, err - } - - // verify 'version' for .pb files - // application should die if they don't match - var worked bool - newver, err = GetString(pb, "version") - if err != nil { - return newver, pbver, err - } - // maybe don't really verify .json files (?) - // doing it for now anyway. maybe just return an error - if strings.HasSuffix(fullname, ".json") { - if err = loadJSON(pb, fullname); err != nil { - return newver, pbver, err - } - worked = true - } - if strings.HasSuffix(fullname, ".pb") { - if err = loadPB(pb, fullname); err != nil { - return newver, pbver, err - } - _, err = SetFilename(pb, fullname) - if err != nil { - return newver, pbver, err - } - worked = true - } - if !worked { - return newver, pbver, fmt.Errorf("unknown filetype '%s'", fullname) - } - // get the version from the current PB saved on disk - pbver, _ = GetString(pb, "version") - if newver != pbver { - return newver, pbver, VersionMismatch - } - return newver, pbver, nil -} - -// uses the version to die. This is needed because loading binary -// protobuf files with rearranged messages is indeterminate -func LoadPB(pb proto.Message) error { - fullname, err := GetFilename(pb) - if fullname == "" { - panic("config.LoadPB() got blank filename = ''") - } - if err != nil { - return err - } - // this code needs work - newver, pbver, err := LoadVersionCheckPB(pb) - if errors.Is(err, os.ErrNotExist) { - return err - } - if errors.Is(err, VersionMismatch) || (newver != pbver) { - fmt.Println("") - fmt.Printf("VERSION new '%s' != cur PB '%s'\n", newver, pbver) - fmt.Println("") - fmt.Println("Your protobuf file is old and can not be loaded") - fmt.Println("your application must decide how to handle this (delete or fix)") - fmt.Println("always die here. application is broken") - fmt.Println("You must delete or convert the file", fullname) - fmt.Println("") - // probably should ALWAYS PANIC HERE - // upon further study, always die here is better than not - s := fmt.Sprintf("protobuf version wrong. delete or fix %s", fullname) - panic(s) - } - if err != nil { - // return to let the application figure this out - return err - } - return nil -} - -func LoadFromFilename(pb proto.Message, fullname string) error { - return LoadFile(pb, fullname) -} - -func LoadFile(pb proto.Message, fullname string) error { - if strings.HasSuffix(fullname, ".text") { - return loadTEXT(pb, fullname) - } - if strings.HasSuffix(fullname, ".json") { - return loadJSON(pb, fullname) - } - if strings.HasSuffix(fullname, ".pb") { - return loadPB(pb, fullname) - } - - return fmt.Errorf("unknown filetype '%s'", fullname) -} - -func loadPB(pb proto.Message, fullname string) error { - data, err := loadFile(fullname) - if err != nil { - // set pb.Filename that was attempted - return err - } - - if err = proto.Unmarshal(data, pb); err != nil { - return err - } - - return nil -} - -func LoadConfigPB(pb proto.Message, appname string, protoname string) (string, error) { - var fullname string - if strings.HasPrefix(appname, "/") { - fullname = filepath.Join(appname, protoname+".pb") - } else { - configDir, err := os.UserConfigDir() - if err != nil { - return "", err - } - - fullname = filepath.Join(configDir, appname, protoname+".pb") - } - - data, err := loadFile(fullname) - if err != nil { - return fullname, err - } - - // Unmarshal() - if err = proto.Unmarshal(data, pb); err != nil { - return fullname, err - } - - return fullname, nil -} - -func loadTEXT(pb proto.Message, fullname string) error { - var data []byte - var err error - SetFilename(pb, fullname) - if data, err = loadFile(fullname); err != nil { - return err - } - - // don't even bother with Marshal() - if data == nil { - return ErrEmpty // file is empty - } - - // Unmarshal() - if err = prototext.Unmarshal(data, pb); err != nil { - return ErrMarshal - } - - if fn, err := GetFilename(pb); err != nil { - if fn != fullname { - SetFilename(pb, fullname) - } - } - return nil -} - -// json files are backup Marshal() data in case .text Unmarshal() fails -// they always should have the ".text" filename in them -func loadJSON(pb proto.Message, fullname string) error { - var data []byte - var err error - if data, err = loadFile(fullname); err != nil { - return err - } - - // don't even bother with Marshal() - if data == nil { - return ErrEmpty // file is empty - } - - // Unmarshal() - if err = protojson.Unmarshal(data, pb); err != nil { - return ErrMarshal - } - - if fn, err := GetFilename(pb); err != nil { - if fn != fullname { - SetFilename(pb, fullname) - } - } - return nil -} - -/* left this here to remind myself just how dumb I can be - -// dumb but simple to read logic -func missingConfig(fullname string) error { - data1, err1 := os.ReadFile(fullname) - if !errors.Is(err1, os.ErrNotExist) { - return err1 - } - - data2, err2 := os.ReadFile(fullname + ".json") - if !errors.Is(err2, os.ErrNotExist) { - return err2 - } - if errors.Is(err1, os.ErrNotExist) && errors.Is(err2, os.ErrNotExist) { - return os.ErrNotExist - } - if (len(data1) == 0) && (len(data2) == 0) { - return ErrEmpty - } - return nil -} -*/ - -func loadFile(fullname string) ([]byte, error) { - data, err := os.ReadFile(fullname) - if errors.Is(err, os.ErrNotExist) { - // if file does not exist, just return nil. this - return nil, err - } - if err != nil { - return nil, err - } - if len(data) == 0 { - return data, ErrEmpty - } - return data, nil -} diff --git a/loadConfig.go b/loadConfig.go deleted file mode 100644 index 8c7cee8..0000000 --- a/loadConfig.go +++ /dev/null @@ -1,176 +0,0 @@ -package config - -import ( - "errors" - "strings" - - "go.wit.com/lib/ENV" - "go.wit.com/log" - "google.golang.org/protobuf/proto" -) - -// loads foo.proto from ~/.config//foo.text -func ConfigLoad(pb proto.Message) error { - appname, err := ENV.GetAppname() // already configured by your application - if err != nil { - return err - } - protoname, err := GetProtobufName(pb) // defined in the foo.proto file - if err != nil { - return err - } - - curfilename, err := GetFilename(pb) - if err == nil { - return err - } - - // Get ~/.config/appname/protoname.text - fullname := makeConfigFilename(appname, protoname) - - if err = loadTEXT(pb, fullname); err == nil { - // 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) - return errors.Join(err, errors.New("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 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) - return errors.Join(err, errors.New("something is wrong in lib/config")) - } - } - return nil - } - } - } - return ErrMarshal -} - -// loads foo.proto from ~/.config//customName.text -func ConfigLoadByName(pb proto.Message, customName string) (string, error) { - if customName == "" { - return "", errors.New("config.Load() customName can not be blank") - } - appname, err := ENV.GetAppname() // already configured by your application - if err != nil { - return "", err - } - - // Get ~/.config/appname/customName.text - fullname := makeConfigFilename(appname, customName) - - curfilename, err := GetFilename(pb) - if err != nil { - // pb doesn't have 'Filename' - // probably try to load anyway? - log.Info("ConfigLoadByName() FILENAME old=", curfilename) - log.Info("ConfigLoadByName() FILENAME new=", fullname) - log.Info("pb doesn't have 'Filename'") - log.Printf("err = (%v)\n", err) - panic("blah") - // return fullname, err - } - if curfilename == "" { - // log.Printf("ConfigLoadByName() read in %s\n", fullname) - } - - if err = loadTEXT(pb, fullname); err == nil { - // 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) - return fullname, errors.Join(err, errors.New("something is wrong in lib/config")) - } - } - return fullname, 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 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) - return fullname, errors.Join(err, errors.New("something is wrong in lib/config")) - } - } - return fullname, nil - } - } - } - return fullname, ErrMarshal -} - -func LoadByAppName(pb proto.Message, customName string, appName string) (string, error) { - if customName == "" { - return "", errors.New("config.LoadByAppName() customName can not be blank") - } - if appName == "" { - return "", errors.New("config.LoadByAppName() appName can not be blank") - } - - // Get ~/.config/appname/customName.text - fullname := makeConfigFilename(appName, customName) - - curfilename, err := GetFilename(pb) - if err != nil { - // pb doesn't have 'Filename' - // probably try to load anyway? - log.Info("ConfigLoadByName() FILENAME old=", curfilename) - log.Info("ConfigLoadByName() FILENAME new=", fullname) - log.Info("pb doesn't have 'Filename'") - log.Printf("err = (%v)\n", err) - panic("blah") - // return fullname, err - } - if curfilename == "" { - // log.Printf("ConfigLoadByName() read in %s\n", fullname) - } - - if err = loadTEXT(pb, fullname); err == nil { - // 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) - return fullname, errors.Join(err, errors.New("something is wrong in lib/config")) - } - } - return fullname, 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 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) - return fullname, errors.Join(err, errors.New("something is wrong in lib/config")) - } - } - return fullname, nil - } - } - } - return fullname, ErrMarshal -} diff --git a/makeFilenames.go b/makeFilenames.go index 6ade093..7c1b86a 100644 --- a/makeFilenames.go +++ b/makeFilenames.go @@ -9,7 +9,7 @@ import ( ) // ~/.config/appname/protoname.text -func makeConfigFilename(appname string, protoname string) string { +func MakeConfigFilename(appname string, protoname string) string { var err error configDir, err := os.UserConfigDir() if err != nil { @@ -20,7 +20,7 @@ func makeConfigFilename(appname string, protoname string) string { } // ~/.cache/appname/protoname.pb -func makeCacheFilename(appname string, protoname string) string { +func MakeCacheFilename(appname string, protoname string) string { var err error cacheDir, err := os.UserCacheDir() if err != nil { diff --git a/panic.go b/panic.go index 8ed4df7..efab850 100644 --- a/panic.go +++ b/panic.go @@ -1,15 +1,24 @@ package config -import "google.golang.org/protobuf/proto" +import ( + "fmt" + + "google.golang.org/protobuf/proto" +) // should this be a function? -func LoadPanicPB(pb proto.Message) error { +func LoadPanic(pb proto.Message) error { fullname, err := GetFilename(pb) if err != nil { - panic("config.LoadPB() err") + panic("config.LoadPanic() pb doesn't have pb.Filename") } if fullname == "" { - panic("config.LoadPB() got blank filename = ''") + panic("config.LoadPanic() got blank filename = ''") + } + err = ReLoad(pb) + if err != nil { + panic(fmt.Sprintf("config.LoadPanic() err (%v)", err)) } - return LoadPB(pb) + + return nil } diff --git a/save.go b/save.go index 01dcd0c..b352601 100644 --- a/save.go +++ b/save.go @@ -13,13 +13,13 @@ import ( var ErrProtoFilename error = fmt.Errorf("proto does not have Filename") -func ConfigSave(pb proto.Message) error { - return saveTEXT(pb, "") +func SavePB(pb proto.Message) error { + return Save(pb) } // writes the protobuf to disk // uses the already configured Filename -func SavePB(pb proto.Message) error { +func Save(pb proto.Message) error { fullname, err := GetFilename(pb) if err != nil { return err @@ -27,6 +27,12 @@ func SavePB(pb proto.Message) error { return SaveToFilename(pb, fullname) } +// special case. adds a header to the TEXT output +func SaveWithHeader(pb proto.Message, header string) error { + err := saveTEXT(pb, header) + return err +} + // writes the protobuf to disk (sets Filename if PB has 'Filename') func SaveToFilename(pb proto.Message, fullname string) error { basedir, _ := filepath.Split(fullname) @@ -67,11 +73,6 @@ func saveProto(pb proto.Message, fullname string) error { return configWrite(fullname, data) } -func SaveWithHeader(pb proto.Message, header string) error { - err := saveTEXT(pb, header) - return err -} - func saveTEXT(pb proto.Message, header string) error { // get pb.Filename if it is there in the .proto file fullname, err := GetFilename(pb) diff --git a/versionCheck.go b/versionCheck.go new file mode 100644 index 0000000..1beb7a5 --- /dev/null +++ b/versionCheck.go @@ -0,0 +1,53 @@ +package config + +import ( + "errors" + + "go.wit.com/lib/protobuf/filepb" + "google.golang.org/protobuf/proto" +) + +// verify 'version' for .pb files +// application should die if they don't match +// returns (newVersion, oldVersion, error) +func VersionCheck(pb proto.Message) (string, string, error) { + fullname, err := GetFilename(pb) + if err != nil { + return "", "", err + } + return VersionCheckFile(pb, fullname) +} + +// verify 'version' for .pb files +// application should die if they don't match +// returns (newVersion, oldVersion, error) +func VersionCheckFile(pb proto.Message, filename string) (string, string, error) { + var newVersion string + var newUuid string + var err error + + newVersion, err = GetString(pb, "version") + if err != nil { + return newVersion, "", err + } + + newUuid, err = GetString(pb, "uuid") + if err != nil { + return newVersion, newUuid, err + } + + oldVersion, oldUuid, err := filepb.IdentifyPB(filename) + if err != nil { + return newVersion, newUuid, err + } + + if newVersion != oldVersion { + return newVersion, oldVersion, errors.New("version mismatch") + } + + if newUuid != oldUuid { + return newVersion, oldVersion, errors.New("UUID mismatch") + } + + return newVersion, oldVersion, nil +} -- cgit v1.2.3