v2 / vlib / x / json2 / strict / strict.v
196 lines · 168 sloc · 4.12 KB · 8986645ee93015bf9b44e7839c3a3370aff4f51b
Raw
1module strict
2
3import arrays
4
5pub struct KeyStruct {
6pub:
7 key string
8 value_type KeyType
9 token_pos int // the position of the token
10}
11
12pub enum KeyType {
13 literal
14 map
15 array
16}
17
18pub struct StructCheckResult {
19pub:
20 duplicates []string
21 superfluous []string
22}
23
24// strict_check .
25pub fn strict_check[T](json_data string) StructCheckResult {
26 // REVIEW how performatic is it?
27 $if T is $struct {
28 tokens := tokenize(json_data)
29
30 key_struct := get_keys_from_json(tokens)
31
32 mut duplicates := get_duplicates_keys(key_struct)
33 mut superfluous := get_superfluous_keys[T](key_struct)
34
35 mut val := T{}
36
37 $for field in T.fields {
38 $if field.typ is $struct {
39 last_key := find_last_key(key_struct, field.name) or {
40 panic('field not found: ' + field.name)
41 }
42
43 // TODO: get path here from `last_key.key`
44 if last_key.value_type == .map {
45 check(val.$(field.name), tokens[last_key.token_pos + 2..], mut duplicates, mut
46 superfluous)
47 }
48 }
49 }
50 return StructCheckResult{
51 duplicates: duplicates
52 superfluous: superfluous
53 }
54 } $else {
55 return StructCheckResult{}
56 }
57}
58
59fn check[T](val T, tokens []string, mut duplicates []string, mut superfluous []string) {
60 $if T is $struct {
61 key_struct := get_keys_from_json(tokens)
62
63 for duplicate in get_duplicates_keys(key_struct) {
64 duplicates << duplicate
65 }
66
67 for unnecessary in get_superfluous_keys[T](key_struct) {
68 superfluous << unnecessary
69 }
70
71 $for field in T.fields {
72 $if field.typ is $struct {
73 last_key := find_last_key(key_struct, field.name) or {
74 panic('field not found: ' + field.name)
75 }
76 if last_key.value_type == .map {
77 check(val.$(field.name), tokens[last_key.token_pos + 2..], mut duplicates, mut
78 superfluous)
79 }
80 }
81 }
82 }
83}
84
85fn get_superfluous_keys[T](key_struct []KeyStruct) []string {
86 mut superfluous := []string{}
87
88 struct_keys := get_keys_from_[T]()
89
90 json_keys := key_struct.map(it.key)
91
92 for json_key in json_keys {
93 if !struct_keys.contains(json_key) {
94 superfluous << json_key
95 }
96 }
97 return superfluous
98}
99
100fn get_duplicates_keys(key_struct []KeyStruct) []string {
101 json_keys := key_struct.map(it.key).sorted()
102 return arrays.uniq_only_repeated(json_keys)
103}
104
105fn find_last_key(key_struct []KeyStruct, field_name string) ?KeyStruct {
106 for idx := key_struct.len; idx > 0; idx-- {
107 item := key_struct[idx - 1]
108 if item.key == field_name {
109 return item
110 }
111 }
112 return none
113}
114
115fn get_keys_from_[T]() []string {
116 mut struct_keys := []string{}
117 $if T is $struct {
118 $for field in T.fields {
119 struct_keys << field.name
120 }
121 }
122 return struct_keys
123}
124
125// get_keys_from_json .
126pub fn get_keys_from_json(tokens []string) []KeyStruct {
127 mut key_structs := []KeyStruct{}
128
129 mut nested_map_count := 0
130
131 for i, token in tokens {
132 if token == ':' {
133 mut current_key := tokens[i - 1].replace('"', '')
134 if tokens[i + 1] == '{' {
135 if nested_map_count == 0 {
136 key_type := KeyType.map
137 key_structs << KeyStruct{
138 key: current_key
139 value_type: key_type
140 token_pos: i - 1
141 }
142 }
143 nested_map_count++
144 } else if tokens[i + 1] == '[' {
145 continue
146 } else if nested_map_count > 0 {
147 if tokens[i + 1] == '}' {
148 nested_map_count--
149 } else {
150 // REVIEW Não sei
151 }
152 } else {
153 key_type := KeyType.literal
154 key_structs << KeyStruct{
155 key: current_key
156 value_type: key_type
157 token_pos: i - 1
158 }
159 }
160 }
161 }
162
163 return key_structs
164}
165
166fn tokenize(json_data string) []string {
167 mut tokens := []string{}
168 mut current_token := ''
169 mut inside_string := false
170
171 for letter in json_data.replace('\n', ' ').replace('\t', ' ') {
172 if letter == ` ` && !inside_string {
173 if current_token != '' {
174 tokens << current_token
175 current_token = ''
176 }
177 } else if letter == `\"` {
178 inside_string = !inside_string
179 current_token += '"'
180 } else if letter == `,` || letter == `:` || letter == `{` || letter == `}` || letter == `[`
181 || letter == `]` {
182 if current_token != '' {
183 tokens << current_token
184 current_token = ''
185 }
186 tokens << [letter].bytestr()
187 } else {
188 current_token += [letter].bytestr()
189 }
190 }
191
192 if current_token != '' {
193 tokens << current_token
194 }
195 return tokens
196}
197