mirror of
https://github.com/vlang/v.git
synced 2025-09-13 22:42:26 +03:00
decoder2: prepare for moving into json2 (#25039)
This commit is contained in:
parent
13ba08fba2
commit
c5b6d2a1c6
8 changed files with 702 additions and 696 deletions
444
vlib/x/json2/decoder2/check.v
Normal file
444
vlib/x/json2/decoder2/check.v
Normal file
|
@ -0,0 +1,444 @@
|
||||||
|
module decoder2
|
||||||
|
|
||||||
|
// check_json_format checks if the JSON string is valid and updates the decoder state.
|
||||||
|
fn (mut checker Decoder) check_json_format() ! {
|
||||||
|
// skip whitespace
|
||||||
|
for checker.json[checker.checker_idx] in whitespace_chars {
|
||||||
|
if checker.checker_idx == checker.json.len {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
checker.checker_idx++
|
||||||
|
}
|
||||||
|
|
||||||
|
start_idx_position := checker.checker_idx
|
||||||
|
|
||||||
|
mut actual_value_info_pointer := unsafe { &ValueInfo(nil) }
|
||||||
|
|
||||||
|
match checker.json[checker.checker_idx] {
|
||||||
|
`"` {
|
||||||
|
checker.values_info.push(ValueInfo{
|
||||||
|
position: checker.checker_idx
|
||||||
|
value_kind: .string_
|
||||||
|
})
|
||||||
|
|
||||||
|
actual_value_info_pointer = checker.values_info.last()
|
||||||
|
|
||||||
|
checker.check_string()!
|
||||||
|
}
|
||||||
|
`-`, `0`...`9` {
|
||||||
|
checker.values_info.push(ValueInfo{
|
||||||
|
position: checker.checker_idx
|
||||||
|
value_kind: .number
|
||||||
|
})
|
||||||
|
|
||||||
|
actual_value_info_pointer = checker.values_info.last()
|
||||||
|
|
||||||
|
checker.check_number()!
|
||||||
|
}
|
||||||
|
`t`, `f` {
|
||||||
|
checker.values_info.push(ValueInfo{
|
||||||
|
position: checker.checker_idx
|
||||||
|
value_kind: .boolean
|
||||||
|
})
|
||||||
|
|
||||||
|
actual_value_info_pointer = checker.values_info.last()
|
||||||
|
|
||||||
|
checker.check_boolean()!
|
||||||
|
}
|
||||||
|
`n` {
|
||||||
|
checker.values_info.push(ValueInfo{
|
||||||
|
position: checker.checker_idx
|
||||||
|
value_kind: .null
|
||||||
|
})
|
||||||
|
|
||||||
|
actual_value_info_pointer = checker.values_info.last()
|
||||||
|
|
||||||
|
checker.check_null()!
|
||||||
|
}
|
||||||
|
`[` {
|
||||||
|
checker.values_info.push(ValueInfo{
|
||||||
|
position: checker.checker_idx
|
||||||
|
value_kind: .array
|
||||||
|
})
|
||||||
|
|
||||||
|
actual_value_info_pointer = checker.values_info.last()
|
||||||
|
|
||||||
|
checker.check_array()!
|
||||||
|
}
|
||||||
|
`{` {
|
||||||
|
checker.values_info.push(ValueInfo{
|
||||||
|
position: checker.checker_idx
|
||||||
|
value_kind: .object
|
||||||
|
})
|
||||||
|
|
||||||
|
actual_value_info_pointer = checker.values_info.last()
|
||||||
|
|
||||||
|
checker.check_object()!
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
checker.checker_error('unknown value kind')!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
actual_value_info_pointer.length = checker.checker_idx + 1 - start_idx_position
|
||||||
|
|
||||||
|
if checker.checker_idx < checker.json.len {
|
||||||
|
checker.checker_idx++
|
||||||
|
}
|
||||||
|
|
||||||
|
for checker.checker_idx < checker.json.len
|
||||||
|
&& checker.json[checker.checker_idx] !in [`,`, `:`, `}`, `]`] {
|
||||||
|
// get trash characters after the value
|
||||||
|
if checker.json[checker.checker_idx] !in whitespace_chars {
|
||||||
|
checker.checker_error('invalid value. Unexpected character after ${actual_value_info_pointer.value_kind} end')!
|
||||||
|
} else {
|
||||||
|
// whitespace
|
||||||
|
}
|
||||||
|
checker.checker_idx++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut checker Decoder) check_string() ! {
|
||||||
|
// check if the JSON string is a valid string
|
||||||
|
if checker.checker_idx == checker.json.len {
|
||||||
|
checker.checker_idx--
|
||||||
|
return checker.checker_error('EOF error: string not closed')
|
||||||
|
}
|
||||||
|
|
||||||
|
checker.checker_idx++
|
||||||
|
|
||||||
|
// check if the JSON string is a valid escape sequence
|
||||||
|
for checker.json[checker.checker_idx] != `"` {
|
||||||
|
if checker.json[checker.checker_idx] == `\\` {
|
||||||
|
if checker.checker_idx + 1 >= checker.json.len - 1 {
|
||||||
|
return checker.checker_error('invalid escape sequence')
|
||||||
|
}
|
||||||
|
escaped_char := checker.json[checker.checker_idx + 1]
|
||||||
|
match escaped_char {
|
||||||
|
`/`, `b`, `f`, `n`, `r`, `t`, `"`, `\\` {
|
||||||
|
checker.checker_idx++ // make sure escaped quotation marks are skipped
|
||||||
|
}
|
||||||
|
`u` {
|
||||||
|
// check if the JSON string is a valid unicode escape sequence
|
||||||
|
escaped_char_last_index := checker.checker_idx + 5
|
||||||
|
|
||||||
|
if escaped_char_last_index < checker.json.len {
|
||||||
|
// 2 bytes for the unicode escape sequence `\u`
|
||||||
|
checker.checker_idx += 2
|
||||||
|
|
||||||
|
for checker.checker_idx < escaped_char_last_index {
|
||||||
|
match checker.json[checker.checker_idx] {
|
||||||
|
`0`...`9`, `a`...`f`, `A`...`F` {
|
||||||
|
checker.checker_idx++
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return checker.checker_error('invalid unicode escape sequence')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
return checker.checker_error('short unicode escape sequence ${checker.json[checker.checker_idx..escaped_char_last_index]}')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return checker.checker_error('unknown escape sequence')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
checker.checker_idx++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut checker Decoder) check_number() ! {
|
||||||
|
// check if the JSON string is a valid float or integer
|
||||||
|
if checker.json[checker.checker_idx] == `-` {
|
||||||
|
checker.checker_idx++
|
||||||
|
}
|
||||||
|
|
||||||
|
if checker.checker_idx == checker.json.len {
|
||||||
|
checker.checker_idx--
|
||||||
|
return checker.checker_error('expected digit got EOF')
|
||||||
|
}
|
||||||
|
|
||||||
|
// integer part
|
||||||
|
if checker.json[checker.checker_idx] == `0` {
|
||||||
|
checker.checker_idx++
|
||||||
|
} else if checker.json[checker.checker_idx] >= `1` && checker.json[checker.checker_idx] <= `9` {
|
||||||
|
checker.checker_idx++
|
||||||
|
|
||||||
|
for checker.checker_idx < checker.json.len && checker.json[checker.checker_idx] >= `0`
|
||||||
|
&& checker.json[checker.checker_idx] <= `9` {
|
||||||
|
checker.checker_idx++
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return checker.checker_error('expected digit got ${checker.json[checker.checker_idx].ascii_str()}')
|
||||||
|
}
|
||||||
|
|
||||||
|
// fraction part
|
||||||
|
if checker.checker_idx != checker.json.len && checker.json[checker.checker_idx] == `.` {
|
||||||
|
checker.checker_idx++
|
||||||
|
|
||||||
|
if checker.checker_idx == checker.json.len {
|
||||||
|
checker.checker_idx--
|
||||||
|
return checker.checker_error('expected digit got EOF')
|
||||||
|
}
|
||||||
|
|
||||||
|
if checker.json[checker.checker_idx] >= `0` && checker.json[checker.checker_idx] <= `9` {
|
||||||
|
for checker.checker_idx < checker.json.len && checker.json[checker.checker_idx] >= `0`
|
||||||
|
&& checker.json[checker.checker_idx] <= `9` {
|
||||||
|
checker.checker_idx++
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return checker.checker_error('expected digit got ${checker.json[checker.checker_idx].ascii_str()}')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// exponent part
|
||||||
|
if checker.checker_idx != checker.json.len
|
||||||
|
&& (checker.json[checker.checker_idx] == `e` || checker.json[checker.checker_idx] == `E`) {
|
||||||
|
checker.checker_idx++
|
||||||
|
|
||||||
|
if checker.checker_idx == checker.json.len {
|
||||||
|
checker.checker_idx--
|
||||||
|
return checker.checker_error('expected digit got EOF')
|
||||||
|
}
|
||||||
|
|
||||||
|
if checker.json[checker.checker_idx] == `-` || checker.json[checker.checker_idx] == `+` {
|
||||||
|
checker.checker_idx++
|
||||||
|
|
||||||
|
if checker.checker_idx == checker.json.len {
|
||||||
|
checker.checker_idx--
|
||||||
|
return checker.checker_error('expected digit got EOF')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if checker.json[checker.checker_idx] >= `0` && checker.json[checker.checker_idx] <= `9` {
|
||||||
|
for checker.checker_idx < checker.json.len && checker.json[checker.checker_idx] >= `0`
|
||||||
|
&& checker.json[checker.checker_idx] <= `9` {
|
||||||
|
checker.checker_idx++
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return checker.checker_error('expected digit got ${checker.json[checker.checker_idx].ascii_str()}')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
checker.checker_idx--
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut checker Decoder) check_boolean() ! {
|
||||||
|
// check if the JSON string is a valid boolean
|
||||||
|
match checker.json[checker.checker_idx] {
|
||||||
|
`t` {
|
||||||
|
if checker.json.len - checker.checker_idx <= 3 {
|
||||||
|
return checker.checker_error('EOF error: expecting `true`')
|
||||||
|
}
|
||||||
|
|
||||||
|
is_not_ok := unsafe {
|
||||||
|
vmemcmp(checker.json.str + checker.checker_idx, true_in_string.str, true_in_string.len)
|
||||||
|
}
|
||||||
|
|
||||||
|
if is_not_ok != 0 {
|
||||||
|
return checker.checker_error('invalid boolean value. Got `${checker.json[checker.checker_idx..
|
||||||
|
checker.checker_idx + 4]}` instead of `true`')
|
||||||
|
}
|
||||||
|
checker.checker_idx += 3
|
||||||
|
}
|
||||||
|
`f` {
|
||||||
|
if checker.json.len - checker.checker_idx <= 4 {
|
||||||
|
return checker.checker_error('EOF error: expecting `false`')
|
||||||
|
}
|
||||||
|
|
||||||
|
is_not_ok := unsafe {
|
||||||
|
vmemcmp(checker.json.str + checker.checker_idx, false_in_string.str, false_in_string.len)
|
||||||
|
}
|
||||||
|
|
||||||
|
if is_not_ok != 0 {
|
||||||
|
return checker.checker_error('invalid boolean value. Got `${checker.json[checker.checker_idx..
|
||||||
|
checker.checker_idx + 5]}` instead of `false`')
|
||||||
|
}
|
||||||
|
|
||||||
|
checker.checker_idx += 4
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return checker.checker_error('invalid boolean')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut checker Decoder) check_null() ! {
|
||||||
|
// check if the JSON string is a null value
|
||||||
|
if checker.json.len - checker.checker_idx <= 3 {
|
||||||
|
return checker.checker_error('EOF error: expecting `null`')
|
||||||
|
}
|
||||||
|
|
||||||
|
is_not_ok := unsafe {
|
||||||
|
vmemcmp(checker.json.str + checker.checker_idx, null_in_string.str, null_in_string.len)
|
||||||
|
}
|
||||||
|
|
||||||
|
if is_not_ok != 0 {
|
||||||
|
return checker.checker_error('invalid null value. Got `${checker.json[checker.checker_idx..
|
||||||
|
checker.checker_idx + 4]}` instead of `null`')
|
||||||
|
}
|
||||||
|
checker.checker_idx += 3
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut checker Decoder) check_array() ! {
|
||||||
|
// check if the JSON string is an empty array
|
||||||
|
if checker.json.len >= checker.checker_idx + 2 {
|
||||||
|
checker.checker_idx++
|
||||||
|
} else {
|
||||||
|
return checker.checker_error('EOF error: There are not enough length for an array')
|
||||||
|
}
|
||||||
|
|
||||||
|
for checker.json[checker.checker_idx] != `]` {
|
||||||
|
// skip whitespace
|
||||||
|
for checker.json[checker.checker_idx] in whitespace_chars {
|
||||||
|
if checker.checker_idx == checker.json.len {
|
||||||
|
checker.checker_idx--
|
||||||
|
break
|
||||||
|
}
|
||||||
|
checker.checker_idx++
|
||||||
|
}
|
||||||
|
|
||||||
|
if checker.json[checker.checker_idx] == `]` {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if checker.checker_idx == checker.json.len {
|
||||||
|
checker.checker_idx--
|
||||||
|
return checker.checker_error('EOF error: array not closed')
|
||||||
|
}
|
||||||
|
|
||||||
|
checker.check_json_format()!
|
||||||
|
|
||||||
|
// whitespace
|
||||||
|
for checker.json[checker.checker_idx] in whitespace_chars {
|
||||||
|
checker.checker_idx++
|
||||||
|
}
|
||||||
|
if checker.json[checker.checker_idx] == `]` {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if checker.checker_idx == checker.json.len {
|
||||||
|
checker.checker_idx--
|
||||||
|
return checker.checker_error('EOF error: braces are not closed')
|
||||||
|
}
|
||||||
|
|
||||||
|
if checker.json[checker.checker_idx] == `,` {
|
||||||
|
checker.checker_idx++
|
||||||
|
for checker.json[checker.checker_idx] in whitespace_chars {
|
||||||
|
checker.checker_idx++
|
||||||
|
}
|
||||||
|
if checker.json[checker.checker_idx] == `]` {
|
||||||
|
return checker.checker_error('Cannot use `,`, before `]`')
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
if checker.json[checker.checker_idx] == `]` {
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
return checker.checker_error('`]` after value')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut checker Decoder) check_object() ! {
|
||||||
|
if checker.json.len - checker.checker_idx < 2 {
|
||||||
|
return checker.checker_error('EOF error: expecting a complete object after `{`')
|
||||||
|
}
|
||||||
|
checker.checker_idx++
|
||||||
|
for checker.json[checker.checker_idx] != `}` {
|
||||||
|
// skip whitespace
|
||||||
|
for checker.json[checker.checker_idx] in whitespace_chars {
|
||||||
|
if checker.checker_idx == checker.json.len {
|
||||||
|
checker.checker_idx--
|
||||||
|
break
|
||||||
|
}
|
||||||
|
checker.checker_idx++
|
||||||
|
}
|
||||||
|
|
||||||
|
if checker.json[checker.checker_idx] == `}` {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if checker.json[checker.checker_idx] != `"` {
|
||||||
|
return checker.checker_error('Expecting object key')
|
||||||
|
}
|
||||||
|
|
||||||
|
// Object key
|
||||||
|
checker.check_json_format()!
|
||||||
|
|
||||||
|
for checker.json[checker.checker_idx] != `:` {
|
||||||
|
if checker.checker_idx == checker.json.len {
|
||||||
|
checker.checker_idx--
|
||||||
|
return checker.checker_error('EOF error: key colon not found')
|
||||||
|
}
|
||||||
|
if checker.json[checker.checker_idx] !in whitespace_chars {
|
||||||
|
return checker.checker_error('invalid value after object key')
|
||||||
|
}
|
||||||
|
checker.checker_idx++
|
||||||
|
}
|
||||||
|
|
||||||
|
if checker.json[checker.checker_idx] != `:` {
|
||||||
|
return checker.checker_error('Expecting `:` after object key')
|
||||||
|
}
|
||||||
|
|
||||||
|
// skip `:`
|
||||||
|
checker.checker_idx++
|
||||||
|
|
||||||
|
// skip whitespace
|
||||||
|
for checker.json[checker.checker_idx] in whitespace_chars {
|
||||||
|
checker.checker_idx++
|
||||||
|
}
|
||||||
|
|
||||||
|
match checker.json[checker.checker_idx] {
|
||||||
|
`"`, `[`, `{`, `0`...`9`, `-`, `n`, `t`, `f` {
|
||||||
|
checker.check_json_format()!
|
||||||
|
|
||||||
|
if checker.checker_idx == checker.json.len {
|
||||||
|
checker.checker_idx--
|
||||||
|
return checker.checker_error('EOF error: braces are not closed')
|
||||||
|
}
|
||||||
|
|
||||||
|
// whitespace
|
||||||
|
for checker.json[checker.checker_idx] in whitespace_chars {
|
||||||
|
checker.checker_idx++
|
||||||
|
}
|
||||||
|
if checker.json[checker.checker_idx] == `}` {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if checker.checker_idx == checker.json.len {
|
||||||
|
checker.checker_idx--
|
||||||
|
return checker.checker_error('EOF error: braces are not closed')
|
||||||
|
}
|
||||||
|
|
||||||
|
if checker.json[checker.checker_idx] == `,` {
|
||||||
|
checker.checker_idx++
|
||||||
|
|
||||||
|
if checker.checker_idx == checker.json.len {
|
||||||
|
checker.checker_idx--
|
||||||
|
return checker.checker_error('EOF error: Expecting object key after `,`')
|
||||||
|
}
|
||||||
|
|
||||||
|
for checker.json[checker.checker_idx] in whitespace_chars {
|
||||||
|
checker.checker_idx++
|
||||||
|
}
|
||||||
|
if checker.json[checker.checker_idx] != `"` {
|
||||||
|
return checker.checker_error('Expecting object key after `,`')
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if checker.json[checker.checker_idx] == `}` {
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
return checker.checker_error('invalid object value')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return checker.checker_error('invalid object value')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -22,8 +22,7 @@ mut:
|
||||||
|
|
||||||
// ValueInfo represents the position and length of a value, such as string, number, array, object key, and object value in a JSON string.
|
// ValueInfo represents the position and length of a value, such as string, number, array, object key, and object value in a JSON string.
|
||||||
struct ValueInfo {
|
struct ValueInfo {
|
||||||
position int // The position of the value in the JSON string.
|
position int // The position of the value in the JSON string.
|
||||||
pub:
|
|
||||||
value_kind ValueKind // The kind of the value.
|
value_kind ValueKind // The kind of the value.
|
||||||
mut:
|
mut:
|
||||||
length int // The length of the value in the JSON string.
|
length int // The length of the value in the JSON string.
|
||||||
|
@ -51,20 +50,6 @@ mut:
|
||||||
current_node &Node[ValueInfo] = unsafe { nil } // The current node in the linked list.
|
current_node &Node[ValueInfo] = unsafe { nil } // The current node in the linked list.
|
||||||
}
|
}
|
||||||
|
|
||||||
// new_decoder creates a new JSON decoder.
|
|
||||||
pub fn new_decoder[T](json string) !Decoder {
|
|
||||||
mut decoder := Decoder{
|
|
||||||
json: json
|
|
||||||
}
|
|
||||||
|
|
||||||
decoder.check_json_format(json)!
|
|
||||||
check_if_json_match[T](json)!
|
|
||||||
|
|
||||||
decoder.current_node = decoder.values_info.head
|
|
||||||
|
|
||||||
return decoder
|
|
||||||
}
|
|
||||||
|
|
||||||
// LinkedList represents a linked list to store ValueInfo.
|
// LinkedList represents a linked list to store ValueInfo.
|
||||||
struct LinkedList[T] {
|
struct LinkedList[T] {
|
||||||
mut:
|
mut:
|
||||||
|
@ -138,8 +123,7 @@ fn (list &LinkedList[T]) free() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValueKind represents the kind of a JSON value.
|
// ValueKind represents the kind of a JSON value.
|
||||||
pub enum ValueKind {
|
enum ValueKind {
|
||||||
unknown
|
|
||||||
array
|
array
|
||||||
object
|
object
|
||||||
string_
|
string_
|
||||||
|
@ -148,8 +132,8 @@ pub enum ValueKind {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
const max_context_lenght = 50
|
const max_context_length = 50
|
||||||
const max_extra_charaters = 5
|
const max_extra_characters = 5
|
||||||
const tab_width = 8
|
const tab_width = 8
|
||||||
|
|
||||||
pub struct JsonDecodeError {
|
pub struct JsonDecodeError {
|
||||||
|
@ -189,13 +173,13 @@ fn (mut checker Decoder) checker_error(message string) ! {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cutoff := character_number > max_context_lenght
|
cutoff := character_number > max_context_length
|
||||||
|
|
||||||
// either start of string, last newline or a limited amount of characters
|
// either start of string, last newline or a limited amount of characters
|
||||||
context_start := if cutoff { position - max_context_lenght } else { last_newline }
|
context_start := if cutoff { position - max_context_length } else { last_newline }
|
||||||
|
|
||||||
// print some extra characters
|
// print some extra characters
|
||||||
mut context_end := int_min(checker.json.len, position + max_extra_charaters)
|
mut context_end := int_min(checker.json.len, position + max_extra_characters)
|
||||||
context_end_newline := checker.json[position..context_end].index_u8(`\n`)
|
context_end_newline := checker.json[position..context_end].index_u8(`\n`)
|
||||||
|
|
||||||
if context_end_newline != -1 {
|
if context_end_newline != -1 {
|
||||||
|
@ -213,7 +197,7 @@ fn (mut checker Decoder) checker_error(message string) ! {
|
||||||
context += '\n'
|
context += '\n'
|
||||||
|
|
||||||
if cutoff {
|
if cutoff {
|
||||||
context += ' '.repeat(max_context_lenght + 3)
|
context += ' '.repeat(max_context_length + 3)
|
||||||
} else {
|
} else {
|
||||||
context += ' '.repeat(character_number)
|
context += ' '.repeat(character_number)
|
||||||
}
|
}
|
||||||
|
@ -237,7 +221,7 @@ fn (mut decoder Decoder) decode_error(message string) ! {
|
||||||
}
|
}
|
||||||
|
|
||||||
start := error_info.position
|
start := error_info.position
|
||||||
end := start + int_min(error_info.length, max_context_lenght)
|
end := start + int_min(error_info.length, max_context_length)
|
||||||
|
|
||||||
mut line_number := 0
|
mut line_number := 0
|
||||||
mut character_number := 0
|
mut character_number := 0
|
||||||
|
@ -258,13 +242,13 @@ fn (mut decoder Decoder) decode_error(message string) ! {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cutoff := character_number > max_context_lenght
|
cutoff := character_number > max_context_length
|
||||||
|
|
||||||
// either start of string, last newline or a limited amount of characters
|
// either start of string, last newline or a limited amount of characters
|
||||||
context_start := if cutoff { start - max_context_lenght } else { last_newline }
|
context_start := if cutoff { start - max_context_length } else { last_newline }
|
||||||
|
|
||||||
// print some extra characters
|
// print some extra characters
|
||||||
mut context_end := int_min(decoder.json.len, end + max_extra_charaters)
|
mut context_end := int_min(decoder.json.len, end + max_extra_characters)
|
||||||
context_end_newline := decoder.json[end..context_end].index_u8(`\n`)
|
context_end_newline := decoder.json[end..context_end].index_u8(`\n`)
|
||||||
|
|
||||||
if context_end_newline != -1 {
|
if context_end_newline != -1 {
|
||||||
|
@ -282,7 +266,7 @@ fn (mut decoder Decoder) decode_error(message string) ! {
|
||||||
context += '\n'
|
context += '\n'
|
||||||
|
|
||||||
if cutoff {
|
if cutoff {
|
||||||
context += ' '.repeat(max_context_lenght + 3)
|
context += ' '.repeat(max_context_length + 3)
|
||||||
} else {
|
} else {
|
||||||
context += ' '.repeat(character_number)
|
context += ' '.repeat(character_number)
|
||||||
}
|
}
|
||||||
|
@ -296,413 +280,6 @@ fn (mut decoder Decoder) decode_error(message string) ! {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// check_json_format checks if the JSON string is valid and updates the decoder state.
|
|
||||||
fn (mut checker Decoder) check_json_format(val string) ! {
|
|
||||||
checker_end := checker.json.len
|
|
||||||
// check if the JSON string is empty
|
|
||||||
if val == '' {
|
|
||||||
return checker.checker_error('empty string')
|
|
||||||
}
|
|
||||||
|
|
||||||
// skip whitespace
|
|
||||||
for val[checker.checker_idx] in whitespace_chars {
|
|
||||||
if checker.checker_idx == checker_end {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
checker.checker_idx++
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if generic type matches the JSON type
|
|
||||||
value_kind := get_value_kind(val[checker.checker_idx])
|
|
||||||
start_idx_position := checker.checker_idx
|
|
||||||
checker.values_info.push(ValueInfo{
|
|
||||||
position: start_idx_position
|
|
||||||
value_kind: value_kind
|
|
||||||
})
|
|
||||||
|
|
||||||
mut actual_value_info_pointer := checker.values_info.last()
|
|
||||||
match value_kind {
|
|
||||||
.unknown {
|
|
||||||
return checker.checker_error('unknown value kind')
|
|
||||||
}
|
|
||||||
.null {
|
|
||||||
// check if the JSON string is a null value
|
|
||||||
if checker_end - checker.checker_idx <= 3 {
|
|
||||||
return checker.checker_error('EOF error: expecting `null`')
|
|
||||||
}
|
|
||||||
|
|
||||||
is_not_ok := unsafe {
|
|
||||||
vmemcmp(checker.json.str + checker.checker_idx, null_in_string.str, null_in_string.len)
|
|
||||||
}
|
|
||||||
|
|
||||||
if is_not_ok != 0 {
|
|
||||||
return checker.checker_error('invalid null value. Got `${checker.json[checker.checker_idx..
|
|
||||||
checker.checker_idx + 4]}` instead of `null`')
|
|
||||||
}
|
|
||||||
checker.checker_idx += 3
|
|
||||||
}
|
|
||||||
.object {
|
|
||||||
if checker_end - checker.checker_idx < 2 {
|
|
||||||
return checker.checker_error('EOF error: expecting a complete object after `{`')
|
|
||||||
}
|
|
||||||
checker.checker_idx++
|
|
||||||
for val[checker.checker_idx] != `}` {
|
|
||||||
// skip whitespace
|
|
||||||
for val[checker.checker_idx] in whitespace_chars {
|
|
||||||
if checker.checker_idx == checker_end {
|
|
||||||
checker.checker_idx--
|
|
||||||
break
|
|
||||||
}
|
|
||||||
checker.checker_idx++
|
|
||||||
}
|
|
||||||
|
|
||||||
if val[checker.checker_idx] == `}` {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if val[checker.checker_idx] != `"` {
|
|
||||||
return checker.checker_error('Expecting object key')
|
|
||||||
}
|
|
||||||
|
|
||||||
// Object key
|
|
||||||
checker.check_json_format(val)!
|
|
||||||
|
|
||||||
for val[checker.checker_idx] != `:` {
|
|
||||||
if checker.checker_idx == checker_end {
|
|
||||||
checker.checker_idx--
|
|
||||||
return checker.checker_error('EOF error: key colon not found')
|
|
||||||
}
|
|
||||||
if val[checker.checker_idx] !in whitespace_chars {
|
|
||||||
return checker.checker_error('invalid value after object key')
|
|
||||||
}
|
|
||||||
checker.checker_idx++
|
|
||||||
}
|
|
||||||
|
|
||||||
if val[checker.checker_idx] != `:` {
|
|
||||||
return checker.checker_error('Expecting `:` after object key')
|
|
||||||
}
|
|
||||||
// skip `:`
|
|
||||||
checker.checker_idx++
|
|
||||||
|
|
||||||
// skip whitespace
|
|
||||||
for val[checker.checker_idx] in whitespace_chars {
|
|
||||||
checker.checker_idx++
|
|
||||||
}
|
|
||||||
|
|
||||||
match val[checker.checker_idx] {
|
|
||||||
`"`, `[`, `{`, `0`...`9`, `-`, `n`, `t`, `f` {
|
|
||||||
checker.check_json_format(val)!
|
|
||||||
|
|
||||||
if checker.checker_idx == checker_end {
|
|
||||||
checker.checker_idx--
|
|
||||||
return checker.checker_error('EOF error: braces are not closed')
|
|
||||||
}
|
|
||||||
|
|
||||||
// whitespace
|
|
||||||
for val[checker.checker_idx] in whitespace_chars {
|
|
||||||
checker.checker_idx++
|
|
||||||
}
|
|
||||||
if val[checker.checker_idx] == `}` {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
if checker.checker_idx == checker_end {
|
|
||||||
checker.checker_idx--
|
|
||||||
return checker.checker_error('EOF error: braces are not closed')
|
|
||||||
}
|
|
||||||
|
|
||||||
if val[checker.checker_idx] == `,` {
|
|
||||||
checker.checker_idx++
|
|
||||||
|
|
||||||
if checker.checker_idx == checker_end {
|
|
||||||
checker.checker_idx--
|
|
||||||
return checker.checker_error('EOF error: Expecting object key after `,`')
|
|
||||||
}
|
|
||||||
|
|
||||||
for val[checker.checker_idx] in whitespace_chars {
|
|
||||||
checker.checker_idx++
|
|
||||||
}
|
|
||||||
if val[checker.checker_idx] != `"` {
|
|
||||||
return checker.checker_error('Expecting object key after `,`')
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if val[checker.checker_idx] == `}` {
|
|
||||||
break
|
|
||||||
} else {
|
|
||||||
return checker.checker_error('invalid object value')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return checker.checker_error('invalid object value')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.array {
|
|
||||||
// check if the JSON string is an empty array
|
|
||||||
if checker_end >= checker.checker_idx + 2 {
|
|
||||||
checker.checker_idx++
|
|
||||||
} else {
|
|
||||||
return checker.checker_error('EOF error: There are not enough length for an array')
|
|
||||||
}
|
|
||||||
|
|
||||||
for val[checker.checker_idx] != `]` {
|
|
||||||
// skip whitespace
|
|
||||||
for val[checker.checker_idx] in whitespace_chars {
|
|
||||||
if checker.checker_idx == checker_end {
|
|
||||||
checker.checker_idx--
|
|
||||||
break
|
|
||||||
}
|
|
||||||
checker.checker_idx++
|
|
||||||
}
|
|
||||||
|
|
||||||
if val[checker.checker_idx] == `]` {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
if checker.checker_idx == checker_end {
|
|
||||||
checker.checker_idx--
|
|
||||||
return checker.checker_error('EOF error: array not closed')
|
|
||||||
}
|
|
||||||
|
|
||||||
checker.check_json_format(val)!
|
|
||||||
|
|
||||||
// whitespace
|
|
||||||
for val[checker.checker_idx] in whitespace_chars {
|
|
||||||
checker.checker_idx++
|
|
||||||
}
|
|
||||||
if val[checker.checker_idx] == `]` {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if checker.checker_idx == checker_end {
|
|
||||||
checker.checker_idx--
|
|
||||||
return checker.checker_error('EOF error: braces are not closed')
|
|
||||||
}
|
|
||||||
|
|
||||||
if val[checker.checker_idx] == `,` {
|
|
||||||
checker.checker_idx++
|
|
||||||
for val[checker.checker_idx] in whitespace_chars {
|
|
||||||
checker.checker_idx++
|
|
||||||
}
|
|
||||||
if val[checker.checker_idx] == `]` {
|
|
||||||
return checker.checker_error('Cannot use `,`, before `]`')
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
} else {
|
|
||||||
if val[checker.checker_idx] == `]` {
|
|
||||||
break
|
|
||||||
} else {
|
|
||||||
return checker.checker_error('`]` after value')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.string_ {
|
|
||||||
// check if the JSON string is a valid string
|
|
||||||
|
|
||||||
if checker.checker_idx == checker_end {
|
|
||||||
checker.checker_idx--
|
|
||||||
return checker.checker_error('EOF error: string not closed')
|
|
||||||
}
|
|
||||||
|
|
||||||
checker.checker_idx++
|
|
||||||
|
|
||||||
// check if the JSON string is a valid escape sequence
|
|
||||||
for val[checker.checker_idx] != `"` {
|
|
||||||
if val[checker.checker_idx] == `\\` {
|
|
||||||
if checker.checker_idx + 1 >= checker_end - 1 {
|
|
||||||
return checker.checker_error('invalid escape sequence')
|
|
||||||
}
|
|
||||||
escaped_char := val[checker.checker_idx + 1]
|
|
||||||
match escaped_char {
|
|
||||||
`/`, `b`, `f`, `n`, `r`, `t`, `"`, `\\` {
|
|
||||||
checker.checker_idx++ // make sure escaped quotation marks are skipped
|
|
||||||
}
|
|
||||||
`u` {
|
|
||||||
// check if the JSON string is a valid unicode escape sequence
|
|
||||||
escaped_char_last_index := checker.checker_idx + 5
|
|
||||||
|
|
||||||
if escaped_char_last_index < checker_end {
|
|
||||||
// 2 bytes for the unicode escape sequence `\u`
|
|
||||||
checker.checker_idx += 2
|
|
||||||
|
|
||||||
for checker.checker_idx < escaped_char_last_index {
|
|
||||||
match val[checker.checker_idx] {
|
|
||||||
`0`...`9`, `a`...`f`, `A`...`F` {
|
|
||||||
checker.checker_idx++
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return checker.checker_error('invalid unicode escape sequence')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
} else {
|
|
||||||
return checker.checker_error('short unicode escape sequence ${checker.json[checker.checker_idx..escaped_char_last_index]}')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return checker.checker_error('unknown escape sequence')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
checker.checker_idx++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.number {
|
|
||||||
// check if the JSON string is a valid float or integer
|
|
||||||
if val[checker.checker_idx] == `-` {
|
|
||||||
checker.checker_idx++
|
|
||||||
}
|
|
||||||
|
|
||||||
if checker.checker_idx == checker_end {
|
|
||||||
checker.checker_idx--
|
|
||||||
return checker.checker_error('expected digit got EOF')
|
|
||||||
}
|
|
||||||
|
|
||||||
// integer part
|
|
||||||
if val[checker.checker_idx] == `0` {
|
|
||||||
checker.checker_idx++
|
|
||||||
} else if val[checker.checker_idx] >= `1` && val[checker.checker_idx] <= `9` {
|
|
||||||
checker.checker_idx++
|
|
||||||
|
|
||||||
for checker.checker_idx < checker_end && val[checker.checker_idx] >= `0`
|
|
||||||
&& val[checker.checker_idx] <= `9` {
|
|
||||||
checker.checker_idx++
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return checker.checker_error('expected digit got ${val[checker.checker_idx].ascii_str()}')
|
|
||||||
}
|
|
||||||
|
|
||||||
// fraction part
|
|
||||||
if checker.checker_idx != checker_end && val[checker.checker_idx] == `.` {
|
|
||||||
checker.checker_idx++
|
|
||||||
|
|
||||||
if checker.checker_idx == checker_end {
|
|
||||||
checker.checker_idx--
|
|
||||||
return checker.checker_error('expected digit got EOF')
|
|
||||||
}
|
|
||||||
|
|
||||||
if val[checker.checker_idx] >= `0` && val[checker.checker_idx] <= `9` {
|
|
||||||
for checker.checker_idx < checker_end && val[checker.checker_idx] >= `0`
|
|
||||||
&& val[checker.checker_idx] <= `9` {
|
|
||||||
checker.checker_idx++
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return checker.checker_error('expected digit got ${val[checker.checker_idx].ascii_str()}')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// exponent part
|
|
||||||
if checker.checker_idx != checker_end
|
|
||||||
&& (val[checker.checker_idx] == `e` || val[checker.checker_idx] == `E`) {
|
|
||||||
checker.checker_idx++
|
|
||||||
|
|
||||||
if checker.checker_idx == checker_end {
|
|
||||||
checker.checker_idx--
|
|
||||||
return checker.checker_error('expected digit got EOF')
|
|
||||||
}
|
|
||||||
|
|
||||||
if val[checker.checker_idx] == `-` || val[checker.checker_idx] == `+` {
|
|
||||||
checker.checker_idx++
|
|
||||||
|
|
||||||
if checker.checker_idx == checker_end {
|
|
||||||
checker.checker_idx--
|
|
||||||
return checker.checker_error('expected digit got EOF')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if val[checker.checker_idx] >= `0` && val[checker.checker_idx] <= `9` {
|
|
||||||
for checker.checker_idx < checker_end && val[checker.checker_idx] >= `0`
|
|
||||||
&& val[checker.checker_idx] <= `9` {
|
|
||||||
checker.checker_idx++
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return checker.checker_error('expected digit got ${val[checker.checker_idx].ascii_str()}')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
checker.checker_idx--
|
|
||||||
}
|
|
||||||
.boolean {
|
|
||||||
// check if the JSON string is a valid boolean
|
|
||||||
match val[checker.checker_idx] {
|
|
||||||
`t` {
|
|
||||||
if checker_end - checker.checker_idx <= 3 {
|
|
||||||
return checker.checker_error('EOF error: expecting `true`')
|
|
||||||
}
|
|
||||||
|
|
||||||
is_not_ok := unsafe {
|
|
||||||
vmemcmp(checker.json.str + checker.checker_idx, true_in_string.str,
|
|
||||||
true_in_string.len)
|
|
||||||
}
|
|
||||||
|
|
||||||
if is_not_ok != 0 {
|
|
||||||
return checker.checker_error('invalid boolean value. Got `${checker.json[checker.checker_idx..
|
|
||||||
checker.checker_idx + 4]}` instead of `true`')
|
|
||||||
}
|
|
||||||
checker.checker_idx += 3
|
|
||||||
}
|
|
||||||
`f` {
|
|
||||||
if checker_end - checker.checker_idx <= 4 {
|
|
||||||
return checker.checker_error('EOF error: expecting `false`')
|
|
||||||
}
|
|
||||||
|
|
||||||
is_not_ok := unsafe {
|
|
||||||
vmemcmp(checker.json.str + checker.checker_idx, false_in_string.str,
|
|
||||||
false_in_string.len)
|
|
||||||
}
|
|
||||||
|
|
||||||
if is_not_ok != 0 {
|
|
||||||
return checker.checker_error('invalid boolean value. Got `${checker.json[checker.checker_idx..
|
|
||||||
checker.checker_idx + 5]}` instead of `false`')
|
|
||||||
}
|
|
||||||
|
|
||||||
checker.checker_idx += 4
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return checker.checker_error('invalid boolean')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
actual_value_info_pointer.length = checker.checker_idx + 1 - start_idx_position
|
|
||||||
|
|
||||||
if checker.checker_idx < checker_end {
|
|
||||||
checker.checker_idx++
|
|
||||||
}
|
|
||||||
|
|
||||||
for checker.checker_idx < checker_end && val[checker.checker_idx] !in [`,`, `:`, `}`, `]`] {
|
|
||||||
// get trash characters after the value
|
|
||||||
if val[checker.checker_idx] !in whitespace_chars {
|
|
||||||
checker.checker_error('invalid value. Unexpected character after ${value_kind} end')!
|
|
||||||
} else {
|
|
||||||
// whitespace
|
|
||||||
}
|
|
||||||
checker.checker_idx++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// get_value_kind returns the kind of a JSON value.
|
|
||||||
fn get_value_kind(value u8) ValueKind {
|
|
||||||
if value == u8(`"`) {
|
|
||||||
return .string_
|
|
||||||
} else if value == u8(`t`) || value == u8(`f`) {
|
|
||||||
return .boolean
|
|
||||||
} else if value == u8(`{`) {
|
|
||||||
return .object
|
|
||||||
} else if value == u8(`[`) {
|
|
||||||
return .array
|
|
||||||
} else if (value >= u8(48) && value <= u8(57)) || value == u8(`-`) {
|
|
||||||
return .number
|
|
||||||
} else if value == u8(`n`) {
|
|
||||||
return .null
|
|
||||||
}
|
|
||||||
return .unknown
|
|
||||||
}
|
|
||||||
|
|
||||||
// decode decodes a JSON string into a specified type.
|
// decode decodes a JSON string into a specified type.
|
||||||
@[manualfree]
|
@[manualfree]
|
||||||
pub fn decode[T](val string) !T {
|
pub fn decode[T](val string) !T {
|
||||||
|
@ -717,7 +294,7 @@ pub fn decode[T](val string) !T {
|
||||||
json: val
|
json: val
|
||||||
}
|
}
|
||||||
|
|
||||||
decoder.check_json_format(val)!
|
decoder.check_json_format()!
|
||||||
|
|
||||||
mut result := T{}
|
mut result := T{}
|
||||||
decoder.current_node = decoder.values_info.head
|
decoder.current_node = decoder.values_info.head
|
||||||
|
@ -728,6 +305,10 @@ pub fn decode[T](val string) !T {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_dynamic_from_element[T](t T) []T {
|
||||||
|
return []T{}
|
||||||
|
}
|
||||||
|
|
||||||
// decode_value decodes a value from the JSON nodes.
|
// decode_value decodes a value from the JSON nodes.
|
||||||
@[manualfree]
|
@[manualfree]
|
||||||
fn (mut decoder Decoder) decode_value[T](mut val T) ! {
|
fn (mut decoder Decoder) decode_value[T](mut val T) ! {
|
||||||
|
@ -862,16 +443,29 @@ fn (mut decoder Decoder) decode_value[T](mut val T) ! {
|
||||||
} $else $if T.unaliased_typ is $map {
|
} $else $if T.unaliased_typ is $map {
|
||||||
decoder.decode_map(mut val)!
|
decoder.decode_map(mut val)!
|
||||||
return
|
return
|
||||||
} $else $if T.unaliased_typ is $array {
|
} $else $if T.unaliased_typ is $array_dynamic {
|
||||||
unsafe {
|
val.clear()
|
||||||
val.len = 0
|
|
||||||
}
|
|
||||||
decoder.decode_array(mut val)!
|
decoder.decode_array(mut val)!
|
||||||
// return to avoid the next increment of the current node
|
// return to avoid the next increment of the current node
|
||||||
// this is because the current node is already incremented in the decode_array function
|
// this is because the current node is already incremented in the decode_array function
|
||||||
// remove this line will cause the current node to be incremented twice
|
// remove this line will cause the current node to be incremented twice
|
||||||
// and bug recursive array decoding like `[][]int{}`
|
// and bug recursive array decoding like `[][]int{}`
|
||||||
return
|
return
|
||||||
|
} $else $if T.unaliased_typ is $array_fixed {
|
||||||
|
mut dynamic_val := get_dynamic_from_element(val[0])
|
||||||
|
|
||||||
|
// avoid copying by pointing dynamic_val to val
|
||||||
|
unsafe {
|
||||||
|
dynamic_val.len = 0
|
||||||
|
dynamic_val.cap = val.len // ensures data wont reallocate
|
||||||
|
dynamic_val.data = &val
|
||||||
|
}
|
||||||
|
decoder.decode_array(mut dynamic_val)!
|
||||||
|
|
||||||
|
if dynamic_val.len != val.len {
|
||||||
|
decoder.decode_error('Fixed size array expected ${val.len} elements but got ${dynamic_val.len} elements')!
|
||||||
|
}
|
||||||
|
return
|
||||||
} $else $if T.unaliased_typ is $struct {
|
} $else $if T.unaliased_typ is $struct {
|
||||||
struct_info := decoder.current_node.value
|
struct_info := decoder.current_node.value
|
||||||
|
|
||||||
|
@ -927,8 +521,6 @@ fn (mut decoder Decoder) decode_value[T](mut val T) ! {
|
||||||
|
|
||||||
current_field_info = struct_fields_info.head
|
current_field_info = struct_fields_info.head
|
||||||
|
|
||||||
mut field_used := false
|
|
||||||
|
|
||||||
// field loop
|
// field loop
|
||||||
for {
|
for {
|
||||||
if current_field_info == unsafe { nil } {
|
if current_field_info == unsafe { nil } {
|
||||||
|
@ -939,7 +531,6 @@ fn (mut decoder Decoder) decode_value[T](mut val T) ! {
|
||||||
|
|
||||||
if current_field_info.value.is_skip {
|
if current_field_info.value.is_skip {
|
||||||
if current_field_info.value.is_required == false {
|
if current_field_info.value.is_required == false {
|
||||||
field_used = true
|
|
||||||
current_field_info = current_field_info.next
|
current_field_info = current_field_info.next
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -948,13 +539,11 @@ fn (mut decoder Decoder) decode_value[T](mut val T) ! {
|
||||||
if current_field_info.value.is_omitempty {
|
if current_field_info.value.is_omitempty {
|
||||||
match decoder.current_node.next.value.value_kind {
|
match decoder.current_node.next.value.value_kind {
|
||||||
.null {
|
.null {
|
||||||
field_used = true
|
|
||||||
current_field_info = current_field_info.next
|
current_field_info = current_field_info.next
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
.string_ {
|
.string_ {
|
||||||
if decoder.current_node.next.value.length == 2 {
|
if decoder.current_node.next.value.length == 2 {
|
||||||
field_used = true
|
|
||||||
current_field_info = current_field_info.next
|
current_field_info = current_field_info.next
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -962,7 +551,6 @@ fn (mut decoder Decoder) decode_value[T](mut val T) ! {
|
||||||
.number {
|
.number {
|
||||||
if decoder.json[decoder.current_node.next.value.position] == `0` {
|
if decoder.json[decoder.current_node.next.value.position] == `0` {
|
||||||
if decoder.current_node.next.value.length == 1 {
|
if decoder.current_node.next.value.length == 1 {
|
||||||
field_used = true
|
|
||||||
current_field_info = current_field_info.next
|
current_field_info = current_field_info.next
|
||||||
continue
|
continue
|
||||||
} else if decoder.current_node.next.value.length == 3 {
|
} else if decoder.current_node.next.value.length == 3 {
|
||||||
|
@ -971,7 +559,6 @@ fn (mut decoder Decoder) decode_value[T](mut val T) ! {
|
||||||
decoder.current_node.next.value.position,
|
decoder.current_node.next.value.position,
|
||||||
float_zero_in_string.str, float_zero_in_string.len) == 0
|
float_zero_in_string.str, float_zero_in_string.len) == 0
|
||||||
} {
|
} {
|
||||||
field_used = true
|
|
||||||
current_field_info = current_field_info.next
|
current_field_info = current_field_info.next
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -988,8 +575,6 @@ fn (mut decoder Decoder) decode_value[T](mut val T) ! {
|
||||||
vmemcmp(decoder.json.str + key_info.position + 1, current_field_info.value.json_name_ptr,
|
vmemcmp(decoder.json.str + key_info.position + 1, current_field_info.value.json_name_ptr,
|
||||||
current_field_info.value.json_name_len) == 0
|
current_field_info.value.json_name_len) == 0
|
||||||
} {
|
} {
|
||||||
field_used = true
|
|
||||||
|
|
||||||
$for field in T.fields {
|
$for field in T.fields {
|
||||||
if field.name.len == current_field_info.value.field_name_len {
|
if field.name.len == current_field_info.value.field_name_len {
|
||||||
if unsafe {
|
if unsafe {
|
||||||
|
@ -1076,12 +661,6 @@ fn (mut decoder Decoder) decode_value[T](mut val T) ! {
|
||||||
}
|
}
|
||||||
current_field_info = current_field_info.next
|
current_field_info = current_field_info.next
|
||||||
}
|
}
|
||||||
|
|
||||||
if !field_used
|
|
||||||
&& decoder.json[key_info.position + 1..key_info.position + key_info.length - 1] != '_type' {
|
|
||||||
decoder.decode_error('unknown field `${decoder.json[key_info.position..
|
|
||||||
key_info.position + key_info.length]}`')!
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if all required fields are present
|
// check if all required fields are present
|
||||||
|
@ -1125,6 +704,12 @@ fn (mut decoder Decoder) decode_value[T](mut val T) ! {
|
||||||
value_info := decoder.current_node.value
|
value_info := decoder.current_node.value
|
||||||
|
|
||||||
if value_info.value_kind == .number {
|
if value_info.value_kind == .number {
|
||||||
|
unsafe { decoder.decode_number(&val)! }
|
||||||
|
} else if value_info.value_kind == .string_ {
|
||||||
|
// recheck if string contains number
|
||||||
|
decoder.checker_idx = value_info.position + 1
|
||||||
|
decoder.check_number()!
|
||||||
|
|
||||||
unsafe { decoder.decode_number(&val)! }
|
unsafe { decoder.decode_number(&val)! }
|
||||||
} else {
|
} else {
|
||||||
return decoder.decode_error('Expected number, but got ${value_info.value_kind}')
|
return decoder.decode_error('Expected number, but got ${value_info.value_kind}')
|
||||||
|
@ -1277,7 +862,14 @@ fn get_number_digits[T](num T) int {
|
||||||
// use pointer instead of mut so enum cast works
|
// use pointer instead of mut so enum cast works
|
||||||
@[unsafe]
|
@[unsafe]
|
||||||
fn (mut decoder Decoder) decode_number[T](val &T) ! {
|
fn (mut decoder Decoder) decode_number[T](val &T) ! {
|
||||||
number_info := decoder.current_node.value
|
mut number_info := decoder.current_node.value
|
||||||
|
|
||||||
|
if decoder.json[number_info.position] == `"` { // fake number
|
||||||
|
number_info = ValueInfo{
|
||||||
|
position: number_info.position + 1
|
||||||
|
length: number_info.length - 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$if T.unaliased_typ is $float {
|
$if T.unaliased_typ is $float {
|
||||||
*val = T(strconv.atof_quick(decoder.json[number_info.position..number_info.position +
|
*val = T(strconv.atof_quick(decoder.json[number_info.position..number_info.position +
|
||||||
|
|
|
@ -92,7 +92,6 @@ fn (mut decoder Decoder) check_element_type_valid[T](element T, current_node &No
|
||||||
return decoder.check_struct_type_valid(element, current_node)
|
return decoder.check_struct_type_valid(element, current_node)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
|
@ -304,7 +303,6 @@ fn (mut decoder Decoder) init_sumtype_by_value_kind[T](mut val T, value_info Val
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if failed_struct {
|
if failed_struct {
|
||||||
|
|
|
@ -1,230 +0,0 @@
|
||||||
module decoder2
|
|
||||||
|
|
||||||
fn test_check_if_json_match() {
|
|
||||||
// /* Test wrong string values */
|
|
||||||
mut has_error := false
|
|
||||||
|
|
||||||
decode[string]('{"key": "value"}') or {
|
|
||||||
if err is JsonDecodeError {
|
|
||||||
assert err.line == 1
|
|
||||||
assert err.character == 1
|
|
||||||
assert err.message == 'Data: Expected string, but got object'
|
|
||||||
}
|
|
||||||
has_error = true
|
|
||||||
}
|
|
||||||
assert has_error, 'Expected error'
|
|
||||||
has_error = false
|
|
||||||
|
|
||||||
decode[map[string]string]('"value"') or {
|
|
||||||
if err is JsonDecodeError {
|
|
||||||
assert err.line == 1
|
|
||||||
assert err.character == 1
|
|
||||||
assert err.message == 'Data: Expected object, but got string_'
|
|
||||||
}
|
|
||||||
has_error = true
|
|
||||||
}
|
|
||||||
assert has_error, 'Expected error'
|
|
||||||
has_error = false
|
|
||||||
|
|
||||||
decode[[]int]('{"key": "value"}') or {
|
|
||||||
if err is JsonDecodeError {
|
|
||||||
assert err.line == 1
|
|
||||||
assert err.character == 1
|
|
||||||
assert err.message == 'Data: Expected array, but got object'
|
|
||||||
}
|
|
||||||
has_error = true
|
|
||||||
}
|
|
||||||
assert has_error, 'Expected error'
|
|
||||||
has_error = false
|
|
||||||
|
|
||||||
decode[string]('[1, 2, 3]') or {
|
|
||||||
if err is JsonDecodeError {
|
|
||||||
assert err.line == 1
|
|
||||||
assert err.character == 1
|
|
||||||
assert err.message == 'Data: Expected string, but got array'
|
|
||||||
}
|
|
||||||
has_error = true
|
|
||||||
}
|
|
||||||
assert has_error, 'Expected error'
|
|
||||||
has_error = false
|
|
||||||
|
|
||||||
decode[int]('{"key": "value"}') or {
|
|
||||||
if err is JsonDecodeError {
|
|
||||||
assert err.line == 1
|
|
||||||
assert err.character == 1
|
|
||||||
assert err.message == 'Data: Expected number, but got object'
|
|
||||||
}
|
|
||||||
has_error = true
|
|
||||||
}
|
|
||||||
assert has_error, 'Expected error'
|
|
||||||
has_error = false
|
|
||||||
|
|
||||||
decode[bool]('{"key": "value"}') or {
|
|
||||||
if err is JsonDecodeError {
|
|
||||||
assert err.line == 1
|
|
||||||
assert err.character == 1
|
|
||||||
assert err.message == 'Data: Expected boolean, but got object'
|
|
||||||
}
|
|
||||||
has_error = true
|
|
||||||
}
|
|
||||||
assert has_error, 'Expected error'
|
|
||||||
has_error = false
|
|
||||||
|
|
||||||
// /* Right string values */
|
|
||||||
decode[string]('"value"') or { assert false }
|
|
||||||
|
|
||||||
decode[map[string]string]('{"key": "value"}') or { assert false }
|
|
||||||
|
|
||||||
decode[[]int]('[1, 2, 3]') or { assert false }
|
|
||||||
|
|
||||||
decode[string]('"string"') or { assert false }
|
|
||||||
|
|
||||||
decode[int]('123') or { assert false }
|
|
||||||
|
|
||||||
decode[bool]('true') or { assert false }
|
|
||||||
|
|
||||||
decode[bool]('false') or { assert false }
|
|
||||||
|
|
||||||
// TODO: test null
|
|
||||||
}
|
|
||||||
|
|
||||||
fn test_check_json_format() {
|
|
||||||
// primitives
|
|
||||||
for variable in ['""', '"string"', '123', '0', 'true'] {
|
|
||||||
mut checker := Decoder{
|
|
||||||
checker_idx: 0
|
|
||||||
json: variable
|
|
||||||
}
|
|
||||||
|
|
||||||
checker.check_json_format(variable) or { assert false, err.str() }
|
|
||||||
assert checker.checker_idx == checker.json.len, 'Expected to reach the end of the json string ${checker.json}'
|
|
||||||
}
|
|
||||||
|
|
||||||
// simple objects
|
|
||||||
for variable in ['{}', '{"key": null}', '{"key": "value"}', '{"key": 123}', '{"key": true}'] {
|
|
||||||
mut checker := Decoder{
|
|
||||||
checker_idx: 0
|
|
||||||
json: variable
|
|
||||||
}
|
|
||||||
|
|
||||||
checker.check_json_format(variable) or { assert false, err.str() }
|
|
||||||
assert checker.checker_idx == checker.json.len, 'Expected to reach the end of the json string ${checker.json}'
|
|
||||||
}
|
|
||||||
|
|
||||||
// Nested objects
|
|
||||||
for variable in ['{"key": {"key": 123}}'] {
|
|
||||||
mut checker := Decoder{
|
|
||||||
checker_idx: 0
|
|
||||||
json: variable
|
|
||||||
}
|
|
||||||
|
|
||||||
checker.check_json_format(variable) or { assert false, err.str() }
|
|
||||||
assert checker.checker_idx == checker.json.len, 'Expected to reach the end of the json string ${checker.json}'
|
|
||||||
}
|
|
||||||
|
|
||||||
// simple arrays
|
|
||||||
for variable in ['[]', '[1, 2, 3]', '["a", "b", "c"]', '[true, false]'] {
|
|
||||||
mut checker := Decoder{
|
|
||||||
checker_idx: 0
|
|
||||||
json: variable
|
|
||||||
}
|
|
||||||
|
|
||||||
checker.check_json_format(variable) or { assert false, err.str() }
|
|
||||||
assert checker.checker_idx == checker.json.len, 'Expected to reach the end of the json string ${checker.json}'
|
|
||||||
}
|
|
||||||
|
|
||||||
// Nested arrays
|
|
||||||
for variable in ['[[1, 2, 3], [4, 5, 6]]'] {
|
|
||||||
mut checker := Decoder{
|
|
||||||
checker_idx: 0
|
|
||||||
json: variable
|
|
||||||
}
|
|
||||||
|
|
||||||
checker.check_json_format(variable) or { assert false, err.str() }
|
|
||||||
// assert checker.checker_idx == checker.json.len - 1, 'Expected to reach the end of the json string ${checker.json}'
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wrong jsons
|
|
||||||
|
|
||||||
json_and_error_message := [
|
|
||||||
{
|
|
||||||
'json': ']'
|
|
||||||
'error': 'Syntax: unknown value kind'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'json': '}'
|
|
||||||
'error': 'Syntax: unknown value kind'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'json': 'truely'
|
|
||||||
'error': 'Syntax: invalid value. Unexpected character after boolean end'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'json': '0[1]'
|
|
||||||
'error': 'Syntax: invalid value. Unexpected character after number end'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'json': '[1, 2, g3]'
|
|
||||||
'error': 'Syntax: unknown value kind'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'json': '[1, 2,, 3]'
|
|
||||||
'error': 'Syntax: unknown value kind'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'json': '{"key": 123'
|
|
||||||
'error': 'Syntax: EOF error: braces are not closed'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'json': '{"key": 123,'
|
|
||||||
'error': 'Syntax: EOF error: Expecting object key after `,`'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'json': '{"key": 123, "key2": 456,}'
|
|
||||||
'error': 'Syntax: Expecting object key after `,`'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'json': '[[1, 2, 3], [4, 5, 6],]'
|
|
||||||
'error': 'Syntax: Cannot use `,`, before `]`'
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
for json_and_error in json_and_error_message {
|
|
||||||
mut has_error := false
|
|
||||||
mut checker := Decoder{
|
|
||||||
checker_idx: 0
|
|
||||||
json: json_and_error['json']
|
|
||||||
}
|
|
||||||
|
|
||||||
checker.check_json_format(json_and_error['json']) or {
|
|
||||||
if err is JsonDecodeError {
|
|
||||||
assert err.message == json_and_error['error']
|
|
||||||
}
|
|
||||||
has_error = true
|
|
||||||
}
|
|
||||||
assert has_error, 'Expected error ${json_and_error['error']}'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn test_get_value_kind() {
|
|
||||||
struct Object_ {
|
|
||||||
byte_ u8
|
|
||||||
value_kind ValueKind
|
|
||||||
}
|
|
||||||
|
|
||||||
array_ := [
|
|
||||||
Object_{`"`, .string_},
|
|
||||||
Object_{`t`, .boolean},
|
|
||||||
Object_{`f`, .boolean},
|
|
||||||
Object_{`{`, .object},
|
|
||||||
Object_{`[`, .array},
|
|
||||||
Object_{`0`, .number},
|
|
||||||
Object_{`-`, .number},
|
|
||||||
Object_{`n`, .null},
|
|
||||||
Object_{`x`, .unknown},
|
|
||||||
]
|
|
||||||
|
|
||||||
for value in array_ {
|
|
||||||
assert get_value_kind(value.byte_) == value.value_kind
|
|
||||||
}
|
|
||||||
}
|
|
145
vlib/x/json2/decoder2/tests/checker_test.v
Normal file
145
vlib/x/json2/decoder2/tests/checker_test.v
Normal file
|
@ -0,0 +1,145 @@
|
||||||
|
import x.json2.decoder2 as json
|
||||||
|
import x.json2
|
||||||
|
|
||||||
|
fn test_check_if_json_match() {
|
||||||
|
// /* Test wrong string values */
|
||||||
|
mut has_error := false
|
||||||
|
|
||||||
|
json.decode[string]('{"key": "value"}') or {
|
||||||
|
if err is json.JsonDecodeError {
|
||||||
|
assert err.line == 1
|
||||||
|
assert err.character == 1
|
||||||
|
assert err.message == 'Data: Expected string, but got object'
|
||||||
|
}
|
||||||
|
has_error = true
|
||||||
|
}
|
||||||
|
assert has_error, 'Expected error'
|
||||||
|
has_error = false
|
||||||
|
|
||||||
|
json.decode[map[string]string]('"value"') or {
|
||||||
|
if err is json.JsonDecodeError {
|
||||||
|
assert err.line == 1
|
||||||
|
assert err.character == 1
|
||||||
|
assert err.message == 'Data: Expected object, but got string_'
|
||||||
|
}
|
||||||
|
has_error = true
|
||||||
|
}
|
||||||
|
assert has_error, 'Expected error'
|
||||||
|
has_error = false
|
||||||
|
|
||||||
|
json.decode[[]int]('{"key": "value"}') or {
|
||||||
|
if err is json.JsonDecodeError {
|
||||||
|
assert err.line == 1
|
||||||
|
assert err.character == 1
|
||||||
|
assert err.message == 'Data: Expected array, but got object'
|
||||||
|
}
|
||||||
|
has_error = true
|
||||||
|
}
|
||||||
|
assert has_error, 'Expected error'
|
||||||
|
has_error = false
|
||||||
|
|
||||||
|
json.decode[string]('[1, 2, 3]') or {
|
||||||
|
if err is json.JsonDecodeError {
|
||||||
|
assert err.line == 1
|
||||||
|
assert err.character == 1
|
||||||
|
assert err.message == 'Data: Expected string, but got array'
|
||||||
|
}
|
||||||
|
has_error = true
|
||||||
|
}
|
||||||
|
assert has_error, 'Expected error'
|
||||||
|
has_error = false
|
||||||
|
|
||||||
|
json.decode[int]('{"key": "value"}') or {
|
||||||
|
if err is json.JsonDecodeError {
|
||||||
|
assert err.line == 1
|
||||||
|
assert err.character == 1
|
||||||
|
assert err.message == 'Data: Expected number, but got object'
|
||||||
|
}
|
||||||
|
has_error = true
|
||||||
|
}
|
||||||
|
assert has_error, 'Expected error'
|
||||||
|
has_error = false
|
||||||
|
|
||||||
|
json.decode[bool]('{"key": "value"}') or {
|
||||||
|
if err is json.JsonDecodeError {
|
||||||
|
assert err.line == 1
|
||||||
|
assert err.character == 1
|
||||||
|
assert err.message == 'Data: Expected boolean, but got object'
|
||||||
|
}
|
||||||
|
has_error = true
|
||||||
|
}
|
||||||
|
assert has_error, 'Expected error'
|
||||||
|
has_error = false
|
||||||
|
|
||||||
|
// /* Right string values */
|
||||||
|
json.decode[string]('"value"') or { assert false }
|
||||||
|
|
||||||
|
json.decode[map[string]string]('{"key": "value"}') or { assert false }
|
||||||
|
|
||||||
|
json.decode[[]int]('[1, 2, 3]') or { assert false }
|
||||||
|
|
||||||
|
json.decode[string]('"string"') or { assert false }
|
||||||
|
|
||||||
|
json.decode[int]('123') or { assert false }
|
||||||
|
|
||||||
|
json.decode[bool]('true') or { assert false }
|
||||||
|
|
||||||
|
json.decode[bool]('false') or { assert false }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_check_json_format() {
|
||||||
|
json_and_error_message := [
|
||||||
|
{
|
||||||
|
'json': ']'
|
||||||
|
'error': 'Syntax: unknown value kind'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'json': '}'
|
||||||
|
'error': 'Syntax: unknown value kind'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'json': 'truely'
|
||||||
|
'error': 'Syntax: invalid value. Unexpected character after boolean end'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'json': '0[1]'
|
||||||
|
'error': 'Syntax: invalid value. Unexpected character after number end'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'json': '[1, 2, g3]'
|
||||||
|
'error': 'Syntax: unknown value kind'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'json': '[1, 2,, 3]'
|
||||||
|
'error': 'Syntax: unknown value kind'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'json': '{"key": 123'
|
||||||
|
'error': 'Syntax: EOF error: braces are not closed'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'json': '{"key": 123,'
|
||||||
|
'error': 'Syntax: EOF error: Expecting object key after `,`'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'json': '{"key": 123, "key2": 456,}'
|
||||||
|
'error': 'Syntax: Expecting object key after `,`'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'json': '[[1, 2, 3], [4, 5, 6],]'
|
||||||
|
'error': 'Syntax: Cannot use `,`, before `]`'
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
for json_and_error in json_and_error_message {
|
||||||
|
mut has_error := false
|
||||||
|
|
||||||
|
json.decode[json2.Any](json_and_error['json']) or {
|
||||||
|
if err is json.JsonDecodeError {
|
||||||
|
assert err.message == json_and_error['error']
|
||||||
|
}
|
||||||
|
has_error = true
|
||||||
|
}
|
||||||
|
assert has_error, 'Expected error ${json_and_error['error']}'
|
||||||
|
}
|
||||||
|
}
|
23
vlib/x/json2/decoder2/tests/decode_budget_number_test.v
Normal file
23
vlib/x/json2/decoder2/tests/decode_budget_number_test.v
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
import x.json2.decoder2 as json
|
||||||
|
|
||||||
|
fn test_budget_number() {
|
||||||
|
assert json.decode[int]('"0"')! == 0
|
||||||
|
assert json.decode[int]('"100"')! == 100
|
||||||
|
assert json.decode[f64]('"-23.6e1"')! == -236.0
|
||||||
|
|
||||||
|
assert json.decode[[]int]('["100", 99, "98", 97]')! == [100, 99, 98, 97]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_budget_number_malformed() {
|
||||||
|
json.decode[int]('"+100"') or {
|
||||||
|
if err is json.JsonDecodeError {
|
||||||
|
assert err.line == 1
|
||||||
|
assert err.character == 2
|
||||||
|
assert err.message == 'Syntax: expected digit got +'
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
assert false
|
||||||
|
}
|
35
vlib/x/json2/decoder2/tests/decode_fixed_array_test.v
Normal file
35
vlib/x/json2/decoder2/tests/decode_fixed_array_test.v
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
import x.json2.decoder2 as json
|
||||||
|
|
||||||
|
fn test_fixed_array() {
|
||||||
|
mut expected := [3]int{}
|
||||||
|
expected[0] = 1
|
||||||
|
expected[1] = 2
|
||||||
|
expected[2] = 3
|
||||||
|
assert json.decode[[3]int]('[1, 2, 3]')! == expected
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_fixed_array_to_few() {
|
||||||
|
json.decode[[4]int]('[1, 2, 3]') or {
|
||||||
|
if err is json.JsonDecodeError {
|
||||||
|
assert err.line == 1
|
||||||
|
assert err.character == 8
|
||||||
|
assert err.message == 'Data: Fixed size array expected 4 elements but got 3 elements'
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
assert false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_fixed_array_to_many() {
|
||||||
|
json.decode[[2]int]('[1, 2, 3]') or {
|
||||||
|
if err is json.JsonDecodeError {
|
||||||
|
assert err.line == 1
|
||||||
|
assert err.character == 8
|
||||||
|
assert err.message == 'Data: Fixed size array expected 2 elements but got 3 elements'
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
assert false
|
||||||
|
}
|
|
@ -23,11 +23,6 @@ pub type Any = []Any
|
||||||
| u8
|
| u8
|
||||||
| Null
|
| Null
|
||||||
|
|
||||||
// Decodable is an interface, that allows custom implementations for decoding structs from JSON encoded values.
|
|
||||||
pub interface Decodable {
|
|
||||||
from_json(f Any)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Encodable is an interface, that allows custom implementations for encoding structs to their string based JSON representations.
|
// Encodable is an interface, that allows custom implementations for encoding structs to their string based JSON representations.
|
||||||
pub interface Encodable {
|
pub interface Encodable {
|
||||||
json_str() string
|
json_str() string
|
||||||
|
@ -45,21 +40,25 @@ pub const null = Null{}
|
||||||
pub fn (mut n Null) from_json_null() {}
|
pub fn (mut n Null) from_json_null() {}
|
||||||
|
|
||||||
// ValueKind enumerates the kinds of possible values of the Any sumtype.
|
// ValueKind enumerates the kinds of possible values of the Any sumtype.
|
||||||
pub enum ValueKind {
|
enum ValueKind {
|
||||||
unknown
|
unknown
|
||||||
array
|
array
|
||||||
object
|
object
|
||||||
string_
|
string_
|
||||||
number
|
number
|
||||||
|
boolean
|
||||||
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
// str returns the string representation of the specific ValueKind.
|
// str returns the string representation of the specific ValueKind.
|
||||||
pub fn (k ValueKind) str() string {
|
fn (k ValueKind) str() string {
|
||||||
return match k {
|
return match k {
|
||||||
.unknown { 'unknown' }
|
.unknown { 'unknown' }
|
||||||
.array { 'array' }
|
.array { 'array' }
|
||||||
.object { 'object' }
|
.object { 'object' }
|
||||||
.string_ { 'string' }
|
.string_ { 'string' }
|
||||||
.number { 'number' }
|
.number { 'number' }
|
||||||
|
.boolean { 'boolean' }
|
||||||
|
.null { 'null' }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue