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