v/vlib/x/json2/decoder2/decode.v
2024-10-01 10:15:51 +03:00

420 lines
13 KiB
V

module decoder2
import time
// Node represents a node in a JSON decoder tree.
struct Node {
key_pos int // The position of the key in the JSON string.
key_len int // The length of the key in the JSON string.
children ?[]Node // The children nodes of the current node.
}
// Decoder represents a JSON decoder.
struct Decoder {
json string // json is the JSON data to be decoded.
mut:
idx int // idx is the current index of the decoder.
}
pub enum ValueKind {
unknown
array
object
string_
number
boolean
}
// check_json checks if the JSON string is valid.
fn check_json(val string) ! {
if val == '' {
return error('empty string')
}
}
// decode decodes a JSON string into a specified type.
pub fn decode[T](val string) !T {
check_json(val)!
mut nodes := []Node{}
mut decoder := Decoder{
json: val
}
// TODO: needs performance improvements
decoder.fulfill_nodes(mut nodes)
mut result := T{}
decoder.decode_value(nodes, &result)
return result
}
// decode_value decodes a value from the JSON nodes.
fn (mut decoder Decoder) decode_value[T](nodes []Node, val &T) {
$if val is $option {
} $else $if T is string {
} $else $if T is $sumtype {
$for v in val.variants {
if val is v {
decoder.decode_value(nodes, val)
}
}
} $else $if T is $alias {
} $else $if T is time.Time {
} $else $if T is $map {
} $else $if T is $array {
} $else $if T is $struct {
decoder.decode_struct(nodes, val)
} $else $if T is $enum {
} $else $if T is $int {
} $else $if T is $float {
} $else $if T is bool {
} $else {
return error('cannot encode value with ${typeof(val).name} type')
}
}
// get_value_kind returns the kind of a JSON value.
fn get_value_kind(value rune) ValueKind {
return match value {
`"` { .string_ }
`t`, `f` { .boolean }
`{` { .object }
`[` { .array }
`0`...`9` { .number }
else { .unknown }
}
}
// decode_optional_value_in_actual_node decodes an optional value in a node.
fn (mut decoder Decoder) decode_optional_value_in_actual_node[T](node Node, val ?T) T {
start := (node.key_pos + node.key_len) + 3
mut end := start
for decoder.json[end] != `,` && decoder.json[end] != `}` {
end++
}
mut value_kind := get_value_kind(decoder.json[start])
$if T is string {
if value_kind == .string_ {
return decoder.json[start + 1..end - 1]
} else if value_kind == .object {
} else if value_kind == .array {
} else {
return decoder.json[start..end]
}
return ''
} $else $if T is $int {
if value_kind == .string_ {
return decoder.json[start + 1..end - 1].int()
} else if value_kind == .object {
} else if value_kind == .array {
} else {
return decoder.json[start..end].int()
}
}
return T{}
}
// decode_struct decodes a struct from the JSON nodes.
fn (mut decoder Decoder) decode_struct[T](nodes []Node, value &T) {
$for field in T.fields {
for i := 0; i < nodes.len; i++ {
mut node := nodes[i]
if node.key_len == field.name.len {
// This `vmemcmp` compares the name of a key in a JSON with a given struct field.
if unsafe {
vmemcmp(decoder.json.str + node.key_pos, field.name.str, field.name.len) == 0
} {
start := (node.key_pos + node.key_len) + 3
mut end := start
for decoder.json[end] != `,` && decoder.json[end] != `}` {
end++
}
value_kind := get_value_kind(decoder.json[start])
$if field.indirections != 0 {
// REVIEW Needs clone?
$if field.indirections == 1 {
// TODO
// unsafe {
// value.$(field.name) = &(decoder.json[start + 1..end - 1])
// }
} $else $if field.indirections == 2 {
// TODO
// unsafe {
// value.$(field.name) = &&(decoder.json[start + 1..end - 1])
// }
} $else $if field.indirections == 3 {
// TODO
// unsafe {
// value.$(field.name) = &&&(decoder.json[start + 1..end - 1])
// }
}
} $else $if field.typ is $option {
value.$(field.name) = decoder.decode_optional_value_in_actual_node(node,
value.$(field.name))
} $else $if field.typ is $sumtype {
// dump(value.$(field.name))
workaround := value.$(field.name)
// z := value.$(field.name)
$for v in workaround.variants {
$if v.typ is string {
if value_kind == .string_ {
// value.$(field.name) = decoder.json[start + 1..end - 1]
} else {
// value.$(field.name) = decoder.json[start..end]
}
} $else $if v.typ in [$int, $float] {
$if v.typ is u32 {
value.$(field.name) = decoder.json[start..end].u32()
} $else $if v.typ is u32 {
}
$if v.typ is i8 {
value.$(field.name) = decoder.json[start..end].i8()
} $else $if v.typ is i16 {
value.$(field.name) = decoder.json[start..end].i16()
} $else $if v.typ is i32 {
value.$(field.name) = decoder.json[start..end].i32()
} $else $if v.typ is int {
value.$(field.name) = decoder.json[start..end].int()
} $else $if v.typ is i64 {
value.$(field.name) = decoder.json[start..end].i64()
} $else $if v.typ is u8 {
value.$(field.name) = decoder.json[start..end].u8()
} $else $if v.typ is u16 {
value.$(field.name) = decoder.json[start..end].u16()
} $else $if v.typ is u32 {
value.$(field.name) = decoder.json[start..end].u32()
} $else $if v.typ is u64 {
value.$(field.name) = decoder.json[start..end].u64()
} $else $if v.typ is f32 {
value.$(field.name) = decoder.json[start..end].f32()
} $else $if v.typ is f64 {
value.$(field.name) = decoder.json[start..end].f64()
}
} $else $if v.typ is bool {
if decoder.json[start] == `t` {
value.$(field.name) = true
} else if decoder.json[start] == `f` {
value.$(field.name) = false
}
} $else $if v.typ is time.Time {
if value_kind == .string_ {
value.$(field.name) = time.parse(decoder.json[start + 1..end - 1]) or {
time.Time{}
}
}
} $else $if v.typ is $struct {
if node.children != none {
// FIXME
// decoder.decode_value(node.children or {
// panic('It will never happens')
// }, value.$(field.name))
}
} $else $if v.typ is $array {
if value_kind == .array {
// TODO
}
} $else $if v.typ is $map {
if value_kind == .object {
// TODO
}
} $else $if T is $enum {
} $else {
eprintln('not supported')
}
}
if value_kind == .string_ {
// value.$(field.name) = decoder.json[start + 1..end - 1]
} else if decoder.json[start] == `t` {
value.$(field.name) = true
} else if decoder.json[start] == `f` {
value.$(field.name) = false
} else if value_kind == .object {
} else if value_kind == .array {
} else if value_kind == .number {
// value.$(field.name) = decoder.json[start..end].int()
} else {
}
} $else $if field.typ is string {
value.$(field.name) = if value_kind == .string_ {
decoder.json[start + 1..end - 1]
} else {
decoder.json[start..end]
}
} $else $if field.typ in [$int, $float] {
$if field.typ is i8 {
value.$(field.name) = decoder.json[start..end].i8()
} $else $if field.typ is i16 {
value.$(field.name) = decoder.json[start..end].i16()
} $else $if field.typ is i32 {
value.$(field.name) = decoder.json[start..end].i32()
} $else $if field.typ is int {
value.$(field.name) = decoder.json[start..end].int()
} $else $if field.typ is i64 {
value.$(field.name) = decoder.json[start..end].i64()
} $else $if field.typ is u8 {
value.$(field.name) = decoder.json[start..end].u8()
} $else $if field.typ is u16 {
value.$(field.name) = decoder.json[start..end].u16()
} $else $if field.typ is u32 {
value.$(field.name) = decoder.json[start..end].u32()
} $else $if field.typ is u64 {
value.$(field.name) = decoder.json[start..end].u64()
} $else $if field.typ is f32 {
value.$(field.name) = decoder.json[start..end].f32()
} $else $if field.typ is f64 {
value.$(field.name) = decoder.json[start..end].f64()
}
} $else $if field.typ is bool {
value.$(field.name) = decoder.json[start] == `t`
} $else $if field.typ is time.Time {
if value_kind == .string_ {
value.$(field.name) = time.parse_rfc3339(decoder.json[start + 1..end - 1]) or {
time.Time{}
}
}
} $else $if field.typ is $struct {
if node.children != none {
decoder.decode_value(node.children or { panic('It will never happen') },
value.$(field.name))
}
} $else $if field.typ is $array {
if value_kind == .array {
// TODO
}
} $else $if field.typ is $map {
if value_kind == .object && node.children != none {
decoder.decode_map(node.children or { panic('It will never happen') }, mut
value.$(field.name))
}
} $else $if field.typ is $enum {
value.$(field.name) = decoder.json[start..end].int()
} $else $if field.typ is $alias {
$if field.unaliased_typ is string {
if value_kind == .string_ {
value.$(field.name) = decoder.json[start + 1..end - 1]
}
} $else $if field.unaliased_typ is time.Time {
} $else $if field.unaliased_typ is bool {
} $else $if field.unaliased_typ in [$float, $int] {
$if field.unaliased_typ is i8 {
value.$(field.name) = decoder.json[start..end].i8()
} $else $if field.unaliased_typ is i16 {
value.$(field.name) = decoder.json[start..end].i16()
} $else $if field.unaliased_typ is i32 {
value.$(field.name) = decoder.json[start..end].i32()
} $else $if field.unaliased_typ is int {
value.$(field.name) = decoder.json[start..end].int()
} $else $if field.unaliased_typ is i64 {
value.$(field.name) = decoder.json[start..end].i64()
} $else $if field.unaliased_typ is u8 {
value.$(field.name) = decoder.json[start..end].u8()
} $else $if field.unaliased_typ is u16 {
value.$(field.name) = decoder.json[start..end].u16()
} $else $if field.unaliased_typ is u32 {
value.$(field.name) = decoder.json[start..end].u32()
} $else $if field.unaliased_typ is u64 {
value.$(field.name) = decoder.json[start..end].u64()
} $else $if field.unaliased_typ is f32 {
value.$(field.name) = decoder.json[start..end].f32()
} $else $if field.unaliased_typ is f64 {
value.$(field.name) = decoder.json[start..end].f64()
}
} $else $if field.unaliased_typ is $array {
// TODO
} $else $if field.unaliased_typ is $struct {
} $else $if field.unaliased_typ is $enum {
// TODO
} $else $if field.unaliased_typ is $sumtype {
// TODO
} $else {
eprintln('the alias ${field.unaliased_typ} cannot be encoded')
}
} $else {
eprintln('not supported')
}
break
}
}
}
}
}
// decode_map decodes a map from the JSON nodes.
fn (mut decoder Decoder) decode_map[T](nodes []Node, mut val T) {
for i := 0; i < nodes.len; i++ {
mut node := nodes[i]
start := (node.key_pos + node.key_len) + 3
mut end := start
for decoder.json[end] != `,` && decoder.json[end] != `}` {
end++
}
value_kind := get_value_kind(decoder.json[start])
val[decoder.json[node.key_pos..node.key_pos + node.key_len]] = if value_kind == .string_ {
decoder.json[start + 1..end - 1]
} else {
decoder.json[start..end]
}
}
}
// fulfill_nodes fills the nodes from the JSON string.
fn (mut decoder Decoder) fulfill_nodes(mut nodes []Node) {
mut inside_string := false
mut inside_key := false
mut actual_key_len := 0
for decoder.idx < decoder.json.len {
letter := decoder.json[decoder.idx]
match letter {
` ` {
if !inside_string {
}
}
`\"` {
if decoder.json[decoder.idx - 1] == `{` || decoder.json[decoder.idx - 2] == `,` {
inside_key = true
} else if decoder.json[decoder.idx + 1] == `:` {
if decoder.json[decoder.idx + 3] == `{` {
mut children := []Node{}
key_pos := decoder.idx - actual_key_len
key_len := actual_key_len
decoder.idx += 3
decoder.fulfill_nodes(mut children)
nodes << Node{
key_pos: key_pos
key_len: key_len
children: children
}
} else {
nodes << Node{
key_pos: decoder.idx - actual_key_len
key_len: actual_key_len
}
}
inside_key = false
}
inside_string = !inside_string
decoder.idx++
continue
}
`:` {
actual_key_len = 0
}
`,`, `{`, `}`, `[`, `]` {}
else {}
}
if inside_key {
actual_key_len++
}
decoder.idx++
}
}