git.sr.ht/~pingoo/stdx@v0.0.0-20240218134121-094174641f6e/toml/decode.go (about) 1 package toml 2 3 import ( 4 "bytes" 5 "encoding" 6 "encoding/json" 7 "fmt" 8 "io" 9 "io/ioutil" 10 "math" 11 "os" 12 "reflect" 13 "strconv" 14 "strings" 15 "time" 16 ) 17 18 // Unmarshaler is the interface implemented by objects that can unmarshal a 19 // TOML description of themselves. 20 type Unmarshaler interface { 21 UnmarshalTOML(interface{}) error 22 } 23 24 // Unmarshal decodes the contents of `data` in TOML format into a pointer `v`. 25 func Unmarshal(data []byte, v interface{}) error { 26 _, err := NewDecoder(bytes.NewReader(data)).Decode(v) 27 return err 28 } 29 30 // Decode the TOML data in to the pointer v. 31 // 32 // See the documentation on Decoder for a description of the decoding process. 33 func Decode(data string, v interface{}) (MetaData, error) { 34 return NewDecoder(strings.NewReader(data)).Decode(v) 35 } 36 37 // DecodeFile is just like Decode, except it will automatically read the 38 // contents of the file at path and decode it for you. 39 func DecodeFile(path string, v interface{}) (MetaData, error) { 40 fp, err := os.Open(path) 41 if err != nil { 42 return MetaData{}, err 43 } 44 defer fp.Close() 45 return NewDecoder(fp).Decode(v) 46 } 47 48 // Primitive is a TOML value that hasn't been decoded into a Go value. 49 // 50 // This type can be used for any value, which will cause decoding to be delayed. 51 // You can use the PrimitiveDecode() function to "manually" decode these values. 52 // 53 // NOTE: The underlying representation of a `Primitive` value is subject to 54 // change. Do not rely on it. 55 // 56 // NOTE: Primitive values are still parsed, so using them will only avoid the 57 // overhead of reflection. They can be useful when you don't know the exact type 58 // of TOML data until runtime. 59 type Primitive struct { 60 undecoded interface{} 61 context Key 62 } 63 64 // The significand precision for float32 and float64 is 24 and 53 bits; this is 65 // the range a natural number can be stored in a float without loss of data. 66 const ( 67 maxSafeFloat32Int = 16777215 // 2^24-1 68 maxSafeFloat64Int = int64(9007199254740991) // 2^53-1 69 ) 70 71 // Decoder decodes TOML data. 72 // 73 // TOML tables correspond to Go structs or maps (dealer's choice – they can be 74 // used interchangeably). 75 // 76 // TOML table arrays correspond to either a slice of structs or a slice of maps. 77 // 78 // TOML datetimes correspond to Go time.Time values. Local datetimes are parsed 79 // in the local timezone. 80 // 81 // time.Duration types are treated as nanoseconds if the TOML value is an 82 // integer, or they're parsed with time.ParseDuration() if they're strings. 83 // 84 // All other TOML types (float, string, int, bool and array) correspond to the 85 // obvious Go types. 86 // 87 // An exception to the above rules is if a type implements the TextUnmarshaler 88 // interface, in which case any primitive TOML value (floats, strings, integers, 89 // booleans, datetimes) will be converted to a []byte and given to the value's 90 // UnmarshalText method. See the Unmarshaler example for a demonstration with 91 // email addresses. 92 // 93 // # Key mapping 94 // 95 // TOML keys can map to either keys in a Go map or field names in a Go struct. 96 // The special `toml` struct tag can be used to map TOML keys to struct fields 97 // that don't match the key name exactly (see the example). A case insensitive 98 // match to struct names will be tried if an exact match can't be found. 99 // 100 // The mapping between TOML values and Go values is loose. That is, there may 101 // exist TOML values that cannot be placed into your representation, and there 102 // may be parts of your representation that do not correspond to TOML values. 103 // This loose mapping can be made stricter by using the IsDefined and/or 104 // Undecoded methods on the MetaData returned. 105 // 106 // This decoder does not handle cyclic types. Decode will not terminate if a 107 // cyclic type is passed. 108 type Decoder struct { 109 r io.Reader 110 } 111 112 // NewDecoder creates a new Decoder. 113 func NewDecoder(r io.Reader) *Decoder { 114 return &Decoder{r: r} 115 } 116 117 var ( 118 unmarshalToml = reflect.TypeOf((*Unmarshaler)(nil)).Elem() 119 unmarshalText = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem() 120 primitiveType = reflect.TypeOf((*Primitive)(nil)).Elem() 121 ) 122 123 // Decode TOML data in to the pointer `v`. 124 func (dec *Decoder) Decode(v interface{}) (MetaData, error) { 125 rv := reflect.ValueOf(v) 126 if rv.Kind() != reflect.Ptr { 127 s := "%q" 128 if reflect.TypeOf(v) == nil { 129 s = "%v" 130 } 131 132 return MetaData{}, fmt.Errorf("toml: cannot decode to non-pointer "+s, reflect.TypeOf(v)) 133 } 134 if rv.IsNil() { 135 return MetaData{}, fmt.Errorf("toml: cannot decode to nil value of %q", reflect.TypeOf(v)) 136 } 137 138 // Check if this is a supported type: struct, map, interface{}, or something 139 // that implements UnmarshalTOML or UnmarshalText. 140 rv = indirect(rv) 141 rt := rv.Type() 142 if rv.Kind() != reflect.Struct && rv.Kind() != reflect.Map && 143 !(rv.Kind() == reflect.Interface && rv.NumMethod() == 0) && 144 !rt.Implements(unmarshalToml) && !rt.Implements(unmarshalText) { 145 return MetaData{}, fmt.Errorf("toml: cannot decode to type %s", rt) 146 } 147 148 // TODO: parser should read from io.Reader? Or at the very least, make it 149 // read from []byte rather than string 150 data, err := ioutil.ReadAll(dec.r) 151 if err != nil { 152 return MetaData{}, err 153 } 154 155 p, err := parse(string(data)) 156 if err != nil { 157 return MetaData{}, err 158 } 159 160 md := MetaData{ 161 mapping: p.mapping, 162 keyInfo: p.keyInfo, 163 keys: p.ordered, 164 decoded: make(map[string]struct{}, len(p.ordered)), 165 context: nil, 166 data: data, 167 } 168 return md, md.unify(p.mapping, rv) 169 } 170 171 // PrimitiveDecode is just like the other `Decode*` functions, except it 172 // decodes a TOML value that has already been parsed. Valid primitive values 173 // can *only* be obtained from values filled by the decoder functions, 174 // including this method. (i.e., `v` may contain more `Primitive` 175 // values.) 176 // 177 // Meta data for primitive values is included in the meta data returned by 178 // the `Decode*` functions with one exception: keys returned by the Undecoded 179 // method will only reflect keys that were decoded. Namely, any keys hidden 180 // behind a Primitive will be considered undecoded. Executing this method will 181 // update the undecoded keys in the meta data. (See the example.) 182 func (md *MetaData) PrimitiveDecode(primValue Primitive, v interface{}) error { 183 md.context = primValue.context 184 defer func() { md.context = nil }() 185 return md.unify(primValue.undecoded, rvalue(v)) 186 } 187 188 // unify performs a sort of type unification based on the structure of `rv`, 189 // which is the client representation. 190 // 191 // Any type mismatch produces an error. Finding a type that we don't know 192 // how to handle produces an unsupported type error. 193 func (md *MetaData) unify(data interface{}, rv reflect.Value) error { 194 // Special case. Look for a `Primitive` value. 195 // TODO: #76 would make this superfluous after implemented. 196 if rv.Type() == primitiveType { 197 // Save the undecoded data and the key context into the primitive 198 // value. 199 context := make(Key, len(md.context)) 200 copy(context, md.context) 201 rv.Set(reflect.ValueOf(Primitive{ 202 undecoded: data, 203 context: context, 204 })) 205 return nil 206 } 207 208 rvi := rv.Interface() 209 if v, ok := rvi.(Unmarshaler); ok { 210 return v.UnmarshalTOML(data) 211 } 212 if v, ok := rvi.(encoding.TextUnmarshaler); ok { 213 return md.unifyText(data, v) 214 } 215 216 // TODO: 217 // The behavior here is incorrect whenever a Go type satisfies the 218 // encoding.TextUnmarshaler interface but also corresponds to a TOML hash or 219 // array. In particular, the unmarshaler should only be applied to primitive 220 // TOML values. But at this point, it will be applied to all kinds of values 221 // and produce an incorrect error whenever those values are hashes or arrays 222 // (including arrays of tables). 223 224 k := rv.Kind() 225 226 if k >= reflect.Int && k <= reflect.Uint64 { 227 return md.unifyInt(data, rv) 228 } 229 switch k { 230 case reflect.Ptr: 231 elem := reflect.New(rv.Type().Elem()) 232 err := md.unify(data, reflect.Indirect(elem)) 233 if err != nil { 234 return err 235 } 236 rv.Set(elem) 237 return nil 238 case reflect.Struct: 239 return md.unifyStruct(data, rv) 240 case reflect.Map: 241 return md.unifyMap(data, rv) 242 case reflect.Array: 243 return md.unifyArray(data, rv) 244 case reflect.Slice: 245 return md.unifySlice(data, rv) 246 case reflect.String: 247 return md.unifyString(data, rv) 248 case reflect.Bool: 249 return md.unifyBool(data, rv) 250 case reflect.Interface: 251 if rv.NumMethod() > 0 { // Only support empty interfaces are supported. 252 return md.e("unsupported type %s", rv.Type()) 253 } 254 return md.unifyAnything(data, rv) 255 case reflect.Float32, reflect.Float64: 256 return md.unifyFloat64(data, rv) 257 } 258 return md.e("unsupported type %s", rv.Kind()) 259 } 260 261 func (md *MetaData) unifyStruct(mapping interface{}, rv reflect.Value) error { 262 tmap, ok := mapping.(map[string]interface{}) 263 if !ok { 264 if mapping == nil { 265 return nil 266 } 267 return md.e("type mismatch for %s: expected table but found %T", 268 rv.Type().String(), mapping) 269 } 270 271 for key, datum := range tmap { 272 var f *field 273 fields := cachedTypeFields(rv.Type()) 274 for i := range fields { 275 ff := &fields[i] 276 if ff.name == key { 277 f = ff 278 break 279 } 280 if f == nil && strings.EqualFold(ff.name, key) { 281 f = ff 282 } 283 } 284 if f != nil { 285 subv := rv 286 for _, i := range f.index { 287 subv = indirect(subv.Field(i)) 288 } 289 290 if isUnifiable(subv) { 291 md.decoded[md.context.add(key).String()] = struct{}{} 292 md.context = append(md.context, key) 293 294 err := md.unify(datum, subv) 295 if err != nil { 296 return err 297 } 298 md.context = md.context[0 : len(md.context)-1] 299 } else if f.name != "" { 300 return md.e("cannot write unexported field %s.%s", rv.Type().String(), f.name) 301 } 302 } 303 } 304 return nil 305 } 306 307 func (md *MetaData) unifyMap(mapping interface{}, rv reflect.Value) error { 308 keyType := rv.Type().Key().Kind() 309 if keyType != reflect.String && keyType != reflect.Interface { 310 return fmt.Errorf("toml: cannot decode to a map with non-string key type (%s in %q)", 311 keyType, rv.Type()) 312 } 313 314 tmap, ok := mapping.(map[string]interface{}) 315 if !ok { 316 if tmap == nil { 317 return nil 318 } 319 return md.badtype("map", mapping) 320 } 321 if rv.IsNil() { 322 rv.Set(reflect.MakeMap(rv.Type())) 323 } 324 for k, v := range tmap { 325 md.decoded[md.context.add(k).String()] = struct{}{} 326 md.context = append(md.context, k) 327 328 rvval := reflect.Indirect(reflect.New(rv.Type().Elem())) 329 330 err := md.unify(v, indirect(rvval)) 331 if err != nil { 332 return err 333 } 334 md.context = md.context[0 : len(md.context)-1] 335 336 rvkey := indirect(reflect.New(rv.Type().Key())) 337 338 switch keyType { 339 case reflect.Interface: 340 rvkey.Set(reflect.ValueOf(k)) 341 case reflect.String: 342 rvkey.SetString(k) 343 } 344 345 rv.SetMapIndex(rvkey, rvval) 346 } 347 return nil 348 } 349 350 func (md *MetaData) unifyArray(data interface{}, rv reflect.Value) error { 351 datav := reflect.ValueOf(data) 352 if datav.Kind() != reflect.Slice { 353 if !datav.IsValid() { 354 return nil 355 } 356 return md.badtype("slice", data) 357 } 358 if l := datav.Len(); l != rv.Len() { 359 return md.e("expected array length %d; got TOML array of length %d", rv.Len(), l) 360 } 361 return md.unifySliceArray(datav, rv) 362 } 363 364 func (md *MetaData) unifySlice(data interface{}, rv reflect.Value) error { 365 datav := reflect.ValueOf(data) 366 if datav.Kind() != reflect.Slice { 367 if !datav.IsValid() { 368 return nil 369 } 370 return md.badtype("slice", data) 371 } 372 n := datav.Len() 373 if rv.IsNil() || rv.Cap() < n { 374 rv.Set(reflect.MakeSlice(rv.Type(), n, n)) 375 } 376 rv.SetLen(n) 377 return md.unifySliceArray(datav, rv) 378 } 379 380 func (md *MetaData) unifySliceArray(data, rv reflect.Value) error { 381 l := data.Len() 382 for i := 0; i < l; i++ { 383 err := md.unify(data.Index(i).Interface(), indirect(rv.Index(i))) 384 if err != nil { 385 return err 386 } 387 } 388 return nil 389 } 390 391 func (md *MetaData) unifyString(data interface{}, rv reflect.Value) error { 392 _, ok := rv.Interface().(json.Number) 393 if ok { 394 if i, ok := data.(int64); ok { 395 rv.SetString(strconv.FormatInt(i, 10)) 396 } else if f, ok := data.(float64); ok { 397 rv.SetString(strconv.FormatFloat(f, 'f', -1, 64)) 398 } else { 399 return md.badtype("string", data) 400 } 401 return nil 402 } 403 404 if s, ok := data.(string); ok { 405 rv.SetString(s) 406 return nil 407 } 408 return md.badtype("string", data) 409 } 410 411 func (md *MetaData) unifyFloat64(data interface{}, rv reflect.Value) error { 412 rvk := rv.Kind() 413 414 if num, ok := data.(float64); ok { 415 switch rvk { 416 case reflect.Float32: 417 if num < -math.MaxFloat32 || num > math.MaxFloat32 { 418 return md.parseErr(errParseRange{i: num, size: rvk.String()}) 419 } 420 fallthrough 421 case reflect.Float64: 422 rv.SetFloat(num) 423 default: 424 panic("bug") 425 } 426 return nil 427 } 428 429 if num, ok := data.(int64); ok { 430 if (rvk == reflect.Float32 && (num < -maxSafeFloat32Int || num > maxSafeFloat32Int)) || 431 (rvk == reflect.Float64 && (num < -maxSafeFloat64Int || num > maxSafeFloat64Int)) { 432 return md.parseErr(errParseRange{i: num, size: rvk.String()}) 433 } 434 rv.SetFloat(float64(num)) 435 return nil 436 } 437 438 return md.badtype("float", data) 439 } 440 441 func (md *MetaData) unifyInt(data interface{}, rv reflect.Value) error { 442 _, ok := rv.Interface().(time.Duration) 443 if ok { 444 // Parse as string duration, and fall back to regular integer parsing 445 // (as nanosecond) if this is not a string. 446 if s, ok := data.(string); ok { 447 dur, err := time.ParseDuration(s) 448 if err != nil { 449 return md.parseErr(errParseDuration{s}) 450 } 451 rv.SetInt(int64(dur)) 452 return nil 453 } 454 } 455 456 num, ok := data.(int64) 457 if !ok { 458 return md.badtype("integer", data) 459 } 460 461 rvk := rv.Kind() 462 switch { 463 case rvk >= reflect.Int && rvk <= reflect.Int64: 464 if (rvk == reflect.Int8 && (num < math.MinInt8 || num > math.MaxInt8)) || 465 (rvk == reflect.Int16 && (num < math.MinInt16 || num > math.MaxInt16)) || 466 (rvk == reflect.Int32 && (num < math.MinInt32 || num > math.MaxInt32)) { 467 return md.parseErr(errParseRange{i: num, size: rvk.String()}) 468 } 469 rv.SetInt(num) 470 case rvk >= reflect.Uint && rvk <= reflect.Uint64: 471 unum := uint64(num) 472 if rvk == reflect.Uint8 && (num < 0 || unum > math.MaxUint8) || 473 rvk == reflect.Uint16 && (num < 0 || unum > math.MaxUint16) || 474 rvk == reflect.Uint32 && (num < 0 || unum > math.MaxUint32) { 475 return md.parseErr(errParseRange{i: num, size: rvk.String()}) 476 } 477 rv.SetUint(unum) 478 default: 479 panic("unreachable") 480 } 481 return nil 482 } 483 484 func (md *MetaData) unifyBool(data interface{}, rv reflect.Value) error { 485 if b, ok := data.(bool); ok { 486 rv.SetBool(b) 487 return nil 488 } 489 return md.badtype("boolean", data) 490 } 491 492 func (md *MetaData) unifyAnything(data interface{}, rv reflect.Value) error { 493 rv.Set(reflect.ValueOf(data)) 494 return nil 495 } 496 497 func (md *MetaData) unifyText(data interface{}, v encoding.TextUnmarshaler) error { 498 var s string 499 switch sdata := data.(type) { 500 case Marshaler: 501 text, err := sdata.MarshalTOML() 502 if err != nil { 503 return err 504 } 505 s = string(text) 506 case encoding.TextMarshaler: 507 text, err := sdata.MarshalText() 508 if err != nil { 509 return err 510 } 511 s = string(text) 512 case fmt.Stringer: 513 s = sdata.String() 514 case string: 515 s = sdata 516 case bool: 517 s = fmt.Sprintf("%v", sdata) 518 case int64: 519 s = fmt.Sprintf("%d", sdata) 520 case float64: 521 s = fmt.Sprintf("%f", sdata) 522 default: 523 return md.badtype("primitive (string-like)", data) 524 } 525 if err := v.UnmarshalText([]byte(s)); err != nil { 526 return err 527 } 528 return nil 529 } 530 531 func (md *MetaData) badtype(dst string, data interface{}) error { 532 return md.e("incompatible types: TOML value has type %T; destination has type %s", data, dst) 533 } 534 535 func (md *MetaData) parseErr(err error) error { 536 k := md.context.String() 537 return ParseError{ 538 LastKey: k, 539 Position: md.keyInfo[k].pos, 540 Line: md.keyInfo[k].pos.Line, 541 err: err, 542 input: string(md.data), 543 } 544 } 545 546 func (md *MetaData) e(format string, args ...interface{}) error { 547 f := "toml: " 548 if len(md.context) > 0 { 549 f = fmt.Sprintf("toml: (last key %q): ", md.context) 550 p := md.keyInfo[md.context.String()].pos 551 if p.Line > 0 { 552 f = fmt.Sprintf("toml: line %d (last key %q): ", p.Line, md.context) 553 } 554 } 555 return fmt.Errorf(f+format, args...) 556 } 557 558 // rvalue returns a reflect.Value of `v`. All pointers are resolved. 559 func rvalue(v interface{}) reflect.Value { 560 return indirect(reflect.ValueOf(v)) 561 } 562 563 // indirect returns the value pointed to by a pointer. 564 // 565 // Pointers are followed until the value is not a pointer. New values are 566 // allocated for each nil pointer. 567 // 568 // An exception to this rule is if the value satisfies an interface of interest 569 // to us (like encoding.TextUnmarshaler). 570 func indirect(v reflect.Value) reflect.Value { 571 if v.Kind() != reflect.Ptr { 572 if v.CanSet() { 573 pv := v.Addr() 574 pvi := pv.Interface() 575 if _, ok := pvi.(encoding.TextUnmarshaler); ok { 576 return pv 577 } 578 if _, ok := pvi.(Unmarshaler); ok { 579 return pv 580 } 581 } 582 return v 583 } 584 if v.IsNil() { 585 v.Set(reflect.New(v.Type().Elem())) 586 } 587 return indirect(reflect.Indirect(v)) 588 } 589 590 func isUnifiable(rv reflect.Value) bool { 591 if rv.CanSet() { 592 return true 593 } 594 rvi := rv.Interface() 595 if _, ok := rvi.(encoding.TextUnmarshaler); ok { 596 return true 597 } 598 if _, ok := rvi.(Unmarshaler); ok { 599 return true 600 } 601 return false 602 }