reflect/prototype: add HasDefault to FieldDescriptor
Provide a way to distinguish between a field with a zero-value
default and one with no default.
Change-Id: I4b1231de2d1bb141099cb1a415b35184dd198f93
Reviewed-on: https://go-review.googlesource.com/135255
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
diff --git a/reflect/protoreflect/type.go b/reflect/protoreflect/type.go
index 9b8dd34..223532d 100644
--- a/reflect/protoreflect/type.go
+++ b/reflect/protoreflect/type.go
@@ -352,6 +352,9 @@
// The Value type is determined by the Kind.
Default() Value
+ // HasDefault reports whether this field has a default value.
+ HasDefault() bool
+
// OneofType is the containing oneof that this field belongs to,
// and is nil if this field is not part of a oneof.
OneofType() OneofDescriptor
diff --git a/reflect/prototype/protofile_type.go b/reflect/prototype/protofile_type.go
index 9a98082..f058403 100644
--- a/reflect/prototype/protofile_type.go
+++ b/reflect/prototype/protofile_type.go
@@ -213,6 +213,7 @@
func (t fieldDesc) IsMap() bool { return isMap(t) }
func (t fieldDesc) IsWeak() bool { return t.f.IsWeak }
func (t fieldDesc) Default() pref.Value { return t.f.dv.lazyInit(t, t.f.Default) }
+func (t fieldDesc) HasDefault() bool { return !t.f.Default.IsNull() }
func (t fieldDesc) OneofType() pref.OneofDescriptor { return t.f.ot.lazyInit(t, t.f.OneofName) }
func (t fieldDesc) ExtendedType() pref.MessageDescriptor { return nil }
func (t fieldDesc) MessageType() pref.MessageDescriptor { return t.f.mt.lazyInit(t, &t.f.MessageType) }
@@ -321,6 +322,7 @@
func (t extensionDesc) IsMap() bool { return false }
func (t extensionDesc) IsWeak() bool { return false }
func (t extensionDesc) Default() pref.Value { return t.x.dv.lazyInit(t, t.x.Default) }
+func (t extensionDesc) HasDefault() bool { return !t.x.Default.IsNull() }
func (t extensionDesc) OneofType() pref.OneofDescriptor { return nil }
func (t extensionDesc) ExtendedType() pref.MessageDescriptor {
return t.x.xt.lazyInit(t, &t.x.ExtendedType)
diff --git a/reflect/prototype/standalone.go b/reflect/prototype/standalone.go
index 84fdac1..18d77f4 100644
--- a/reflect/prototype/standalone.go
+++ b/reflect/prototype/standalone.go
@@ -71,6 +71,8 @@
MessageType protoreflect.MessageDescriptor
EnumType protoreflect.EnumDescriptor
ExtendedType protoreflect.MessageDescriptor
+
+ dv defaultValue
}
// NewExtension creates a new protoreflect.ExtensionDescriptor.
diff --git a/reflect/prototype/standalone_type.go b/reflect/prototype/standalone_type.go
index 56dcc3c..0bcee4a 100644
--- a/reflect/prototype/standalone_type.go
+++ b/reflect/prototype/standalone_type.go
@@ -65,7 +65,8 @@
func (t standaloneExtension) IsPacked() bool { return t.x.IsPacked }
func (t standaloneExtension) IsMap() bool { return false }
func (t standaloneExtension) IsWeak() bool { return false }
-func (t standaloneExtension) Default() pref.Value { return t.x.Default }
+func (t standaloneExtension) Default() pref.Value { return t.x.dv.lazyInit(t, t.x.Default) }
+func (t standaloneExtension) HasDefault() bool { return !t.x.Default.IsNull() }
func (t standaloneExtension) OneofType() pref.OneofDescriptor { return nil }
func (t standaloneExtension) MessageType() pref.MessageDescriptor { return t.x.MessageType }
func (t standaloneExtension) EnumType() pref.EnumDescriptor { return t.x.EnumType }
diff --git a/reflect/prototype/stringer.go b/reflect/prototype/stringer.go
index 20805fa..d97e030 100644
--- a/reflect/prototype/stringer.go
+++ b/reflect/prototype/stringer.go
@@ -86,7 +86,7 @@
var descriptorAccessors = map[reflect.Type][]string{
reflect.TypeOf((*pref.FileDescriptor)(nil)).Elem(): {"Path", "Package", "Imports", "Messages", "Enums", "Extensions", "Services"},
reflect.TypeOf((*pref.MessageDescriptor)(nil)).Elem(): {"IsMapEntry", "Fields", "Oneofs", "RequiredNumbers", "ExtensionRanges", "Messages", "Enums", "Extensions"},
- reflect.TypeOf((*pref.FieldDescriptor)(nil)).Elem(): {"Number", "Cardinality", "Kind", "JSONName", "IsPacked", "IsMap", "IsWeak", "Default", "OneofType", "ExtendedType", "MessageType", "EnumType"},
+ reflect.TypeOf((*pref.FieldDescriptor)(nil)).Elem(): {"Number", "Cardinality", "Kind", "JSONName", "IsPacked", "IsMap", "IsWeak", "HasDefault", "Default", "OneofType", "ExtendedType", "MessageType", "EnumType"},
reflect.TypeOf((*pref.OneofDescriptor)(nil)).Elem(): {"Fields"}, // not directly used; must keep in sync with formatDescOpt
reflect.TypeOf((*pref.EnumDescriptor)(nil)).Elem(): {"Values"},
reflect.TypeOf((*pref.EnumValueDescriptor)(nil)).Elem(): {"Number"},
diff --git a/reflect/prototype/type_test.go b/reflect/prototype/type_test.go
index 00404ff..fc04da9 100644
--- a/reflect/prototype/type_test.go
+++ b/reflect/prototype/type_test.go
@@ -672,7 +672,7 @@
}
func testFileFormatCompact(t *testing.T, fd pref.FileDescriptor) {
- const want = `FileDescriptor{Syntax: proto2, Path: "path/to/file.proto", Package: test, Messages: [{Name: A, IsMapEntry: true, Fields: [{Name: key, Number: 1, Cardinality: optional, Kind: string, JSONName: "key"}, {Name: value, Number: 2, Cardinality: optional, Kind: message, JSONName: "value", MessageType: test.B}]}, {Name: B, Fields: [{Name: field_one, Number: 1, Cardinality: optional, Kind: string, JSONName: "fieldOne", Default: "hello", OneofType: O1}, {Name: field_two, Number: 2, Cardinality: optional, Kind: enum, JSONName: "Field2", Default: 1, OneofType: O2, EnumType: test.E1}, {Name: field_three, Number: 3, Cardinality: optional, Kind: message, JSONName: "fieldThree", OneofType: O2, MessageType: test.C}, {Name: field_four, Number: 4, Cardinality: repeated, Kind: message, JSONName: "Field4", IsMap: true, MessageType: test.A}, {Name: field_five, Number: 5, Cardinality: repeated, Kind: int32, JSONName: "fieldFive", IsPacked: true}, {Name: field_six, Number: 6, Cardinality: required, Kind: bytes, JSONName: "fieldSix"}], Oneofs: [{Name: O1, Fields: [field_one]}, {Name: O2, Fields: [field_two, field_three]}], RequiredNumbers: [6], ExtensionRanges: [1000:2000, 3000]}, {Name: C, Messages: [{Name: A, Fields: [{Name: F, Number: 1, Cardinality: required, Kind: bytes, JSONName: "F", Default: "dead\xbe\xef"}], RequiredNumbers: [1]}], Enums: [{Name: E1, Values: [{Name: FOO}, {Name: BAR, Number: 1}]}], Extensions: [{Name: X, Number: 1000, Cardinality: repeated, Kind: message, ExtendedType: test.B, MessageType: test.C}]}], Enums: [{Name: E1, Values: [{Name: FOO}, {Name: BAR, Number: 1}]}], Extensions: [{Name: X, Number: 1000, Cardinality: repeated, Kind: message, IsPacked: true, ExtendedType: test.B, MessageType: test.C}], Services: [{Name: S, Methods: [{Name: M, InputType: test.A, OutputType: test.C.A, IsStreamingClient: true, IsStreamingServer: true}]}]}`
+ const want = `FileDescriptor{Syntax: proto2, Path: "path/to/file.proto", Package: test, Messages: [{Name: A, IsMapEntry: true, Fields: [{Name: key, Number: 1, Cardinality: optional, Kind: string, JSONName: "key"}, {Name: value, Number: 2, Cardinality: optional, Kind: message, JSONName: "value", MessageType: test.B}]}, {Name: B, Fields: [{Name: field_one, Number: 1, Cardinality: optional, Kind: string, JSONName: "fieldOne", HasDefault: true, Default: "hello", OneofType: O1}, {Name: field_two, Number: 2, Cardinality: optional, Kind: enum, JSONName: "Field2", HasDefault: true, Default: 1, OneofType: O2, EnumType: test.E1}, {Name: field_three, Number: 3, Cardinality: optional, Kind: message, JSONName: "fieldThree", OneofType: O2, MessageType: test.C}, {Name: field_four, Number: 4, Cardinality: repeated, Kind: message, JSONName: "Field4", IsMap: true, MessageType: test.A}, {Name: field_five, Number: 5, Cardinality: repeated, Kind: int32, JSONName: "fieldFive", IsPacked: true}, {Name: field_six, Number: 6, Cardinality: required, Kind: bytes, JSONName: "fieldSix"}], Oneofs: [{Name: O1, Fields: [field_one]}, {Name: O2, Fields: [field_two, field_three]}], RequiredNumbers: [6], ExtensionRanges: [1000:2000, 3000]}, {Name: C, Messages: [{Name: A, Fields: [{Name: F, Number: 1, Cardinality: required, Kind: bytes, JSONName: "F", HasDefault: true, Default: "dead\xbe\xef"}], RequiredNumbers: [1]}], Enums: [{Name: E1, Values: [{Name: FOO}, {Name: BAR, Number: 1}]}], Extensions: [{Name: X, Number: 1000, Cardinality: repeated, Kind: message, ExtendedType: test.B, MessageType: test.C}]}], Enums: [{Name: E1, Values: [{Name: FOO}, {Name: BAR, Number: 1}]}], Extensions: [{Name: X, Number: 1000, Cardinality: repeated, Kind: message, IsPacked: true, ExtendedType: test.B, MessageType: test.C}], Services: [{Name: S, Methods: [{Name: M, InputType: test.A, OutputType: test.C.A, IsStreamingClient: true, IsStreamingServer: true}]}]}`
got := fmt.Sprintf("%v", fd)
got = strings.Replace(got, "FileDescriptor ", "FileDescriptor", 1) // cleanup randomizer
if got != want {
@@ -710,6 +710,7 @@
Cardinality: optional
Kind: string
JSONName: "fieldOne"
+ HasDefault: true
Default: "hello"
OneofType: O1
}, {
@@ -718,6 +719,7 @@
Cardinality: optional
Kind: enum
JSONName: "Field2"
+ HasDefault: true
Default: 1
OneofType: O2
EnumType: test.E1
@@ -770,6 +772,7 @@
Cardinality: required
Kind: bytes
JSONName: "F"
+ HasDefault: true
Default: "dead\xbe\xef"
}]
RequiredNumbers: [1]