goprotobuf: Make text parsing functions return RequiredNotSetError as appropriate.
This permits a program to ignore unset required fields when decoding text
just as they can already do when decoding the wire format.
It has the downside of no longer always returning a ParseError unfortunately.
LGTM=gmlewis
R=gmlewis
CC=golang-codereviews
https://codereview.appspot.com/159800043
diff --git a/proto/text_parser.go b/proto/text_parser.go
index d235ca9..9e4c7b1 100644
--- a/proto/text_parser.go
+++ b/proto/text_parser.go
@@ -361,8 +361,8 @@
return &p.cur
}
-// Return an error indicating which required field was not set.
-func (p *textParser) missingRequiredFieldError(sv reflect.Value) *ParseError {
+// Return a RequiredNotSetError indicating which required field was not set.
+func (p *textParser) missingRequiredFieldError(sv reflect.Value) *RequiredNotSetError {
st := sv.Type()
sprops := GetProperties(st)
for i := 0; i < st.NumField(); i++ {
@@ -372,10 +372,10 @@
props := sprops.Prop[i]
if props.Required {
- return p.errorf("message %v missing required field %q", st, props.OrigName)
+ return &RequiredNotSetError{fmt.Sprintf("%v.%v", st, props.OrigName)}
}
}
- return p.errorf("message %v missing required field", st) // should not happen
+ return &RequiredNotSetError{fmt.Sprintf("%v.<unknown field name>", st)} // should not happen
}
// Returns the index in the struct for the named field, as well as the parsed tag properties.
@@ -426,9 +426,10 @@
return nil
}
-func (p *textParser) readStruct(sv reflect.Value, terminator string) *ParseError {
+func (p *textParser) readStruct(sv reflect.Value, terminator string) error {
st := sv.Type()
reqCount := GetProperties(st).reqCount
+ var reqFieldErr error
// A struct is a sequence of "name: value", terminated by one of
// '>' or '}', or the end of the input. A name may also be
// "[extension]".
@@ -489,7 +490,10 @@
ext = reflect.New(typ.Elem()).Elem()
}
if err := p.readAny(ext, props); err != nil {
- return err
+ if _, ok := err.(*RequiredNotSetError); !ok {
+ return err
+ }
+ reqFieldErr = err
}
ep := sv.Addr().Interface().(extendableProto)
if !rep {
@@ -526,10 +530,11 @@
// Parse into the field.
if err := p.readAny(dst, props); err != nil {
- return err
- }
-
- if props.Required {
+ if _, ok := err.(*RequiredNotSetError); !ok {
+ return err
+ }
+ reqFieldErr = err
+ } else if props.Required {
reqCount--
}
}
@@ -547,10 +552,10 @@
if reqCount > 0 {
return p.missingRequiredFieldError(sv)
}
- return nil
+ return reqFieldErr
}
-func (p *textParser) readAny(v reflect.Value, props *Properties) *ParseError {
+func (p *textParser) readAny(v reflect.Value, props *Properties) error {
tok := p.next()
if tok.err != nil {
return tok.err
@@ -670,6 +675,8 @@
// UnmarshalText reads a protocol buffer in Text format. UnmarshalText resets pb
// before starting to unmarshal, so any existing data in pb is always removed.
+// If a required field is not set and no other error occurs,
+// UnmarshalText returns *RequiredNotSetError.
func UnmarshalText(s string, pb Message) error {
if um, ok := pb.(textUnmarshaler); ok {
err := um.UnmarshalText([]byte(s))