github.com/cosmos/cosmos-proto@v1.0.0-beta.3/features/fastreflection/has.go (about) 1 package fastreflection 2 3 import ( 4 "github.com/cosmos/cosmos-proto/generator" 5 "google.golang.org/protobuf/compiler/protogen" 6 "google.golang.org/protobuf/reflect/protoreflect" 7 ) 8 9 const ( 10 mathPkg = protogen.GoImportPath("math") 11 ) 12 13 // fields are not populated if 14 // if scalar: value != zero value 15 // if msg value != nil 16 // if list len(list) != 0 17 // if map len(map) != 0 18 // if oneof: oneof != nil (if oneof is scalar do we need to check it??) 19 // if bytes: len(bytes) != 0 20 type hasGen struct { 21 *generator.GeneratedFile 22 typeName string 23 message *protogen.Message 24 } 25 26 func (g *hasGen) genComments() { 27 g.P("// Has reports whether a field is populated.") 28 g.P("//") 29 g.P("// Some fields have the property of nullability where it is possible to") 30 g.P("// distinguish between the default value of a field and whether the field") 31 g.P("// was explicitly populated with the default value. Singular message fields,") 32 g.P("// member fields of a oneof, and proto2 scalar fields are nullable. Such") 33 g.P("// fields are populated only if explicitly set.") 34 g.P("//") 35 g.P("// In other cases (aside from the nullable cases above),") 36 g.P("// a proto3 scalar field is populated if it contains a non-zero value, and") 37 g.P("// a repeated field is populated if it is non-empty.") 38 } 39 40 func (g *hasGen) generate() { 41 g.genComments() 42 g.P("func (x *", g.typeName, ") Has(fd ", protoreflectPkg.Ident("FieldDescriptor"), ") bool {") 43 g.P("switch fd.FullName() {") 44 for _, field := range g.message.Fields { 45 g.genField(field) 46 } 47 g.P("default:") 48 g.P("if fd.IsExtension() {") 49 g.P("panic(", fmtPkg.Ident("Errorf"), "(\"proto3 declared messages do not support extensions: ", g.message.Desc.FullName(), "\"))") 50 g.P("}") 51 g.P("panic(fmt.Errorf(\"message ", g.message.Desc.FullName(), " does not contain field %s\", fd.FullName()))") 52 g.P("}") 53 g.P("}") 54 } 55 56 func (g *hasGen) genField(field *protogen.Field) { 57 g.P("case \"", field.Desc.FullName(), "\":") 58 if field.Desc.HasPresence() || field.Desc.IsList() || field.Desc.IsMap() || field.Desc.Kind() == protoreflect.BytesKind { 59 g.genNullable(field) 60 return 61 } 62 63 switch field.Desc.Kind() { 64 case protoreflect.FloatKind: 65 g.P("return x.", field.GoName, " != ", zeroValueForField(nil, field), " || ", mathPkg.Ident("Signbit"), "(float64(x.", field.GoName, "))") 66 case protoreflect.DoubleKind: 67 g.P("return x.", field.GoName, " != ", zeroValueForField(nil, field), " || ", mathPkg.Ident("Signbit"), "(x.", field.GoName, ")") 68 default: 69 g.P("return x.", field.GoName, " != ", zeroValueForField(nil, field)) 70 } 71 72 } 73 74 func (g *hasGen) genNullable(field *protogen.Field) { 75 switch { 76 case field.Desc.ContainingOneof() != nil: 77 // case oneof is nil 78 g.P("if x.", field.Oneof.GoName, " == nil {") 79 g.P("return false") 80 // if oneof is not nil we need to try cast it to the concrete type 81 // and if it succeeds then it means the message has the field 82 g.P("} else if _, ok := x.", field.Oneof.GoName, ".(*", field.GoIdent, "); ok {") 83 g.P("return true") 84 g.P("} else { ") 85 g.P("return false") 86 g.P("}") 87 case field.Desc.IsMap(), field.Desc.IsList(), field.Desc.Kind() == protoreflect.BytesKind: 88 g.P("return len(x.", field.GoName, ") != 0") 89 case field.Desc.Kind() == protoreflect.MessageKind: 90 g.P("return x.", field.GoName, " != nil") 91 default: 92 panic("unknown case") 93 } 94 }