-
Notifications
You must be signed in to change notification settings - Fork 6
/
flat.go
123 lines (97 loc) · 2.34 KB
/
flat.go
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
112
113
114
115
116
117
118
119
120
121
122
123
package gox
import (
"encoding/json"
"strconv"
)
type (
flatType interface {
map[string]any | string
}
flatConverter[T flatType] struct {
from T
style *flatStyle
prefix string
}
flatStyle struct {
before string
middle string
after string
}
)
func Flat[T flatType](from T) *flatConverter[T] {
return &flatConverter[T]{
from: from,
style: &flatStyle{middle: "."},
prefix: "",
}
}
func (fc *flatConverter[T]) DotStyle() *flatConverter[T] {
fc.style = &flatStyle{middle: "."}
return fc
}
func (fc *flatConverter[T]) PathStyle() *flatConverter[T] {
fc.style = &flatStyle{middle: "/"}
return fc
}
func (fc *flatConverter[T]) RailsStyle() *flatConverter[T] {
fc.style = &flatStyle{before: "[", after: "]"}
return fc
}
func (fc *flatConverter[T]) UnderscoreStyle() *flatConverter[T] {
fc.style = &flatStyle{middle: "_"}
return fc
}
func (fc *flatConverter[T]) Prefix(prefix string) *flatConverter[T] {
fc.prefix = prefix
return fc
}
func (fc *flatConverter[T]) Convert() (to map[string]any, err error) {
to = make(map[string]any)
switch from := any(fc.from).(type) {
case map[string]any:
err = fc.flatten(true, to, from, fc.prefix)
case string:
err = fc.string([]byte(from), to)
}
return
}
func (fc *flatConverter[T]) string(bytes []byte, to map[string]any) (err error) {
from := new(map[string]any)
if err = json.Unmarshal(bytes, from); nil == err {
err = fc.flatten(true, to, from, fc.prefix)
}
return
}
func (fc *flatConverter[T]) flatten(top bool, flat map[string]any, nested any, prefix string) (err error) {
switch _nested := nested.(type) {
case map[string]any:
for key, value := range _nested {
newKey := fc.enKey(top, prefix, key)
err = fc.assign(flat, newKey, value)
}
case []any:
for index, value := range _nested {
newKey := fc.enKey(top, prefix, strconv.Itoa(index))
err = fc.assign(flat, newKey, value)
}
}
return
}
func (fc *flatConverter[T]) assign(flat map[string]any, newKey string, value any) (err error) {
switch value.(type) {
case map[string]any, []any:
err = fc.flatten(false, flat, value, newKey)
default:
flat[newKey] = value
}
return
}
func (fc *flatConverter[T]) enKey(top bool, prefix string, subKey string) string {
key := prefix
if top {
key += subKey
} else {
key += fc.style.before + fc.style.middle + subKey + fc.style.after
}
return key
}