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