blob: c535cf7a6bc9135b03a39cf3f806074ea1c59f93 [file] [log] [blame]
Damien Neil4686e232019-04-05 13:31:40 -07001// Copyright 2019 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style.
3// license that can be found in the LICENSE file.
4
5package proto
6
7import (
8 "bytes"
9 "fmt"
10
Damien Neile89e6242019-05-13 23:55:40 -070011 "google.golang.org/protobuf/internal/errors"
12 pref "google.golang.org/protobuf/reflect/protoreflect"
Damien Neil4686e232019-04-05 13:31:40 -070013)
14
15// IsInitialized returns an error if any required fields in m are not set.
16func IsInitialized(m Message) error {
17 if methods := protoMethods(m); methods != nil && methods.IsInitialized != nil {
18 // TODO: Do we need a way to disable the fast path here?
19 //
20 // TODO: Should detailed information about missing
21 // fields always be provided by the slow-but-informative
22 // reflective implementation?
23 return methods.IsInitialized(m)
24 }
25 return isInitialized(m.ProtoReflect(), nil)
26}
27
28// IsInitialized returns an error if any required fields in m are not set.
29func isInitialized(m pref.Message, stack []interface{}) error {
Joe Tsai0fc49f82019-05-01 12:29:25 -070030 md := m.Descriptor()
Joe Tsai378c1322019-04-25 23:48:08 -070031 fds := md.Fields()
Damien Neil4686e232019-04-05 13:31:40 -070032 for i, nums := 0, md.RequiredNumbers(); i < nums.Len(); i++ {
Joe Tsai378c1322019-04-25 23:48:08 -070033 fd := fds.ByNumber(nums.Get(i))
34 if !m.Has(fd) {
35 stack = append(stack, fd.Name())
Damien Neil4686e232019-04-05 13:31:40 -070036 return newRequiredNotSetError(stack)
37 }
38 }
39 var err error
Joe Tsai378c1322019-04-25 23:48:08 -070040 m.Range(func(fd pref.FieldDescriptor, v pref.Value) bool {
41 // Recurse into fields containing message values.
42 stack := append(stack, fd.Name())
43 switch {
44 case fd.IsList():
45 if fd.Message() == nil {
Damien Neil4686e232019-04-05 13:31:40 -070046 return true
47 }
Joe Tsai378c1322019-04-25 23:48:08 -070048 for i, list := 0, v.List(); i < list.Len() && err == nil; i++ {
Damien Neil4686e232019-04-05 13:31:40 -070049 stack := append(stack, "[", i, "].")
50 err = isInitialized(list.Get(i).Message(), stack)
Damien Neil4686e232019-04-05 13:31:40 -070051 }
Joe Tsai378c1322019-04-25 23:48:08 -070052 case fd.IsMap():
53 if fd.MapValue().Message() == nil {
54 return true
55 }
Joe Tsaiac31a352019-05-13 14:32:56 -070056 v.Map().Range(func(key pref.MapKey, v pref.Value) bool {
57 stack := append(stack, "[", key, "].")
58 err = isInitialized(v.Message(), stack)
59 return err == nil
60 })
Damien Neil4686e232019-04-05 13:31:40 -070061 default:
Joe Tsai378c1322019-04-25 23:48:08 -070062 if fd.Message() == nil {
63 return true
64 }
Damien Neil4686e232019-04-05 13:31:40 -070065 stack := append(stack, ".")
66 err = isInitialized(v.Message(), stack)
67 }
68 return err == nil
69 })
70 return err
71}
72
73func newRequiredNotSetError(stack []interface{}) error {
74 var buf bytes.Buffer
75 for _, s := range stack {
76 fmt.Fprint(&buf, s)
77 }
Damien Neil8c86fc52019-06-19 09:28:29 -070078 return errors.RequiredNotSet(buf.String())
Damien Neil4686e232019-04-05 13:31:40 -070079}