github.com/cosmos/cosmos-proto@v1.0.0-beta.3/features/fastreflection/mutable.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 type mutableGen struct { 10 *generator.GeneratedFile 11 typeName string 12 message *protogen.Message 13 } 14 15 func (g *mutableGen) generate() { 16 g.genComment() 17 g.P("func (x *", g.typeName, ") Mutable(fd ", protoreflectPkg.Ident("FieldDescriptor"), ") ", protoreflectPkg.Ident("Value"), " {") 18 g.P("switch fd.FullName() {") 19 // we first output all the fields that are mutable 20 for _, field := range g.message.Fields { 21 if !mutable(field) { 22 continue 23 } 24 g.P("case \"", field.Desc.FullName(), "\":") 25 g.genField(field) 26 } 27 // then we parse those that are not mutable 28 for _, field := range g.message.Fields { 29 if mutable(field) { 30 continue 31 } 32 g.P("case \"", field.Desc.FullName(), "\":") 33 g.P("panic(", fmtPkg.Ident("Errorf"), "(\"field ", field.Desc.Name(), " of message ", g.message.Desc.FullName(), " is not mutable\"))") 34 } 35 // then the default case 36 g.P("default:") 37 g.P("if fd.IsExtension() {") 38 g.P("panic(", fmtPkg.Ident("Errorf"), "(\"proto3 declared messages do not support extensions: ", g.message.Desc.FullName(), "\"))") 39 g.P("}") 40 g.P("panic(fmt.Errorf(\"message ", g.message.Desc.FullName(), " does not contain field %s\", fd.FullName()))") 41 g.P("}") 42 g.P("}") 43 } 44 45 func mutable(field *protogen.Field) bool { 46 switch { 47 case field.Desc.IsMap(): 48 return true 49 case field.Desc.IsList(): 50 return true 51 case field.Desc.Kind() == protoreflect.MessageKind: 52 return true 53 default: 54 return false 55 } 56 } 57 58 func (g *mutableGen) genComment() { 59 g.P("// Mutable returns a mutable reference to a composite type.") 60 g.P("//") 61 g.P("// If the field is unpopulated, it may allocate a composite value.") 62 g.P("// For a field belonging to a oneof, it implicitly clears any other field") 63 g.P("// that may be currently set within the same oneof.") 64 g.P("// For extension fields, it implicitly stores the provided ExtensionType") 65 g.P("// if not already stored.") 66 g.P("// It panics if the field does not contain a composite type.") 67 g.P("//") 68 g.P("// Mutable is a mutating operation and unsafe for concurrent use.") 69 } 70 71 func (g *mutableGen) genField(field *protogen.Field) { 72 if field.Oneof != nil { 73 g.genOneof(field) 74 return 75 } 76 77 switch { 78 case field.Desc.IsMap(): 79 // if map is invalid then we make it valid 80 g.P("if x.", field.GoName, " == nil {") 81 g.P("x.", field.GoName, " = make(map[", getGoType(g.GeneratedFile, field.Message.Fields[0]), "]", getGoType(g.GeneratedFile, field.Message.Fields[1]), ")") 82 g.P("}") 83 // return value of map 84 g.P("value := &", mapTypeName(field), "{m: &x.", field.GoName, "}") 85 g.P("return ", protoreflectPkg.Ident("ValueOfMap"), "(value)") 86 case field.Desc.IsList(): 87 g.P("if x.", field.GoName, " == nil {") 88 g.P("x.", field.GoName, " = []", getGoType(g.GeneratedFile, field), "{}") 89 g.P("}") 90 g.P("value := &", listTypeName(field), "{list: &x.", field.GoName, "}") 91 g.P("return ", protoreflectPkg.Ident("ValueOfList"), "(value)") 92 case field.Desc.Kind() == protoreflect.MessageKind: 93 g.P("if x.", field.GoName, " == nil {") 94 g.P("x.", field.GoName, " = new(", g.QualifiedGoIdent(field.Message.GoIdent), ")") 95 g.P("}") 96 g.P("return ", protoreflectPkg.Ident("ValueOfMessage"), "(x.", field.GoName, ".ProtoReflect())") 97 default: 98 panic("unreachable") 99 } 100 } 101 102 func (g *mutableGen) genOneof(field *protogen.Field) { 103 // if the oneof is nil then we just create a new instance of the object and return it 104 g.P("if x.", field.Oneof.GoName, " == nil {") 105 g.P("value := &", g.QualifiedGoIdent(field.Message.GoIdent), "{}") 106 g.P("oneofValue := &", g.QualifiedGoIdent(field.GoIdent), "{", field.GoName, ": value}") 107 g.P("x.", field.Oneof.GoName, " = oneofValue") 108 g.P("return ", protoreflectPkg.Ident("ValueOfMessage"), "(value.ProtoReflect())") 109 g.P("}") 110 // if the oneof is not nil, 111 g.P("switch m := x.", field.Oneof.GoName, ".(type) {") 112 // we check if the type matches the oneof type of the field 113 g.P("case *", g.QualifiedGoIdent(field.GoIdent), ":") 114 // if it does we return it 115 g.P("return ", protoreflectPkg.Ident("ValueOfMessage"), "(m.", field.GoName, ".ProtoReflect())") 116 // otherwise we reset the field with the new instance 117 g.P("default:") 118 g.P("value := &", g.QualifiedGoIdent(field.Message.GoIdent), "{}") 119 g.P("oneofValue := &", g.QualifiedGoIdent(field.GoIdent), "{", field.GoName, ": value}") 120 g.P("x.", field.Oneof.GoName, " = oneofValue") 121 g.P("return ", protoreflectPkg.Ident("ValueOfMessage"), "(value.ProtoReflect())") 122 g.P("}") 123 124 }