github.com/jhump/protoreflect@v1.16.0/desc/protoparse/ast/options.go (about) 1 package ast 2 3 import "fmt" 4 5 // OptionDeclNode is a placeholder interface for AST nodes that represent 6 // options. This allows NoSourceNode to be used in place of *OptionNode 7 // for some usages. 8 type OptionDeclNode interface { 9 Node 10 GetName() Node 11 GetValue() ValueNode 12 } 13 14 var _ OptionDeclNode = (*OptionNode)(nil) 15 var _ OptionDeclNode = NoSourceNode{} 16 17 // OptionNode represents the declaration of a single option for an element. 18 // It is used both for normal option declarations (start with "option" keyword 19 // and end with semicolon) and for compact options found in fields, enum values, 20 // and extension ranges. Example: 21 // 22 // option (custom.option) = "foo"; 23 type OptionNode struct { 24 compositeNode 25 Keyword *KeywordNode // absent for compact options 26 Name *OptionNameNode 27 Equals *RuneNode 28 Val ValueNode 29 Semicolon *RuneNode // absent for compact options 30 } 31 32 func (e *OptionNode) fileElement() {} 33 func (e *OptionNode) msgElement() {} 34 func (e *OptionNode) oneOfElement() {} 35 func (e *OptionNode) enumElement() {} 36 func (e *OptionNode) serviceElement() {} 37 func (e *OptionNode) methodElement() {} 38 39 // NewOptionNode creates a new *OptionNode for a full option declaration (as 40 // used in files, messages, oneofs, enums, services, and methods). All arguments 41 // must be non-nil. (Also see NewCompactOptionNode.) 42 // - keyword: The token corresponding to the "option" keyword. 43 // - name: The token corresponding to the name of the option. 44 // - equals: The token corresponding to the "=" rune after the name. 45 // - val: The token corresponding to the option value. 46 // - semicolon: The token corresponding to the ";" rune that ends the declaration. 47 func NewOptionNode(keyword *KeywordNode, name *OptionNameNode, equals *RuneNode, val ValueNode, semicolon *RuneNode) *OptionNode { 48 if keyword == nil { 49 panic("keyword is nil") 50 } 51 if name == nil { 52 panic("name is nil") 53 } 54 if equals == nil { 55 panic("equals is nil") 56 } 57 if val == nil { 58 panic("val is nil") 59 } 60 if semicolon == nil { 61 panic("semicolon is nil") 62 } 63 children := []Node{keyword, name, equals, val, semicolon} 64 return &OptionNode{ 65 compositeNode: compositeNode{ 66 children: children, 67 }, 68 Keyword: keyword, 69 Name: name, 70 Equals: equals, 71 Val: val, 72 Semicolon: semicolon, 73 } 74 } 75 76 // NewCompactOptionNode creates a new *OptionNode for a full compact declaration 77 // (as used in fields, enum values, and extension ranges). All arguments must be 78 // non-nil. 79 // - name: The token corresponding to the name of the option. 80 // - equals: The token corresponding to the "=" rune after the name. 81 // - val: The token corresponding to the option value. 82 func NewCompactOptionNode(name *OptionNameNode, equals *RuneNode, val ValueNode) *OptionNode { 83 if name == nil { 84 panic("name is nil") 85 } 86 if equals == nil { 87 panic("equals is nil") 88 } 89 if val == nil { 90 panic("val is nil") 91 } 92 children := []Node{name, equals, val} 93 return &OptionNode{ 94 compositeNode: compositeNode{ 95 children: children, 96 }, 97 Name: name, 98 Equals: equals, 99 Val: val, 100 } 101 } 102 103 func (n *OptionNode) GetName() Node { 104 return n.Name 105 } 106 107 func (n *OptionNode) GetValue() ValueNode { 108 return n.Val 109 } 110 111 // OptionNameNode represents an option name or even a traversal through message 112 // types to name a nested option field. Example: 113 // 114 // (foo.bar).baz.(bob) 115 type OptionNameNode struct { 116 compositeNode 117 Parts []*FieldReferenceNode 118 // Dots represent the separating '.' characters between name parts. The 119 // length of this slice must be exactly len(Parts)-1, each item in Parts 120 // having a corresponding item in this slice *except the last* (since a 121 // trailing dot is not allowed). 122 // 123 // These do *not* include dots that are inside of an extension name. For 124 // example: (foo.bar).baz.(bob) has three parts: 125 // 1. (foo.bar) - an extension name 126 // 2. baz - a regular field in foo.bar 127 // 3. (bob) - an extension field in baz 128 // Note that the dot in foo.bar will thus not be present in Dots but is 129 // instead in Parts[0]. 130 Dots []*RuneNode 131 } 132 133 // NewOptionNameNode creates a new *OptionNameNode. The dots arg must have a 134 // length that is one less than the length of parts. The parts arg must not be 135 // empty. 136 func NewOptionNameNode(parts []*FieldReferenceNode, dots []*RuneNode) *OptionNameNode { 137 if len(parts) == 0 { 138 panic("must have at least one part") 139 } 140 if len(dots) != len(parts)-1 { 141 panic(fmt.Sprintf("%d parts requires %d dots, not %d", len(parts), len(parts)-1, len(dots))) 142 } 143 children := make([]Node, 0, len(parts)*2-1) 144 for i, part := range parts { 145 if part == nil { 146 panic(fmt.Sprintf("parts[%d] is nil", i)) 147 } 148 if i > 0 { 149 if dots[i-1] == nil { 150 panic(fmt.Sprintf("dots[%d] is nil", i-1)) 151 } 152 children = append(children, dots[i-1]) 153 } 154 children = append(children, part) 155 } 156 return &OptionNameNode{ 157 compositeNode: compositeNode{ 158 children: children, 159 }, 160 Parts: parts, 161 Dots: dots, 162 } 163 } 164 165 // FieldReferenceNode is a reference to a field name. It can indicate a regular 166 // field (simple unqualified name), an extension field (possibly-qualified name 167 // that is enclosed either in brackets or parentheses), or an "any" type 168 // reference (a type URL in the form "server.host/fully.qualified.Name" that is 169 // enclosed in brackets). 170 // 171 // Extension names are used in options to refer to custom options (which are 172 // actually extensions), in which case the name is enclosed in parentheses "(" 173 // and ")". They can also be used to refer to extension fields of options. 174 // 175 // Extension names are also used in message literals to set extension fields, 176 // in which case the name is enclosed in square brackets "[" and "]". 177 // 178 // "Any" type references can only be used in message literals, and are not 179 // allowed in option names. They are always enclosed in square brackets. An 180 // "any" type reference is distinguished from an extension name by the presence 181 // of a slash, which must be present in an "any" type reference and must be 182 // absent in an extension name. 183 // 184 // Examples: 185 // 186 // foobar 187 // (foo.bar) 188 // [foo.bar] 189 // [type.googleapis.com/foo.bar] 190 type FieldReferenceNode struct { 191 compositeNode 192 Open *RuneNode // only present for extension names and "any" type references 193 194 // only present for "any" type references 195 UrlPrefix IdentValueNode 196 Slash *RuneNode 197 198 Name IdentValueNode 199 200 Close *RuneNode // only present for extension names and "any" type references 201 } 202 203 // NewFieldReferenceNode creates a new *FieldReferenceNode for a regular field. 204 // The name arg must not be nil. 205 func NewFieldReferenceNode(name *IdentNode) *FieldReferenceNode { 206 if name == nil { 207 panic("name is nil") 208 } 209 children := []Node{name} 210 return &FieldReferenceNode{ 211 compositeNode: compositeNode{ 212 children: children, 213 }, 214 Name: name, 215 } 216 } 217 218 // NewExtensionFieldReferenceNode creates a new *FieldReferenceNode for an 219 // extension field. All args must be non-nil. The openSym and closeSym runes 220 // should be "(" and ")" or "[" and "]". 221 func NewExtensionFieldReferenceNode(openSym *RuneNode, name IdentValueNode, closeSym *RuneNode) *FieldReferenceNode { 222 if name == nil { 223 panic("name is nil") 224 } 225 if openSym == nil { 226 panic("openSym is nil") 227 } 228 if closeSym == nil { 229 panic("closeSym is nil") 230 } 231 children := []Node{openSym, name, closeSym} 232 return &FieldReferenceNode{ 233 compositeNode: compositeNode{ 234 children: children, 235 }, 236 Open: openSym, 237 Name: name, 238 Close: closeSym, 239 } 240 } 241 242 // NewAnyTypeReferenceNode creates a new *FieldReferenceNode for an "any" 243 // type reference. All args must be non-nil. The openSym and closeSym runes 244 // should be "[" and "]". The slashSym run should be "/". 245 func NewAnyTypeReferenceNode(openSym *RuneNode, urlPrefix IdentValueNode, slashSym *RuneNode, name IdentValueNode, closeSym *RuneNode) *FieldReferenceNode { 246 if name == nil { 247 panic("name is nil") 248 } 249 if openSym == nil { 250 panic("openSym is nil") 251 } 252 if closeSym == nil { 253 panic("closeSym is nil") 254 } 255 if urlPrefix == nil { 256 panic("urlPrefix is nil") 257 } 258 if slashSym == nil { 259 panic("slashSym is nil") 260 } 261 children := []Node{openSym, urlPrefix, slashSym, name, closeSym} 262 return &FieldReferenceNode{ 263 compositeNode: compositeNode{ 264 children: children, 265 }, 266 Open: openSym, 267 UrlPrefix: urlPrefix, 268 Slash: slashSym, 269 Name: name, 270 Close: closeSym, 271 } 272 } 273 274 // IsExtension reports if this is an extension name or not (e.g. enclosed in 275 // punctuation, such as parentheses or brackets). 276 func (a *FieldReferenceNode) IsExtension() bool { 277 return a.Open != nil && a.Slash == nil 278 } 279 280 // IsExtension reports if this is an extension name or not (e.g. enclosed in 281 // punctuation, such as parentheses or brackets). 282 func (a *FieldReferenceNode) IsAnyTypeReference() bool { 283 return a.Slash != nil 284 } 285 286 func (a *FieldReferenceNode) Value() string { 287 if a.Open != nil { 288 if a.Slash != nil { 289 return string(a.Open.Rune) + string(a.UrlPrefix.AsIdentifier()) + string(a.Slash.Rune) + string(a.Name.AsIdentifier()) + string(a.Close.Rune) 290 } 291 return string(a.Open.Rune) + string(a.Name.AsIdentifier()) + string(a.Close.Rune) 292 } else { 293 return string(a.Name.AsIdentifier()) 294 } 295 } 296 297 // CompactOptionsNode represents a compact options declaration, as used with 298 // fields, enum values, and extension ranges. Example: 299 // 300 // [deprecated = true, json_name = "foo_bar"] 301 type CompactOptionsNode struct { 302 compositeNode 303 OpenBracket *RuneNode 304 Options []*OptionNode 305 // Commas represent the separating ',' characters between options. The 306 // length of this slice must be exactly len(Options)-1, with each item 307 // in Options having a corresponding item in this slice *except the last* 308 // (since a trailing comma is not allowed). 309 Commas []*RuneNode 310 CloseBracket *RuneNode 311 } 312 313 // NewCompactOptionsNode creates a *CompactOptionsNode. All args must be 314 // non-nil. The commas arg must have a length that is one less than the 315 // length of opts. The opts arg must not be empty. 316 func NewCompactOptionsNode(openBracket *RuneNode, opts []*OptionNode, commas []*RuneNode, closeBracket *RuneNode) *CompactOptionsNode { 317 if openBracket == nil { 318 panic("openBracket is nil") 319 } 320 if closeBracket == nil { 321 panic("closeBracket is nil") 322 } 323 if len(opts) == 0 { 324 panic("must have at least one part") 325 } 326 if len(commas) != len(opts)-1 { 327 panic(fmt.Sprintf("%d opts requires %d commas, not %d", len(opts), len(opts)-1, len(commas))) 328 } 329 children := make([]Node, 0, len(opts)*2+1) 330 children = append(children, openBracket) 331 for i, opt := range opts { 332 if i > 0 { 333 if commas[i-1] == nil { 334 panic(fmt.Sprintf("commas[%d] is nil", i-1)) 335 } 336 children = append(children, commas[i-1]) 337 } 338 if opt == nil { 339 panic(fmt.Sprintf("opts[%d] is nil", i)) 340 } 341 children = append(children, opt) 342 } 343 children = append(children, closeBracket) 344 345 return &CompactOptionsNode{ 346 compositeNode: compositeNode{ 347 children: children, 348 }, 349 OpenBracket: openBracket, 350 Options: opts, 351 Commas: commas, 352 CloseBracket: closeBracket, 353 } 354 } 355 356 func (e *CompactOptionsNode) GetElements() []*OptionNode { 357 if e == nil { 358 return nil 359 } 360 return e.Options 361 }