vxx2 / vlib / x / json2 / decode.v
1594 lines · 1435 sloc · 45.51 KB · 1bd23ec35474c798d48348dac2aad8cf246188d4
Raw
1module json2
2
3import strconv
4import time
5
6const null_in_string = 'null'
7
8const true_in_string = 'true'
9
10const false_in_string = 'false'
11
12const float_zero_in_string = '0.0'
13
14const whitespace_chars = [` `, `\t`, `\n`, `\r`]!
15
16// Node represents a node in a linked list to store ValueInfo.
17struct Node[T] {
18mut:
19 value T
20 next &Node[T] = unsafe { nil } // next is the next node in the linked list.
21}
22
23// ValueInfo represents the position and length of a value, such as string, number, array, object key, and object value in a JSON string.
24struct ValueInfo {
25 position int // The position of the value in the JSON string.
26 value_kind ValueKind // The kind of the value.
27mut:
28 length int // The length of the value in the JSON string.
29}
30
31struct DecoderFieldInfo {
32 key_name string
33
34 is_omitempty bool
35 is_skip bool
36 is_required bool
37 is_raw bool
38}
39
40@[markused]
41struct StructFieldInfo {
42 json_name_ptr voidptr
43 json_name_len int
44 is_omitempty bool
45 is_skip bool
46 is_required bool
47 is_raw bool
48}
49
50struct StructKeyDecodeResult[T] {
51 matched bool
52 value T
53}
54
55// DecoderOptions provides options for JSON decoding.
56// By default, decoding is lenient. Use `strict: true` for strict JSON spec compliance.
57@[markused; params]
58pub struct DecoderOptions {
59pub:
60 // In strict mode, quoted strings are not accepted as numbers.
61 // For example, '"123"' will fail to decode as int in strict mode,
62 // but will succeed in default mode.
63 strict bool
64}
65
66// Decoder is the internal decoding state.
67@[markused]
68struct Decoder {
69 json string // json is the JSON data to be decoded.
70 strict bool // strict mode rejects quoted strings as numbers
71mut:
72 values_info LinkedList[ValueInfo] // A linked list to store ValueInfo.
73 checker_idx int // checker_idx is the current index of the decoder.
74 current_node &Node[ValueInfo] = unsafe { nil } // The current node in the linked list.
75}
76
77// LinkedList represents a linked list to store ValueInfo.
78struct LinkedList[T] {
79mut:
80 head &Node[T] = unsafe { nil } // head is the first node in the linked list.
81 tail &Node[T] = unsafe { nil } // tail is the last node in the linked list.
82 len int // len is the length of the linked list.
83}
84
85// push adds a new element to the linked list.
86fn (mut list LinkedList[T]) push(value T) {
87 node := &Node[T]{
88 value: value
89 }
90 if list.head == unsafe { nil } {
91 list.head = node
92 list.tail = node
93 } else {
94 list.tail.next = node
95 list.tail = node
96 }
97 list.len++
98}
99
100// last returns the last element added to the linked list.
101fn (list &LinkedList[T]) last() &T {
102 return &list.tail.value
103}
104
105// str returns a string representation of the linked list.
106fn (list &LinkedList[ValueInfo]) str() string {
107 mut result_buffer := []u8{}
108 mut current := list.head
109 for current != unsafe { nil } {
110 value_kind_as_string := current.value.value_kind.str()
111 unsafe { result_buffer.push_many(value_kind_as_string.str, value_kind_as_string.len) }
112 result_buffer << u8(` `)
113
114 current = current.next
115 }
116 return result_buffer.bytestr()
117}
118
119@[unsafe]
120fn (list &LinkedList[T]) free() {
121 mut current := list.head
122 for current != unsafe { nil } {
123 mut next := current.next
124 current.next = unsafe { nil }
125 unsafe { free(current) }
126 current = next
127 }
128 list.head = unsafe { nil }
129 list.tail = unsafe { nil }
130 list.len = 0
131}
132
133const max_context_length = 50
134const max_extra_characters = 5
135const tab_width = 8
136
137pub struct JsonDecodeError {
138 Error
139 context string
140pub:
141 message string
142
143 line int
144 character int
145}
146
147fn (e JsonDecodeError) msg() string {
148 return '\n${e.line}:${e.character}: Invalid json: ${e.message}\n${e.context}'
149}
150
151// checker_error generates a checker error message showing the position in the json string
152@[markused]
153fn (mut checker Decoder) checker_error(message string) ! {
154 position := checker.checker_idx
155
156 mut line_number := 0
157 mut character_number := 0
158 mut last_newline := 0
159
160 for i := position - 1; i >= 0; i-- {
161 if last_newline == 0 {
162 if checker.json[i] == `\n` {
163 last_newline = i + 1
164 } else if checker.json[i] == `\t` {
165 character_number += tab_width
166 } else {
167 character_number++
168 }
169 }
170 if checker.json[i] == `\n` {
171 line_number++
172 }
173 }
174
175 cutoff := character_number > max_context_length
176
177 // either start of string, last newline or a limited amount of characters
178 context_start := if cutoff { position - max_context_length } else { last_newline }
179
180 // print some extra characters
181 mut context_end := int_min(checker.json.len, position + max_extra_characters)
182 context_end_newline := checker.json[position..context_end].index_u8(`\n`)
183
184 if context_end_newline != -1 {
185 context_end = position + context_end_newline
186 }
187
188 mut context := ''
189
190 if cutoff {
191 context += '...'
192 }
193 context += checker.json[context_start..position]
194 context += '\e[31m${checker.json[position].ascii_str()}\e[0m'
195 context += checker.json[position + 1..context_end]
196 context += '\n'
197
198 if cutoff {
199 context += ' '.repeat(max_context_length + 3)
200 } else {
201 context += ' '.repeat(character_number)
202 }
203 context += '\e[31m^\e[0m'
204
205 return JsonDecodeError{
206 context: context
207 message: 'Syntax: ${message}'
208 line: line_number + 1
209 character: character_number + 1
210 }
211}
212
213// decode_error generates a decoding error from the decoding stage
214@[markused]
215fn (mut decoder Decoder) decode_error(message string) ! {
216 mut error_info := ValueInfo{}
217 if decoder.current_node != unsafe { nil } {
218 error_info = decoder.current_node.value
219 } else {
220 error_info = decoder.values_info.tail.value
221 }
222
223 start := error_info.position
224 end := start + int_min(error_info.length, max_context_length)
225
226 mut line_number := 0
227 mut character_number := 0
228 mut last_newline := 0
229
230 for i := start - 1; i >= 0; i-- {
231 if last_newline == 0 {
232 if decoder.json[i] == `\n` {
233 last_newline = i + 1
234 } else if decoder.json[i] == `\t` {
235 character_number += tab_width
236 } else {
237 character_number++
238 }
239 }
240 if decoder.json[i] == `\n` {
241 line_number++
242 }
243 }
244
245 cutoff := character_number > max_context_length
246
247 // either start of string, last newline or a limited amount of characters
248 context_start := if cutoff { start - max_context_length } else { last_newline }
249
250 // print some extra characters
251 mut context_end := int_min(decoder.json.len, end + max_extra_characters)
252 context_end_newline := decoder.json[end..context_end].index_u8(`\n`)
253
254 if context_end_newline != -1 {
255 context_end = end + context_end_newline
256 }
257
258 mut context := ''
259
260 if cutoff {
261 context += '...'
262 }
263 context += decoder.json[context_start..start]
264 context += '\e[31m${decoder.json[start..end]}\e[0m'
265 context += decoder.json[end..context_end]
266 context += '\n'
267
268 if cutoff {
269 context += ' '.repeat(max_context_length + 3)
270 } else {
271 context += ' '.repeat(character_number)
272 }
273 context += '\e[31m${'~'.repeat(error_info.length)}\e[0m'
274
275 return JsonDecodeError{
276 context: context
277 message: 'Data: ${message}'
278 line: line_number + 1
279 character: character_number + 1
280 }
281}
282
283// decode decodes a JSON string into a specified type.
284// By default, decoding is lenient. Use `strict: true` for strict JSON spec compliance.
285@[manualfree]
286pub fn decode[T](val string, params DecoderOptions) !T {
287 if val == '' {
288 return JsonDecodeError{
289 message: 'empty string'
290 line: 1
291 character: 1
292 }
293 }
294 mut decoder := Decoder{
295 json: val
296 strict: params.strict
297 }
298
299 decoder.check_json_format()!
300
301 mut result := T{}
302 decoder.current_node = decoder.values_info.head
303 $if T.unaliased_typ is $array_dynamic {
304 result.clear()
305 decoder.decode_array(mut result)!
306 } $else $if T.unaliased_typ is $map {
307 decoder.decode_map(mut result)!
308 } $else $if T.indirections == 1 {
309 if decoder.current_node.value.value_kind == .null {
310 if decoder.current_node != unsafe { nil } {
311 decoder.current_node = decoder.current_node.next
312 }
313 } else {
314 mut decoded_ptr := create_decoded_ptr(result)
315 decoder.decode_value(mut decoded_ptr)!
316 result = decoded_ptr
317 }
318 } $else {
319 decoder.decode_value(mut result)!
320 }
321 return result
322}
323
324fn get_dynamic_from_element[T](_t T) []T {
325 return []T{}
326}
327
328fn create_decoded_ptr[T](_ &T) &T {
329 $if T is $interface {
330 return unsafe { nil }
331 } $else $if T.unaliased_typ is voidptr {
332 return unsafe { nil }
333 } $else {
334 return unsafe { &T(vcalloc(sizeof(T))) }
335 }
336}
337
338fn decoder_field_infos[T]() []DecoderFieldInfo {
339 mut field_infos := []DecoderFieldInfo{}
340 $for field in T.fields {
341 mut key_name := field.name
342 mut is_json_skip := false
343 for attr in field.attrs {
344 if start, end := json_attr_value_range(attr) {
345 if end <= start {
346 continue
347 }
348 if end == start + 1 && attr[start] == `-` {
349 is_json_skip = true
350 break
351 }
352 key_name = attr[start..end]
353 break
354 }
355 }
356 field_infos << DecoderFieldInfo{
357 key_name: key_name
358 is_omitempty: field.attrs.contains('omitempty')
359 is_skip: field.attrs.contains('skip') || is_json_skip
360 is_required: field.attrs.contains('required')
361 is_raw: field.attrs.contains('raw')
362 }
363 }
364 return field_infos
365}
366
367@[manualfree; unsafe]
368fn (mut decoder Decoder) cached_struct_field_infos[T]() []StructFieldInfo {
369 static field_infos := &[]StructFieldInfo(nil)
370 if field_infos == nil {
371 field_infos = &[]StructFieldInfo{}
372 $for field in T.fields {
373 mut json_name_str := field.name.str
374 mut json_name_len := field.name.len
375 mut is_json_skip := false
376 for attr in field.attrs {
377 if start, end := json_attr_value_range(attr) {
378 if end <= start {
379 continue
380 }
381 if end == start + 1 && attr[start] == `-` {
382 is_json_skip = true
383 break
384 }
385 json_name_str = unsafe { attr.str + start }
386 json_name_len = end - start
387 break
388 }
389 }
390 field_infos << StructFieldInfo{
391 json_name_ptr: voidptr(json_name_str)
392 json_name_len: json_name_len
393 is_omitempty: field.attrs.contains('omitempty')
394 is_skip: field.attrs.contains('skip') || is_json_skip
395 is_required: field.attrs.contains('required')
396 is_raw: field.attrs.contains('raw')
397 }
398 }
399 }
400 return *field_infos
401}
402
403@[inline; markused]
404fn struct_field_is_decoded(decoded_mask u64, decoded_fields []bool, field_idx int) bool {
405 if field_idx < 64 {
406 return (decoded_mask & (u64(1) << u64(field_idx))) != 0
407 }
408 return decoded_fields[field_idx]
409}
410
411@[inline; markused]
412fn mark_struct_field_decoded(decoded_mask u64, mut decoded_fields []bool, field_idx int) u64 {
413 if field_idx < 64 {
414 return decoded_mask | (u64(1) << u64(field_idx))
415 }
416 decoded_fields[field_idx] = true
417 return decoded_mask
418}
419
420@[inline]
421fn (decoder &Decoder) json_key_matches(key_info ValueInfo, key_name string) bool {
422 if key_info.length - 2 != key_name.len {
423 return false
424 }
425 return unsafe {
426 vmemcmp(decoder.json.str + key_info.position + 1, key_name.str, key_name.len) == 0
427 }
428}
429
430@[inline; markused]
431fn (decoder &Decoder) is_empty_value(value_info ValueInfo) bool {
432 match value_info.value_kind {
433 .null {
434 return true
435 }
436 .string {
437 return value_info.length == 2
438 }
439 .number {
440 if decoder.json[value_info.position] == `0` {
441 if value_info.length == 1 {
442 return true
443 }
444 if value_info.length == 3 {
445 return unsafe {
446 vmemcmp(decoder.json.str + value_info.position, float_zero_in_string.str,
447 float_zero_in_string.len) == 0
448 }
449 }
450 }
451 }
452 else {}
453 }
454
455 return false
456}
457
458@[markused]
459fn (mut decoder Decoder) skip_current_value() {
460 if decoder.current_node == unsafe { nil } {
461 return
462 }
463 value_end := decoder.current_node.value.position + decoder.current_node.value.length
464 for decoder.current_node != unsafe { nil } && decoder.current_node.value.position < value_end {
465 decoder.current_node = decoder.current_node.next
466 }
467}
468
469@[manualfree]
470fn decode_struct_key[T](mut decoder Decoder, val T, key_info ValueInfo, prefix string, mut seen_required []string) !StructKeyDecodeResult[T] {
471 field_infos := decoder_field_infos[T]()
472 mut new_val := val
473 mut i := 0
474 $for field in T.fields {
475 field_info := field_infos[i]
476 $if !field.is_embed {
477 if decoder.json_key_matches(key_info, field_info.key_name)
478 || (prefix != ''
479 && decoder.json_key_matches(key_info, prefix + field_info.key_name)) {
480 decoder.current_node = decoder.current_node.next
481
482 if field_info.is_skip {
483 if field_info.is_required {
484 seen_required << prefix + field.name
485 }
486 decoder.skip_current_value()
487 return StructKeyDecodeResult[T]{
488 matched: true
489 value: new_val
490 }
491 }
492
493 if field_info.is_omitempty && decoder.current_node != unsafe { nil }
494 && decoder.is_empty_value(decoder.current_node.value) {
495 decoder.skip_current_value()
496 return StructKeyDecodeResult[T]{
497 matched: true
498 value: new_val
499 }
500 }
501
502 if field_info.is_required {
503 seen_required << prefix + field.name
504 }
505
506 if field_info.is_raw {
507 $if field.unaliased_typ is $enum {
508 decoder.decode_error('`raw` attribute cannot be used with enum fields')!
509 } $else $if field.typ is ?string {
510 position := decoder.current_node.value.position
511 end := position + decoder.current_node.value.length
512
513 new_val.$(field.name) = decoder.json[position..end]
514 decoder.current_node = decoder.current_node.next
515
516 for {
517 if decoder.current_node == unsafe { nil }
518 || decoder.current_node.value.position + decoder.current_node.value.length >= end {
519 break
520 }
521 decoder.current_node = decoder.current_node.next
522 }
523 } $else $if field.typ is string {
524 position := decoder.current_node.value.position
525 end := position + decoder.current_node.value.length
526
527 new_val.$(field.name) = decoder.json[position..end]
528 decoder.current_node = decoder.current_node.next
529
530 for {
531 if decoder.current_node == unsafe { nil }
532 || decoder.current_node.value.position + decoder.current_node.value.length >= end {
533 break
534 }
535 decoder.current_node = decoder.current_node.next
536 }
537 } $else {
538 decoder.decode_error('`raw` attribute can only be used with string fields')!
539 }
540 } else {
541 $if field.typ is $option {
542 if decoder.current_node.value.value_kind == .null {
543 new_val.$(field.name) = none
544
545 if decoder.current_node != unsafe { nil } {
546 decoder.current_node = decoder.current_node.next
547 }
548 } else {
549 $if field.indirections == 1 {
550 mut decoded_ptr := $new(field.typ.payload_type.pointee_type)
551 decoder.decode_value(mut decoded_ptr)!
552 new_val.$(field.name) = decoded_ptr
553 } $else {
554 mut unwrapped_val := $zero(field.typ.payload_type)
555 decoder.decode_value(mut unwrapped_val)!
556 new_val.$(field.name) = unwrapped_val
557 }
558 }
559 } $else $if field.unaliased_typ is $array_dynamic {
560 if decoder.current_node.value.value_kind == .null && !field_info.is_required {
561 new_val.$(field.name).clear()
562 decoder.skip_current_value()
563 } else {
564 new_val.$(field.name).clear()
565 decoder.decode_array(mut new_val.$(field.name))!
566 }
567 } $else $if field.unaliased_typ is $map {
568 if decoder.current_node.value.value_kind == .null && !field_info.is_required {
569 new_val.$(field.name).clear()
570 decoder.skip_current_value()
571 } else {
572 decoder.decode_map(mut new_val.$(field.name))!
573 }
574 } $else $if field.unaliased_typ is string {
575 value_info := decoder.current_node.value
576 if value_info.value_kind == .object || value_info.value_kind == .array {
577 new_val.$(field.name) = decoder.json[value_info.position..
578 value_info.position + value_info.length]
579 decoder.skip_current_value()
580 } else if value_info.value_kind == .null && !field_info.is_required {
581 new_val.$(field.name) = ''
582 decoder.skip_current_value()
583 } else {
584 decoder.decode_value(mut new_val.$(field.name))!
585 }
586 } $else $if field.indirections == 1 {
587 if decoder.current_node.value.value_kind == .null {
588 new_val.$(field.name) = unsafe { nil }
589
590 if decoder.current_node != unsafe { nil } {
591 decoder.current_node = decoder.current_node.next
592 }
593 } else {
594 mut decoded_ptr := create_decoded_ptr(new_val.$(field.name))
595 decoder.decode_value(mut decoded_ptr)!
596 new_val.$(field.name) = decoded_ptr
597 }
598 } $else {
599 decoder.decode_value(mut new_val.$(field.name))!
600 }
601 }
602 return StructKeyDecodeResult[T]{
603 matched: true
604 value: new_val
605 }
606 }
607 }
608 i++
609 }
610 i = 0
611 $for field in T.fields {
612 field_info := field_infos[i]
613 $if field.is_embed {
614 if decoder.json_key_matches(key_info, field_info.key_name)
615 && decoder.current_node.next != unsafe { nil }
616 && decoder.current_node.next.value.value_kind == .object {
617 if field_info.is_required {
618 seen_required << prefix + field.name
619 }
620 decoder.current_node = decoder.current_node.next
621 decoder.decode_value(mut new_val.$(field.name))!
622 return StructKeyDecodeResult[T]{
623 matched: true
624 value: new_val
625 }
626 }
627 {
628 embed_result := decode_struct_key(mut decoder, new_val.$(field.name), key_info,
629
630 prefix + field.name + '.', mut seen_required)!
631 if embed_result.matched {
632 new_val.$(field.name) = embed_result.value
633 return StructKeyDecodeResult[T]{
634 matched: true
635 value: new_val
636 }
637 }
638 }
639 }
640 i++
641 }
642 return StructKeyDecodeResult[T]{
643 matched: false
644 value: val
645 }
646}
647
648fn check_required_struct_fields[T](mut decoder Decoder, val T, seen_required []string, prefix string) ! {
649 field_infos := decoder_field_infos[T]()
650 mut i := 0
651 $for field in T.fields {
652 field_info := field_infos[i]
653 if field_info.is_required && prefix + field.name !in seen_required {
654 decoder.decode_error('missing required field `${field.name}`')!
655 }
656 $if field.is_embed {
657 check_required_struct_fields(mut decoder, val.$(field.name), seen_required, prefix +
658 field.name + '.')!
659 }
660 i++
661 }
662}
663
664// decode_value decodes a value from the JSON nodes.
665@[manualfree]
666fn (mut decoder Decoder) decode_value[T](mut val T) ! {
667 $if T.unaliased_typ is voidptr {
668 // skip voidptr fields - they cannot be decoded from JSON
669 if decoder.current_node != unsafe { nil } {
670 decoder.current_node = decoder.current_node.next
671 }
672 return
673 } $else $if T is $interface {
674 // skip interface fields - they cannot be decoded from JSON
675 if decoder.current_node != unsafe { nil } {
676 decoder.current_node = decoder.current_node.next
677 }
678 return
679 } $else {
680 // Custom Decoders
681 $if val is StringDecoder {
682 struct_info := decoder.current_node.value
683
684 if struct_info.value_kind == .string {
685 val.from_json_string(decoder.json[struct_info.position + 1..struct_info.position +
686 struct_info.length - 1]) or {
687 decoder.decode_error('${typeof(val).name}: ${err.msg()}')!
688 }
689 if decoder.current_node != unsafe { nil } {
690 decoder.current_node = decoder.current_node.next
691 }
692
693 return
694 }
695 }
696 $if val is NumberDecoder {
697 struct_info := decoder.current_node.value
698
699 if struct_info.value_kind == .number {
700 val.from_json_number(decoder.json[struct_info.position..struct_info.position +
701 struct_info.length]) or {
702 decoder.decode_error('${typeof(val).name}: ${err.msg()}')!
703 }
704 if decoder.current_node != unsafe { nil } {
705 decoder.current_node = decoder.current_node.next
706 }
707
708 return
709 }
710 }
711 $if val is BooleanDecoder {
712 struct_info := decoder.current_node.value
713
714 if struct_info.value_kind == .boolean {
715 val.from_json_boolean(decoder.json[struct_info.position] == `t`)
716 if decoder.current_node != unsafe { nil } {
717 decoder.current_node = decoder.current_node.next
718 }
719
720 return
721 }
722 }
723 $if val is NullDecoder {
724 struct_info := decoder.current_node.value
725
726 if struct_info.value_kind == .null {
727 val.from_json_null()
728 if decoder.current_node != unsafe { nil } {
729 decoder.current_node = decoder.current_node.next
730 }
731
732 return
733 }
734 }
735 $if T.unaliased_typ is voidptr {
736 // skip voidptr fields - they cannot be decoded from JSON
737 if decoder.current_node != unsafe { nil } {
738 decoder.current_node = decoder.current_node.next
739 }
740 return
741 } $else $if T.unaliased_typ is string {
742 decoder.decode_string(mut val)!
743 } $else $if T.unaliased_typ is time.Time {
744 value_info := decoder.current_node.value
745 mut decoded_time := time.Time{}
746 if value_info.value_kind == .string {
747 decoded_time.from_json_string(decoder.json[value_info.position + 1..
748 value_info.position + value_info.length - 1]) or {
749 decoder.decode_error('${typeof(val).name}: ${err.msg()}')!
750 }
751 } else if value_info.value_kind == .number {
752 decoded_time.from_json_number(decoder.json[value_info.position..
753 value_info.position + value_info.length]) or {
754 decoder.decode_error('${typeof(val).name}: ${err.msg()}')!
755 }
756 } else {
757 decoder.decode_error('Expected string or number, but got ${value_info.value_kind}')!
758 }
759 val = T(decoded_time)
760 } $else $if T.unaliased_typ is $sumtype {
761 decoder.decode_sumtype(mut val)!
762 return
763 } $else $if T.unaliased_typ is $map {
764 decoder.decode_map(mut val)!
765 return
766 } $else $if T.unaliased_typ is $array_dynamic {
767 val.clear()
768 decoder.decode_array(mut val)!
769 // return to avoid the next increment of the current node
770 // this is because the current node is already incremented in the decode_array function
771 // remove this line will cause the current node to be incremented twice
772 // and bug recursive array decoding like `[][]int{}`
773 return
774 } $else $if T.unaliased_typ is $array_fixed {
775 mut dynamic_val := get_dynamic_from_element(val[0])
776
777 // avoid copying by pointing dynamic_val to val
778 unsafe {
779 dynamic_val.len = 0
780 dynamic_val.cap = val.len // ensures data wont reallocate
781 dynamic_val.data = &val
782 }
783 decoder.decode_array(mut dynamic_val)!
784
785 if dynamic_val.len != val.len {
786 decoder.decode_error('Fixed size array expected ${val.len} elements but got ${dynamic_val.len} elements')!
787 }
788 return
789 } $else $if T.unaliased_typ is $struct {
790 struct_info := decoder.current_node.value
791
792 if struct_info.value_kind == .object {
793 struct_position := struct_info.position
794 struct_end := struct_position + struct_info.length
795 mut has_embeds := false
796 $for field in T.fields {
797 $if field.is_embed {
798 has_embeds = true
799 }
800 }
801 decoder.current_node = decoder.current_node.next
802 if has_embeds {
803 mut seen_required := []string{}
804
805 // json object loop
806 for {
807 if decoder.current_node == unsafe { nil } {
808 break
809 }
810
811 key_info := decoder.current_node.value
812
813 if key_info.position >= struct_end {
814 break
815 }
816
817 decode_result := decode_struct_key(mut decoder, val, key_info, '', mut
818 seen_required)!
819 if decode_result.matched {
820 val = decode_result.value
821 } else {
822 // The key doesn't match any field in the struct, skip the entire value
823 // including all nested objects/arrays.
824 decoder.current_node = decoder.current_node.next
825 decoder.skip_current_value()
826 }
827 }
828
829 check_required_struct_fields(mut decoder, val, seen_required, '')!
830 } else {
831 field_infos := unsafe { decoder.cached_struct_field_infos[T]() }
832 mut decoded_mask := u64(0)
833 mut decoded_fields := []bool{}
834 if field_infos.len > 64 {
835 decoded_fields = []bool{len: field_infos.len}
836 }
837
838 mut field_idx := 0
839 // json object loop
840 for {
841 if decoder.current_node == unsafe { nil } {
842 break
843 }
844
845 key_info := decoder.current_node.value
846
847 if key_info.position >= struct_end {
848 break
849 }
850
851 mut matched := false
852 field_idx = 0
853 $for field in T.fields {
854 if !matched {
855 field_info := field_infos[field_idx]
856 field_can_match := (!field_info.is_skip || field_info.is_required)
857 && !(field_info.is_omitempty
858 && decoder.is_empty_value(decoder.current_node.next.value))
859 field_name_matches := key_info.length - 2 == field_info.json_name_len && unsafe {
860 vmemcmp(decoder.json.str + key_info.position + 1, field_info.json_name_ptr, field_info.json_name_len) == 0
861 }
862 if field_can_match && field_name_matches {
863 // value node
864 decoder.current_node = decoder.current_node.next
865
866 if field_info.is_skip {
867 // Preserve the existing inline decode behavior for `skip`+`required`.
868 decoded_mask = mark_struct_field_decoded(decoded_mask, mut
869 decoded_fields, field_idx)
870 if decoder.current_node != unsafe { nil } {
871 decoder.current_node = decoder.current_node.next
872 }
873 } else if field_info.is_raw {
874 $if field.unaliased_typ is $enum {
875 // workaround to avoid the error: enums can only be assigned `int` values
876 decoder.decode_error('`raw` attribute cannot be used with enum fields')!
877 } $else $if field.typ is ?string {
878 position := decoder.current_node.value.position
879 end := position + decoder.current_node.value.length
880
881 val.$(field.name) = decoder.json[position..end]
882 decoder.current_node = decoder.current_node.next
883
884 for {
885 if decoder.current_node == unsafe { nil }
886 || decoder.current_node.value.position + decoder.current_node.value.length >= end {
887 break
888 }
889 decoder.current_node = decoder.current_node.next
890 }
891 } $else $if field.typ is string {
892 position := decoder.current_node.value.position
893 end := position + decoder.current_node.value.length
894
895 val.$(field.name) = decoder.json[position..end]
896 decoder.current_node = decoder.current_node.next
897
898 for {
899 if decoder.current_node == unsafe { nil }
900 || decoder.current_node.value.position + decoder.current_node.value.length >= end {
901 break
902 }
903 decoder.current_node = decoder.current_node.next
904 }
905 } $else {
906 decoder.decode_error('`raw` attribute can only be used with string fields')!
907 }
908 } else {
909 $if field.typ is $option {
910 if decoder.current_node.value.value_kind == .null {
911 val.$(field.name) = none
912
913 if decoder.current_node != unsafe { nil } {
914 decoder.current_node = decoder.current_node.next
915 }
916 } else {
917 $if field.indirections == 1 {
918 mut decoded_ptr := $new(field.typ.payload_type.pointee_type)
919 decoder.decode_value(mut decoded_ptr)!
920 val.$(field.name) = decoded_ptr
921 } $else {
922 mut unwrapped_val := $zero(field.typ.payload_type)
923 decoder.decode_value(mut unwrapped_val)!
924 val.$(field.name) = unwrapped_val
925 }
926 }
927 } $else $if field.unaliased_typ is $array_dynamic {
928 if decoder.current_node.value.value_kind == .null
929 && !field_info.is_required {
930 val.$(field.name).clear()
931 decoder.skip_current_value()
932 } else {
933 val.$(field.name).clear()
934 decoder.decode_array(mut val.$(field.name))!
935 }
936 } $else $if field.unaliased_typ is $map {
937 if decoder.current_node.value.value_kind == .null
938 && !field_info.is_required {
939 val.$(field.name).clear()
940 decoder.skip_current_value()
941 } else {
942 decoder.decode_map(mut val.$(field.name))!
943 }
944 } $else $if field.unaliased_typ is string {
945 value_info := decoder.current_node.value
946 if value_info.value_kind == .object
947 || value_info.value_kind == .array {
948 val.$(field.name) = decoder.json[value_info.position..
949 value_info.position + value_info.length]
950 decoder.skip_current_value()
951 } else if value_info.value_kind == .null
952 && !field_info.is_required {
953 val.$(field.name) = ''
954 decoder.skip_current_value()
955 } else {
956 mut decoded_field_value := val.$(field.name)
957 decoder.decode_value(mut decoded_field_value)!
958 val.$(field.name) = decoded_field_value
959 }
960 } $else $if field.indirections == 1 {
961 if decoder.current_node.value.value_kind == .null {
962 val.$(field.name) = unsafe { nil }
963
964 if decoder.current_node != unsafe { nil } {
965 decoder.current_node = decoder.current_node.next
966 }
967 } else {
968 mut decoded_ptr :=
969 create_decoded_ptr(val.$(field.name))
970 decoder.decode_value(mut decoded_ptr)!
971 val.$(field.name) = decoded_ptr
972 }
973 } $else {
974 mut decoded_field_value := val.$(field.name)
975 decoder.decode_value(mut decoded_field_value)!
976 $if field.unaliased_typ is $map {
977 val.$(field.name) = decoded_field_value.move()
978 } $else {
979 val.$(field.name) = decoded_field_value
980 }
981 }
982 }
983
984 decoded_mask = mark_struct_field_decoded(decoded_mask, mut
985 decoded_fields, field_idx)
986 matched = true
987 }
988 }
989 field_idx++
990 }
991 if !matched {
992 // The key doesn't match any field in the struct, skip the entire value
993 // including all nested objects/arrays.
994 decoder.current_node = decoder.current_node.next
995 decoder.skip_current_value()
996 }
997 }
998
999 // check if all required fields are present
1000 field_idx = 0
1001 $for field in T.fields {
1002 field_info := field_infos[field_idx]
1003 if field_info.is_required
1004 && !struct_field_is_decoded(decoded_mask, decoded_fields, field_idx) {
1005 decoder.decode_error('missing required field `${field.name}`')!
1006 }
1007 field_idx++
1008 }
1009 }
1010 } else {
1011 decoder.decode_error('Expected object, but got ${struct_info.value_kind}')!
1012 }
1013 return
1014 } $else $if T.unaliased_typ is bool {
1015 value_info := decoder.current_node.value
1016
1017 if value_info.value_kind != .boolean {
1018 decoder.decode_error('Expected boolean, but got ${value_info.value_kind}')!
1019 }
1020
1021 unsafe {
1022 val = vmemcmp(decoder.json.str + value_info.position, c'true', 'true'.len) == 0
1023 }
1024 } $else $if T.unaliased_typ is $float || T.unaliased_typ is $int {
1025 value_info := decoder.current_node.value
1026
1027 if value_info.value_kind == .number {
1028 unsafe { decoder.decode_number(&val)! }
1029 } else if value_info.value_kind == .string && !decoder.strict {
1030 // In default mode, try to parse quoted strings as numbers
1031 val = decoder.decode_number_from_string[T]()!
1032 } else {
1033 decoder.decode_error('Expected number, but got ${value_info.value_kind}')!
1034 }
1035 } $else $if T.unaliased_typ is $enum {
1036 decoder.decode_enum(mut val)!
1037 } $else {
1038 decoder.decode_error('cannot decode value with ${typeof(val).name} type')!
1039 }
1040
1041 if decoder.current_node != unsafe { nil } {
1042 decoder.current_node = decoder.current_node.next
1043 }
1044 } // $else (not voidptr / interface)
1045}
1046
1047fn (mut decoder Decoder) decode_string[T](mut val T) ! {
1048 _ = val
1049 string_info := decoder.current_node.value
1050
1051 if string_info.value_kind == .string {
1052 string_start := string_info.position + 1
1053 string_end := string_info.position + string_info.length - 1
1054 string_body := decoder.json[string_start..string_end]
1055 if string_body.index_u8(`\\`) == -1 {
1056 val = string_body
1057 return
1058 }
1059
1060 mut string_buffer := []u8{cap: string_info.length} // might be too long but most json strings don't contain many escape characters anyways
1061
1062 mut buffer_index := 1
1063 mut string_index := 1
1064
1065 for string_index < string_info.length - 1 {
1066 current_byte := decoder.json[string_info.position + string_index]
1067
1068 if current_byte == `\\` {
1069 // push all characters up to this point
1070 unsafe {
1071 string_buffer.push_many(decoder.json.str + string_info.position + buffer_index,
1072 string_index - buffer_index)
1073 }
1074
1075 string_index++
1076
1077 escaped_char := decoder.json[string_info.position + string_index]
1078
1079 string_index++
1080
1081 match escaped_char {
1082 `/`, `"`, `\\` {
1083 string_buffer << escaped_char
1084 }
1085 `b` {
1086 string_buffer << `\b`
1087 }
1088 `f` {
1089 string_buffer << `\f`
1090 }
1091 `n` {
1092 string_buffer << `\n`
1093 }
1094 `r` {
1095 string_buffer << `\r`
1096 }
1097 `t` {
1098 string_buffer << `\t`
1099 }
1100 `u` {
1101 unicode_point := rune(strconv.parse_uint(decoder.json[
1102 string_info.position + string_index..string_info.position +
1103 string_index + 4], 16, 32)!)
1104
1105 string_index += 4
1106
1107 if unicode_point < 0xD800 || unicode_point > 0xDFFF { // normal utf-8
1108 string_buffer << unicode_point.bytes()
1109 } else if unicode_point >= 0xDC00 { // trail surrogate -> invalid
1110 decoder.decode_error('Got trail surrogate: ${u32(unicode_point):04X} before head surrogate.')!
1111 } else { // head surrogate -> treat as utf-16
1112 if string_index > string_info.length - 6 {
1113 decoder.decode_error('Expected a trail surrogate after a head surrogate, but got no valid escape sequence.')!
1114 }
1115 if decoder.json[string_info.position + string_index..
1116 string_info.position + string_index + 2] != '\\u' {
1117 decoder.decode_error('Expected a trail surrogate after a head surrogate, but got no valid escape sequence.')!
1118 }
1119
1120 string_index += 2
1121
1122 unicode_point2 := rune(strconv.parse_uint(decoder.json[
1123 string_info.position + string_index..string_info.position +
1124 string_index + 4], 16, 32)!)
1125
1126 string_index += 4
1127
1128 if unicode_point2 < 0xDC00 {
1129 decoder.decode_error('Expected a trail surrogate after a head surrogate, but got ${u32(unicode_point):04X}.')!
1130 }
1131
1132 final_unicode_point := (unicode_point2 & 0x3FF) +
1133 ((unicode_point & 0x3FF) << 10) + 0x10000
1134 string_buffer << final_unicode_point.bytes()
1135 }
1136 }
1137 else {} // has already been checked
1138 }
1139
1140 buffer_index = string_index
1141 } else {
1142 string_index++
1143 }
1144 }
1145
1146 // push the rest
1147 unsafe {
1148 string_buffer.push_many(decoder.json.str + string_info.position + buffer_index,
1149 string_index - buffer_index)
1150 }
1151
1152 val = string_buffer.bytestr()
1153 } else {
1154 decoder.decode_error('Expected string, but got ${string_info.value_kind}')!
1155 }
1156}
1157
1158fn (mut decoder Decoder) decode_array[T](mut val []T) ! {
1159 $if T is $interface {
1160 decoder.skip_current_value()
1161 return
1162 } $else $if T.unaliased_typ is voidptr {
1163 decoder.skip_current_value()
1164 return
1165 } $else {
1166 array_info := decoder.current_node.value
1167
1168 if array_info.value_kind == .array {
1169 decoder.current_node = decoder.current_node.next
1170
1171 array_position := array_info.position
1172 array_end := array_position + array_info.length
1173
1174 for {
1175 if decoder.current_node == unsafe { nil }
1176 || decoder.current_node.value.position >= array_end {
1177 break
1178 }
1179
1180 mut array_element := T{}
1181
1182 $if T.indirections == 1 {
1183 if decoder.current_node.value.value_kind == .null {
1184 if decoder.current_node != unsafe { nil } {
1185 decoder.current_node = decoder.current_node.next
1186 }
1187 } else {
1188 mut decoded_ptr := create_decoded_ptr(array_element)
1189 decoder.decode_value(mut decoded_ptr)!
1190 array_element = decoded_ptr
1191 }
1192 } $else {
1193 decoder.decode_value(mut array_element)!
1194 }
1195
1196 val << array_element
1197 }
1198 } else {
1199 decoder.decode_error('Expected array, but got ${array_info.value_kind}')!
1200 }
1201 }
1202}
1203
1204fn (mut decoder Decoder) decode_map[V](mut val map[string]V) ! {
1205 $if V is $interface {
1206 decoder.skip_current_value()
1207 return
1208 } $else $if V.unaliased_typ is voidptr {
1209 decoder.skip_current_value()
1210 return
1211 } $else {
1212 map_info := decoder.current_node.value
1213
1214 if map_info.value_kind == .object {
1215 map_position := map_info.position
1216 map_end := map_position + map_info.length
1217
1218 decoder.current_node = decoder.current_node.next
1219 for {
1220 if decoder.current_node == unsafe { nil }
1221 || decoder.current_node.value.position >= map_end {
1222 break
1223 }
1224
1225 key_info := decoder.current_node.value
1226
1227 if key_info.position >= map_end {
1228 break
1229 }
1230
1231 key_str := decoder.json[key_info.position + 1..key_info.position + key_info.length -
1232 1]
1233
1234 decoder.current_node = decoder.current_node.next
1235
1236 value_info := decoder.current_node.value
1237
1238 if value_info.position + value_info.length > map_end {
1239 break
1240 }
1241
1242 mut map_value := V{}
1243
1244 $if V.indirections == 1 {
1245 if decoder.current_node.value.value_kind == .null {
1246 if decoder.current_node != unsafe { nil } {
1247 decoder.current_node = decoder.current_node.next
1248 }
1249 } else {
1250 mut decoded_ptr := create_decoded_ptr(map_value)
1251 decoder.decode_value(mut decoded_ptr)!
1252 map_value = decoded_ptr
1253 }
1254 } $else {
1255 decoder.decode_value(mut map_value)!
1256 }
1257
1258 $if V is $alias && V.unaliased_typ is $map {
1259 // V is a map alias (e.g. `type SyntaxStyle = map[string]X`).
1260 // V's checker reports `val[key]` as the unaliased element type
1261 // while `map_value` keeps the alias type, so direct assignment
1262 // fails to type-check. Skip to keep generic instantiations
1263 // compilable; decoding into map-alias map values is unsupported.
1264 } $else $if K is string {
1265 $if V is $map {
1266 val[key_str] = map_value.move()
1267 } $else {
1268 val[key_str] = map_value
1269 }
1270 } $else $if K is rune {
1271 $if V is $map {
1272 val[rune(key_str.int())] = map_value.move()
1273 } $else {
1274 val[rune(key_str.int())] = map_value
1275 }
1276 } $else $if K is $int {
1277 $if V is $map {
1278 val[K(key_str.int())] = map_value.move()
1279 } $else {
1280 val[K(key_str.int())] = map_value
1281 }
1282 } $else {
1283 $if V is $map {
1284 val[key_str] = map_value.move()
1285 } $else {
1286 val[key_str] = map_value
1287 }
1288 }
1289 }
1290 } else {
1291 decoder.decode_error('Expected object, but got ${map_info.value_kind}')!
1292 }
1293 }
1294}
1295
1296fn (mut decoder Decoder) decode_enum[T](mut val T) ! {
1297 enum_info := decoder.current_node.value
1298
1299 if enum_info.value_kind == .number {
1300 mut result := 0
1301 unsafe { decoder.decode_number(&result)! }
1302
1303 $for value in T.values {
1304 if int(value.value) == result {
1305 val = value.value
1306 return
1307 }
1308 }
1309 decoder.decode_error('Number value: `${result}` does not match any field in enum: ${typeof(val).name}')!
1310 } else if enum_info.value_kind == .string {
1311 mut result := ''
1312 decoder.decode_string(mut result)!
1313
1314 $for value in T.values {
1315 for attr in value.attrs {
1316 if json_attr := json_attr_value(attr) {
1317 if json_attr == result {
1318 val = value.value
1319 return
1320 }
1321 }
1322 }
1323 if value.name == result {
1324 val = value.value
1325 return
1326 }
1327 }
1328 decoder.decode_error('String value: `${result}` does not match any field in enum: ${typeof(val).name}')!
1329 }
1330
1331 decoder.decode_error('Expected number or string value for enum, got: ${enum_info.value_kind}')!
1332}
1333
1334const max_integer_number_digits = 20
1335
1336@[markused]
1337fn has_exponent_number_syntax(str string) bool {
1338 for c in str {
1339 if c == `e` || c == `E` {
1340 return true
1341 }
1342 }
1343 return false
1344}
1345
1346@[markused]
1347fn scientific_number_to_integer_string(str string) !string {
1348 if !has_exponent_number_syntax(str) {
1349 // Handle plain decimal numbers with zero fractional part (e.g., "-123.0")
1350 dot_pos := str.index_u8(`.`)
1351 if dot_pos >= 0 {
1352 frac := str[dot_pos + 1..]
1353 mut all_zeros := frac.len > 0
1354 for c in frac {
1355 if c != `0` {
1356 all_zeros = false
1357 break
1358 }
1359 }
1360 if all_zeros {
1361 result := str[..dot_pos]
1362 if result.len == 0 || result == '-' || result == '+' {
1363 return '0'
1364 }
1365 return result
1366 }
1367 }
1368 return str
1369 }
1370 if str.len == 0 {
1371 return error('invalid scientific notation number')
1372 }
1373 mut i := 0
1374 mut is_negative := false
1375 if str[i] == `+` || str[i] == `-` {
1376 is_negative = str[i] == `-`
1377 i++
1378 }
1379 if i >= str.len {
1380 return error('invalid scientific notation number')
1381 }
1382 mut digits := []u8{cap: str.len}
1383 mut fractional_digits := 0
1384 mut seen_digit := false
1385 mut seen_dot := false
1386 for i < str.len {
1387 c := str[i]
1388 if c >= `0` && c <= `9` {
1389 digits << c
1390 seen_digit = true
1391 if seen_dot {
1392 fractional_digits++
1393 }
1394 i++
1395 continue
1396 }
1397 if c == `.` && !seen_dot {
1398 seen_dot = true
1399 i++
1400 continue
1401 }
1402 break
1403 }
1404 if !seen_digit || i >= str.len || (str[i] != `e` && str[i] != `E`) {
1405 return error('invalid scientific notation number')
1406 }
1407 i++
1408 mut exponent_sign := 1
1409 if i < str.len && (str[i] == `+` || str[i] == `-`) {
1410 if str[i] == `-` {
1411 exponent_sign = -1
1412 }
1413 i++
1414 }
1415 if i >= str.len || str[i] < `0` || str[i] > `9` {
1416 return error('invalid scientific notation number')
1417 }
1418 mut exponent := 0
1419 exponent_cap := max_integer_number_digits + fractional_digits + 1
1420 for i < str.len && str[i] >= `0` && str[i] <= `9` {
1421 if exponent < exponent_cap {
1422 exponent = (exponent * 10) + int(str[i] - `0`)
1423 }
1424 i++
1425 }
1426 if i != str.len {
1427 return error('invalid scientific notation number')
1428 }
1429 if exponent_sign == -1 {
1430 exponent = -exponent
1431 }
1432 mut first_non_zero := 0
1433 for first_non_zero < digits.len && digits[first_non_zero] == `0` {
1434 first_non_zero++
1435 }
1436 if first_non_zero == digits.len {
1437 return '0'
1438 }
1439 digits = digits[first_non_zero..].clone()
1440 scale := exponent - fractional_digits
1441 if scale < 0 {
1442 truncated_digits := -scale
1443 if truncated_digits >= digits.len {
1444 return '0'
1445 }
1446 digits = digits[..digits.len - truncated_digits].clone()
1447 } else if scale > 0 {
1448 final_len := digits.len + scale
1449 if final_len > max_integer_number_digits {
1450 return error('number `${str}` exceeds 64-bit integer range')
1451 }
1452 mut out := []u8{cap: final_len + if is_negative { 1 } else { 0 }}
1453 if is_negative {
1454 out << `-`
1455 }
1456 out << digits
1457 for _ in 0 .. scale {
1458 out << `0`
1459 }
1460 return out.bytestr()
1461 }
1462 if digits.len == 0 {
1463 return '0'
1464 }
1465 mut out := []u8{cap: digits.len + if is_negative { 1 } else { 0 }}
1466 if is_negative {
1467 out << `-`
1468 }
1469 out << digits
1470 return out.bytestr()
1471}
1472
1473fn parse_integer_number[T](str string) !T {
1474 int_str := scientific_number_to_integer_string(str)!
1475 $if T.unaliased_typ is i8 {
1476 return T(strconv.atoi8(int_str)!)
1477 } $else $if T.unaliased_typ is i16 {
1478 return T(strconv.atoi16(int_str)!)
1479 } $else $if T.unaliased_typ is i32 {
1480 return T(strconv.atoi32(int_str)!)
1481 } $else $if T.unaliased_typ is i64 {
1482 return T(strconv.atoi64(int_str)!)
1483 } $else $if T.unaliased_typ is u8 {
1484 return T(strconv.atou8(int_str)!)
1485 } $else $if T.unaliased_typ is u16 {
1486 return T(strconv.atou16(int_str)!)
1487 } $else $if T.unaliased_typ is u32 {
1488 return T(strconv.atou32(int_str)!)
1489 } $else $if T.unaliased_typ is u64 {
1490 return T(strconv.atou64(int_str)!)
1491 } $else $if T is int {
1492 return int(strconv.atoi64(int_str)!)
1493 } $else $if T.unaliased_typ is int {
1494 return T(int(strconv.atoi64(int_str)!))
1495 } $else $if T.unaliased_typ is isize {
1496 return T(isize(strconv.atoi64(int_str)!))
1497 } $else $if T.unaliased_typ is usize {
1498 return T(usize(strconv.atou64(int_str)!))
1499 } $else {
1500 return error('`parse_integer_number` cannot decode ${T.name} type')
1501 }
1502}
1503
1504@[markused]
1505fn parse_int_number(str string) !int {
1506 int_str := scientific_number_to_integer_string(str)!
1507 return int(strconv.atoi64(int_str)!)
1508}
1509
1510fn parse_float_number[T](str string) !T {
1511 $if js {
1512 $if T.unaliased_typ is f32 {
1513 return T(f32(strconv.atof64(str)!))
1514 } $else $if T.unaliased_typ is f64 {
1515 return T(strconv.atof64(str)!)
1516 } $else {
1517 return error('`parse_float_number` cannot decode ${T.name} type')
1518 }
1519 } $else {
1520 $if T.unaliased_typ is f32 {
1521 return T(f32(strconv.atof64(str, allow_extra_chars: false)!))
1522 } $else $if T.unaliased_typ is f64 {
1523 return T(strconv.atof64(str, allow_extra_chars: false)!)
1524 } $else {
1525 return error('`parse_float_number` cannot decode ${T.name} type')
1526 }
1527 }
1528}
1529
1530// use pointer instead of mut so enum cast works
1531@[unsafe]
1532fn (mut decoder Decoder) decode_number[T](val &T) ! {
1533 _ = val
1534 number_info := decoder.current_node.value
1535 str := decoder.json[number_info.position..number_info.position + number_info.length]
1536 $match T.unaliased_typ {
1537 i8 { *val = parse_integer_number[T](str)! }
1538 i16 { *val = parse_integer_number[T](str)! }
1539 i32 { *val = parse_integer_number[T](str)! }
1540 i64 { *val = parse_integer_number[T](str)! }
1541 u8 { *val = parse_integer_number[T](str)! }
1542 u16 { *val = parse_integer_number[T](str)! }
1543 u32 { *val = parse_integer_number[T](str)! }
1544 u64 { *val = parse_integer_number[T](str)! }
1545 int { *val = parse_int_number(str)! }
1546 isize { *val = parse_integer_number[T](str)! }
1547 usize { *val = parse_integer_number[T](str)! }
1548 f32 { *val = parse_float_number[T](str)! }
1549 f64 { *val = parse_float_number[T](str)! }
1550 $else { return error('`decode_number` can not decode ${T.name} type') }
1551 }
1552}
1553
1554// decode_number_from_string parses a number from a JSON string value (default mode).
1555// This extracts the content between quotes and parses it as a number.
1556fn (mut decoder Decoder) decode_number_from_string[T]() !T {
1557 string_info := decoder.current_node.value
1558 // Extract string content without quotes (position+1 to skip opening quote, length-2 to exclude both quotes)
1559 if string_info.length < 2 {
1560 return error('invalid string for number conversion')
1561 }
1562 str := decoder.json[string_info.position + 1..string_info.position + string_info.length - 1]
1563 $if T.unaliased_typ is i8 {
1564 return parse_integer_number[T](str)!
1565 } $else $if T.unaliased_typ is i16 {
1566 return parse_integer_number[T](str)!
1567 } $else $if T.unaliased_typ is i32 {
1568 return parse_integer_number[T](str)!
1569 } $else $if T.unaliased_typ is i64 {
1570 return parse_integer_number[T](str)!
1571 } $else $if T.unaliased_typ is u8 {
1572 return parse_integer_number[T](str)!
1573 } $else $if T.unaliased_typ is u16 {
1574 return parse_integer_number[T](str)!
1575 } $else $if T.unaliased_typ is u32 {
1576 return parse_integer_number[T](str)!
1577 } $else $if T.unaliased_typ is u64 {
1578 return parse_integer_number[T](str)!
1579 } $else $if T is int {
1580 return parse_int_number(str)!
1581 } $else $if T.unaliased_typ is int {
1582 return parse_integer_number[T](str)!
1583 } $else $if T.unaliased_typ is isize {
1584 return parse_integer_number[T](str)!
1585 } $else $if T.unaliased_typ is usize {
1586 return parse_integer_number[T](str)!
1587 } $else $if T.unaliased_typ is f32 {
1588 return parse_float_number[T](str)!
1589 } $else $if T.unaliased_typ is f64 {
1590 return parse_float_number[T](str)!
1591 } $else {
1592 return error('`decode_number_from_string` cannot decode ${T.name} type')
1593 }
1594}
1595