1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
|
package tag
import (
"fmt"
"strconv"
"time"
"github.com/BurntSushi/toml/internal"
)
// Remove JSON tags to a data structure as returned by toml-test.
func Remove(typedJson any) (any, error) {
// Switch on the data type.
switch v := typedJson.(type) {
// Object: this can either be a TOML table or a primitive with tags.
case map[string]any:
// This value represents a primitive: remove the tags and return just
// the primitive value.
if len(v) == 2 && in("type", v) && in("value", v) {
ut, err := untag(v)
if err != nil {
return ut, fmt.Errorf("tag.Remove: %w", err)
}
return ut, nil
}
// Table: remove tags on all children.
m := make(map[string]any, len(v))
for k, v2 := range v {
var err error
m[k], err = Remove(v2)
if err != nil {
return nil, err
}
}
return m, nil
// Array: remove tags from all items.
case []any:
a := make([]any, len(v))
for i := range v {
var err error
a[i], err = Remove(v[i])
if err != nil {
return nil, err
}
}
return a, nil
}
// The top level must be an object or array.
return nil, fmt.Errorf("tag.Remove: unrecognized JSON format '%T'", typedJson)
}
// Check if key is in the table m.
func in(key string, m map[string]any) bool {
_, ok := m[key]
return ok
}
// Return a primitive: read the "type" and convert the "value" to that.
func untag(typed map[string]any) (any, error) {
t := typed["type"].(string)
v := typed["value"].(string)
switch t {
case "string":
return v, nil
case "integer":
n, err := strconv.ParseInt(v, 10, 64)
if err != nil {
return nil, fmt.Errorf("untag: %w", err)
}
return n, nil
case "float":
f, err := strconv.ParseFloat(v, 64)
if err != nil {
return nil, fmt.Errorf("untag: %w", err)
}
return f, nil
case "datetime":
return parseTime(v, "2006-01-02T15:04:05.999999999Z07:00", nil)
case "datetime-local":
return parseTime(v, "2006-01-02T15:04:05.999999999", internal.LocalDatetime)
case "date-local":
return parseTime(v, "2006-01-02", internal.LocalDate)
case "time-local":
return parseTime(v, "15:04:05.999999999", internal.LocalTime)
case "bool":
switch v {
case "true":
return true, nil
case "false":
return false, nil
}
return nil, fmt.Errorf("untag: could not parse %q as a boolean", v)
}
return nil, fmt.Errorf("untag: unrecognized tag type %q", t)
}
func parseTime(v, format string, l *time.Location) (time.Time, error) {
t, err := time.Parse(format, v)
if err != nil {
return time.Time{}, fmt.Errorf("could not parse %q as a datetime: %w", v, err)
}
if l != nil {
t = t.In(l)
}
return t, nil
}
|