github.com/maresnic/mr-kong@v1.0.0/mapper.go (about) 1 package kong 2 3 import ( 4 "encoding" 5 "encoding/json" 6 "errors" 7 "fmt" 8 "io" 9 "math/bits" 10 "net/url" 11 "os" 12 "reflect" 13 "strconv" 14 "strings" 15 "time" 16 ) 17 18 var ( 19 mapperValueType = reflect.TypeOf((*MapperValue)(nil)).Elem() 20 boolMapperValueType = reflect.TypeOf((*BoolMapperValue)(nil)).Elem() 21 jsonUnmarshalerType = reflect.TypeOf((*json.Unmarshaler)(nil)).Elem() 22 textUnmarshalerType = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem() 23 binaryUnmarshalerType = reflect.TypeOf((*encoding.BinaryUnmarshaler)(nil)).Elem() 24 ) 25 26 // DecodeContext is passed to a Mapper's Decode(). 27 // 28 // It contains the Value being decoded into and the Scanner to parse from. 29 type DecodeContext struct { 30 // Value being decoded into. 31 Value *Value 32 // Scan contains the input to scan into Target. 33 Scan *Scanner 34 } 35 36 // WithScanner creates a clone of this context with a new Scanner. 37 func (r *DecodeContext) WithScanner(scan *Scanner) *DecodeContext { 38 return &DecodeContext{ 39 Value: r.Value, 40 Scan: scan, 41 } 42 } 43 44 // MapperValue may be implemented by fields in order to provide custom mapping. 45 // Mappers may additionally implement PlaceHolderProvider to provide custom placeholder text. 46 type MapperValue interface { 47 Decode(ctx *DecodeContext) error 48 } 49 50 // BoolMapperValue may be implemented by fields in order to provide custom mappings for boolean values. 51 type BoolMapperValue interface { 52 MapperValue 53 IsBool() bool 54 } 55 56 type mapperValueAdapter struct { 57 isBool bool 58 } 59 60 func (m *mapperValueAdapter) Decode(ctx *DecodeContext, target reflect.Value) error { 61 if target.Type().Implements(mapperValueType) { 62 return target.Interface().(MapperValue).Decode(ctx) //nolint 63 } 64 return target.Addr().Interface().(MapperValue).Decode(ctx) //nolint 65 } 66 67 func (m *mapperValueAdapter) IsBool() bool { 68 return m.isBool 69 } 70 71 type textUnmarshalerAdapter struct{} 72 73 func (m *textUnmarshalerAdapter) Decode(ctx *DecodeContext, target reflect.Value) error { 74 var value string 75 err := ctx.Scan.PopValueInto("value", &value) 76 if err != nil { 77 return err 78 } 79 if target.Type().Implements(textUnmarshalerType) { 80 return target.Interface().(encoding.TextUnmarshaler).UnmarshalText([]byte(value)) //nolint 81 } 82 return target.Addr().Interface().(encoding.TextUnmarshaler).UnmarshalText([]byte(value)) //nolint 83 } 84 85 type binaryUnmarshalerAdapter struct{} 86 87 func (m *binaryUnmarshalerAdapter) Decode(ctx *DecodeContext, target reflect.Value) error { 88 var value string 89 err := ctx.Scan.PopValueInto("value", &value) 90 if err != nil { 91 return err 92 } 93 if target.Type().Implements(binaryUnmarshalerType) { 94 return target.Interface().(encoding.BinaryUnmarshaler).UnmarshalBinary([]byte(value)) //nolint 95 } 96 return target.Addr().Interface().(encoding.BinaryUnmarshaler).UnmarshalBinary([]byte(value)) //nolint 97 } 98 99 type jsonUnmarshalerAdapter struct{} 100 101 func (j *jsonUnmarshalerAdapter) Decode(ctx *DecodeContext, target reflect.Value) error { 102 var value string 103 err := ctx.Scan.PopValueInto("value", &value) 104 if err != nil { 105 return err 106 } 107 if target.Type().Implements(jsonUnmarshalerType) { 108 return target.Interface().(json.Unmarshaler).UnmarshalJSON([]byte(value)) //nolint 109 } 110 return target.Addr().Interface().(json.Unmarshaler).UnmarshalJSON([]byte(value)) //nolint 111 } 112 113 // A Mapper represents how a field is mapped from command-line values to Go. 114 // 115 // Mappers can be associated with concrete fields via pointer, reflect.Type, reflect.Kind, or via a "type" tag. 116 // 117 // Additionally, if a type implements the MapperValue interface, it will be used. 118 type Mapper interface { 119 // Decode ctx.Value with ctx.Scanner into target. 120 Decode(ctx *DecodeContext, target reflect.Value) error 121 } 122 123 // VarsContributor can be implemented by a Mapper to contribute Vars during interpolation. 124 type VarsContributor interface { 125 Vars(ctx *Value) Vars 126 } 127 128 // A BoolMapper is a Mapper to a value that is a boolean. 129 // 130 // This is used solely for formatting help. 131 type BoolMapper interface { 132 Mapper 133 IsBool() bool 134 } 135 136 // BoolMapperExt allows a Mapper to dynamically determine if a value is a boolean. 137 type BoolMapperExt interface { 138 Mapper 139 IsBoolFromValue(v reflect.Value) bool 140 } 141 142 // A MapperFunc is a single function that complies with the Mapper interface. 143 type MapperFunc func(ctx *DecodeContext, target reflect.Value) error 144 145 func (m MapperFunc) Decode(ctx *DecodeContext, target reflect.Value) error { //nolint: revive 146 return m(ctx, target) 147 } 148 149 // A Registry contains a set of mappers and supporting lookup methods. 150 type Registry struct { 151 names map[string]Mapper 152 types map[reflect.Type]Mapper 153 kinds map[reflect.Kind]Mapper 154 values map[reflect.Value]Mapper 155 } 156 157 // NewRegistry creates a new (empty) Registry. 158 func NewRegistry() *Registry { 159 return &Registry{ 160 names: map[string]Mapper{}, 161 types: map[reflect.Type]Mapper{}, 162 kinds: map[reflect.Kind]Mapper{}, 163 values: map[reflect.Value]Mapper{}, 164 } 165 } 166 167 // ForNamedValue finds a mapper for a value with a user-specified name. 168 // 169 // Will return nil if a mapper can not be determined. 170 func (r *Registry) ForNamedValue(name string, value reflect.Value) Mapper { 171 if mapper, ok := r.names[name]; ok { 172 return mapper 173 } 174 return r.ForValue(value) 175 } 176 177 // ForValue looks up the Mapper for a reflect.Value. 178 func (r *Registry) ForValue(value reflect.Value) Mapper { 179 if mapper, ok := r.values[value]; ok { 180 return mapper 181 } 182 return r.ForType(value.Type()) 183 } 184 185 // ForNamedType finds a mapper for a type with a user-specified name. 186 // 187 // Will return nil if a mapper can not be determined. 188 func (r *Registry) ForNamedType(name string, typ reflect.Type) Mapper { 189 if mapper, ok := r.names[name]; ok { 190 return mapper 191 } 192 return r.ForType(typ) 193 } 194 195 // ForType finds a mapper from a type, by type, then kind. 196 // 197 // Will return nil if a mapper can not be determined. 198 func (r *Registry) ForType(typ reflect.Type) Mapper { 199 // Check if the type implements MapperValue. 200 for _, impl := range []reflect.Type{typ, reflect.PtrTo(typ)} { 201 if impl.Implements(mapperValueType) { 202 // FIXME: This should pass in the bool mapper. 203 return &mapperValueAdapter{impl.Implements(boolMapperValueType)} 204 } 205 } 206 // Next, try explicitly registered types. 207 var mapper Mapper 208 var ok bool 209 if mapper, ok = r.types[typ]; ok { 210 return mapper 211 } 212 // Next try stdlib unmarshaler interfaces. 213 for _, impl := range []reflect.Type{typ, reflect.PtrTo(typ)} { 214 switch { 215 case impl.Implements(textUnmarshalerType): 216 return &textUnmarshalerAdapter{} 217 case impl.Implements(binaryUnmarshalerType): 218 return &binaryUnmarshalerAdapter{} 219 case impl.Implements(jsonUnmarshalerType): 220 return &jsonUnmarshalerAdapter{} 221 } 222 } 223 // Finally try registered kinds. 224 if mapper, ok = r.kinds[typ.Kind()]; ok { 225 return mapper 226 } 227 return nil 228 } 229 230 // RegisterKind registers a Mapper for a reflect.Kind. 231 func (r *Registry) RegisterKind(kind reflect.Kind, mapper Mapper) *Registry { 232 r.kinds[kind] = mapper 233 return r 234 } 235 236 // RegisterName registers a mapper to be used if the value mapper has a "type" tag matching name. 237 // 238 // eg. 239 // 240 // Mapper string `kong:"type='colour'` 241 // registry.RegisterName("colour", ...) 242 func (r *Registry) RegisterName(name string, mapper Mapper) *Registry { 243 r.names[name] = mapper 244 return r 245 } 246 247 // RegisterType registers a Mapper for a reflect.Type. 248 func (r *Registry) RegisterType(typ reflect.Type, mapper Mapper) *Registry { 249 r.types[typ] = mapper 250 return r 251 } 252 253 // RegisterValue registers a Mapper by pointer to the field value. 254 func (r *Registry) RegisterValue(ptr interface{}, mapper Mapper) *Registry { 255 key := reflect.ValueOf(ptr) 256 if key.Kind() != reflect.Ptr { 257 panic("expected a pointer") 258 } 259 key = key.Elem() 260 r.values[key] = mapper 261 return r 262 } 263 264 // RegisterDefaults registers Mappers for all builtin supported Go types and some common stdlib types. 265 func (r *Registry) RegisterDefaults() *Registry { 266 return r.RegisterKind(reflect.Int, intDecoder(bits.UintSize)). 267 RegisterKind(reflect.Int8, intDecoder(8)). 268 RegisterKind(reflect.Int16, intDecoder(16)). 269 RegisterKind(reflect.Int32, intDecoder(32)). 270 RegisterKind(reflect.Int64, intDecoder(64)). 271 RegisterKind(reflect.Uint, uintDecoder(bits.UintSize)). 272 RegisterKind(reflect.Uint8, uintDecoder(8)). 273 RegisterKind(reflect.Uint16, uintDecoder(16)). 274 RegisterKind(reflect.Uint32, uintDecoder(32)). 275 RegisterKind(reflect.Uint64, uintDecoder(64)). 276 RegisterKind(reflect.Float32, floatDecoder(32)). 277 RegisterKind(reflect.Float64, floatDecoder(64)). 278 RegisterKind(reflect.String, MapperFunc(func(ctx *DecodeContext, target reflect.Value) error { 279 return ctx.Scan.PopValueInto("string", target.Addr().Interface()) 280 })). 281 RegisterKind(reflect.Bool, boolMapper{}). 282 RegisterKind(reflect.Slice, sliceDecoder(r)). 283 RegisterKind(reflect.Map, mapDecoder(r)). 284 RegisterType(reflect.TypeOf(time.Time{}), timeDecoder()). 285 RegisterType(reflect.TypeOf(time.Duration(0)), durationDecoder()). 286 RegisterType(reflect.TypeOf(&url.URL{}), urlMapper()). 287 RegisterType(reflect.TypeOf(&os.File{}), fileMapper(r)). 288 RegisterName("path", pathMapper(r)). 289 RegisterName("existingfile", existingFileMapper(r)). 290 RegisterName("existingdir", existingDirMapper(r)). 291 RegisterName("counter", counterMapper()). 292 RegisterName("filecontent", fileContentMapper(r)). 293 RegisterKind(reflect.Ptr, ptrMapper{r}) 294 } 295 296 type boolMapper struct{} 297 298 func (boolMapper) Decode(ctx *DecodeContext, target reflect.Value) error { 299 if ctx.Scan.Peek().Type == FlagValueToken { 300 token := ctx.Scan.Pop() 301 switch v := token.Value.(type) { 302 case string: 303 v = strings.ToLower(v) 304 switch v { 305 case "true", "1", "yes": 306 target.SetBool(true) 307 308 case "false", "0", "no": 309 target.SetBool(false) 310 311 default: 312 return fmt.Errorf("bool value must be true, 1, yes, false, 0 or no but got %q", v) 313 } 314 315 case bool: 316 target.SetBool(v) 317 318 default: 319 return fmt.Errorf("expected bool but got %q (%T)", token.Value, token.Value) 320 } 321 } else { 322 target.SetBool(true) 323 } 324 return nil 325 } 326 func (boolMapper) IsBool() bool { return true } 327 328 func durationDecoder() MapperFunc { 329 return func(ctx *DecodeContext, target reflect.Value) error { 330 t, err := ctx.Scan.PopValue("duration") 331 if err != nil { 332 return err 333 } 334 var d time.Duration 335 switch v := t.Value.(type) { 336 case string: 337 d, err = time.ParseDuration(v) 338 if err != nil { 339 return fmt.Errorf("expected duration but got %q: %v", v, err) 340 } 341 case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64: 342 d = reflect.ValueOf(v).Convert(reflect.TypeOf(time.Duration(0))).Interface().(time.Duration) //nolint: forcetypeassert 343 default: 344 return fmt.Errorf("expected duration but got %q", v) 345 } 346 target.Set(reflect.ValueOf(d)) 347 return nil 348 } 349 } 350 351 func timeDecoder() MapperFunc { 352 return func(ctx *DecodeContext, target reflect.Value) error { 353 format := time.RFC3339 354 if ctx.Value.Format != "" { 355 format = ctx.Value.Format 356 } 357 var value string 358 if err := ctx.Scan.PopValueInto("time", &value); err != nil { 359 return err 360 } 361 t, err := time.Parse(format, value) 362 if err != nil { 363 return err 364 } 365 target.Set(reflect.ValueOf(t)) 366 return nil 367 } 368 } 369 370 func intDecoder(bits int) MapperFunc { //nolint: dupl 371 return func(ctx *DecodeContext, target reflect.Value) error { 372 t, err := ctx.Scan.PopValue("int") 373 if err != nil { 374 return err 375 } 376 var sv string 377 switch v := t.Value.(type) { 378 case string: 379 sv = v 380 381 case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64: 382 sv = fmt.Sprintf("%v", v) 383 384 case float32, float64: 385 sv = fmt.Sprintf("%0.f", v) 386 387 default: 388 return fmt.Errorf("expected an int but got %q (%T)", t, t.Value) 389 } 390 n, err := strconv.ParseInt(sv, 10, bits) 391 if err != nil { 392 return fmt.Errorf("expected a valid %d bit int but got %q", bits, sv) 393 } 394 target.SetInt(n) 395 return nil 396 } 397 } 398 399 func uintDecoder(bits int) MapperFunc { //nolint: dupl 400 return func(ctx *DecodeContext, target reflect.Value) error { 401 t, err := ctx.Scan.PopValue("uint") 402 if err != nil { 403 return err 404 } 405 var sv string 406 switch v := t.Value.(type) { 407 case string: 408 sv = v 409 410 case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64: 411 sv = fmt.Sprintf("%v", v) 412 413 case float32, float64: 414 sv = fmt.Sprintf("%0.f", v) 415 416 default: 417 return fmt.Errorf("expected an int but got %q (%T)", t, t.Value) 418 } 419 n, err := strconv.ParseUint(sv, 10, bits) 420 if err != nil { 421 return fmt.Errorf("expected a valid %d bit uint but got %q", bits, sv) 422 } 423 target.SetUint(n) 424 return nil 425 } 426 } 427 428 func floatDecoder(bits int) MapperFunc { 429 return func(ctx *DecodeContext, target reflect.Value) error { 430 t, err := ctx.Scan.PopValue("float") 431 if err != nil { 432 return err 433 } 434 switch v := t.Value.(type) { 435 case string: 436 n, err := strconv.ParseFloat(v, bits) 437 if err != nil { 438 return fmt.Errorf("expected a float but got %q (%T)", t, t.Value) 439 } 440 target.SetFloat(n) 441 442 case float32: 443 target.SetFloat(float64(v)) 444 445 case float64: 446 target.SetFloat(v) 447 448 case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64: 449 target.Set(reflect.ValueOf(v)) 450 451 default: 452 return fmt.Errorf("expected an int but got %q (%T)", t, t.Value) 453 } 454 return nil 455 } 456 } 457 458 func mapDecoder(r *Registry) MapperFunc { 459 return func(ctx *DecodeContext, target reflect.Value) error { 460 if target.IsNil() { 461 target.Set(reflect.MakeMap(target.Type())) 462 } 463 el := target.Type() 464 mapsep := ctx.Value.Tag.MapSep 465 var childScanner *Scanner 466 if ctx.Value.Flag != nil { 467 t := ctx.Scan.Pop() 468 // If decoding a flag, we need an value. 469 if t.IsEOL() { 470 return fmt.Errorf("missing value, expecting \"<key>=<value>%c...\"", mapsep) 471 } 472 switch v := t.Value.(type) { 473 case string: 474 childScanner = ScanAsType(t.Type, SplitEscaped(v, mapsep)...) 475 476 case []map[string]interface{}: 477 for _, m := range v { 478 err := jsonTranscode(m, target.Addr().Interface()) 479 if err != nil { 480 return err 481 } 482 } 483 return nil 484 485 case map[string]interface{}: 486 return jsonTranscode(v, target.Addr().Interface()) 487 488 default: 489 return fmt.Errorf("invalid map value %q (of type %T)", t, t.Value) 490 } 491 } else { 492 tokens := ctx.Scan.PopWhile(func(t Token) bool { return t.IsValue() }) 493 childScanner = ScanFromTokens(tokens...) 494 } 495 for !childScanner.Peek().IsEOL() { 496 var token string 497 err := childScanner.PopValueInto("map", &token) 498 if err != nil { 499 return err 500 } 501 parts := strings.SplitN(token, "=", 2) 502 if len(parts) != 2 { 503 return fmt.Errorf("expected \"<key>=<value>\" but got %q", token) 504 } 505 key, value := parts[0], parts[1] 506 507 keyTypeName, valueTypeName := "", "" 508 if typ := ctx.Value.Tag.Type; typ != "" { 509 parts := strings.Split(typ, ":") 510 if len(parts) != 2 { 511 return errors.New("type:\"\" on map field must be in the form \"[<keytype>]:[<valuetype>]\"") 512 } 513 keyTypeName, valueTypeName = parts[0], parts[1] 514 } 515 516 keyScanner := ScanAsType(FlagValueToken, key) 517 keyDecoder := r.ForNamedType(keyTypeName, el.Key()) 518 keyValue := reflect.New(el.Key()).Elem() 519 if err := keyDecoder.Decode(ctx.WithScanner(keyScanner), keyValue); err != nil { 520 return fmt.Errorf("invalid map key %q", key) 521 } 522 523 valueScanner := ScanAsType(FlagValueToken, value) 524 valueDecoder := r.ForNamedType(valueTypeName, el.Elem()) 525 valueValue := reflect.New(el.Elem()).Elem() 526 if err := valueDecoder.Decode(ctx.WithScanner(valueScanner), valueValue); err != nil { 527 return fmt.Errorf("invalid map value %q", value) 528 } 529 530 target.SetMapIndex(keyValue, valueValue) 531 } 532 return nil 533 } 534 } 535 536 func sliceDecoder(r *Registry) MapperFunc { 537 return func(ctx *DecodeContext, target reflect.Value) error { 538 el := target.Type().Elem() 539 sep := ctx.Value.Tag.Sep 540 var childScanner *Scanner 541 if ctx.Value.Flag != nil { 542 t := ctx.Scan.Pop() 543 // If decoding a flag, we need a value. 544 if t.IsEOL() { 545 return fmt.Errorf("missing value, expecting \"<arg>%c...\"", sep) 546 } 547 switch v := t.Value.(type) { 548 case string: 549 childScanner = ScanAsType(t.Type, SplitEscaped(v, sep)...) 550 551 case []interface{}: 552 return jsonTranscode(v, target.Addr().Interface()) 553 554 default: 555 v = []interface{}{v} 556 return jsonTranscode(v, target.Addr().Interface()) 557 } 558 } else { 559 tokens := ctx.Scan.PopWhile(func(t Token) bool { return t.IsValue() }) 560 childScanner = ScanFromTokens(tokens...) 561 } 562 childDecoder := r.ForNamedType(ctx.Value.Tag.Type, el) 563 if childDecoder == nil { 564 return fmt.Errorf("no mapper for element type of %s", target.Type()) 565 } 566 for !childScanner.Peek().IsEOL() { 567 childValue := reflect.New(el).Elem() 568 err := childDecoder.Decode(ctx.WithScanner(childScanner), childValue) 569 if err != nil { 570 return err 571 } 572 target.Set(reflect.Append(target, childValue)) 573 } 574 return nil 575 } 576 } 577 578 func pathMapper(r *Registry) MapperFunc { 579 return func(ctx *DecodeContext, target reflect.Value) error { 580 if target.Kind() == reflect.Slice { 581 return sliceDecoder(r)(ctx, target) 582 } 583 if target.Kind() == reflect.Ptr && target.Elem().Kind() == reflect.String { 584 if target.IsNil() { 585 return nil 586 } 587 target = target.Elem() 588 } 589 if target.Kind() != reflect.String { 590 return fmt.Errorf("\"path\" type must be applied to a string not %s", target.Type()) 591 } 592 var path string 593 err := ctx.Scan.PopValueInto("file", &path) 594 if err != nil { 595 return err 596 } 597 if path != "-" { 598 path = ExpandPath(path) 599 } 600 target.SetString(path) 601 return nil 602 } 603 } 604 605 func fileMapper(r *Registry) MapperFunc { 606 return func(ctx *DecodeContext, target reflect.Value) error { 607 if target.Kind() == reflect.Slice { 608 return sliceDecoder(r)(ctx, target) 609 } 610 var path string 611 err := ctx.Scan.PopValueInto("file", &path) 612 if err != nil { 613 return err 614 } 615 var file *os.File 616 if path == "-" { 617 file = os.Stdin 618 } else { 619 path = ExpandPath(path) 620 file, err = os.Open(path) //nolint: gosec 621 if err != nil { 622 return err 623 } 624 } 625 target.Set(reflect.ValueOf(file)) 626 return nil 627 } 628 } 629 630 func existingFileMapper(r *Registry) MapperFunc { 631 return func(ctx *DecodeContext, target reflect.Value) error { 632 if target.Kind() == reflect.Slice { 633 return sliceDecoder(r)(ctx, target) 634 } 635 if target.Kind() != reflect.String { 636 return fmt.Errorf("\"existingfile\" type must be applied to a string not %s", target.Type()) 637 } 638 var path string 639 err := ctx.Scan.PopValueInto("file", &path) 640 if err != nil { 641 return err 642 } 643 644 if !ctx.Value.Active || (ctx.Value.Set && ctx.Value.Target.Type() == target.Type()) { 645 // early return to avoid checking extra files that may not exist; 646 // this hack only works because the value provided on the cli is 647 // checked before the default value is checked (if default is set). 648 return nil 649 } 650 651 if path != "-" { 652 path = ExpandPath(path) 653 stat, err := os.Stat(path) 654 if err != nil { 655 return err 656 } 657 if stat.IsDir() { 658 return fmt.Errorf("%q exists but is a directory", path) 659 } 660 } 661 target.SetString(path) 662 return nil 663 } 664 } 665 666 func existingDirMapper(r *Registry) MapperFunc { 667 return func(ctx *DecodeContext, target reflect.Value) error { 668 if target.Kind() == reflect.Slice { 669 return sliceDecoder(r)(ctx, target) 670 } 671 if target.Kind() != reflect.String { 672 return fmt.Errorf("\"existingdir\" must be applied to a string not %s", target.Type()) 673 } 674 var path string 675 err := ctx.Scan.PopValueInto("file", &path) 676 if err != nil { 677 return err 678 } 679 680 if !ctx.Value.Active || (ctx.Value.Set && ctx.Value.Target.Type() == target.Type()) { 681 // early return to avoid checking extra dirs that may not exist; 682 // this hack only works because the value provided on the cli is 683 // checked before the default value is checked (if default is set). 684 return nil 685 } 686 687 path = ExpandPath(path) 688 stat, err := os.Stat(path) 689 if err != nil { 690 return err 691 } 692 if !stat.IsDir() { 693 return fmt.Errorf("%q exists but is not a directory", path) 694 } 695 target.SetString(path) 696 return nil 697 } 698 } 699 700 func fileContentMapper(r *Registry) MapperFunc { 701 return func(ctx *DecodeContext, target reflect.Value) error { 702 if target.Kind() != reflect.Slice && target.Elem().Kind() != reflect.Uint8 { 703 return fmt.Errorf("\"filecontent\" must be applied to []byte not %s", target.Type()) 704 } 705 var path string 706 err := ctx.Scan.PopValueInto("file", &path) 707 if err != nil { 708 return err 709 } 710 711 if !ctx.Value.Active || ctx.Value.Set { 712 // early return to avoid checking extra dirs that may not exist; 713 // this hack only works because the value provided on the cli is 714 // checked before the default value is checked (if default is set). 715 return nil 716 } 717 718 var data []byte 719 if path != "-" { 720 path = ExpandPath(path) 721 data, err = os.ReadFile(path) //nolint:gosec 722 } else { 723 data, err = io.ReadAll(os.Stdin) 724 } 725 if err != nil { 726 if info, statErr := os.Stat(path); statErr == nil && info.IsDir() { 727 return fmt.Errorf("%q exists but is a directory: %w", path, err) 728 } 729 return err 730 } 731 target.SetBytes(data) 732 return nil 733 } 734 } 735 736 type ptrMapper struct { 737 r *Registry 738 } 739 740 var _ BoolMapperExt = (*ptrMapper)(nil) 741 742 // IsBoolFromValue implements BoolMapperExt 743 func (p ptrMapper) IsBoolFromValue(target reflect.Value) bool { 744 elem := reflect.New(target.Type().Elem()).Elem() 745 nestedMapper := p.r.ForValue(elem) 746 if nestedMapper == nil { 747 return false 748 } 749 if bm, ok := nestedMapper.(BoolMapper); ok && bm.IsBool() { 750 return true 751 } 752 if bm, ok := nestedMapper.(BoolMapperExt); ok && bm.IsBoolFromValue(target) { 753 return true 754 } 755 return target.Kind() == reflect.Ptr && target.Type().Elem().Kind() == reflect.Bool 756 } 757 758 func (p ptrMapper) Decode(ctx *DecodeContext, target reflect.Value) error { 759 elem := reflect.New(target.Type().Elem()).Elem() 760 nestedMapper := p.r.ForValue(elem) 761 if nestedMapper == nil { 762 return fmt.Errorf("cannot find mapper for %v", target.Type().Elem().String()) 763 } 764 err := nestedMapper.Decode(ctx, elem) 765 if err != nil { 766 return err 767 } 768 target.Set(elem.Addr()) 769 return nil 770 } 771 772 func counterMapper() MapperFunc { 773 return func(ctx *DecodeContext, target reflect.Value) error { 774 if ctx.Scan.Peek().Type == FlagValueToken { 775 t, err := ctx.Scan.PopValue("counter") 776 if err != nil { 777 return err 778 } 779 switch v := t.Value.(type) { 780 case string: 781 n, err := strconv.ParseInt(v, 10, 64) 782 if err != nil { 783 return fmt.Errorf("expected a counter but got %q (%T)", t, t.Value) 784 } 785 target.SetInt(n) 786 787 case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64: 788 target.Set(reflect.ValueOf(v)) 789 790 default: 791 return fmt.Errorf("expected a counter but got %q (%T)", t, t.Value) 792 } 793 return nil 794 } 795 796 switch target.Kind() { 797 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 798 target.SetInt(target.Int() + 1) 799 800 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 801 target.SetUint(target.Uint() + 1) 802 803 case reflect.Float32, reflect.Float64: 804 target.SetFloat(target.Float() + 1) 805 806 default: 807 return fmt.Errorf("type:\"counter\" must be used with a numeric field") 808 } 809 return nil 810 } 811 } 812 813 func urlMapper() MapperFunc { 814 return func(ctx *DecodeContext, target reflect.Value) error { 815 var urlStr string 816 err := ctx.Scan.PopValueInto("url", &urlStr) 817 if err != nil { 818 return err 819 } 820 url, err := url.Parse(urlStr) 821 if err != nil { 822 return err 823 } 824 target.Set(reflect.ValueOf(url)) 825 return nil 826 } 827 } 828 829 // SplitEscaped splits a string on a separator. 830 // 831 // It differs from strings.Split() in that the separator can exist in a field by escaping it with a \. eg. 832 // 833 // SplitEscaped(`hello\,there,bob`, ',') == []string{"hello,there", "bob"} 834 func SplitEscaped(s string, sep rune) (out []string) { 835 if sep == -1 { 836 return []string{s} 837 } 838 escaped := false 839 token := "" 840 for i, ch := range s { 841 switch { 842 case escaped: 843 if ch != sep { 844 token += `\` 845 } 846 token += string(ch) 847 escaped = false 848 case ch == '\\' && i < len(s)-1: 849 escaped = true 850 case ch == sep && !escaped: 851 out = append(out, token) 852 token = "" 853 escaped = false 854 default: 855 token += string(ch) 856 } 857 } 858 if token != "" { 859 out = append(out, token) 860 } 861 return 862 } 863 864 // JoinEscaped joins a slice of strings on sep, but also escapes any instances of sep in the fields with \. eg. 865 // 866 // JoinEscaped([]string{"hello,there", "bob"}, ',') == `hello\,there,bob` 867 func JoinEscaped(s []string, sep rune) string { 868 escaped := []string{} 869 for _, e := range s { 870 escaped = append(escaped, strings.ReplaceAll(e, string(sep), `\`+string(sep))) 871 } 872 return strings.Join(escaped, string(sep)) 873 } 874 875 // NamedFileContentFlag is a flag value that loads a file's contents and filename into its value. 876 type NamedFileContentFlag struct { 877 Filename string 878 Contents []byte 879 } 880 881 func (f *NamedFileContentFlag) Decode(ctx *DecodeContext) error { //nolint: revive 882 var filename string 883 err := ctx.Scan.PopValueInto("filename", &filename) 884 if err != nil { 885 return err 886 } 887 // This allows unsetting of file content flags. 888 if filename == "" { 889 *f = NamedFileContentFlag{} 890 return nil 891 } 892 filename = ExpandPath(filename) 893 data, err := os.ReadFile(filename) //nolint: gosec 894 if err != nil { 895 return fmt.Errorf("failed to open %q: %v", filename, err) 896 } 897 f.Contents = data 898 f.Filename = filename 899 return nil 900 } 901 902 // FileContentFlag is a flag value that loads a file's contents into its value. 903 type FileContentFlag []byte 904 905 func (f *FileContentFlag) Decode(ctx *DecodeContext) error { //nolint: revive 906 var filename string 907 err := ctx.Scan.PopValueInto("filename", &filename) 908 if err != nil { 909 return err 910 } 911 // This allows unsetting of file content flags. 912 if filename == "" { 913 *f = nil 914 return nil 915 } 916 filename = ExpandPath(filename) 917 data, err := os.ReadFile(filename) //nolint: gosec 918 if err != nil { 919 return fmt.Errorf("failed to open %q: %v", filename, err) 920 } 921 *f = data 922 return nil 923 } 924 925 func jsonTranscode(in, out interface{}) error { 926 data, err := json.Marshal(in) 927 if err != nil { 928 return err 929 } 930 if err = json.Unmarshal(data, out); err != nil { 931 return fmt.Errorf("%#v -> %T: %v", in, out, err) 932 } 933 return nil 934 }