v2 / vlib / toml / tests / encode_and_decode_test.v
403 lines · 351 sloc · 7.68 KB · 5f028d40938eac800d193f9b875497eb1fa2e053
Raw
1import toml
2
3enum JobTitle {
4 worker
5 executive
6 manager
7}
8
9struct Pet {
10 name string
11 nicknames []string
12 age u64
13 income int
14 height f32
15 has_furr bool
16 title JobTitle
17 address Address
18 meal_frequency map[string]int
19 // *¹ Currently it is only possible to decode a single nested struct generically.
20 // As soon as we decode another nested struct (e.g. within this struct, like `contact` below)
21 // or only one nested struct within another struct, it results in wrong values or errors.
22 // Related issue: https://github.com/vlang/v/issues/18110
23 // contact Contact
24}
25
26struct Address {
27 street string
28 city string
29}
30
31// *¹
32/*
33struct Contact {
34 phone string
35}*/
36
37struct AnyStruct {
38 val toml.Any
39}
40
41struct Employee {
42mut:
43 name string
44 age int
45 salary f32
46 is_human bool
47 title JobTitle
48}
49
50struct Arrs {
51 strs []string
52 bools []bool
53 ints []int
54 i64s []i64
55 u64s []u64
56 f32s []f32
57 f64s []f64
58 dts []toml.DateTime
59 dates []toml.Date
60 times []toml.Time
61}
62
63// individual because toml.decode[Foo](str)! == foo is false
64struct AnyArr {
65 arr []toml.Any
66}
67
68struct Item {
69 name string
70}
71
72struct Doc {
73 items []Item
74}
75
76struct MapDoc {
77 items map[string]Item
78}
79
80fn test_encode_and_decode() {
81 // *¹
82 // p := Pet{'Mr. Scratchy McEvilPaws', ['Freddy', 'Fred', 'Charles'], 8, -1, 0.8, true, .manager, Address{'1428 Elm Street', 'Springwood'}, Contact{'123-456-7890'}}
83 p := Pet{'Mr. Scratchy McEvilPaws', ['Freddy', 'Fred', 'Charles'], 8, -1, 0.8, true, .manager, Address{'1428 Elm Street', 'Springwood'}, {
84 'bones': 2
85 'kibble': 5
86 }}
87 s := 'name = "Mr. Scratchy McEvilPaws"
88nicknames = [
89 "Freddy",
90 "Fred",
91 "Charles"
92]
93age = 8
94income = -1
95height = 0.8
96has_furr = true
97title = 2
98address = { street = "1428 Elm Street", city = "Springwood" }
99meal_frequency = { bones = 2, kibble = 5 }'
100 // contact = { phone = "123-456-7890" }' // *¹
101
102 assert toml.encode[Pet](p) == s
103 assert toml.decode[Pet](s)! == p
104}
105
106fn test_encode_and_decode_any() {
107 a := AnyStruct{toml.Any(10)}
108 s := 'val = 10'
109
110 assert toml.encode[AnyStruct](a) == s
111 assert toml.decode[AnyStruct](s)!.val.int() == 10
112}
113
114pub fn (e Employee) to_toml() string {
115 mut mp := map[string]toml.Any{}
116 mp['name'] = toml.Any(e.name)
117 mp['age'] = toml.Any(e.age)
118 mp['is_human'] = toml.Any(e.is_human)
119 // Change some values to assert that the custom method is used instead of generic encoding.
120 mp['salary'] = toml.Any(f32(e.salary) + 5000.0)
121 mp['title'] = toml.Any(int(e.title) + 1)
122 return mp.to_toml()
123}
124
125pub fn (mut e Employee) from_toml(any toml.Any) {
126 mp := any.as_map()
127 e.name = mp['name'] or { toml.Any('') }.string()
128 e.age = mp['age'] or { toml.Any(0) }.int()
129 e.is_human = mp['is_human'] or { toml.Any(false) }.bool()
130 // Change some values to assert that the custom method is used instead of generic decoding.
131 e.salary = mp['salary'] or { toml.Any(0) }.f32() - 15000.0
132 e.title = unsafe { JobTitle(mp['title'] or { toml.Any(0) }.int() - 2) }
133}
134
135fn test_custom_encode_and_decode() {
136 x := Employee{'Peter', 28, 95000.5, true, .executive}
137 s := toml.encode[Employee](x)
138 eprintln('Employee x: ${s}')
139 assert s == r'name = "Peter"
140age = 28
141is_human = true
142salary = 100000.5
143title = 2'
144
145 y := toml.decode[Employee](s) or {
146 println(err)
147 assert false
148 return
149 }
150 eprintln('Employee y: ${y}')
151 assert y.name == 'Peter'
152 assert y.age == 28
153 assert y.salary == 85000.5
154 assert y.is_human == true
155 assert y.title == .worker
156}
157
158struct Example1 {
159 arr []Problem
160}
161
162struct Example2 {
163 arr []Problem
164}
165
166struct Problem {
167 x int
168}
169
170pub fn (example Example1) to_toml() string {
171 return '[This is Valid]'
172}
173
174pub fn (problem Problem) to_toml() string {
175 return 'a problem'
176}
177
178fn test_custom_encode_of_complex_struct() {
179 assert toml.encode(Example1{}) == '[This is Valid]'
180 assert toml.encode(Example2{[Problem{}, Problem{}]}) == 'arr = [
181 "a problem",
182 "a problem"
183]'
184}
185
186struct Example3 {
187 arr_arr [][]Problem
188}
189
190struct Example4 {
191 mp map[string]Problem
192}
193
194pub fn (example Example3) to_toml() string {
195 return '[This is Valid]'
196}
197
198pub fn (example Example4) to_toml() string {
199 return '[This is Valid]'
200}
201
202fn test_custom_encode_of_nested_complex_struct() {
203 assert toml.encode(Example3{}) == '[This is Valid]'
204 assert toml.encode(Example4{}) == '[This is Valid]'
205}
206
207struct Example5 {
208 mp map[string]Problem
209}
210
211fn test_map_encode_of_complex_struct() {
212 mut mp := map[string]Problem{}
213 mp['key_one'] = Problem{}
214 mp['key_two'] = Problem{}
215 assert toml.encode(Example5{ mp: mp }) == 'mp = { key_one = "a problem", key_two = "a problem" }'
216}
217
218struct Example6 {
219 ptr voidptr
220 r rune
221}
222
223fn test_encode_for_exotic_types() {
224 assert toml.encode(Example6{ ptr: unsafe { &voidptr(nil) }, r: `🚀` }) == 'ptr = "0x0"\nr = "🚀"'
225}
226
227fn test_array_encode_decode() {
228 a := Arrs{
229 strs: ['foo', 'bar']
230 bools: [true, false]
231 ints: [-1, 2]
232 i64s: [i64(-2)]
233 u64s: [u64(123)]
234 f32s: [f32(1.0), f32(2.5)]
235 f64s: [100000.5, -123.0]
236 dts: [toml.DateTime{'1979-05-27T07:32:00Z'}, toml.DateTime{'1979-05-27T07:32:00Z'}]
237 dates: [toml.Date{'1979-05-27'}, toml.Date{'2022-12-31'}]
238 times: [toml.Time{'07:32:59'}, toml.Time{'17:32:04'}]
239 }
240
241 s := 'strs = [
242 "foo",
243 "bar"
244]
245bools = [
246 true,
247 false
248]
249ints = [
250 -1,
251 2
252]
253i64s = [
254 -2
255]
256u64s = [
257 123
258]
259f32s = [
260 1.0,
261 2.5
262]
263f64s = [
264 100000.5,
265 -123.0
266]
267dts = [
268 1979-05-27T07:32:00Z,
269 1979-05-27T07:32:00Z
270]
271dates = [
272 1979-05-27,
273 2022-12-31
274]
275times = [
276 07:32:59,
277 17:32:04
278]'
279
280 assert toml.encode[Arrs](a) == s
281 assert toml.decode[Arrs](s)! == a
282
283 any_a := AnyArr{[toml.Any(10), 20, 30]}
284 any_s := 'arr = [
285 10,
286 20,
287 30
288]'
289
290 assert toml.encode[AnyArr](any_a) == any_s
291 assert toml.decode[AnyArr](any_s)!.arr.map(it.int()) == [10, 20, 30]
292}
293
294fn test_array_of_struct_decode() {
295 doc := Doc{
296 items: [
297 Item{
298 name: 'item #1'
299 },
300 Item{
301 name: 'item #2'
302 },
303 Item{
304 name: 'item #3'
305 },
306 ]
307 }
308
309 encoded := toml.encode[Doc](doc)
310 assert toml.decode[Doc](encoded)! == doc
311
312 array_of_tables := '[[items]]
313name = "item #1"
314
315[[items]]
316name = "item #2"
317
318[[items]]
319name = "item #3"'
320 assert toml.decode[Doc](array_of_tables)! == doc
321}
322
323fn test_map_of_struct_decode() {
324 doc := MapDoc{
325 items: {
326 'first': Item{
327 name: 'first one'
328 }
329 'second': Item{
330 name: 'second one'
331 }
332 }
333 }
334
335 encoded := toml.encode[MapDoc](doc)
336 assert toml.decode[MapDoc](encoded)! == doc
337
338 table_doc := '[items.first]
339name = "first one"
340
341[items.second]
342name = "second one"'
343 assert toml.decode[MapDoc](table_doc)! == doc
344}
345
346fn test_decode_doc() {
347 doc := toml.parse_text('name = "Peter"
348age = 28
349is_human = true
350salary = 100000.5
351title = 2')!
352 e := doc.decode[Employee]()!
353 assert e.name == 'Peter'
354 assert e.age == 28
355 assert e.salary == 100000.5
356 assert e.is_human == true
357 assert e.title == .manager
358}
359
360fn test_unsupported_type() {
361 s := 'name = "Peter"'
362 err_msg := 'toml.decode: expected struct, found '
363 if _ := toml.decode[string](s) {
364 assert false
365 } else {
366 assert err.msg() == err_msg + 'string'
367 }
368 if _ := toml.decode[[]string](s) {
369 assert false
370 } else {
371 assert err.msg() == err_msg + '[]string'
372 }
373 if _ := toml.decode[int](s) {
374 assert false
375 } else {
376 assert err.msg() == err_msg + 'int'
377 }
378 if _ := toml.decode[[]f32](s) {
379 assert false
380 } else {
381 assert err.msg() == err_msg + '[]f32'
382 }
383 // ...
384
385 doc := toml.parse_text('name = "Peter"')!
386 assert doc.value('name').string() == 'Peter'
387 if _ := doc.decode[string]() {
388 assert false
389 } else {
390 assert err.msg() == 'Doc.decode: expected struct, found string'
391 }
392}
393
394fn test_date_array_decode_with_spaces() {
395 // test space after yyyy-mm-dd is not date-time separator but white to consume. issue # 25279
396 s := '
397dates = [ 1979-05-27 , 2022-12-31 ]
398'
399 a := Arrs{
400 dates: [toml.Date{'1979-05-27'}, toml.Date{'2022-12-31'}]
401 }
402 assert toml.decode[Arrs](s)! == a
403}
404