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