1// Copyright 2020 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 protopath 6 7import ( 8 "fmt" 9 "strconv" 10 "strings" 11 12 "google.golang.org/protobuf/internal/encoding/text" 13 "google.golang.org/protobuf/reflect/protoreflect" 14) 15 16// StepKind identifies the kind of step operation. 17// Each kind of step corresponds with some protobuf reflection operation. 18type StepKind int 19 20const ( 21 invalidStep StepKind = iota 22 // RootStep identifies a step as the Root step operation. 23 RootStep 24 // FieldAccessStep identifies a step as the FieldAccess step operation. 25 FieldAccessStep 26 // UnknownAccessStep identifies a step as the UnknownAccess step operation. 27 UnknownAccessStep 28 // ListIndexStep identifies a step as the ListIndex step operation. 29 ListIndexStep 30 // MapIndexStep identifies a step as the MapIndex step operation. 31 MapIndexStep 32 // AnyExpandStep identifies a step as the AnyExpand step operation. 33 AnyExpandStep 34) 35 36func (k StepKind) String() string { 37 switch k { 38 case invalidStep: 39 return "<invalid>" 40 case RootStep: 41 return "Root" 42 case FieldAccessStep: 43 return "FieldAccess" 44 case UnknownAccessStep: 45 return "UnknownAccess" 46 case ListIndexStep: 47 return "ListIndex" 48 case MapIndexStep: 49 return "MapIndex" 50 case AnyExpandStep: 51 return "AnyExpand" 52 default: 53 return fmt.Sprintf("<unknown:%d>", k) 54 } 55} 56 57// Step is a union where only one step operation may be specified at a time. 58// The different kinds of steps are specified by the constants defined for 59// the StepKind type. 60type Step struct { 61 kind StepKind 62 desc protoreflect.Descriptor 63 key protoreflect.Value 64} 65 66// Root indicates the root message that a path is relative to. 67// It should always (and only ever) be the first step in a path. 68func Root(md protoreflect.MessageDescriptor) Step { 69 if md == nil { 70 panic("nil message descriptor") 71 } 72 return Step{kind: RootStep, desc: md} 73} 74 75// FieldAccess describes access of a field within a message. 76// Extension field accesses are also represented using a FieldAccess and 77// must be provided with a protoreflect.FieldDescriptor 78// 79// Within the context of Values, 80// the type of the previous step value is always a message, and 81// the type of the current step value is determined by the field descriptor. 82func FieldAccess(fd protoreflect.FieldDescriptor) Step { 83 if fd == nil { 84 panic("nil field descriptor") 85 } else if _, ok := fd.(protoreflect.ExtensionTypeDescriptor); !ok && fd.IsExtension() { 86 panic(fmt.Sprintf("extension field %q must implement protoreflect.ExtensionTypeDescriptor", fd.FullName())) 87 } 88 return Step{kind: FieldAccessStep, desc: fd} 89} 90 91// UnknownAccess describes access to the unknown fields within a message. 92// 93// Within the context of Values, 94// the type of the previous step value is always a message, and 95// the type of the current step value is always a bytes type. 96func UnknownAccess() Step { 97 return Step{kind: UnknownAccessStep} 98} 99 100// ListIndex describes index of an element within a list. 101// 102// Within the context of Values, 103// the type of the previous, previous step value is always a message, 104// the type of the previous step value is always a list, and 105// the type of the current step value is determined by the field descriptor. 106func ListIndex(i int) Step { 107 if i < 0 { 108 panic(fmt.Sprintf("invalid list index: %v", i)) 109 } 110 return Step{kind: ListIndexStep, key: protoreflect.ValueOfInt64(int64(i))} 111} 112 113// MapIndex describes index of an entry within a map. 114// The key type is determined by field descriptor that the map belongs to. 115// 116// Within the context of Values, 117// the type of the previous previous step value is always a message, 118// the type of the previous step value is always a map, and 119// the type of the current step value is determined by the field descriptor. 120func MapIndex(k protoreflect.MapKey) Step { 121 if !k.IsValid() { 122 panic("invalid map index") 123 } 124 return Step{kind: MapIndexStep, key: k.Value()} 125} 126 127// AnyExpand describes expansion of a google.protobuf.Any message into 128// a structured representation of the underlying message. 129// 130// Within the context of Values, 131// the type of the previous step value is always a google.protobuf.Any message, and 132// the type of the current step value is always a message. 133func AnyExpand(md protoreflect.MessageDescriptor) Step { 134 if md == nil { 135 panic("nil message descriptor") 136 } 137 return Step{kind: AnyExpandStep, desc: md} 138} 139 140// MessageDescriptor returns the message descriptor for Root or AnyExpand steps, 141// otherwise it returns nil. 142func (s Step) MessageDescriptor() protoreflect.MessageDescriptor { 143 switch s.kind { 144 case RootStep, AnyExpandStep: 145 return s.desc.(protoreflect.MessageDescriptor) 146 default: 147 return nil 148 } 149} 150 151// FieldDescriptor returns the field descriptor for FieldAccess steps, 152// otherwise it returns nil. 153func (s Step) FieldDescriptor() protoreflect.FieldDescriptor { 154 switch s.kind { 155 case FieldAccessStep: 156 return s.desc.(protoreflect.FieldDescriptor) 157 default: 158 return nil 159 } 160} 161 162// ListIndex returns the list index for ListIndex steps, 163// otherwise it returns 0. 164func (s Step) ListIndex() int { 165 switch s.kind { 166 case ListIndexStep: 167 return int(s.key.Int()) 168 default: 169 return 0 170 } 171} 172 173// MapIndex returns the map key for MapIndex steps, 174// otherwise it returns an invalid map key. 175func (s Step) MapIndex() protoreflect.MapKey { 176 switch s.kind { 177 case MapIndexStep: 178 return s.key.MapKey() 179 default: 180 return protoreflect.MapKey{} 181 } 182} 183 184// Kind reports which kind of step this is. 185func (s Step) Kind() StepKind { 186 return s.kind 187} 188 189func (s Step) String() string { 190 return string(s.appendString(nil)) 191} 192 193func (s Step) appendString(b []byte) []byte { 194 switch s.kind { 195 case RootStep: 196 b = append(b, '(') 197 b = append(b, s.desc.FullName()...) 198 b = append(b, ')') 199 case FieldAccessStep: 200 b = append(b, '.') 201 if fd := s.desc.(protoreflect.FieldDescriptor); fd.IsExtension() { 202 b = append(b, '(') 203 b = append(b, strings.Trim(fd.TextName(), "[]")...) 204 b = append(b, ')') 205 } else { 206 b = append(b, fd.TextName()...) 207 } 208 case UnknownAccessStep: 209 b = append(b, '.') 210 b = append(b, '?') 211 case ListIndexStep: 212 b = append(b, '[') 213 b = strconv.AppendInt(b, s.key.Int(), 10) 214 b = append(b, ']') 215 case MapIndexStep: 216 b = append(b, '[') 217 switch k := s.key.Interface().(type) { 218 case bool: 219 b = strconv.AppendBool(b, bool(k)) // e.g., "true" or "false" 220 case int32: 221 b = strconv.AppendInt(b, int64(k), 10) // e.g., "-32" 222 case int64: 223 b = strconv.AppendInt(b, int64(k), 10) // e.g., "-64" 224 case uint32: 225 b = strconv.AppendUint(b, uint64(k), 10) // e.g., "32" 226 case uint64: 227 b = strconv.AppendUint(b, uint64(k), 10) // e.g., "64" 228 case string: 229 b = text.AppendString(b, k) // e.g., `"hello, world"` 230 } 231 b = append(b, ']') 232 case AnyExpandStep: 233 b = append(b, '.') 234 b = append(b, '(') 235 b = append(b, s.desc.FullName()...) 236 b = append(b, ')') 237 default: 238 b = append(b, "<invalid>"...) 239 } 240 return b 241} 242