diff options
| author | Jeff Carr <[email protected]> | 2025-10-22 09:19:26 -0500 | 
|---|---|---|
| committer | Jeff Carr <[email protected]> | 2025-10-22 09:19:26 -0500 | 
| commit | 23b2d19bc03b4db253b15cc6fef7176ab31d4f18 (patch) | |
| tree | 275cb2d3fc92cb5af997582c715f4619eb1d68d2 | |
| parent | 820fa4b7e71690accbd17063894032140bd9c4b6 (diff) | |
finally can fix the Load() and Save() names
| -rw-r--r-- | cacheDir.go | 68 | ||||
| -rw-r--r-- | configDir.go | 88 | ||||
| -rw-r--r-- | load.go | 170 | ||||
| -rw-r--r-- | loadByAppname.go | 51 | ||||
| -rw-r--r-- | loadCache.go | 318 | ||||
| -rw-r--r-- | loadConfig.go | 176 | ||||
| -rw-r--r-- | makeFilenames.go | 4 | ||||
| -rw-r--r-- | panic.go | 19 | ||||
| -rw-r--r-- | save.go | 17 | ||||
| -rw-r--r-- | versionCheck.go | 53 | 
10 files changed, 404 insertions, 560 deletions
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/<appname>/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/<appname>/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/<appname>/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 +} @@ -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/<appname>/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/<appname>/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/<appname>/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/<appname>/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 { @@ -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  } @@ -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 +}  | 
