1package builder
2
3import (
4	"fmt"
5
6	dpb "github.com/golang/protobuf/protoc-gen-go/descriptor"
7
8	"github.com/jhump/protoreflect/desc"
9)
10
11// FieldType represents the type of a field or extension. It can represent a
12// message or enum type or any of the scalar types supported by protobufs.
13//
14// Message and enum types can reference a message or enum builder. A type that
15// refers to a built message or enum descriptor is called an "imported" type.
16//
17// There are numerous factory methods for creating FieldType instances.
18type FieldType struct {
19	fieldType       dpb.FieldDescriptorProto_Type
20	foreignMsgType  *desc.MessageDescriptor
21	localMsgType    *MessageBuilder
22	foreignEnumType *desc.EnumDescriptor
23	localEnumType   *EnumBuilder
24}
25
26// GetType returns the enum value indicating the type of the field. If the type
27// is a message (or group) or enum type, GetTypeName provides the name of the
28// referenced type.
29func (ft *FieldType) GetType() dpb.FieldDescriptorProto_Type {
30	return ft.fieldType
31}
32
33// GetTypeName returns the fully-qualified name of the referenced message or
34// enum type. It returns an empty string if this type does not represent a
35// message or enum type.
36func (ft *FieldType) GetTypeName() string {
37	if ft.foreignMsgType != nil {
38		return ft.foreignMsgType.GetFullyQualifiedName()
39	} else if ft.foreignEnumType != nil {
40		return ft.foreignEnumType.GetFullyQualifiedName()
41	} else if ft.localMsgType != nil {
42		return GetFullyQualifiedName(ft.localMsgType)
43	} else if ft.localEnumType != nil {
44		return GetFullyQualifiedName(ft.localEnumType)
45	} else {
46		return ""
47	}
48}
49
50var scalarTypes = map[dpb.FieldDescriptorProto_Type]*FieldType{
51	dpb.FieldDescriptorProto_TYPE_BOOL:     {fieldType: dpb.FieldDescriptorProto_TYPE_BOOL},
52	dpb.FieldDescriptorProto_TYPE_INT32:    {fieldType: dpb.FieldDescriptorProto_TYPE_INT32},
53	dpb.FieldDescriptorProto_TYPE_INT64:    {fieldType: dpb.FieldDescriptorProto_TYPE_INT64},
54	dpb.FieldDescriptorProto_TYPE_SINT32:   {fieldType: dpb.FieldDescriptorProto_TYPE_SINT32},
55	dpb.FieldDescriptorProto_TYPE_SINT64:   {fieldType: dpb.FieldDescriptorProto_TYPE_SINT64},
56	dpb.FieldDescriptorProto_TYPE_UINT32:   {fieldType: dpb.FieldDescriptorProto_TYPE_UINT32},
57	dpb.FieldDescriptorProto_TYPE_UINT64:   {fieldType: dpb.FieldDescriptorProto_TYPE_UINT64},
58	dpb.FieldDescriptorProto_TYPE_FIXED32:  {fieldType: dpb.FieldDescriptorProto_TYPE_FIXED32},
59	dpb.FieldDescriptorProto_TYPE_FIXED64:  {fieldType: dpb.FieldDescriptorProto_TYPE_FIXED64},
60	dpb.FieldDescriptorProto_TYPE_SFIXED32: {fieldType: dpb.FieldDescriptorProto_TYPE_SFIXED32},
61	dpb.FieldDescriptorProto_TYPE_SFIXED64: {fieldType: dpb.FieldDescriptorProto_TYPE_SFIXED64},
62	dpb.FieldDescriptorProto_TYPE_FLOAT:    {fieldType: dpb.FieldDescriptorProto_TYPE_FLOAT},
63	dpb.FieldDescriptorProto_TYPE_DOUBLE:   {fieldType: dpb.FieldDescriptorProto_TYPE_DOUBLE},
64	dpb.FieldDescriptorProto_TYPE_STRING:   {fieldType: dpb.FieldDescriptorProto_TYPE_STRING},
65	dpb.FieldDescriptorProto_TYPE_BYTES:    {fieldType: dpb.FieldDescriptorProto_TYPE_BYTES},
66}
67
68// FieldTypeScalar returns a FieldType for the given scalar type. If the given
69// type is not scalar (e.g. it is a message, group, or enum) than this function
70// will panic.
71func FieldTypeScalar(t dpb.FieldDescriptorProto_Type) *FieldType {
72	if ft, ok := scalarTypes[t]; ok {
73		return ft
74	}
75	panic(fmt.Sprintf("field %v is not scalar", t))
76}
77
78// FieldTypeInt32 returns a FieldType for the int32 scalar type.
79func FieldTypeInt32() *FieldType {
80	return FieldTypeScalar(dpb.FieldDescriptorProto_TYPE_INT32)
81}
82
83// FieldTypeUInt32 returns a FieldType for the uint32 scalar type.
84func FieldTypeUInt32() *FieldType {
85	return FieldTypeScalar(dpb.FieldDescriptorProto_TYPE_UINT32)
86}
87
88// FieldTypeSInt32 returns a FieldType for the sint32 scalar type.
89func FieldTypeSInt32() *FieldType {
90	return FieldTypeScalar(dpb.FieldDescriptorProto_TYPE_SINT32)
91}
92
93// FieldTypeFixed32 returns a FieldType for the fixed32 scalar type.
94func FieldTypeFixed32() *FieldType {
95	return FieldTypeScalar(dpb.FieldDescriptorProto_TYPE_FIXED32)
96}
97
98// FieldTypeSFixed32 returns a FieldType for the sfixed32 scalar type.
99func FieldTypeSFixed32() *FieldType {
100	return FieldTypeScalar(dpb.FieldDescriptorProto_TYPE_SFIXED32)
101}
102
103// FieldTypeInt64 returns a FieldType for the int64 scalar type.
104func FieldTypeInt64() *FieldType {
105	return FieldTypeScalar(dpb.FieldDescriptorProto_TYPE_INT64)
106}
107
108// FieldTypeUInt64 returns a FieldType for the uint64 scalar type.
109func FieldTypeUInt64() *FieldType {
110	return FieldTypeScalar(dpb.FieldDescriptorProto_TYPE_UINT64)
111}
112
113// FieldTypeSInt64 returns a FieldType for the sint64 scalar type.
114func FieldTypeSInt64() *FieldType {
115	return FieldTypeScalar(dpb.FieldDescriptorProto_TYPE_SINT64)
116}
117
118// FieldTypeFixed64 returns a FieldType for the fixed64 scalar type.
119func FieldTypeFixed64() *FieldType {
120	return FieldTypeScalar(dpb.FieldDescriptorProto_TYPE_FIXED64)
121}
122
123// FieldTypeSFixed64 returns a FieldType for the sfixed64 scalar type.
124func FieldTypeSFixed64() *FieldType {
125	return FieldTypeScalar(dpb.FieldDescriptorProto_TYPE_SFIXED64)
126}
127
128// FieldTypeFloat returns a FieldType for the float scalar type.
129func FieldTypeFloat() *FieldType {
130	return FieldTypeScalar(dpb.FieldDescriptorProto_TYPE_FLOAT)
131}
132
133// FieldTypeDouble returns a FieldType for the double scalar type.
134func FieldTypeDouble() *FieldType {
135	return FieldTypeScalar(dpb.FieldDescriptorProto_TYPE_DOUBLE)
136}
137
138// FieldTypeBool returns a FieldType for the bool scalar type.
139func FieldTypeBool() *FieldType {
140	return FieldTypeScalar(dpb.FieldDescriptorProto_TYPE_BOOL)
141}
142
143// FieldTypeString returns a FieldType for the string scalar type.
144func FieldTypeString() *FieldType {
145	return FieldTypeScalar(dpb.FieldDescriptorProto_TYPE_STRING)
146}
147
148// FieldTypeBytes returns a FieldType for the bytes scalar type.
149func FieldTypeBytes() *FieldType {
150	return FieldTypeScalar(dpb.FieldDescriptorProto_TYPE_BYTES)
151}
152
153// FieldTypeMessage returns a FieldType for the given message type.
154func FieldTypeMessage(mb *MessageBuilder) *FieldType {
155	return &FieldType{
156		fieldType:    dpb.FieldDescriptorProto_TYPE_MESSAGE,
157		localMsgType: mb,
158	}
159}
160
161// FieldTypeImportedMessage returns a FieldType that references the given
162// message descriptor.
163func FieldTypeImportedMessage(md *desc.MessageDescriptor) *FieldType {
164	return &FieldType{
165		fieldType:      dpb.FieldDescriptorProto_TYPE_MESSAGE,
166		foreignMsgType: md,
167	}
168}
169
170// FieldTypeEnum returns a FieldType for the given enum type.
171func FieldTypeEnum(eb *EnumBuilder) *FieldType {
172	return &FieldType{
173		fieldType:     dpb.FieldDescriptorProto_TYPE_ENUM,
174		localEnumType: eb,
175	}
176}
177
178// FieldTypeImportedEnum returns a FieldType that references the given enum
179// descriptor.
180func FieldTypeImportedEnum(ed *desc.EnumDescriptor) *FieldType {
181	return &FieldType{
182		fieldType:       dpb.FieldDescriptorProto_TYPE_ENUM,
183		foreignEnumType: ed,
184	}
185}
186
187func fieldTypeFromDescriptor(fld *desc.FieldDescriptor) *FieldType {
188	switch fld.GetType() {
189	case dpb.FieldDescriptorProto_TYPE_GROUP:
190		return &FieldType{fieldType: dpb.FieldDescriptorProto_TYPE_GROUP, foreignMsgType: fld.GetMessageType()}
191	case dpb.FieldDescriptorProto_TYPE_MESSAGE:
192		return FieldTypeImportedMessage(fld.GetMessageType())
193	case dpb.FieldDescriptorProto_TYPE_ENUM:
194		return FieldTypeImportedEnum(fld.GetEnumType())
195	default:
196		return FieldTypeScalar(fld.GetType())
197	}
198}
199
200// RpcType represents the type of an RPC request or response. The only allowed
201// types are messages, but can be streams or unary messages.
202//
203// Message types can reference a message builder. A type that refers to a built
204// message descriptor is called an "imported" type.
205//
206// To create an RpcType, see RpcTypeMessage and RpcTypeImportedMessage.
207type RpcType struct {
208	IsStream bool
209
210	foreignType *desc.MessageDescriptor
211	localType   *MessageBuilder
212}
213
214// RpcTypeMessage creates an RpcType that refers to the given message builder.
215func RpcTypeMessage(mb *MessageBuilder, stream bool) *RpcType {
216	return &RpcType{
217		IsStream:  stream,
218		localType: mb,
219	}
220}
221
222// RpcTypeImportedMessage creates an RpcType that refers to the given message
223// descriptor.
224func RpcTypeImportedMessage(md *desc.MessageDescriptor, stream bool) *RpcType {
225	return &RpcType{
226		IsStream:    stream,
227		foreignType: md,
228	}
229}
230
231// GetTypeName returns the fully qualified name of the message type to which
232// this RpcType refers.
233func (rt *RpcType) GetTypeName() string {
234	if rt.foreignType != nil {
235		return rt.foreignType.GetFullyQualifiedName()
236	} else {
237		return GetFullyQualifiedName(rt.localType)
238	}
239}
240