diff --git a/findFilename.go b/findFilename.go new file mode 100644 index 0000000..d69b538 --- /dev/null +++ b/findFilename.go @@ -0,0 +1,70 @@ +package config + +import ( + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/reflect/protoreflect" +) + +// Gemini AI can help author some pretty good protobuf code. +// I never remember the syntax for 'reflect' on these things. + +// returns "Filename" if it exists in the protobuf +func GetFilename(pb proto.Message) (string, bool) { + // 1. Get the protoreflect.Message interface from the message. + // This is the entry point to the reflection API. + msg := pb.ProtoReflect() + + // 2. Get the message's descriptor, which contains metadata about its fields. + descriptor := msg.Descriptor() + + // 3. Find the specific field descriptor by its protobuf name ("Filename"). + // Note: The field name must match the name in the .proto file. + fieldName := protoreflect.Name("Filename") + fieldDescriptor := descriptor.Fields().ByName(fieldName) + + // 4. Check if the field was found. If not, return false. + if fieldDescriptor == nil { + return "", false + } + + // 5. (Optional but recommended) Verify the field is a string type. + if fieldDescriptor.Kind() != protoreflect.StringKind { + // The field exists but is not a string, so we can't return it as one. + return "", false + } + + // 6. If the field exists and is a string, get its value. + // The value is returned as a protoreflect.Value. + value := msg.Get(fieldDescriptor) + + // 7. Convert the protoreflect.Value to a native Go string. + return value.String(), true +} + +// sets "Filename" if it exists in the protobuf +func SetFilename(pb proto.Message, filename string) bool { + msg := pb.ProtoReflect() // This is the entry point to the reflection API. + + descriptor := msg.Descriptor() // Get the message's descriptor, which contains metadata about its fields. + + fieldName := protoreflect.Name("Filename") + fieldDescriptor := descriptor.Fields().ByName(fieldName) + + if fieldDescriptor == nil { + return false + } + + if fieldDescriptor.Kind() != protoreflect.StringKind { + // The field exists but is not a string, so we can't return it as one. + return false + } + + valueToSet := protoreflect.ValueOfString(filename) + + // 6. If the field exists and is a string, get its value. + // The value is returned as a protoreflect.Value. + msg.Set(fieldDescriptor, valueToSet) + + // 7. Convert the protoreflect.Value to a native Go string. + return true +} diff --git a/load.go b/load.go index 31e169a..1568968 100644 --- a/load.go +++ b/load.go @@ -1,14 +1,71 @@ package config +// functions to import and export the protobuf +// data to and from config files + import ( + "errors" "os" - "strings" - "unicode" + "path/filepath" "go.wit.com/log" - "golang.org/x/term" + "google.golang.org/protobuf/encoding/prototext" + "google.golang.org/protobuf/proto" ) +/* // loads a file from ~/.config// func Load(argname string) ([]byte, string) { } +*/ + +var ErrEmpty error = log.Errorf("file was empty") + +// returns: +// - Full path to the config file. usually: ~/.config/ +// - []byte : the contents of the file +// - error on read +func ConfigLoad(pb proto.Message, argname string, protoname string) error { + var data []byte + var fullname string + homeDir, err := os.UserHomeDir() + if err != nil { + return err + } + + fullname = filepath.Join(homeDir, ".config", argname, protoname+".text") + if data, err = loadFile(fullname); err != nil { + log.Warn("config file failed to load", err) + // something went wrong loading the file + 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 err + } + + // set pb.Filename if it is there in the .proto file + SetFilename(pb, fullname) + + log.Infof("ConfigLoad() arg=%s, proto=%s\n", argname, protoname) + 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 { + // log.Info("open config file :", err) + return nil, err + } + return data, nil +} diff --git a/save.go b/save.go new file mode 100644 index 0000000..35c5bab --- /dev/null +++ b/save.go @@ -0,0 +1,50 @@ +package config + +// functions to import and export the protobuf +// data to and from config files + +/* + +func (e *Events) Save() { + var fullname string + base, _ := filepath.Split(argv.Config) + fullname = filepath.Join(base, "events.pb") + + data, err := e.Marshal() + if err != nil { + log.Info("proto.Marshal() failed", err) + return + } + log.Info("proto.Marshal() worked len", len(data)) + configWrite(fullname, data) +} + + +func configWrite(fullname string, data []byte) error { + if _, base := filepath.Split(fullname); base == "" { + return fmt.Errorf("--config option not set") + } + cfgfile, err := os.OpenFile(fullname, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644) + defer cfgfile.Close() + if err != nil { + log.Warn("open config file :", err) + return err + } + cfgfile.Write(data) + return nil +} + +func (m *Portmaps) configWrite(fullname string, data []byte) error { + if _, base := filepath.Split(fullname); base == "" { + return fmt.Errorf("--config option not set") + } + cfgfile, err := os.OpenFile(fullname, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644) + defer cfgfile.Close() + if err != nil { + log.Warn("open config file :", err) + return err + } + cfgfile.Write(data) + return nil +} +*/