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  }