diff options
| author | Jeff Carr <[email protected]> | 2025-10-21 08:03:57 -0500 |
|---|---|---|
| committer | Jeff Carr <[email protected]> | 2025-10-21 08:03:57 -0500 |
| commit | e734c024bd18ad13e0f5d162dfee256b5e020be7 (patch) | |
| tree | fe747882ee0eda816c02cf0b30d8f3454be3c3a8 | |
| parent | f112df6b773b81fa4f5deb665859bbd005ed3e84 (diff) | |
housecleaning
| -rw-r--r-- | loadCache.go | 271 | ||||
| -rw-r--r-- | loadRaw.go | 339 |
2 files changed, 271 insertions, 339 deletions
diff --git a/loadCache.go b/loadCache.go index 40746a3..f5cbef1 100644 --- a/loadCache.go +++ b/loadCache.go @@ -2,12 +2,21 @@ 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" ) +var ErrEmpty error = fmt.Errorf("config file was empty") +var ErrMarshal error = fmt.Errorf("protobuf parse error") + // loads foo.proto from ~/.cache/<appname>/foo.text func CacheLoad(pb proto.Message) error { appname, err := ENV.GetAppname() // already configured by your application @@ -48,3 +57,265 @@ func CacheLoad(pb proto.Message) error { } 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/loadRaw.go b/loadRaw.go deleted file mode 100644 index 3198783..0000000 --- a/loadRaw.go +++ /dev/null @@ -1,339 +0,0 @@ -package config - -// functions to import and export the protobuf -// data to and from config files - -import ( - "errors" - "fmt" - "os" - "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/<appname>/ -func Load(appname string) ([]byte, string) { -} -*/ - -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/<appname> -// - []byte : the contents of the file -// - error on read -func ConfigLoadRaw(pb proto.Message, appname string, protoname string) error { - // Get ~/.config/appname/protoname.text - fullname := makeConfigFilename(appname, protoname) - - var pbFilenameSupport bool - var err error - curfilename, err := GetFilename(pb) - if err == nil { - pbFilenameSupport = true - } else { - // this .proto doesn't have the Filename variable/message - } - - 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 - } - } - } - return ErrMarshal -} - -// 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 -} |
