package zoopb import ( "fmt" "strings" "go.wit.com/lib/config" "go.wit.com/lib/gui/shell" "go.wit.com/log" "google.golang.org/protobuf/encoding/prototext" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/reflect/protoreflect" ) func (pb *Package) Print() (string, error) { shell.RunVerbose([]string{"dpkg", "-I", pb.Filename}) log.Info("\nNEW PB START") log.Info(strings.TrimSpace(prototext.Format(pb))) log.Info("NEW PB END\n") log.Info("Attempt to walk pb.DebInfo") if s, err := printDebInfoStrings(pb, true); err != nil { return s, err } return "todo", nil } func (pb *Package) GetDebianControlFile() (string, error) { var s string var err error if s, err = printDebInfoStrings(pb, true); err != nil { return s, err } return s, err } // if the varname isn't in the proto msg, then it'll try to read // the same varname from the base message. This allows the // .proto file to define the print order of the fields func printDebInfoStrings(pb proto.Message, fallback bool) (string, error) { var verbose bool var controlfile string // 1. Get the reflection interface for the top-level message. msg := pb.ProtoReflect() descriptor := msg.Descriptor() // 2. Find the FieldDescriptor for the nested "debInfo" message. // Note: Protobuf field names like "deb_info" are often canonicalized // to "debInfo" in Go code, but reflection should use the name from the .proto file. // We'll check for both common variations for robustness. debInfoFieldDesc := descriptor.Fields().ByName("debInfo") if debInfoFieldDesc == nil { debInfoFieldDesc = descriptor.Fields().ByName("deb_info") } if debInfoFieldDesc == nil { return controlfile, fmt.Errorf("field 'debInfo' or 'deb_info' not found in message %s", descriptor.FullName()) } // 3. Get the actual nested message object from the parent. debInfoValue := msg.Get(debInfoFieldDesc) debInfoMsg := debInfoValue.Message() // 4. Check if the nested message is valid and has been set. if !debInfoMsg.IsValid() { fmt.Printf("--- Field '%s' in [%s] is not set. --- \n", debInfoFieldDesc.Name(), descriptor.FullName()) return controlfile, nil // Not an error, just nothing to print. } // 5. Now, iterate over the fields of the nested debInfo message. debInfoDescriptor := debInfoMsg.Descriptor() fields := debInfoDescriptor.Fields() if verbose { fmt.Printf("--- Listing String Fields in '%s' --- \n", debInfoFieldDesc.Name()) } foundStrings := false for i := 0; i < fields.Len(); i++ { fieldDesc := fields.Get(i) // 6. Check if the field's kind is a string. if fieldDesc.Kind() == protoreflect.StringKind { var value string var fieldName protoreflect.Name // Get the value from the debInfo message object. value = debInfoMsg.Get(fieldDesc).String() fieldName = fieldDesc.Name() if value == "" { if verbose { log.Info(string(fieldName), "is blank") } newval, err := config.GetString(pb, string(fieldName)) if err != nil { if verbose { log.Info("GetString() failed", newval, err) } continue } else { if verbose { log.Info("GetString() worked:", newval) } value = newval } } if value == "" { // still blank after backup lookup continue } // Print the result. fmt.Printf("%s: \"%s\"\n", string(fieldName), value) controlfile += fmt.Sprintf("%s: %s\n", string(fieldName), value) foundStrings = true } } if !foundStrings { fmt.Println(" (No string fields found)") } return controlfile, nil } /* // --- Mock structs for demonstration. In a real project, these would be auto-generated. --- type DebInfo struct { PackageName string `protobuf:"bytes,1,opt,name=package_name,json=packageName,proto3"` Version string `protobuf:"bytes,2,opt,name=version,proto3"` Size int64 `protobuf:"varint,3,opt,name=size,proto3"` Architecture string `protobuf:"bytes,4,opt,name=architecture,proto3"` Maintainer string `protobuf:"bytes,5,opt,name=maintainer,proto3"` } func (x *DebInfo) Reset() { *x = DebInfo{} } func (x *DebInfo) String() string { return "dummy" } func (*DebInfo) ProtoMessage() {} func (x *DebInfo) ProtoReflect() protoreflect.Message { return nil } // Simplified for example type Package struct { Filename string `protobuf:"bytes,1,opt,name=filename,proto3"` DebInfo *DebInfo `protobuf:"bytes,2,opt,name=deb_info,json=debInfo,proto3"` } func (x *Package) Reset() { *x = Package{} } func (x *Package) String() string { return "dummy" } func (*Package) ProtoMessage() {} func (x *Package) ProtoReflect() protoreflect.Message { return nil } // Simplified for example // main function to demonstrate the usage of PrintDebInfoStrings func main() { fmt.Println("This file contains a utility function 'PrintDebInfoStrings'.") fmt.Println("The main function is for demonstration purposes and relies on mock protobuf structs.") fmt.Println("To use the utility, import this package into your project.") // In a real application, you would need to have the actual generated protobuf code // for the reflection to work. The following is a conceptual demonstration. pkg := &Package{ Filename: "example_1.0_amd64.deb", DebInfo: &DebInfo{ PackageName: "example", Version: "1.0", Size: 1024, Architecture: "amd64", Maintainer: "A Developer ", }, } fmt.Println("\n--- Conceptual run with a populated 'debInfo' field ---") // Simulate what the real output would be, as the mock structs lack reflection data. fmt.Printf("--- Listing String Fields in 'deb_info' --- ") fmt.Printf(" %s: \"%s\"\n", "package_name", pkg.DebInfo.PackageName) fmt.Printf(" %s: \"%s\"\n", "version", pkg.DebInfo.Version) fmt.Printf(" %s: \"%s\"\n", "architecture", pkg.DebInfo.Architecture) fmt.Printf(" %s: \"%s\"\n", "maintainer", pkg.DebInfo.Maintainer) fmt.Println("\n--- Conceptual run with a nil 'debInfo' field ---") fmt.Printf("--- Field 'deb_info' in [] is not set. --- ") } */ // func printDebInfoStrings(pb proto.Message, fallback bool) (string, error) { func GetDebInfoFields(pb proto.Message) ([]string, error) { var debInfo []string // 1. Get the reflection interface for the top-level message. msg := pb.ProtoReflect() descriptor := msg.Descriptor() // 2. Find the FieldDescriptor for the nested "debInfo" message. // Note: Protobuf field names like "deb_info" are often canonicalized // to "debInfo" in Go code, but reflection should use the name from the .proto file. // We'll check for both common variations for robustness. debInfoFieldDesc := descriptor.Fields().ByName("debInfo") if debInfoFieldDesc == nil { debInfoFieldDesc = descriptor.Fields().ByName("deb_info") } if debInfoFieldDesc == nil { // return controlfile, fmt.Errorf("field 'debInfo' or 'deb_info' not found in message %s", descriptor.FullName()) return nil, fmt.Errorf("field 'debInfo' or 'deb_info' not found in message %s", descriptor.FullName()) } // 3. Get the actual nested message object from the parent. debInfoValue := msg.Get(debInfoFieldDesc) debInfoMsg := debInfoValue.Message() // 4. Check if the nested message is valid and has been set. if !debInfoMsg.IsValid() { // fmt.Printf("--- Field '%s' in [%s] is not set. --- \n", debInfoFieldDesc.Name(), descriptor.FullName()) return nil, fmt.Errorf("debInfoMsg.InValid()") } // 5. Now, iterate over the fields of the nested debInfo message. debInfoDescriptor := debInfoMsg.Descriptor() fields := debInfoDescriptor.Fields() for i := 0; i < fields.Len(); i++ { fieldDesc := fields.Get(i) debInfo = append(debInfo, string(fieldDesc.Name())) // debInfo = append(debInfo, fieldDesc.Name()) } return debInfo, nil }