blob: 01f956d94c74fe1c2c5a7e9683aa823a94464268 [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
11 "github.com/golang/protobuf/v2/internal/errors"
12 pref "github.com/golang/protobuf/v2/reflect/protoreflect"
13)
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()
Damien Neil4686e232019-04-05 13:31:40 -070031 known := m.KnownFields()
32 fields := md.Fields()
33 for i, nums := 0, md.RequiredNumbers(); i < nums.Len(); i++ {
34 num := nums.Get(i)
35 if !known.Has(num) {
36 stack = append(stack, fields.ByNumber(num).Name())
37 return newRequiredNotSetError(stack)
38 }
39 }
40 var err error
41 known.Range(func(num pref.FieldNumber, v pref.Value) bool {
42 field := fields.ByNumber(num)
43 if field == nil {
Joe Tsai0fc49f82019-05-01 12:29:25 -070044 field = known.ExtensionTypes().ByNumber(num).Descriptor()
Damien Neil4686e232019-04-05 13:31:40 -070045 }
46 if field == nil {
47 panic(fmt.Errorf("no descriptor for field %d in %q", num, md.FullName()))
48 }
49 // Look for fields containing a message: Messages, groups, and maps
50 // with a message or group value.
Joe Tsaid24bc722019-04-15 23:39:09 -070051 md := field.Message()
52 if md == nil {
Damien Neil4686e232019-04-05 13:31:40 -070053 return true
54 }
55 if field.IsMap() {
Joe Tsaiac31a352019-05-13 14:32:56 -070056 if field.MapValue().Message() == nil {
Damien Neil4686e232019-04-05 13:31:40 -070057 return true
58 }
59 }
60 // Recurse into the field
61 stack := append(stack, field.Name())
62 switch {
Joe Tsaiac31a352019-05-13 14:32:56 -070063 case field.IsList():
Damien Neil4686e232019-04-05 13:31:40 -070064 for i, list := 0, v.List(); i < list.Len(); i++ {
65 stack := append(stack, "[", i, "].")
66 err = isInitialized(list.Get(i).Message(), stack)
67 if err != nil {
68 break
69 }
70 }
Joe Tsaiac31a352019-05-13 14:32:56 -070071 case field.IsMap():
72 v.Map().Range(func(key pref.MapKey, v pref.Value) bool {
73 stack := append(stack, "[", key, "].")
74 err = isInitialized(v.Message(), stack)
75 return err == nil
76 })
Damien Neil4686e232019-04-05 13:31:40 -070077 default:
78 stack := append(stack, ".")
79 err = isInitialized(v.Message(), stack)
80 }
81 return err == nil
82 })
83 return err
84}
85
86func newRequiredNotSetError(stack []interface{}) error {
87 var buf bytes.Buffer
88 for _, s := range stack {
89 fmt.Fprint(&buf, s)
90 }
91 var nerr errors.NonFatal
92 nerr.AppendRequiredNotSet(buf.String())
93 return nerr.E
94}