github.com/alex123012/deckhouse-controller-tools@v0.0.0-20230510090815-d594daf1af8c/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 "sigs.k8s.io/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 firstElemType := guessType(subScanner, subRaw, false) 314 if firstElemType.Type == StringType { 315 // might be a map or slice, parse the string and check for colon 316 // (blech, basically arbitrary look-ahead due to raw strings). 317 var keyVal string // just ignore this 318 (&Argument{Type: StringType}).parseString(subScanner, raw, reflect.Indirect(reflect.ValueOf(&keyVal))) 319 320 if subScanner.Scan() == ':' { 321 // it's got a string followed by a colon -- it's a map 322 return &Argument{ 323 Type: MapType, 324 ItemType: &Argument{Type: AnyType}, 325 } 326 } 327 } 328 329 // definitely a slice -- maps have to have string keys and have a value followed by a colon 330 return &Argument{ 331 Type: SliceType, 332 ItemType: firstElemType, 333 } 334 } 335 336 // then, bools... 337 probablyString := false 338 if hint == 't' || hint == 'f' { 339 // maybe a bool 340 if nextTok := subScanner.Scan(); nextTok == sc.Ident { 341 switch subScanner.TokenText() { 342 case "true", "false": 343 // definitely a bool 344 return &Argument{Type: BoolType} 345 } 346 // probably a string 347 probablyString = true 348 } else { 349 // we shouldn't ever get here 350 scanner.Error(scanner, fmt.Sprintf("got a token (%q) that looked like an ident, but was not", scanner.TokenText())) 351 return &Argument{Type: InvalidType} 352 } 353 } 354 355 // then, integers... 356 if !probablyString { 357 nextTok := subScanner.Scan() 358 if nextTok == '-' { 359 nextTok = subScanner.Scan() 360 } 361 362 if nextTok == sc.Int { 363 return &Argument{Type: IntType} 364 } 365 if nextTok == sc.Float { 366 return &Argument{Type: NumberType} 367 } 368 } 369 370 // otherwise assume bare strings 371 return &Argument{Type: StringType} 372 } 373 374 // parseString parses either of the two accepted string forms (quoted, or bare tokens). 375 func (a *Argument) parseString(scanner *sc.Scanner, raw string, out reflect.Value) { 376 // we need to temporarily disable the scanner's int/float parsing, since we want to 377 // prevent number parsing errors. 378 oldMode := scanner.Mode 379 scanner.Mode = oldMode &^ sc.ScanInts &^ sc.ScanFloats 380 defer func() { 381 scanner.Mode = oldMode 382 }() 383 384 // strings are a bit weird -- the "easy" case is quoted strings (tokenized as strings), 385 // the "hard" case (present for backwards compat) is a bare sequence of tokens that aren't 386 // a comma. 387 tok := scanner.Scan() 388 if tok == sc.String || tok == sc.RawString { 389 // the easy case 390 val, err := strconv.Unquote(scanner.TokenText()) 391 if err != nil { 392 scanner.Error(scanner, fmt.Sprintf("unable to parse string: %v", err)) 393 return 394 } 395 castAndSet(out, reflect.ValueOf(val)) 396 return 397 } 398 399 // the "hard" case -- bare tokens not including ',' (the argument 400 // separator), ';' (the slice separator), ':' (the map separator), or '}' 401 // (delimitted slice ender) 402 startPos := scanner.Position.Offset 403 for hint := peekNoSpace(scanner); hint != ',' && hint != ';' && hint != ':' && hint != '}' && hint != sc.EOF; hint = peekNoSpace(scanner) { 404 // skip this token 405 scanner.Scan() 406 } 407 endPos := scanner.Position.Offset + len(scanner.TokenText()) 408 castAndSet(out, reflect.ValueOf(raw[startPos:endPos])) 409 } 410 411 // parseSlice parses either of the two slice forms (curly-brace-delimitted and semicolon-separated). 412 func (a *Argument) parseSlice(scanner *sc.Scanner, raw string, out reflect.Value) { 413 // slices have two supported formats, like string: 414 // - `{val, val, val}` (preferred) 415 // - `val;val;val` (legacy) 416 resSlice := reflect.Zero(out.Type()) 417 elem := reflect.Indirect(reflect.New(out.Type().Elem())) 418 419 // preferred case 420 if peekNoSpace(scanner) == '{' { 421 // NB(directxman12): supporting delimitted slices in bare slices 422 // would require an extra look-ahead here :-/ 423 424 scanner.Scan() // skip '{' 425 for hint := peekNoSpace(scanner); hint != '}' && hint != sc.EOF; hint = peekNoSpace(scanner) { 426 a.ItemType.parse(scanner, raw, elem, true /* parsing a slice */) 427 resSlice = reflect.Append(resSlice, elem) 428 tok := peekNoSpace(scanner) 429 if tok == '}' { 430 break 431 } 432 if !expect(scanner, ',', "comma") { 433 return 434 } 435 } 436 if !expect(scanner, '}', "close curly brace") { 437 return 438 } 439 castAndSet(out, resSlice) 440 return 441 } 442 443 // legacy case 444 for hint := peekNoSpace(scanner); hint != ',' && hint != '}' && hint != sc.EOF; hint = peekNoSpace(scanner) { 445 a.ItemType.parse(scanner, raw, elem, true /* parsing a slice */) 446 resSlice = reflect.Append(resSlice, elem) 447 tok := peekNoSpace(scanner) 448 if tok == ',' || tok == '}' || tok == sc.EOF { 449 break 450 } 451 scanner.Scan() 452 if tok != ';' { 453 scanner.Error(scanner, fmt.Sprintf("expected comma, got %q", scanner.TokenText())) 454 return 455 } 456 } 457 castAndSet(out, resSlice) 458 } 459 460 // parseMap parses a map of the form {string: val, string: val, string: val} 461 func (a *Argument) parseMap(scanner *sc.Scanner, raw string, out reflect.Value) { 462 resMap := reflect.MakeMap(out.Type()) 463 elem := reflect.Indirect(reflect.New(out.Type().Elem())) 464 key := reflect.Indirect(reflect.New(out.Type().Key())) 465 466 if !expect(scanner, '{', "open curly brace") { 467 return 468 } 469 470 for hint := peekNoSpace(scanner); hint != '}' && hint != sc.EOF; hint = peekNoSpace(scanner) { 471 a.parseString(scanner, raw, key) 472 if !expect(scanner, ':', "colon") { 473 return 474 } 475 a.ItemType.parse(scanner, raw, elem, false /* not in a slice */) 476 resMap.SetMapIndex(key, elem) 477 478 if peekNoSpace(scanner) == '}' { 479 break 480 } 481 if !expect(scanner, ',', "comma") { 482 return 483 } 484 } 485 486 if !expect(scanner, '}', "close curly brace") { 487 return 488 } 489 490 castAndSet(out, resMap) 491 } 492 493 // parse functions like Parse, except that it allows passing down whether or not we're 494 // already in a slice, to avoid duplicate legacy slice detection for AnyType 495 func (a *Argument) parse(scanner *sc.Scanner, raw string, out reflect.Value, inSlice bool) { 496 // nolint:gocyclo 497 if a.Type == InvalidType { 498 scanner.Error(scanner, "cannot parse invalid type") 499 return 500 } 501 if a.Pointer { 502 out.Set(reflect.New(out.Type().Elem())) 503 out = reflect.Indirect(out) 504 } 505 switch a.Type { 506 case RawType: 507 // raw consumes everything else 508 castAndSet(out, reflect.ValueOf(raw[scanner.Pos().Offset:])) 509 // consume everything else 510 var tok rune 511 for { 512 tok = scanner.Scan() 513 if tok == sc.EOF { 514 break 515 } 516 } 517 case NumberType: 518 nextChar := scanner.Peek() 519 isNegative := false 520 if nextChar == '-' { 521 isNegative = true 522 scanner.Scan() // eat the '-' 523 } 524 525 tok := scanner.Scan() 526 if tok != sc.Float && tok != sc.Int { 527 scanner.Error(scanner, fmt.Sprintf("expected integer or float, got %q", scanner.TokenText())) 528 return 529 } 530 531 text := scanner.TokenText() 532 if isNegative { 533 text = "-" + text 534 } 535 536 val, err := strconv.ParseFloat(text, 64) 537 if err != nil { 538 scanner.Error(scanner, fmt.Sprintf("unable to parse number: %v", err)) 539 return 540 } 541 542 castAndSet(out, reflect.ValueOf(val)) 543 case IntType: 544 nextChar := scanner.Peek() 545 isNegative := false 546 if nextChar == '-' { 547 isNegative = true 548 scanner.Scan() // eat the '-' 549 } 550 if !expect(scanner, sc.Int, "integer") { 551 return 552 } 553 // TODO(directxman12): respect the size when parsing 554 text := scanner.TokenText() 555 if isNegative { 556 text = "-" + text 557 } 558 val, err := strconv.Atoi(text) 559 if err != nil { 560 scanner.Error(scanner, fmt.Sprintf("unable to parse integer: %v", err)) 561 return 562 } 563 castAndSet(out, reflect.ValueOf(val)) 564 case StringType: 565 // strings are a bit weird -- the "easy" case is quoted strings (tokenized as strings), 566 // the "hard" case (present for backwards compat) is a bare sequence of tokens that aren't 567 // a comma. 568 a.parseString(scanner, raw, out) 569 case BoolType: 570 if !expect(scanner, sc.Ident, "true or false") { 571 return 572 } 573 switch scanner.TokenText() { 574 case "true": 575 castAndSet(out, reflect.ValueOf(true)) 576 case "false": 577 castAndSet(out, reflect.ValueOf(false)) 578 default: 579 scanner.Error(scanner, fmt.Sprintf("expected true or false, got %q", scanner.TokenText())) 580 return 581 } 582 case AnyType: 583 guessedType := guessType(scanner, raw, !inSlice) 584 newOut := out 585 586 // we need to be able to construct the right element types, below 587 // in parse, so construct a concretely-typed value to use as "out" 588 switch guessedType.Type { 589 case SliceType: 590 newType, err := makeSliceType(*guessedType.ItemType) 591 if err != nil { 592 scanner.Error(scanner, err.Error()) 593 return 594 } 595 newOut = reflect.Indirect(reflect.New(newType)) 596 case MapType: 597 newType, err := makeMapType(*guessedType.ItemType) 598 if err != nil { 599 scanner.Error(scanner, err.Error()) 600 return 601 } 602 newOut = reflect.Indirect(reflect.New(newType)) 603 } 604 if !newOut.CanSet() { 605 panic("at the disco") // TODO(directxman12): this is left over from debugging -- it might need to be an error 606 } 607 guessedType.Parse(scanner, raw, newOut) 608 castAndSet(out, newOut) 609 case SliceType: 610 // slices have two supported formats, like string: 611 // - `{val, val, val}` (preferred) 612 // - `val;val;val` (legacy) 613 a.parseSlice(scanner, raw, out) 614 case MapType: 615 // maps are {string: val, string: val, string: val} 616 a.parseMap(scanner, raw, out) 617 } 618 } 619 620 // Parse attempts to consume the argument from the given scanner (based on the given 621 // raw input as well for collecting ranges of content), and places the output value 622 // in the given reflect.Value. Errors are reported via the given scanner. 623 func (a *Argument) Parse(scanner *sc.Scanner, raw string, out reflect.Value) { 624 a.parse(scanner, raw, out, false) 625 } 626 627 // ArgumentFromType constructs an Argument by examining the given 628 // raw reflect.Type. It can construct arguments from the Go types 629 // corresponding to any of the types listed in ArgumentType. 630 func ArgumentFromType(rawType reflect.Type) (Argument, error) { 631 if rawType == rawArgsType { 632 return Argument{ 633 Type: RawType, 634 }, nil 635 } 636 637 if rawType == interfaceType { 638 return Argument{ 639 Type: AnyType, 640 }, nil 641 } 642 643 arg := Argument{} 644 if rawType.Kind() == reflect.Ptr { 645 rawType = rawType.Elem() 646 arg.Pointer = true 647 arg.Optional = true 648 } 649 650 switch rawType.Kind() { 651 case reflect.String: 652 arg.Type = StringType 653 case reflect.Int, reflect.Int32: // NB(directxman12): all ints in kubernetes are int32, so explicitly support that 654 arg.Type = IntType 655 case reflect.Float64: 656 arg.Type = NumberType 657 case reflect.Bool: 658 arg.Type = BoolType 659 case reflect.Slice: 660 arg.Type = SliceType 661 itemType, err := ArgumentFromType(rawType.Elem()) 662 if err != nil { 663 return Argument{}, fmt.Errorf("bad slice item type: %w", err) 664 } 665 arg.ItemType = &itemType 666 case reflect.Map: 667 arg.Type = MapType 668 if rawType.Key().Kind() != reflect.String { 669 return Argument{}, fmt.Errorf("bad map key type: map keys must be strings") 670 } 671 itemType, err := ArgumentFromType(rawType.Elem()) 672 if err != nil { 673 return Argument{}, fmt.Errorf("bad slice item type: %w", err) 674 } 675 arg.ItemType = &itemType 676 default: 677 return Argument{}, fmt.Errorf("type has unsupported kind %s", rawType.Kind()) 678 } 679 680 return arg, nil 681 } 682 683 // TargetType describes which kind of node a given marker is associated with. 684 type TargetType int 685 686 const ( 687 // DescribesPackage indicates that a marker is associated with a package. 688 DescribesPackage TargetType = iota 689 // DescribesType indicates that a marker is associated with a type declaration. 690 DescribesType 691 // DescribesField indicates that a marker is associated with a struct field. 692 DescribesField 693 ) 694 695 func (t TargetType) String() string { 696 switch t { 697 case DescribesPackage: 698 return "package" 699 case DescribesType: 700 return "type" 701 case DescribesField: 702 return "field" 703 default: 704 return "(unknown)" 705 } 706 } 707 708 // Definition is a parsed definition of a marker. 709 type Definition struct { 710 // Output is the deserialized Go type of the marker. 711 Output reflect.Type 712 // Name is the marker's name. 713 Name string 714 // Target indicates which kind of node this marker can be associated with. 715 Target TargetType 716 // Fields lists out the types of each field that this marker has, by 717 // argument name as used in the marker (if the output type isn't a struct, 718 // it'll have a single, blank field name). This only lists exported fields, 719 // (as per reflection rules). 720 Fields map[string]Argument 721 // FieldNames maps argument names (as used in the marker) to struct field name 722 // in the output type. 723 FieldNames map[string]string 724 // Strict indicates that this definition should error out when parsing if 725 // not all non-optional fields were seen. 726 Strict bool 727 } 728 729 // AnonymousField indicates that the definition has one field, 730 // (actually the original object), and thus the field 731 // doesn't get named as part of the name. 732 func (d *Definition) AnonymousField() bool { 733 if len(d.Fields) != 1 { 734 return false 735 } 736 _, hasAnonField := d.Fields[""] 737 return hasAnonField 738 } 739 740 // Empty indicates that this definition has no fields. 741 func (d *Definition) Empty() bool { 742 return len(d.Fields) == 0 743 } 744 745 // argumentInfo returns information about an argument field as the marker parser's field loader 746 // would see it. This can be useful if you have to interact with marker definition structs 747 // externally (e.g. at compile time). 748 func argumentInfo(fieldName string, tag reflect.StructTag) (argName string, optionalOpt bool) { 749 argName = lowerCamelCase(fieldName) 750 markerTag, tagSpecified := tag.Lookup("marker") 751 markerTagParts := strings.Split(markerTag, ",") 752 if tagSpecified && markerTagParts[0] != "" { 753 // allow overriding to support legacy cases where we don't follow camelCase conventions 754 argName = markerTagParts[0] 755 } 756 optionalOpt = false 757 for _, tagOption := range markerTagParts[1:] { 758 switch tagOption { 759 case "optional": 760 optionalOpt = true 761 } 762 } 763 764 return argName, optionalOpt 765 } 766 767 // loadFields uses reflection to populate argument information from the Output type. 768 func (d *Definition) loadFields() error { 769 if d.Fields == nil { 770 d.Fields = make(map[string]Argument) 771 d.FieldNames = make(map[string]string) 772 } 773 if d.Output.Kind() != reflect.Struct { 774 // anonymous field type 775 argType, err := ArgumentFromType(d.Output) 776 if err != nil { 777 return err 778 } 779 d.Fields[""] = argType 780 d.FieldNames[""] = "" 781 return nil 782 } 783 784 for i := 0; i < d.Output.NumField(); i++ { 785 field := d.Output.Field(i) 786 if field.PkgPath != "" { 787 // as per the reflect package docs, pkgpath is empty for exported fields, 788 // so non-empty package path means a private field, which we should skip 789 continue 790 } 791 argName, optionalOpt := argumentInfo(field.Name, field.Tag) 792 793 argType, err := ArgumentFromType(field.Type) 794 if err != nil { 795 return fmt.Errorf("unable to extract type information for field %q: %w", field.Name, err) 796 } 797 798 if argType.Type == RawType { 799 return fmt.Errorf("RawArguments must be the direct type of a marker, and not a field") 800 } 801 802 argType.Optional = optionalOpt || argType.Optional 803 804 d.Fields[argName] = argType 805 d.FieldNames[argName] = field.Name 806 } 807 808 return nil 809 } 810 811 // parserScanner makes a new scanner appropriate for use in parsing definitions and arguments. 812 func parserScanner(raw string, err func(*sc.Scanner, string)) *sc.Scanner { 813 scanner := &sc.Scanner{} 814 scanner.Init(bytes.NewBufferString(raw)) 815 scanner.Mode = sc.ScanIdents | sc.ScanInts | sc.ScanFloats | sc.ScanStrings | sc.ScanRawStrings | sc.SkipComments 816 scanner.Error = err 817 818 return scanner 819 } 820 821 // Parse uses the type information in this Definition to parse the given 822 // raw marker in the form `+a:b:c=arg,d=arg` into an output object of the 823 // type specified in the definition. 824 func (d *Definition) Parse(rawMarker string) (interface{}, error) { 825 name, anonName, fields := splitMarker(rawMarker) 826 827 out := reflect.Indirect(reflect.New(d.Output)) 828 829 // if we're a not a struct or have no arguments, treat the full `a:b:c` as the name, 830 // otherwise, treat `c` as a field name, and `a:b` as the marker name. 831 if !d.AnonymousField() && !d.Empty() && len(anonName) >= len(name)+1 { 832 fields = anonName[len(name)+1:] + "=" + fields 833 } 834 835 var errs []error 836 scanner := parserScanner(fields, func(scanner *sc.Scanner, msg string) { 837 errs = append(errs, &ScannerError{Msg: msg, Pos: scanner.Position}) 838 }) 839 840 // TODO(directxman12): strict parsing where we error out if certain fields aren't optional 841 seen := make(map[string]struct{}, len(d.Fields)) 842 if d.AnonymousField() && scanner.Peek() != sc.EOF { 843 // might still be a struct that something fiddled with, so double check 844 structFieldName := d.FieldNames[""] 845 outTarget := out 846 if structFieldName != "" { 847 // it's a struct field mapped to an anonymous marker 848 outTarget = out.FieldByName(structFieldName) 849 if !outTarget.CanSet() { 850 scanner.Error(scanner, fmt.Sprintf("cannot set field %q (might not exist)", structFieldName)) 851 return out.Interface(), loader.MaybeErrList(errs) 852 } 853 } 854 855 // no need for trying to parse field names if we're not a struct 856 field := d.Fields[""] 857 field.Parse(scanner, fields, outTarget) 858 seen[""] = struct{}{} // mark as seen for strict definitions 859 } else if !d.Empty() && scanner.Peek() != sc.EOF { 860 // if we expect *and* actually have arguments passed 861 for { 862 // parse the argument name 863 if !expect(scanner, sc.Ident, "argument name") { 864 break 865 } 866 argName := scanner.TokenText() 867 if !expect(scanner, '=', "equals") { 868 break 869 } 870 871 // make sure we know the field 872 fieldName, known := d.FieldNames[argName] 873 if !known { 874 scanner.Error(scanner, fmt.Sprintf("unknown argument %q", argName)) 875 break 876 } 877 fieldType, known := d.Fields[argName] 878 if !known { 879 scanner.Error(scanner, fmt.Sprintf("unknown argument %q", argName)) 880 break 881 } 882 seen[argName] = struct{}{} // mark as seen for strict definitions 883 884 // parse the field value 885 fieldVal := out.FieldByName(fieldName) 886 if !fieldVal.CanSet() { 887 scanner.Error(scanner, fmt.Sprintf("cannot set field %q (might not exist)", fieldName)) 888 break 889 } 890 fieldType.Parse(scanner, fields, fieldVal) 891 892 if len(errs) > 0 { 893 break 894 } 895 896 if scanner.Peek() == sc.EOF { 897 break 898 } 899 if !expect(scanner, ',', "comma") { 900 break 901 } 902 } 903 } 904 905 if tok := scanner.Scan(); tok != sc.EOF { 906 scanner.Error(scanner, fmt.Sprintf("extra arguments provided: %q", fields[scanner.Position.Offset:])) 907 } 908 909 if d.Strict { 910 for argName, arg := range d.Fields { 911 if _, wasSeen := seen[argName]; !wasSeen && !arg.Optional { 912 scanner.Error(scanner, fmt.Sprintf("missing argument %q", argName)) 913 } 914 } 915 } 916 917 return out.Interface(), loader.MaybeErrList(errs) 918 } 919 920 // MakeDefinition constructs a definition from a name, type, and the output type. 921 // All such definitions are strict by default. If a struct is passed as the output 922 // type, its public fields will automatically be populated into Fields (and similar 923 // fields in Definition). Other values will have a single, empty-string-named Fields 924 // entry. 925 func MakeDefinition(name string, target TargetType, output interface{}) (*Definition, error) { 926 def := &Definition{ 927 Name: name, 928 Target: target, 929 Output: reflect.TypeOf(output), 930 Strict: true, 931 } 932 933 if err := def.loadFields(); err != nil { 934 return nil, err 935 } 936 937 return def, nil 938 } 939 940 // MakeAnyTypeDefinition constructs a definition for an output struct with a 941 // field named `Value` of type `interface{}`. The argument to the marker will 942 // be parsed as AnyType and assigned to the field named `Value`. 943 func MakeAnyTypeDefinition(name string, target TargetType, output interface{}) (*Definition, error) { 944 defn, err := MakeDefinition(name, target, output) 945 if err != nil { 946 return nil, err 947 } 948 defn.FieldNames = map[string]string{"": "Value"} 949 defn.Fields = map[string]Argument{"": defn.Fields["value"]} 950 return defn, nil 951 } 952 953 // splitMarker takes a marker in the form of `+a:b:c=arg,d=arg` and splits it 954 // into the name (`a:b`), the name if it's not a struct (`a:b:c`), and the parts 955 // that are definitely fields (`arg,d=arg`). 956 func splitMarker(raw string) (name string, anonymousName string, restFields string) { 957 raw = raw[1:] // get rid of the leading '+' 958 nameFieldParts := strings.SplitN(raw, "=", 2) 959 if len(nameFieldParts) == 1 { 960 return nameFieldParts[0], nameFieldParts[0], "" 961 } 962 anonymousName = nameFieldParts[0] 963 name = anonymousName 964 restFields = nameFieldParts[1] 965 966 nameParts := strings.Split(name, ":") 967 if len(nameParts) > 1 { 968 name = strings.Join(nameParts[:len(nameParts)-1], ":") 969 } 970 return name, anonymousName, restFields 971 } 972 973 type ScannerError struct { 974 Msg string 975 Pos sc.Position 976 } 977 978 func (e *ScannerError) Error() string { 979 return fmt.Sprintf("%s (at %s)", e.Msg, e.Pos) 980 }