From 367f681a490a48944c5d05ba0fe89d0099c0337d Mon Sep 17 00:00:00 2001 From: Jeff Carr Date: Fri, 17 Oct 2025 09:41:52 -0500 Subject: move stuff here from lib/config --- Makefile | 3 + fakefile.disabled | 10 ++++ fakefile.go | 159 ++++++++++++++++++++++++++++++++++++++++++++++++++++ identify.go | 4 +- lookupPB.go | 165 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ mockProto.go | 44 +++++++++++++++ 6 files changed, 383 insertions(+), 2 deletions(-) create mode 100644 fakefile.disabled create mode 100644 fakefile.go create mode 100644 lookupPB.go create mode 100644 mockProto.go diff --git a/Makefile b/Makefile index aa6482e..1f704e3 100644 --- a/Makefile +++ b/Makefile @@ -17,6 +17,9 @@ generate: clean go mod tidy go generate +raw-protoc: + cd ~/go/src && protoc --go_out=. --proto_path=go.wit.com/lib/protobuf/filepb --go_opt=Mfakefile.proto=go.wit.com/lib/protobuf/filepb fakefile.proto + go-generate: rm -f *.pb.go *.patch diff --git a/fakefile.disabled b/fakefile.disabled new file mode 100644 index 0000000..72a06d9 --- /dev/null +++ b/fakefile.disabled @@ -0,0 +1,10 @@ +syntax = "proto3"; + +// name changed so automated .proto tools don't see it + +package filepb; + +message FakeFile { + string uuid = 1; // `autogenpb:uuid:` + string version = 2; // `autogenpb:version:` +} diff --git a/fakefile.go b/fakefile.go new file mode 100644 index 0000000..1400eb9 --- /dev/null +++ b/fakefile.go @@ -0,0 +1,159 @@ +// kept here to use for identifying .pb files using autogenpb + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.33.0 +// protoc v3.21.12 +// source: fakefile.proto + +package filepb + +import ( + reflect "reflect" + sync "sync" + + "google.golang.org/protobuf/proto" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type FakeFile struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Uuid string `protobuf:"bytes,1,opt,name=uuid,proto3" json:"uuid,omitempty"` // `autogenpb:uuid:` + Version string `protobuf:"bytes,2,opt,name=version,proto3" json:"version,omitempty"` // `autogenpb:version:` +} + +// unmarshal from wire. You have won. +func (v *FakeFile) Unmarshal(data []byte) error { + return proto.Unmarshal(data, v) +} + +func (x *FakeFile) Reset() { + *x = FakeFile{} + if protoimpl.UnsafeEnabled { + mi := &file_fakefile_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FakeFile) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FakeFile) ProtoMessage() {} + +func (x *FakeFile) ProtoReflect() protoreflect.Message { + mi := &file_fakefile_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FakeFile.ProtoReflect.Descriptor instead. +func (*FakeFile) Descriptor() ([]byte, []int) { + return file_fakefile_proto_rawDescGZIP(), []int{0} +} + +func (x *FakeFile) GetUuid() string { + if x != nil { + return x.Uuid + } + return "" +} + +func (x *FakeFile) GetVersion() string { + if x != nil { + return x.Version + } + return "" +} + +var File_fakefile_proto protoreflect.FileDescriptor + +var file_fakefile_proto_rawDesc = []byte{ + 0x0a, 0x0e, 0x66, 0x61, 0x6b, 0x65, 0x66, 0x69, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x12, 0x06, 0x66, 0x69, 0x6c, 0x65, 0x70, 0x62, 0x22, 0x38, 0x0a, 0x08, 0x46, 0x61, 0x6b, 0x65, + 0x46, 0x69, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x75, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x75, 0x75, 0x69, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, + 0x6f, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_fakefile_proto_rawDescOnce sync.Once + file_fakefile_proto_rawDescData = file_fakefile_proto_rawDesc +) + +func file_fakefile_proto_rawDescGZIP() []byte { + file_fakefile_proto_rawDescOnce.Do(func() { + file_fakefile_proto_rawDescData = protoimpl.X.CompressGZIP(file_fakefile_proto_rawDescData) + }) + return file_fakefile_proto_rawDescData +} + +var file_fakefile_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_fakefile_proto_goTypes = []interface{}{ + (*FakeFile)(nil), // 0: filepb.FakeFile +} +var file_fakefile_proto_depIdxs = []int32{ + 0, // [0:0] is the sub-list for method output_type + 0, // [0:0] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_fakefile_proto_init() } +func file_fakefile_proto_init() { + if File_fakefile_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_fakefile_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FakeFile); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_fakefile_proto_rawDesc, + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_fakefile_proto_goTypes, + DependencyIndexes: file_fakefile_proto_depIdxs, + MessageInfos: file_fakefile_proto_msgTypes, + }.Build() + File_fakefile_proto = out.File + file_fakefile_proto_rawDesc = nil + file_fakefile_proto_goTypes = nil + file_fakefile_proto_depIdxs = nil +} diff --git a/identify.go b/identify.go index 618e34c..0de53a2 100644 --- a/identify.go +++ b/identify.go @@ -12,8 +12,8 @@ func IdentifyPB(filename string) (string, string, error) { return "", "", err } - var pb *Identify - pb = new(Identify) + var pb *FakeFile + pb = new(FakeFile) if err := pb.Unmarshal(data); err != nil { return "", "", err } diff --git a/lookupPB.go b/lookupPB.go new file mode 100644 index 0000000..531e0d2 --- /dev/null +++ b/lookupPB.go @@ -0,0 +1,165 @@ +package filepb + +import ( + "errors" + "fmt" + + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/reflect/protoreflect" +) + +var ErrProtoNoVarName error = errors.New("name not in .proto") +var ErrProtoVarNotString error = errors.New("name exists but is not a string") + +// Gemini AI can help author some pretty good protobuf code. +// I never remember the syntax for 'reflect' on these things. + +// sets "Filename" if it exists in the protobuf +func SetFilename(pb proto.Message, filename string) (bool, error) { + 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 { + fieldName = protoreflect.Name("filename") + fieldDescriptor = descriptor.Fields().ByName(fieldName) + } + + if fieldDescriptor == nil { + return false, fmt.Errorf("fieldDescriptor == nil") + } + + if fieldDescriptor.Kind() != protoreflect.StringKind { + // The field exists but is not a string, so we can't return it as one. + return false, fmt.Errorf("The field exists but is not a string") + } + + 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, nil +} + +// this will try both "filename" and "Filename" +func GetFilename(pb proto.Message) (string, error) { + // 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) + + // try upper case + if fieldDescriptor == nil { + fieldName = protoreflect.Name("filename") + fieldDescriptor = descriptor.Fields().ByName(fieldName) + // if fieldDescriptor == nil { + // panic(".proto file: try 'Filename', not 'filename'? or maybe nomutex if pb.Marshal() fails") + // } + } + + // 4. Check if the field was found. If not, return false. + if fieldDescriptor == nil { + return "", ErrProtoNoVarName + } + + // 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 "", ErrProtoVarNotString + } + + // 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(), nil +} + +// this will try both "filename" and "Filename" +func GetString(pb proto.Message, varname string) (string, error) { + // 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(varname) + fieldDescriptor := descriptor.Fields().ByName(fieldName) + + // 4. Check if the field was found. If not, return false. + if fieldDescriptor == nil { + return "", ErrProtoNoVarName + } + + // 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 "", ErrProtoVarNotString + } + + // 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(), nil +} + +// don't do this. use prototext.Format(pb) +// duh. I'm dumb. I literally wasted my time + was being lazy so I +// just asked asking Gemini AI to make some function for this +// when, for years, I use prototext.Format() all over the place +func printStrings(pb proto.Message) { + // 1. Get the protoreflect.Message interface. + msg := pb.ProtoReflect() + + // It's good practice to check if the message is valid. + if !msg.IsValid() { + fmt.Printf("Error: Provided protobuf message is not valid.") + return + } + + // 2. Get the message's descriptor. + descriptor := msg.Descriptor() + fmt.Printf("--- Listing String Fields in [%s] ---\n", descriptor.FullName()) + + // 3. Get the collection of all field descriptors for this message. + fields := descriptor.Fields() + + // 4. Iterate over all the fields. + for i := 0; i < fields.Len(); i++ { + // Get the descriptor for the field at the current index. + fieldDescriptor := fields.Get(i) + + // 5. Check if the field's kind is a string. + if fieldDescriptor.Kind() == protoreflect.StringKind { + // 6. If it is a string, get its name and value. + fieldName := fieldDescriptor.Name() + value := msg.Get(fieldDescriptor).String() + + // 7. Print the formatted result. + // We add a check to see if the field is populated. An empty string + // is a valid value, but you might only want to see set fields. + if msg.Has(fieldDescriptor) { + fmt.Printf(" %s: \"%s\"\n", fieldName, value) + } + } + } +} diff --git a/mockProto.go b/mockProto.go new file mode 100644 index 0000000..1b104c3 --- /dev/null +++ b/mockProto.go @@ -0,0 +1,44 @@ +package filepb + +import ( + "google.golang.org/protobuf/encoding/prototext" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + timestamppb "google.golang.org/protobuf/types/known/timestamppb" +) + +// --- Mock of a generated user.pb.go file --- +type PatchError struct { + // Note: The `XXX_` fields are part of the older struct tags, + // but the reflection logic comes from the modern API. + // In a real generated file, you wouldn't see these tags. + UserName string `protobuf:"bytes,1,opt,name=user_name,json=userName,proto3" json:"user_name,omitempty"` + UserId int64 `protobuf:"varint,2,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"` + Email string `protobuf:"bytes,3,opt,name=email,proto3" json:"email,omitempty"` + LastLogin *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=last_login,json=lastLogin,proto3" json:"last_login,omitempty"` +} + +func (x *PatchError) Reset() { *x = PatchError{} } +func (x *PatchError) String() string { return prototext.Format(x) } // Modern way to implement String() +func (*PatchError) ProtoMessage() {} + +// In a real generated file, ProtoReflect() would be fully implemented. +// For this example, we don't need it as the marshallers will use reflection. +func (x *PatchError) ProtoReflect() protoreflect.Message { return nil } + +// --- End of mock --- + +/* + user := &User{ + UserName:"jdoe", + UserId: 12345, + Email: "jdoe@example.com", + LastLogin: timestamppb.Now(), + } + + fmt.Println"==========================================================") + fmt.Println"1. The Best Method: prototext.Format (like spew.Dump)") + fmt.Println"==========================================================") +// The prototext.Format function is the most direct equivalent. + outText := prototext.Format(user) + fmt.Println(outText) +*/ -- cgit v1.2.3