| // Copyright 2019 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style. |
| // license that can be found in the LICENSE file. |
| |
| package proto |
| |
| import ( |
| "bytes" |
| "fmt" |
| |
| "google.golang.org/protobuf/internal/errors" |
| pref "google.golang.org/protobuf/reflect/protoreflect" |
| ) |
| |
| // IsInitialized returns an error if any required fields in m are not set. |
| func IsInitialized(m Message) error { |
| if methods := protoMethods(m); methods != nil && methods.IsInitialized != nil { |
| if err := methods.IsInitialized(m); err == nil { |
| return nil |
| } |
| // Fall through to the slow path, since the fast-path |
| // implementation doesn't produce nice errors. |
| // |
| // TODO: Consider producing better errors from the fast path. |
| } |
| return isInitialized(m.ProtoReflect(), nil) |
| } |
| |
| // IsInitialized returns an error if any required fields in m are not set. |
| func isInitialized(m pref.Message, stack []interface{}) error { |
| md := m.Descriptor() |
| fds := md.Fields() |
| for i, nums := 0, md.RequiredNumbers(); i < nums.Len(); i++ { |
| fd := fds.ByNumber(nums.Get(i)) |
| if !m.Has(fd) { |
| stack = append(stack, fd.Name()) |
| return newRequiredNotSetError(stack) |
| } |
| } |
| var err error |
| m.Range(func(fd pref.FieldDescriptor, v pref.Value) bool { |
| // Recurse into fields containing message values. |
| stack := append(stack, fd.Name()) |
| switch { |
| case fd.IsList(): |
| if fd.Message() == nil { |
| return true |
| } |
| for i, list := 0, v.List(); i < list.Len() && err == nil; i++ { |
| stack := append(stack, "[", i, "].") |
| err = isInitialized(list.Get(i).Message(), stack) |
| } |
| case fd.IsMap(): |
| if fd.MapValue().Message() == nil { |
| return true |
| } |
| v.Map().Range(func(key pref.MapKey, v pref.Value) bool { |
| stack := append(stack, "[", key, "].") |
| err = isInitialized(v.Message(), stack) |
| return err == nil |
| }) |
| default: |
| if fd.Message() == nil { |
| return true |
| } |
| stack := append(stack, ".") |
| err = isInitialized(v.Message(), stack) |
| } |
| return err == nil |
| }) |
| return err |
| } |
| |
| func newRequiredNotSetError(stack []interface{}) error { |
| var buf bytes.Buffer |
| for _, s := range stack { |
| fmt.Fprint(&buf, s) |
| } |
| return errors.RequiredNotSet(buf.String()) |
| } |