github.com/Diggs/controller-tools@v0.4.2/pkg/markers/parse.go (about) 1 /* 2 Copyright 2019 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package markers 18 19 import ( 20 "bytes" 21 "fmt" 22 "reflect" 23 "strconv" 24 "strings" 25 sc "text/scanner" 26 "unicode" 27 28 "github.com/Diggs/controller-tools/pkg/loader" 29 ) 30 31 // expect checks that the next token of the scanner is the given token, adding an error 32 // to the scanner if not. It returns whether the token was as expected. 33 func expect(scanner *sc.Scanner, expected rune, errDesc string) bool { 34 tok := scanner.Scan() 35 if tok != expected { 36 scanner.Error(scanner, fmt.Sprintf("expected %s, got %q", errDesc, scanner.TokenText())) 37 return false 38 } 39 return true 40 } 41 42 // peekNoSpace is equivalent to scanner.Peek, except that it will consume intervening whitespace. 43 func peekNoSpace(scanner *sc.Scanner) rune { 44 hint := scanner.Peek() 45 for ; hint <= rune(' ') && ((1<<uint64(hint))&scanner.Whitespace) != 0; hint = scanner.Peek() { 46 scanner.Next() // skip the whitespace 47 } 48 return hint 49 } 50 51 var ( 52 // interfaceType is a pre-computed reflect.Type representing the empty interface. 53 interfaceType = reflect.TypeOf((*interface{})(nil)).Elem() 54 rawArgsType = reflect.TypeOf((*RawArguments)(nil)).Elem() 55 ) 56 57 // lowerCamelCase converts PascalCase string to 58 // a camelCase string (by lowering the first rune). 59 func lowerCamelCase(in string) string { 60 isFirst := true 61 return strings.Map(func(inRune rune) rune { 62 if isFirst { 63 isFirst = false 64 return unicode.ToLower(inRune) 65 } 66 return inRune 67 }, in) 68 } 69 70 // RawArguments is a special type that can be used for a marker 71 // to receive *all* raw, underparsed argument data for a marker. 72 // You probably want to use `interface{}` to match any type instead. 73 // Use *only* for legacy markers that don't follow Definition's normal 74 // parsing logic. It should *not* be used as a field in a marker struct. 75 type RawArguments []byte 76 77 // ArgumentType is the kind of a marker argument type. 78 // It's roughly analogous to a subset of reflect.Kind, with 79 // an extra "AnyType" to represent the empty interface. 80 type ArgumentType int 81 82 const ( 83 // Invalid represents a type that can't be parsed, and should never be used. 84 InvalidType ArgumentType = iota 85 // IntType is an int 86 IntType 87 // StringType is a string 88 StringType 89 // BoolType is a bool 90 BoolType 91 // AnyType is the empty interface, and matches the rest of the content 92 AnyType 93 // SliceType is any slice constructed of the ArgumentTypes 94 SliceType 95 // MapType is any map constructed of string keys, and ArgumentType values. 96 // Keys are strings, and it's common to see AnyType (non-uniform) values. 97 MapType 98 // RawType represents content that gets passed directly to the marker 99 // without any parsing. It should *only* be used with anonymous markers. 100 RawType 101 ) 102 103 // Argument is the type of a marker argument. 104 type Argument struct { 105 // Type is the type of this argument For non-scalar types (map and slice), 106 // further information is specified in ItemType. 107 Type ArgumentType 108 // Optional indicates if this argument is optional. 109 Optional bool 110 // Pointer indicates if this argument was a pointer (this is really only 111 // needed for deserialization, and should alway imply optional) 112 Pointer bool 113 114 // ItemType is the type of the slice item for slices, and the value type 115 // for maps. 116 ItemType *Argument 117 } 118 119 // typeString contains the internals of TypeString. 120 func (a Argument) typeString(out *strings.Builder) { 121 if a.Pointer { 122 out.WriteRune('*') 123 } 124 125 switch a.Type { 126 case InvalidType: 127 out.WriteString("<invalid>") 128 case IntType: 129 out.WriteString("int") 130 case StringType: 131 out.WriteString("string") 132 case BoolType: 133 out.WriteString("bool") 134 case AnyType: 135 out.WriteString("<any>") 136 case SliceType: 137 out.WriteString("[]") 138 // arguments can't be non-pointer optional, so just call into typeString again. 139 a.ItemType.typeString(out) 140 case MapType: 141 out.WriteString("map[string]") 142 a.ItemType.typeString(out) 143 case RawType: 144 out.WriteString("<raw>") 145 } 146 } 147 148 // TypeString returns a string roughly equivalent 149 // (but not identical) to the underlying Go type that 150 // this argument would parse to. It's mainly useful 151 // for user-friendly formatting of this argument (e.g. 152 // help strings). 153 func (a Argument) TypeString() string { 154 out := &strings.Builder{} 155 a.typeString(out) 156 return out.String() 157 } 158 159 func (a Argument) String() string { 160 if a.Optional { 161 return fmt.Sprintf("<optional arg %s>", a.TypeString()) 162 } 163 return fmt.Sprintf("<arg %s>", a.TypeString()) 164 } 165 166 // castAndSet casts val to out's type if needed, 167 // then sets out to val. 168 func castAndSet(out, val reflect.Value) { 169 outType := out.Type() 170 if outType != val.Type() { 171 val = val.Convert(outType) 172 } 173 out.Set(val) 174 } 175 176 // makeSliceType makes a reflect.Type for a slice of the given type. 177 // Useful for constructing the out value for when AnyType's guess returns a slice. 178 func makeSliceType(itemType Argument) (reflect.Type, error) { 179 var itemReflectedType reflect.Type 180 switch itemType.Type { 181 case IntType: 182 itemReflectedType = reflect.TypeOf(int(0)) 183 case StringType: 184 itemReflectedType = reflect.TypeOf("") 185 case BoolType: 186 itemReflectedType = reflect.TypeOf(false) 187 case SliceType: 188 subItemType, err := makeSliceType(*itemType.ItemType) 189 if err != nil { 190 return nil, err 191 } 192 itemReflectedType = subItemType 193 case MapType: 194 subItemType, err := makeMapType(*itemType.ItemType) 195 if err != nil { 196 return nil, err 197 } 198 itemReflectedType = subItemType 199 // TODO(directxman12): support non-uniform slices? (probably not) 200 default: 201 return nil, fmt.Errorf("invalid type when constructing guessed slice out: %v", itemType.Type) 202 } 203 204 if itemType.Pointer { 205 itemReflectedType = reflect.PtrTo(itemReflectedType) 206 } 207 208 return reflect.SliceOf(itemReflectedType), nil 209 } 210 211 // makeMapType makes a reflect.Type for a map of the given item type. 212 // Useful for constructing the out value for when AnyType's guess returns a map. 213 func makeMapType(itemType Argument) (reflect.Type, error) { 214 var itemReflectedType reflect.Type 215 switch itemType.Type { 216 case IntType: 217 itemReflectedType = reflect.TypeOf(int(0)) 218 case StringType: 219 itemReflectedType = reflect.TypeOf("") 220 case BoolType: 221 itemReflectedType = reflect.TypeOf(false) 222 case SliceType: 223 subItemType, err := makeSliceType(*itemType.ItemType) 224 if err != nil { 225 return nil, err 226 } 227 itemReflectedType = subItemType 228 // TODO(directxman12): support non-uniform slices? (probably not) 229 case MapType: 230 subItemType, err := makeMapType(*itemType.ItemType) 231 if err != nil { 232 return nil, err 233 } 234 itemReflectedType = subItemType 235 case AnyType: 236 // NB(directxman12): maps explicitly allow non-uniform item types, unlike slices at the moment 237 itemReflectedType = interfaceType 238 default: 239 return nil, fmt.Errorf("invalid type when constructing guessed slice out: %v", itemType.Type) 240 } 241 242 if itemType.Pointer { 243 itemReflectedType = reflect.PtrTo(itemReflectedType) 244 } 245 246 return reflect.MapOf(reflect.TypeOf(""), itemReflectedType), nil 247 } 248 249 // guessType takes an educated guess about the type of the next field. If allowSlice 250 // is false, it will not guess slices. It's less efficient than parsing with actual 251 // type information, since we need to allocate to peek ahead full tokens, and the scanner 252 // only allows peeking ahead one character. 253 // Maps are *always* non-uniform (i.e. type the AnyType item type), since they're frequently 254 // used to represent things like defaults for an object in JSON. 255 func guessType(scanner *sc.Scanner, raw string, allowSlice bool) *Argument { 256 if allowSlice { 257 maybeItem := guessType(scanner, raw, false) 258 259 subRaw := raw[scanner.Pos().Offset:] 260 subScanner := parserScanner(subRaw, scanner.Error) 261 262 var tok rune 263 for tok = subScanner.Scan(); tok != ',' && tok != sc.EOF && tok != ';'; tok = subScanner.Scan() { 264 // wait till we get something interesting 265 } 266 267 // semicolon means it's a legacy slice 268 if tok == ';' { 269 return &Argument{ 270 Type: SliceType, 271 ItemType: maybeItem, 272 } 273 } 274 275 return maybeItem 276 } 277 278 // everything else needs a duplicate scanner to scan properly 279 // (so we don't consume our scanner tokens until we actually 280 // go to use this -- Go doesn't like scanners that can be rewound). 281 subRaw := raw[scanner.Pos().Offset:] 282 subScanner := parserScanner(subRaw, scanner.Error) 283 284 // skip whitespace 285 hint := peekNoSpace(subScanner) 286 287 // first, try the easy case -- quoted strings strings 288 switch hint { 289 case '"', '\'', '`': 290 return &Argument{Type: StringType} 291 } 292 293 // next, check for slices or maps 294 if hint == '{' { 295 subScanner.Scan() 296 297 // TODO(directxman12): this can't guess at empty objects, but that's generally ok. 298 // We'll cross that bridge when we get there. 299 300 // look ahead till we can figure out if this is a map or a slice 301 firstElemType := guessType(subScanner, subRaw, false) 302 if firstElemType.Type == StringType { 303 // might be a map or slice, parse the string and check for colon 304 // (blech, basically arbitrary look-ahead due to raw strings). 305 var keyVal string // just ignore this 306 (&Argument{Type: StringType}).parseString(subScanner, raw, reflect.Indirect(reflect.ValueOf(&keyVal))) 307 308 if subScanner.Scan() == ':' { 309 // it's got a string followed by a colon -- it's a map 310 return &Argument{ 311 Type: MapType, 312 ItemType: &Argument{Type: AnyType}, 313 } 314 } 315 } 316 317 // definitely a slice -- maps have to have string keys and have a value followed by a colon 318 return &Argument{ 319 Type: SliceType, 320 ItemType: firstElemType, 321 } 322 } 323 324 // then, bools... 325 probablyString := false 326 if hint == 't' || hint == 'f' { 327 // maybe a bool 328 if nextTok := subScanner.Scan(); nextTok == sc.Ident { 329 switch subScanner.TokenText() { 330 case "true", "false": 331 // definitely a bool 332 return &Argument{Type: BoolType} 333 } 334 // probably a string 335 probablyString = true 336 } else { 337 // we shouldn't ever get here 338 scanner.Error(scanner, fmt.Sprintf("got a token (%q) that looked like an ident, but was not", scanner.TokenText())) 339 return &Argument{Type: InvalidType} 340 } 341 } 342 343 // then, integers... 344 if !probablyString { 345 nextTok := subScanner.Scan() 346 if nextTok == '-' { 347 nextTok = subScanner.Scan() 348 } 349 if nextTok == sc.Int { 350 return &Argument{Type: IntType} 351 } 352 } 353 354 // otherwise assume bare strings 355 return &Argument{Type: StringType} 356 } 357 358 // parseString parses either of the two accepted string forms (quoted, or bare tokens). 359 func (a *Argument) parseString(scanner *sc.Scanner, raw string, out reflect.Value) { 360 // strings are a bit weird -- the "easy" case is quoted strings (tokenized as strings), 361 // the "hard" case (present for backwards compat) is a bare sequence of tokens that aren't 362 // a comma. 363 tok := scanner.Scan() 364 if tok == sc.String || tok == sc.RawString { 365 // the easy case 366 val, err := strconv.Unquote(scanner.TokenText()) 367 if err != nil { 368 scanner.Error(scanner, fmt.Sprintf("unable to parse string: %v", err)) 369 return 370 } 371 castAndSet(out, reflect.ValueOf(val)) 372 return 373 } 374 375 // the "hard" case -- bare tokens not including ',' (the argument 376 // separator), ';' (the slice separator), ':' (the map separator), or '}' 377 // (delimitted slice ender) 378 startPos := scanner.Position.Offset 379 for hint := peekNoSpace(scanner); hint != ',' && hint != ';' && hint != ':' && hint != '}' && hint != sc.EOF; hint = peekNoSpace(scanner) { 380 // skip this token 381 scanner.Scan() 382 } 383 endPos := scanner.Position.Offset + len(scanner.TokenText()) 384 castAndSet(out, reflect.ValueOf(raw[startPos:endPos])) 385 } 386 387 // parseSlice parses either of the two slice forms (curly-brace-delimitted and semicolon-separated). 388 func (a *Argument) parseSlice(scanner *sc.Scanner, raw string, out reflect.Value) { 389 // slices have two supported formats, like string: 390 // - `{val, val, val}` (preferred) 391 // - `val;val;val` (legacy) 392 resSlice := reflect.Zero(out.Type()) 393 elem := reflect.Indirect(reflect.New(out.Type().Elem())) 394 395 // preferred case 396 if peekNoSpace(scanner) == '{' { 397 // NB(directxman12): supporting delimitted slices in bare slices 398 // would require an extra look-ahead here :-/ 399 400 scanner.Scan() // skip '{' 401 for hint := peekNoSpace(scanner); hint != '}' && hint != sc.EOF; hint = peekNoSpace(scanner) { 402 a.ItemType.parse(scanner, raw, elem, true /* parsing a slice */) 403 resSlice = reflect.Append(resSlice, elem) 404 tok := peekNoSpace(scanner) 405 if tok == '}' { 406 break 407 } 408 if !expect(scanner, ',', "comma") { 409 return 410 } 411 } 412 if !expect(scanner, '}', "close curly brace") { 413 return 414 } 415 castAndSet(out, resSlice) 416 return 417 } 418 419 // legacy case 420 for hint := peekNoSpace(scanner); hint != ',' && hint != '}' && hint != sc.EOF; hint = peekNoSpace(scanner) { 421 a.ItemType.parse(scanner, raw, elem, true /* parsing a slice */) 422 resSlice = reflect.Append(resSlice, elem) 423 tok := peekNoSpace(scanner) 424 if tok == ',' || tok == '}' || tok == sc.EOF { 425 break 426 } 427 scanner.Scan() 428 if tok != ';' { 429 scanner.Error(scanner, fmt.Sprintf("expected comma, got %q", scanner.TokenText())) 430 return 431 } 432 } 433 castAndSet(out, resSlice) 434 } 435 436 // parseMap parses a map of the form {string: val, string: val, string: val} 437 func (a *Argument) parseMap(scanner *sc.Scanner, raw string, out reflect.Value) { 438 resMap := reflect.MakeMap(out.Type()) 439 elem := reflect.Indirect(reflect.New(out.Type().Elem())) 440 key := reflect.Indirect(reflect.New(out.Type().Key())) 441 442 if !expect(scanner, '{', "open curly brace") { 443 return 444 } 445 446 for hint := peekNoSpace(scanner); hint != '}' && hint != sc.EOF; hint = peekNoSpace(scanner) { 447 a.parseString(scanner, raw, key) 448 if !expect(scanner, ':', "colon") { 449 return 450 } 451 a.ItemType.parse(scanner, raw, elem, false /* not in a slice */) 452 resMap.SetMapIndex(key, elem) 453 454 if peekNoSpace(scanner) == '}' { 455 break 456 } 457 if !expect(scanner, ',', "comma") { 458 return 459 } 460 } 461 462 if !expect(scanner, '}', "close curly brace") { 463 return 464 } 465 466 castAndSet(out, resMap) 467 } 468 469 // parse functions like Parse, except that it allows passing down whether or not we're 470 // already in a slice, to avoid duplicate legacy slice detection for AnyType 471 func (a *Argument) parse(scanner *sc.Scanner, raw string, out reflect.Value, inSlice bool) { 472 // nolint:gocyclo 473 if a.Type == InvalidType { 474 scanner.Error(scanner, fmt.Sprintf("cannot parse invalid type")) 475 return 476 } 477 if a.Pointer { 478 out.Set(reflect.New(out.Type().Elem())) 479 out = reflect.Indirect(out) 480 } 481 switch a.Type { 482 case RawType: 483 // raw consumes everything else 484 castAndSet(out, reflect.ValueOf(raw[scanner.Pos().Offset:])) 485 // consume everything else 486 for tok := scanner.Scan(); tok != sc.EOF; tok = scanner.Scan() { 487 } 488 case IntType: 489 nextChar := scanner.Peek() 490 isNegative := false 491 if nextChar == '-' { 492 isNegative = true 493 scanner.Scan() // eat the '-' 494 } 495 if !expect(scanner, sc.Int, "integer") { 496 return 497 } 498 // TODO(directxman12): respect the size when parsing 499 text := scanner.TokenText() 500 if isNegative { 501 text = "-" + text 502 } 503 val, err := strconv.Atoi(text) 504 if err != nil { 505 scanner.Error(scanner, fmt.Sprintf("unable to parse integer: %v", err)) 506 return 507 } 508 castAndSet(out, reflect.ValueOf(val)) 509 case StringType: 510 // strings are a bit weird -- the "easy" case is quoted strings (tokenized as strings), 511 // the "hard" case (present for backwards compat) is a bare sequence of tokens that aren't 512 // a comma. 513 a.parseString(scanner, raw, out) 514 case BoolType: 515 if !expect(scanner, sc.Ident, "true or false") { 516 return 517 } 518 switch scanner.TokenText() { 519 case "true": 520 castAndSet(out, reflect.ValueOf(true)) 521 case "false": 522 castAndSet(out, reflect.ValueOf(false)) 523 default: 524 scanner.Error(scanner, fmt.Sprintf("expected true or false, got %q", scanner.TokenText())) 525 return 526 } 527 case AnyType: 528 guessedType := guessType(scanner, raw, !inSlice) 529 newOut := out 530 531 // we need to be able to construct the right element types, below 532 // in parse, so construct a concretely-typed value to use as "out" 533 switch guessedType.Type { 534 case SliceType: 535 newType, err := makeSliceType(*guessedType.ItemType) 536 if err != nil { 537 scanner.Error(scanner, err.Error()) 538 return 539 } 540 newOut = reflect.Indirect(reflect.New(newType)) 541 case MapType: 542 newType, err := makeMapType(*guessedType.ItemType) 543 if err != nil { 544 scanner.Error(scanner, err.Error()) 545 return 546 } 547 newOut = reflect.Indirect(reflect.New(newType)) 548 } 549 if !newOut.CanSet() { 550 panic("at the disco") // TODO(directxman12): this is left over from debugging -- it might need to be an error 551 } 552 guessedType.Parse(scanner, raw, newOut) 553 castAndSet(out, newOut) 554 case SliceType: 555 // slices have two supported formats, like string: 556 // - `{val, val, val}` (preferred) 557 // - `val;val;val` (legacy) 558 a.parseSlice(scanner, raw, out) 559 case MapType: 560 // maps are {string: val, string: val, string: val} 561 a.parseMap(scanner, raw, out) 562 } 563 } 564 565 // Parse attempts to consume the argument from the given scanner (based on the given 566 // raw input as well for collecting ranges of content), and places the output value 567 // in the given reflect.Value. Errors are reported via the given scanner. 568 func (a *Argument) Parse(scanner *sc.Scanner, raw string, out reflect.Value) { 569 a.parse(scanner, raw, out, false) 570 } 571 572 // ArgumentFromType constructs an Argument by examining the given 573 // raw reflect.Type. It can construct arguments from the Go types 574 // corresponding to any of the types listed in ArgumentType. 575 func ArgumentFromType(rawType reflect.Type) (Argument, error) { 576 if rawType == rawArgsType { 577 return Argument{ 578 Type: RawType, 579 }, nil 580 } 581 582 if rawType == interfaceType { 583 return Argument{ 584 Type: AnyType, 585 }, nil 586 } 587 588 arg := Argument{} 589 if rawType.Kind() == reflect.Ptr { 590 rawType = rawType.Elem() 591 arg.Pointer = true 592 arg.Optional = true 593 } 594 595 switch rawType.Kind() { 596 case reflect.String: 597 arg.Type = StringType 598 case reflect.Int, reflect.Int32: // NB(directxman12): all ints in kubernetes are int32, so explicitly support that 599 arg.Type = IntType 600 case reflect.Bool: 601 arg.Type = BoolType 602 case reflect.Slice: 603 arg.Type = SliceType 604 itemType, err := ArgumentFromType(rawType.Elem()) 605 if err != nil { 606 return Argument{}, fmt.Errorf("bad slice item type: %w", err) 607 } 608 arg.ItemType = &itemType 609 case reflect.Map: 610 arg.Type = MapType 611 if rawType.Key().Kind() != reflect.String { 612 return Argument{}, fmt.Errorf("bad map key type: map keys must be strings") 613 } 614 itemType, err := ArgumentFromType(rawType.Elem()) 615 if err != nil { 616 return Argument{}, fmt.Errorf("bad slice item type: %w", err) 617 } 618 arg.ItemType = &itemType 619 default: 620 return Argument{}, fmt.Errorf("type has unsupported kind %s", rawType.Kind()) 621 } 622 623 return arg, nil 624 } 625 626 // TargetType describes which kind of node a given marker is associated with. 627 type TargetType int 628 629 const ( 630 // DescribesPackage indicates that a marker is associated with a package. 631 DescribesPackage TargetType = iota 632 // DescribesType indicates that a marker is associated with a type declaration. 633 DescribesType 634 // DescribesField indicates that a marker is associated with a struct field. 635 DescribesField 636 ) 637 638 func (t TargetType) String() string { 639 switch t { 640 case DescribesPackage: 641 return "package" 642 case DescribesType: 643 return "type" 644 case DescribesField: 645 return "field" 646 default: 647 return "(unknown)" 648 } 649 } 650 651 // Definition is a parsed definition of a marker. 652 type Definition struct { 653 // Output is the deserialized Go type of the marker. 654 Output reflect.Type 655 // Name is the marker's name. 656 Name string 657 // Target indicates which kind of node this marker can be associated with. 658 Target TargetType 659 // Fields lists out the types of each field that this marker has, by 660 // argument name as used in the marker (if the output type isn't a struct, 661 // it'll have a single, blank field name). This only lists exported fields, 662 // (as per reflection rules). 663 Fields map[string]Argument 664 // FieldNames maps argument names (as used in the marker) to struct field name 665 // in the output type. 666 FieldNames map[string]string 667 // Strict indicates that this definition should error out when parsing if 668 // not all non-optional fields were seen. 669 Strict bool 670 } 671 672 // AnonymousField indicates that the definition has one field, 673 // (actually the original object), and thus the field 674 // doesn't get named as part of the name. 675 func (d *Definition) AnonymousField() bool { 676 if len(d.Fields) != 1 { 677 return false 678 } 679 _, hasAnonField := d.Fields[""] 680 return hasAnonField 681 } 682 683 // Empty indicates that this definition has no fields. 684 func (d *Definition) Empty() bool { 685 return len(d.Fields) == 0 686 } 687 688 // argumentInfo returns information about an argument field as the marker parser's field loader 689 // would see it. This can be useful if you have to interact with marker definition structs 690 // externally (e.g. at compile time). 691 func argumentInfo(fieldName string, tag reflect.StructTag) (argName string, optionalOpt bool) { 692 argName = lowerCamelCase(fieldName) 693 markerTag, tagSpecified := tag.Lookup("marker") 694 markerTagParts := strings.Split(markerTag, ",") 695 if tagSpecified && markerTagParts[0] != "" { 696 // allow overriding to support legacy cases where we don't follow camelCase conventions 697 argName = markerTagParts[0] 698 } 699 optionalOpt = false 700 for _, tagOption := range markerTagParts[1:] { 701 switch tagOption { 702 case "optional": 703 optionalOpt = true 704 } 705 } 706 707 return argName, optionalOpt 708 } 709 710 // loadFields uses reflection to populate argument information from the Output type. 711 func (d *Definition) loadFields() error { 712 if d.Fields == nil { 713 d.Fields = make(map[string]Argument) 714 d.FieldNames = make(map[string]string) 715 } 716 if d.Output.Kind() != reflect.Struct { 717 // anonymous field type 718 argType, err := ArgumentFromType(d.Output) 719 if err != nil { 720 return err 721 } 722 d.Fields[""] = argType 723 d.FieldNames[""] = "" 724 return nil 725 } 726 727 for i := 0; i < d.Output.NumField(); i++ { 728 field := d.Output.Field(i) 729 if field.PkgPath != "" { 730 // as per the reflect package docs, pkgpath is empty for exported fields, 731 // so non-empty package path means a private field, which we should skip 732 continue 733 } 734 argName, optionalOpt := argumentInfo(field.Name, field.Tag) 735 736 argType, err := ArgumentFromType(field.Type) 737 if err != nil { 738 return fmt.Errorf("unable to extract type information for field %q: %w", field.Name, err) 739 } 740 741 if argType.Type == RawType { 742 return fmt.Errorf("RawArguments must be the direct type of a marker, and not a field") 743 } 744 745 argType.Optional = optionalOpt || argType.Optional 746 747 d.Fields[argName] = argType 748 d.FieldNames[argName] = field.Name 749 } 750 751 return nil 752 } 753 754 // parserScanner makes a new scanner appropriate for use in parsing definitions and arguments. 755 func parserScanner(raw string, err func(*sc.Scanner, string)) *sc.Scanner { 756 scanner := &sc.Scanner{} 757 scanner.Init(bytes.NewBufferString(raw)) 758 scanner.Mode = sc.ScanIdents | sc.ScanInts | sc.ScanStrings | sc.ScanRawStrings | sc.SkipComments 759 scanner.Error = err 760 761 return scanner 762 } 763 764 // Parse uses the type information in this Definition to parse the given 765 // raw marker in the form `+a:b:c=arg,d=arg` into an output object of the 766 // type specified in the definition. 767 func (d *Definition) Parse(rawMarker string) (interface{}, error) { 768 name, anonName, fields := splitMarker(rawMarker) 769 770 out := reflect.Indirect(reflect.New(d.Output)) 771 772 // if we're a not a struct or have no arguments, treat the full `a:b:c` as the name, 773 // otherwise, treat `c` as a field name, and `a:b` as the marker name. 774 if !d.AnonymousField() && !d.Empty() && len(anonName) >= len(name)+1 { 775 fields = anonName[len(name)+1:] + "=" + fields 776 } 777 778 var errs []error 779 scanner := parserScanner(fields, func(scanner *sc.Scanner, msg string) { 780 errs = append(errs, &ScannerError{Msg: msg, Pos: scanner.Position}) 781 }) 782 783 // TODO(directxman12): strict parsing where we error out if certain fields aren't optional 784 seen := make(map[string]struct{}, len(d.Fields)) 785 if d.AnonymousField() && scanner.Peek() != sc.EOF { 786 // might still be a struct that something fiddled with, so double check 787 structFieldName := d.FieldNames[""] 788 outTarget := out 789 if structFieldName != "" { 790 // it's a struct field mapped to an anonymous marker 791 outTarget = out.FieldByName(structFieldName) 792 if !outTarget.CanSet() { 793 scanner.Error(scanner, fmt.Sprintf("cannot set field %q (might not exist)", structFieldName)) 794 return out.Interface(), loader.MaybeErrList(errs) 795 } 796 } 797 798 // no need for trying to parse field names if we're not a struct 799 field := d.Fields[""] 800 field.Parse(scanner, fields, outTarget) 801 seen[""] = struct{}{} // mark as seen for strict definitions 802 } else if !d.Empty() && scanner.Peek() != sc.EOF { 803 // if we expect *and* actually have arguments passed 804 for { 805 // parse the argument name 806 if !expect(scanner, sc.Ident, "argument name") { 807 break 808 } 809 argName := scanner.TokenText() 810 if !expect(scanner, '=', "equals") { 811 break 812 } 813 814 // make sure we know the field 815 fieldName, known := d.FieldNames[argName] 816 if !known { 817 scanner.Error(scanner, fmt.Sprintf("unknown argument %q", argName)) 818 break 819 } 820 fieldType, known := d.Fields[argName] 821 if !known { 822 scanner.Error(scanner, fmt.Sprintf("unknown argument %q", argName)) 823 break 824 } 825 seen[argName] = struct{}{} // mark as seen for strict definitions 826 827 // parse the field value 828 fieldVal := out.FieldByName(fieldName) 829 if !fieldVal.CanSet() { 830 scanner.Error(scanner, fmt.Sprintf("cannot set field %q (might not exist)", fieldName)) 831 break 832 } 833 fieldType.Parse(scanner, fields, fieldVal) 834 835 if len(errs) > 0 { 836 break 837 } 838 839 if scanner.Peek() == sc.EOF { 840 break 841 } 842 if !expect(scanner, ',', "comma") { 843 break 844 } 845 } 846 } 847 848 if tok := scanner.Scan(); tok != sc.EOF { 849 scanner.Error(scanner, fmt.Sprintf("extra arguments provided: %q", fields[scanner.Position.Offset:])) 850 } 851 852 if d.Strict { 853 for argName, arg := range d.Fields { 854 if _, wasSeen := seen[argName]; !wasSeen && !arg.Optional { 855 scanner.Error(scanner, fmt.Sprintf("missing argument %q", argName)) 856 } 857 } 858 } 859 860 return out.Interface(), loader.MaybeErrList(errs) 861 } 862 863 // MakeDefinition constructs a definition from a name, type, and the output type. 864 // All such definitions are strict by default. If a struct is passed as the output 865 // type, its public fields will automatically be populated into Fields (and similar 866 // fields in Definition). Other values will have a single, empty-string-named Fields 867 // entry. 868 func MakeDefinition(name string, target TargetType, output interface{}) (*Definition, error) { 869 def := &Definition{ 870 Name: name, 871 Target: target, 872 Output: reflect.TypeOf(output), 873 Strict: true, 874 } 875 876 if err := def.loadFields(); err != nil { 877 return nil, err 878 } 879 880 return def, nil 881 } 882 883 // MakeAnyTypeDefinition constructs a definition for an output struct with a 884 // field named `Value` of type `interface{}`. The argument to the marker will 885 // be parsed as AnyType and assigned to the field named `Value`. 886 func MakeAnyTypeDefinition(name string, target TargetType, output interface{}) (*Definition, error) { 887 defn, err := MakeDefinition(name, target, output) 888 if err != nil { 889 return nil, err 890 } 891 defn.FieldNames = map[string]string{"": "Value"} 892 defn.Fields = map[string]Argument{"": defn.Fields["value"]} 893 return defn, nil 894 } 895 896 // splitMarker takes a marker in the form of `+a:b:c=arg,d=arg` and splits it 897 // into the name (`a:b`), the name if it's not a struct (`a:b:c`), and the parts 898 // that are definitely fields (`arg,d=arg`). 899 func splitMarker(raw string) (name string, anonymousName string, restFields string) { 900 raw = raw[1:] // get rid of the leading '+' 901 nameFieldParts := strings.SplitN(raw, "=", 2) 902 if len(nameFieldParts) == 1 { 903 return nameFieldParts[0], nameFieldParts[0], "" 904 } 905 anonymousName = nameFieldParts[0] 906 name = anonymousName 907 restFields = nameFieldParts[1] 908 909 nameParts := strings.Split(name, ":") 910 if len(nameParts) > 1 { 911 name = strings.Join(nameParts[:len(nameParts)-1], ":") 912 } 913 return name, anonymousName, restFields 914 } 915 916 type ScannerError struct { 917 Msg string 918 Pos sc.Position 919 } 920 921 func (e *ScannerError) Error() string { 922 return fmt.Sprintf("%s (at %s)", e.Msg, e.Pos) 923 }