blob: 08edbd8f46de72b4f11631e861d9ea1e7fbca0b3 [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 {
30 md := m.Type()
31 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 {
44 field = known.ExtensionTypes().ByNumber(num)
45 }
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 Tsaid24bc722019-04-15 23:39:09 -070056 if md.Fields().ByNumber(2).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 {
63 case field.IsMap():
64 v.Map().Range(func(key pref.MapKey, v pref.Value) bool {
65 stack := append(stack, "[", key, "].")
66 err = isInitialized(v.Message(), stack)
67 return err == nil
68 })
69 case field.Cardinality() == pref.Repeated:
70 for i, list := 0, v.List(); i < list.Len(); i++ {
71 stack := append(stack, "[", i, "].")
72 err = isInitialized(list.Get(i).Message(), stack)
73 if err != nil {
74 break
75 }
76 }
77 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}