blob: 1bb29bdf8997762f627624c28dee124e705bcc2f [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 {
Damien Neil5322bdb2019-04-09 15:57:05 -070018 if err := methods.IsInitialized(m); err == nil {
19 return nil
20 }
21 // Fall through to the slow path, since the fast-path
22 // implementation doesn't produce nice errors.
Damien Neil4686e232019-04-05 13:31:40 -070023 //
Damien Neil5322bdb2019-04-09 15:57:05 -070024 // TODO: Consider producing better errors from the fast path.
Damien Neil4686e232019-04-05 13:31:40 -070025 }
26 return isInitialized(m.ProtoReflect(), nil)
27}
28
29// IsInitialized returns an error if any required fields in m are not set.
30func isInitialized(m pref.Message, stack []interface{}) error {
Joe Tsai0fc49f82019-05-01 12:29:25 -070031 md := m.Descriptor()
Joe Tsai378c1322019-04-25 23:48:08 -070032 fds := md.Fields()
Damien Neil4686e232019-04-05 13:31:40 -070033 for i, nums := 0, md.RequiredNumbers(); i < nums.Len(); i++ {
Joe Tsai378c1322019-04-25 23:48:08 -070034 fd := fds.ByNumber(nums.Get(i))
35 if !m.Has(fd) {
36 stack = append(stack, fd.Name())
Damien Neil4686e232019-04-05 13:31:40 -070037 return newRequiredNotSetError(stack)
38 }
39 }
40 var err error
Joe Tsai378c1322019-04-25 23:48:08 -070041 m.Range(func(fd pref.FieldDescriptor, v pref.Value) bool {
42 // Recurse into fields containing message values.
43 stack := append(stack, fd.Name())
44 switch {
45 case fd.IsList():
46 if fd.Message() == nil {
Damien Neil4686e232019-04-05 13:31:40 -070047 return true
48 }
Joe Tsai378c1322019-04-25 23:48:08 -070049 for i, list := 0, v.List(); i < list.Len() && err == nil; i++ {
Damien Neil4686e232019-04-05 13:31:40 -070050 stack := append(stack, "[", i, "].")
51 err = isInitialized(list.Get(i).Message(), stack)
Damien Neil4686e232019-04-05 13:31:40 -070052 }
Joe Tsai378c1322019-04-25 23:48:08 -070053 case fd.IsMap():
54 if fd.MapValue().Message() == nil {
55 return true
56 }
Joe Tsaiac31a352019-05-13 14:32:56 -070057 v.Map().Range(func(key pref.MapKey, v pref.Value) bool {
58 stack := append(stack, "[", key, "].")
59 err = isInitialized(v.Message(), stack)
60 return err == nil
61 })
Damien Neil4686e232019-04-05 13:31:40 -070062 default:
Joe Tsai378c1322019-04-25 23:48:08 -070063 if fd.Message() == nil {
64 return true
65 }
Damien Neil4686e232019-04-05 13:31:40 -070066 stack := append(stack, ".")
67 err = isInitialized(v.Message(), stack)
68 }
69 return err == nil
70 })
71 return err
72}
73
74func newRequiredNotSetError(stack []interface{}) error {
75 var buf bytes.Buffer
76 for _, s := range stack {
77 fmt.Fprint(&buf, s)
78 }
Damien Neil8c86fc52019-06-19 09:28:29 -070079 return errors.RequiredNotSet(buf.String())
Damien Neil4686e232019-04-05 13:31:40 -070080}