917 lines
23 KiB
V
917 lines
23 KiB
V
module json2
|
|
|
|
import time
|
|
|
|
// EncoderOptions provides a list of options for encoding
|
|
@[params]
|
|
pub struct EncoderOptions {
|
|
pub:
|
|
prettify bool
|
|
indent_string string = ' '
|
|
newline_string string = '\n'
|
|
|
|
enum_as_int bool
|
|
|
|
escape_unicode bool
|
|
}
|
|
|
|
struct Encoder {
|
|
EncoderOptions
|
|
mut:
|
|
level int
|
|
prefix string
|
|
|
|
output []u8 = []u8{cap: 2048}
|
|
}
|
|
|
|
// encode is a generic function that encodes a type into a JSON string.
|
|
pub fn encode[T](val T, config EncoderOptions) string {
|
|
mut encoder := Encoder{
|
|
EncoderOptions: config
|
|
}
|
|
|
|
encoder.encode_value[T](val)
|
|
|
|
return encoder.output.bytestr()
|
|
}
|
|
|
|
fn (mut encoder Encoder) encode_value[T](val T) {
|
|
$if T is $interface {
|
|
encoder.encode_null()
|
|
} $else $if T.unaliased_typ is voidptr {
|
|
encoder.encode_null()
|
|
} $else $if T.unaliased_typ is string {
|
|
encoder.encode_string(string(val))
|
|
} $else $if T.unaliased_typ is bool {
|
|
encoder.encode_boolean(bool(val))
|
|
} $else $if T.unaliased_typ is u8 {
|
|
encoder.encode_number(u8(val))
|
|
} $else $if T.unaliased_typ is u16 {
|
|
encoder.encode_number(u16(val))
|
|
} $else $if T.unaliased_typ is u32 {
|
|
encoder.encode_number(u32(val))
|
|
} $else $if T.unaliased_typ is u64 {
|
|
encoder.encode_number(u64(val))
|
|
} $else $if T.unaliased_typ is i8 {
|
|
encoder.encode_number(i8(val))
|
|
} $else $if T.unaliased_typ is i16 {
|
|
encoder.encode_number(i16(val))
|
|
} $else $if T.unaliased_typ is int || T.unaliased_typ is i32 {
|
|
encoder.encode_number(i32(val))
|
|
} $else $if T.unaliased_typ is i64 {
|
|
encoder.encode_number(i64(val))
|
|
} $else $if T.unaliased_typ is usize {
|
|
encoder.encode_number(usize(val))
|
|
} $else $if T.unaliased_typ is isize {
|
|
encoder.encode_number(isize(val))
|
|
} $else $if T.unaliased_typ is f32 {
|
|
encoder.encode_number(f32(val))
|
|
} $else $if T.unaliased_typ is f64 {
|
|
encoder.encode_number(f64(val))
|
|
} $else $if T.unaliased_typ is voidptr {
|
|
encoder.encode_number(0)
|
|
} $else $if T is $pointer {
|
|
if voidptr(val) == unsafe { nil } {
|
|
encoder.encode_null()
|
|
} else {
|
|
encoder.encode_value(*val)
|
|
}
|
|
} $else $if T.unaliased_typ is $array_fixed {
|
|
encoder.output << `[`
|
|
for i in 0 .. val.len {
|
|
encoder.encode_value(val[i])
|
|
if i < val.len - 1 {
|
|
encoder.output << `,`
|
|
}
|
|
}
|
|
encoder.output << `]`
|
|
} $else $if T.unaliased_typ is $array {
|
|
encoder.encode_array(val)
|
|
} $else $if T.unaliased_typ is $map {
|
|
encoder.output << `{`
|
|
if encoder.prettify {
|
|
encoder.increment_level()
|
|
encoder.add_indent()
|
|
}
|
|
mut mi := 0
|
|
for key, value in val {
|
|
encoder.encode_string('${key}')
|
|
encoder.output << `:`
|
|
if encoder.prettify {
|
|
encoder.output << ` `
|
|
}
|
|
encoder.encode_value(value)
|
|
if mi < val.len - 1 {
|
|
encoder.output << `,`
|
|
if encoder.prettify {
|
|
encoder.add_indent()
|
|
}
|
|
} else {
|
|
if encoder.prettify {
|
|
encoder.decrement_level()
|
|
encoder.add_indent()
|
|
}
|
|
}
|
|
mi++
|
|
}
|
|
encoder.output << `}`
|
|
} $else $if T.unaliased_typ is $enum {
|
|
if encoder.enum_as_int || enum_uses_json_as_number[T]() {
|
|
encoder.encode_number(int(val))
|
|
} else {
|
|
mut enum_val := 'unknown enum value'
|
|
$for member in T.values {
|
|
if member.value == val {
|
|
enum_val = member.name
|
|
for attr in member.attrs {
|
|
if json_attr := json_attr_value(attr) {
|
|
enum_val = json_attr
|
|
}
|
|
}
|
|
}
|
|
}
|
|
encoder.output << `"`
|
|
unsafe { encoder.output.push_many(enum_val.str, enum_val.len) }
|
|
encoder.output << `"`
|
|
}
|
|
} $else $if T.unaliased_typ is $sumtype {
|
|
encoder.encode_sumtype[T](val)
|
|
} $else $if T is JsonEncoder { // uses T, because alias could be implementing JsonEncoder, while the base type does not
|
|
integer_val := val.to_json()
|
|
unsafe { encoder.output.push_many(integer_val.str, integer_val.len) }
|
|
} $else $if T is Encodable { // uses T, because alias could be implementing JsonEncoder, while the base type does not
|
|
integer_val := val.json_str()
|
|
unsafe { encoder.output.push_many(integer_val.str, integer_val.len) }
|
|
} $else $if T.unaliased_typ is $struct {
|
|
unsafe {
|
|
$for field in T.fields {
|
|
$if field.is_embed {
|
|
encoder.encode_struct_with_embeds(val)
|
|
return
|
|
}
|
|
}
|
|
encoder.output << `{`
|
|
is_first := encoder.encode_struct_fields[T](val, true, [], '')
|
|
if encoder.prettify && !is_first {
|
|
encoder.decrement_level()
|
|
encoder.add_indent()
|
|
}
|
|
encoder.output << `}`
|
|
}
|
|
}
|
|
}
|
|
|
|
fn (mut encoder Encoder) encode_string(val string) {
|
|
encoder.output << `"`
|
|
mut buffer_start := 0
|
|
mut buffer_end := 0
|
|
for buffer_end < val.len {
|
|
character := val[buffer_end]
|
|
match character {
|
|
`"`, `\\` {
|
|
unsafe { encoder.output.push_many(val.str + buffer_start, buffer_end - buffer_start) }
|
|
buffer_end++
|
|
buffer_start = buffer_end
|
|
|
|
encoder.output << `\\`
|
|
encoder.output << character
|
|
}
|
|
`\b` {
|
|
unsafe { encoder.output.push_many(val.str + buffer_start, buffer_end - buffer_start) }
|
|
buffer_end++
|
|
buffer_start = buffer_end
|
|
|
|
encoder.output << `\\`
|
|
encoder.output << `b`
|
|
}
|
|
`\n` {
|
|
unsafe { encoder.output.push_many(val.str + buffer_start, buffer_end - buffer_start) }
|
|
buffer_end++
|
|
buffer_start = buffer_end
|
|
|
|
encoder.output << `\\`
|
|
encoder.output << `n`
|
|
}
|
|
`\f` {
|
|
unsafe { encoder.output.push_many(val.str + buffer_start, buffer_end - buffer_start) }
|
|
buffer_end++
|
|
buffer_start = buffer_end
|
|
|
|
encoder.output << `\\`
|
|
encoder.output << `f`
|
|
}
|
|
`\t` {
|
|
unsafe { encoder.output.push_many(val.str + buffer_start, buffer_end - buffer_start) }
|
|
buffer_end++
|
|
buffer_start = buffer_end
|
|
|
|
encoder.output << `\\`
|
|
encoder.output << `t`
|
|
}
|
|
`\r` {
|
|
unsafe { encoder.output.push_many(val.str + buffer_start, buffer_end - buffer_start) }
|
|
buffer_end++
|
|
buffer_start = buffer_end
|
|
|
|
encoder.output << `\\`
|
|
encoder.output << `r`
|
|
}
|
|
else {
|
|
if character < 0x20 { // control characters
|
|
unsafe {
|
|
encoder.output.push_many(val.str + buffer_start, buffer_end - buffer_start)
|
|
}
|
|
buffer_end++
|
|
buffer_start = buffer_end
|
|
|
|
encoder.output << `\\`
|
|
encoder.output << `u`
|
|
|
|
hex_string := '${character:04x}'
|
|
|
|
unsafe { encoder.output.push_many(hex_string.str, 4) }
|
|
|
|
continue
|
|
}
|
|
if encoder.escape_unicode {
|
|
if character >= 0b1111_0000 { // four bytes
|
|
unsafe {
|
|
encoder.output.push_many(val.str + buffer_start,
|
|
buffer_end - buffer_start)
|
|
}
|
|
unicode_point_low := val[buffer_end..buffer_end + 4].bytes().byterune() or {
|
|
0
|
|
} - 0x10000
|
|
|
|
hex_string := '\\u${0xD800 + ((unicode_point_low >> 10) & 0x3FF):04X}\\u${
|
|
0xDC00 + (unicode_point_low & 0x3FF):04x}'
|
|
|
|
buffer_end += 4
|
|
buffer_start = buffer_end
|
|
|
|
unsafe { encoder.output.push_many(hex_string.str, 12) }
|
|
|
|
continue
|
|
} else if character >= 0b1110_0000 { // three bytes
|
|
unsafe {
|
|
encoder.output.push_many(val.str + buffer_start,
|
|
buffer_end - buffer_start)
|
|
}
|
|
hex_string := '\\u${val[buffer_end..buffer_end + 3].bytes().byterune() or {
|
|
0
|
|
}:04x}'
|
|
|
|
buffer_end += 3
|
|
buffer_start = buffer_end
|
|
|
|
unsafe { encoder.output.push_many(hex_string.str, 6) }
|
|
|
|
continue
|
|
} else if character >= 0b1100_0000 { // two bytes
|
|
unsafe {
|
|
encoder.output.push_many(val.str + buffer_start,
|
|
buffer_end - buffer_start)
|
|
}
|
|
hex_string := '\\u${val[buffer_end..buffer_end + 2].bytes().byterune() or {
|
|
0
|
|
}:04x}'
|
|
|
|
buffer_end += 2
|
|
buffer_start = buffer_end
|
|
|
|
unsafe { encoder.output.push_many(hex_string.str, 6) }
|
|
|
|
continue
|
|
}
|
|
}
|
|
|
|
buffer_end++
|
|
}
|
|
}
|
|
}
|
|
unsafe { encoder.output.push_many(val.str + buffer_start, buffer_end - buffer_start) }
|
|
|
|
encoder.output << `"`
|
|
}
|
|
|
|
fn (mut encoder Encoder) encode_boolean(val bool) {
|
|
if val {
|
|
unsafe { encoder.output.push_many(true_string.str, true_string.len) }
|
|
} else {
|
|
unsafe { encoder.output.push_many(false_string.str, false_string.len) }
|
|
}
|
|
}
|
|
|
|
fn (mut encoder Encoder) encode_number[T](val T) {
|
|
mut integer_val := ''
|
|
$if T is u8 {
|
|
integer_val = u8(val).str()
|
|
} $else $if T is u16 {
|
|
integer_val = u16(val).str()
|
|
} $else $if T is u32 {
|
|
integer_val = u32(val).str()
|
|
} $else $if T is u64 {
|
|
integer_val = u64(val).str()
|
|
} $else $if T is i8 {
|
|
integer_val = i8(val).str()
|
|
} $else $if T is i16 {
|
|
integer_val = i16(val).str()
|
|
} $else $if T is int || T is i32 {
|
|
integer_val = i32(val).str()
|
|
} $else $if T is i64 {
|
|
integer_val = i64(val).str()
|
|
} $else $if T is usize {
|
|
integer_val = usize(val).str()
|
|
} $else $if T is isize {
|
|
integer_val = isize(val).str()
|
|
} $else $if T is f32 {
|
|
integer_val = f32(val).str()
|
|
} $else $if T is f64 {
|
|
integer_val = f64(val).str()
|
|
}
|
|
$if T is $float {
|
|
if integer_val.len > 2 && integer_val[integer_val.len - 2] == `.`
|
|
&& integer_val[integer_val.len - 1] == `0` { // ends in .0
|
|
// `2.0` = > `2`
|
|
// but skip float in scientific notation, `1e+10`
|
|
unsafe {
|
|
integer_val.len -= 2
|
|
}
|
|
}
|
|
}
|
|
unsafe { encoder.output.push_many(integer_val.str, integer_val.len) }
|
|
}
|
|
|
|
@[markused]
|
|
fn (mut encoder Encoder) encode_null() {
|
|
unsafe { encoder.output.push_many(null_string.str, null_string.len) }
|
|
}
|
|
|
|
fn (mut encoder Encoder) encode_array[T](val T) {
|
|
encoder.output << `[`
|
|
if encoder.prettify {
|
|
encoder.increment_level()
|
|
encoder.add_indent()
|
|
}
|
|
|
|
for i, item in val {
|
|
$if T is $pointer {
|
|
if voidptr(item) == unsafe { nil } {
|
|
encoder.encode_null()
|
|
} else {
|
|
unsafe { encoder.encode_pointer_array_item(item) }
|
|
}
|
|
} $else {
|
|
encoder.encode_value(item)
|
|
}
|
|
if i < val.len - 1 {
|
|
encoder.output << `,`
|
|
if encoder.prettify {
|
|
encoder.add_indent()
|
|
}
|
|
} else {
|
|
if encoder.prettify {
|
|
encoder.decrement_level()
|
|
encoder.add_indent()
|
|
}
|
|
}
|
|
}
|
|
|
|
encoder.output << `]`
|
|
}
|
|
|
|
@[unsafe]
|
|
fn (mut encoder Encoder) encode_pointer_array_item[T](item T) {
|
|
encoder.encode_value(*item)
|
|
}
|
|
|
|
fn (mut encoder Encoder) encode_map[K, T](val map[K]T) {
|
|
encoder.output << `{`
|
|
if encoder.prettify {
|
|
encoder.increment_level()
|
|
encoder.add_indent()
|
|
}
|
|
|
|
mut i := 0
|
|
for key, value in val {
|
|
encoder.encode_string('${key}')
|
|
encoder.output << `:`
|
|
if encoder.prettify {
|
|
encoder.output << ` `
|
|
}
|
|
encoder.encode_value[T](value)
|
|
if i < val.len - 1 {
|
|
encoder.output << `,`
|
|
if encoder.prettify {
|
|
encoder.add_indent()
|
|
}
|
|
} else {
|
|
if encoder.prettify {
|
|
encoder.decrement_level()
|
|
encoder.add_indent()
|
|
}
|
|
}
|
|
|
|
i++
|
|
}
|
|
|
|
encoder.output << `}`
|
|
}
|
|
|
|
fn (mut encoder Encoder) encode_enum[T](val T) {
|
|
if encoder.enum_as_int || enum_uses_json_as_number[T]() {
|
|
encoder.encode_number(int(val))
|
|
} else {
|
|
mut enum_val := 'unknown enum value'
|
|
$for member in T.values {
|
|
if member.value == val {
|
|
enum_val = member.name
|
|
for attr in member.attrs {
|
|
if json_attr := json_attr_value(attr) {
|
|
enum_val = json_attr
|
|
}
|
|
}
|
|
}
|
|
}
|
|
encoder.output << `"`
|
|
unsafe { encoder.output.push_many(enum_val.str, enum_val.len) }
|
|
encoder.output << `"`
|
|
}
|
|
}
|
|
|
|
fn (mut encoder Encoder) encode_sumtype[T](val T) {
|
|
$if T is $pointer {
|
|
// Pointer types are handled by encode_value's $pointer branch;
|
|
// this instantiation is generated but never called.
|
|
} $else {
|
|
$for variant in T.variants {
|
|
if val is variant {
|
|
variant_name := sumtype_variant_name(typeof(variant.typ).name)
|
|
$if variant.typ is time.Time {
|
|
if T.name in ['x.json2.Any', 'json2.Any', 'Any'] {
|
|
variant_value := val
|
|
encoder.encode_value(variant_value)
|
|
} else {
|
|
encoder.encode_sumtype_time_variant(val, variant_name)
|
|
}
|
|
} $else $if variant.typ is $struct {
|
|
if T.name in ['x.json2.Any', 'json2.Any', 'Any'] {
|
|
variant_value := val
|
|
encoder.encode_value(variant_value)
|
|
} else {
|
|
encoder.encode_sumtype_struct_variant(val, variant_name)
|
|
}
|
|
} $else $if variant.typ is $map {
|
|
encoder.encode_value(val)
|
|
} $else $if variant.typ is $array_dynamic {
|
|
if T.name in ['x.json2.Any', 'json2.Any', 'Any'] {
|
|
variant_value := val
|
|
encoder.encode_value(variant_value)
|
|
} else {
|
|
variant_value := val
|
|
encoder.encode_array_of_sumtype_variants(variant_value)
|
|
}
|
|
} $else {
|
|
variant_value := val
|
|
encoder.encode_value(variant_value)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn (mut encoder Encoder) encode_object_key(is_first bool, key string) bool {
|
|
if is_first {
|
|
if encoder.prettify {
|
|
encoder.increment_level()
|
|
}
|
|
} else {
|
|
encoder.output << `,`
|
|
}
|
|
if encoder.prettify {
|
|
encoder.add_indent()
|
|
}
|
|
encoder.encode_string(key)
|
|
encoder.output << `:`
|
|
if encoder.prettify {
|
|
encoder.output << ` `
|
|
}
|
|
return false
|
|
}
|
|
|
|
fn (mut encoder Encoder) encode_sumtype_struct_variant[T](val T, variant_name string) {
|
|
$for field in T.fields {
|
|
$if field.is_embed {
|
|
unsafe { encoder.encode_sumtype_struct_variant_with_embeds(val, variant_name) }
|
|
return
|
|
}
|
|
}
|
|
encoder.output << `{`
|
|
mut is_first := unsafe { encoder.encode_struct_fields[T](val, true, [], '') }
|
|
is_first = encoder.encode_object_key(is_first, '_type')
|
|
encoder.encode_string(variant_name)
|
|
if encoder.prettify && !is_first {
|
|
encoder.decrement_level()
|
|
encoder.add_indent()
|
|
}
|
|
encoder.output << `}`
|
|
}
|
|
|
|
fn (mut encoder Encoder) encode_array_of_sumtype_variants[T](val []T) {
|
|
encoder.output << `[`
|
|
if encoder.prettify {
|
|
encoder.increment_level()
|
|
encoder.add_indent()
|
|
}
|
|
for i, item in val {
|
|
$if T is time.Time {
|
|
encoder.encode_value(item)
|
|
} $else $if T is JsonEncoder {
|
|
encoder.encode_value(item)
|
|
} $else $if T is Encodable {
|
|
encoder.encode_value(item)
|
|
} $else $if T is $struct {
|
|
elem_name := sumtype_variant_name(T.name)
|
|
encoder.encode_sumtype_struct_variant(item, elem_name)
|
|
} $else {
|
|
encoder.encode_value(item)
|
|
}
|
|
if i < val.len - 1 {
|
|
encoder.output << `,`
|
|
if encoder.prettify {
|
|
encoder.add_indent()
|
|
}
|
|
} else {
|
|
if encoder.prettify {
|
|
encoder.decrement_level()
|
|
encoder.add_indent()
|
|
}
|
|
}
|
|
}
|
|
encoder.output << `]`
|
|
}
|
|
|
|
@[markused]
|
|
fn (mut encoder Encoder) encode_sumtype_time_variant(val time.Time, variant_name string) {
|
|
encoder.output << `{`
|
|
mut is_first := true
|
|
is_first = encoder.encode_object_key(is_first, '_type')
|
|
encoder.encode_string(variant_name)
|
|
is_first = encoder.encode_object_key(is_first, 'value')
|
|
encoder.encode_number(val.unix())
|
|
if encoder.prettify && !is_first {
|
|
encoder.decrement_level()
|
|
encoder.add_indent()
|
|
}
|
|
encoder.output << `}`
|
|
}
|
|
|
|
struct EncoderFieldInfo {
|
|
key_name string
|
|
|
|
is_skip bool
|
|
is_omitempty bool
|
|
is_required bool
|
|
is_json_null bool
|
|
}
|
|
|
|
fn get_value_from_optional[T](val ?T) T {
|
|
return val or { T{} }
|
|
}
|
|
|
|
fn check_not_empty[T](val T) ?bool {
|
|
$if val is ?string {
|
|
opt := ?string(val)
|
|
if sval := opt {
|
|
return sval != ''
|
|
}
|
|
return false
|
|
} $else $if val is ?int {
|
|
opt := ?int(val)
|
|
if ival := opt {
|
|
return ival != 0
|
|
}
|
|
return false
|
|
} $else $if val is ?f64 {
|
|
opt := ?f64(val)
|
|
if fval := opt {
|
|
return fval != 0.0
|
|
}
|
|
return false
|
|
} $else $if val is ?f32 {
|
|
opt := ?f32(val)
|
|
if fval := opt {
|
|
return fval != 0.0
|
|
}
|
|
return false
|
|
} $else $if T is $option {
|
|
return !struct_field_is_none(val)
|
|
} $else $if T.indirections != 0 {
|
|
return val != unsafe { nil }
|
|
} $else $if T.unaliased_typ is string {
|
|
if val == '' {
|
|
return false
|
|
}
|
|
} $else $if T.unaliased_typ is $int || T.unaliased_typ is $float {
|
|
if val == 0 {
|
|
return false
|
|
}
|
|
} $else $if T.unaliased_typ is $array || T.unaliased_typ is $map {
|
|
return val.len != 0
|
|
}
|
|
return true
|
|
}
|
|
|
|
// TODO: fix compilation with -autofree, and remove the tag @[manualfree] here:
|
|
@[manualfree; unsafe]
|
|
fn (mut encoder Encoder) cached_field_infos[T]() []EncoderFieldInfo {
|
|
static field_infos := &[]EncoderFieldInfo(nil)
|
|
if field_infos == nil {
|
|
field_infos = &[]EncoderFieldInfo{}
|
|
$for field in T.fields {
|
|
mut is_skip := false
|
|
mut key_name := ''
|
|
mut is_omitempty := false
|
|
mut is_required := false
|
|
mut is_json_null := false
|
|
for attr in field.attrs {
|
|
match attr {
|
|
'skip' {
|
|
is_skip = true
|
|
break
|
|
}
|
|
'omitempty' {
|
|
is_omitempty = true
|
|
}
|
|
'required' {
|
|
is_required = true
|
|
}
|
|
'json_null' {
|
|
is_json_null = true
|
|
}
|
|
else {}
|
|
}
|
|
|
|
if attr.starts_with('json:') {
|
|
json_attr := json_attr_value(attr) or { continue }
|
|
if json_attr == '-' {
|
|
is_skip = true
|
|
break
|
|
}
|
|
key_name = json_attr
|
|
}
|
|
}
|
|
field_infos << EncoderFieldInfo{
|
|
key_name: if key_name == '' { field.name } else { key_name }
|
|
is_skip: is_skip
|
|
is_omitempty: is_omitempty
|
|
is_required: is_required
|
|
is_json_null: is_json_null
|
|
}
|
|
}
|
|
}
|
|
return *field_infos
|
|
}
|
|
|
|
fn (mut encoder Encoder) encode_struct_field_value[T](val T) {
|
|
$if T is $interface {
|
|
encoder.encode_null()
|
|
} $else $if T.unaliased_typ is voidptr {
|
|
encoder.encode_null()
|
|
} $else $if T.pointee_type is $interface {
|
|
encoder.encode_null()
|
|
} $else $if T is $option {
|
|
if val == none {
|
|
unsafe { encoder.output.push_many(null_string.str, null_string.len) }
|
|
} else {
|
|
encoder.encode_value(get_value_from_optional(val))
|
|
}
|
|
} $else $if T.indirections == 1 {
|
|
encoder.encode_value(*val)
|
|
} $else $if T.indirections == 2 {
|
|
encoder.encode_value(**val)
|
|
} $else $if T.indirections == 3 {
|
|
encoder.encode_value(***val)
|
|
} $else {
|
|
encoder.encode_value(val)
|
|
}
|
|
}
|
|
|
|
fn struct_field_is_none[T](val T) bool {
|
|
$if T is $option {
|
|
return val == none
|
|
}
|
|
return false
|
|
}
|
|
|
|
fn struct_field_is_nil[T](val T) bool {
|
|
$if T.indirections != 0 {
|
|
return val == unsafe { nil }
|
|
}
|
|
return false
|
|
}
|
|
|
|
fn struct_field_should_encode[T](field_info EncoderFieldInfo, val T) bool {
|
|
if field_info.is_skip {
|
|
return false
|
|
}
|
|
if field_info.is_omitempty {
|
|
if !(check_not_empty(val) or { false }) {
|
|
return false
|
|
}
|
|
}
|
|
if !field_info.is_required && !field_info.is_json_null && struct_field_is_none(val) {
|
|
return false
|
|
}
|
|
if struct_field_is_nil(val) {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
@[unsafe]
|
|
fn (mut encoder Encoder) encode_struct_with_embeds[T](val T) {
|
|
encoder.output << `{`
|
|
is_first := encoder.encode_embedded_struct_fields[T](val, true, [], [], '')
|
|
if encoder.prettify && !is_first {
|
|
encoder.decrement_level()
|
|
encoder.add_indent()
|
|
}
|
|
encoder.output << `}`
|
|
}
|
|
|
|
@[unsafe]
|
|
fn (mut encoder Encoder) encode_sumtype_struct_variant_with_embeds[T](val T, variant_name string) {
|
|
encoder.output << `{`
|
|
mut is_first := encoder.encode_embedded_struct_fields[T](val, true, [], [], '')
|
|
is_first = encoder.encode_object_key(is_first, '_type')
|
|
encoder.encode_string(variant_name)
|
|
if encoder.prettify && !is_first {
|
|
encoder.decrement_level()
|
|
encoder.add_indent()
|
|
}
|
|
encoder.output << `}`
|
|
}
|
|
|
|
@[unsafe]
|
|
fn (mut encoder Encoder) encode_struct_fields[T](val T, was_first bool, old_used_keys []string, prefix string) bool {
|
|
field_infos := encoder.cached_field_infos[T]()
|
|
mut is_first := was_first
|
|
mut used_keys := old_used_keys
|
|
mut i := 0
|
|
|
|
$for field in T.fields {
|
|
$if !field.is_embed {
|
|
field_info := field_infos[i]
|
|
mut write_field := true
|
|
|
|
$if field.typ is $shared {
|
|
shared field_value := unsafe { val.$(field.name) }
|
|
rlock field_value {
|
|
write_field = struct_field_should_encode(field_info, field_value)
|
|
|
|
if write_field {
|
|
if field_info.key_name in old_used_keys {
|
|
is_first = encoder.encode_object_key(is_first, prefix +
|
|
field_info.key_name)
|
|
} else {
|
|
is_first = encoder.encode_object_key(is_first, field_info.key_name)
|
|
used_keys << field_info.key_name
|
|
}
|
|
encoder.encode_struct_field_value(field_value)
|
|
}
|
|
}
|
|
} $else {
|
|
write_field = struct_field_should_encode(field_info, val.$(field.name))
|
|
|
|
if write_field {
|
|
if field_info.key_name in old_used_keys {
|
|
is_first = encoder.encode_object_key(is_first, prefix + field_info.key_name)
|
|
} else {
|
|
is_first = encoder.encode_object_key(is_first, field_info.key_name)
|
|
used_keys << field_info.key_name
|
|
}
|
|
encoder.encode_struct_field_value(val.$(field.name))
|
|
}
|
|
}
|
|
}
|
|
i++
|
|
}
|
|
$for field in T.fields {
|
|
$if field.is_embed {
|
|
new_prefix := prefix + field.name + '.'
|
|
$if field.typ is $shared {
|
|
shared field_value := unsafe { val.$(field.name) }
|
|
rlock field_value {
|
|
is_first = encoder.encode_struct_fields(field_value, is_first, used_keys,
|
|
new_prefix)
|
|
}
|
|
} $else {
|
|
is_first = encoder.encode_struct_fields(val.$(field.name), is_first, used_keys,
|
|
new_prefix)
|
|
}
|
|
}
|
|
}
|
|
return is_first
|
|
}
|
|
|
|
@[unsafe]
|
|
fn (mut encoder Encoder) encode_embedded_struct_fields[T](val T, was_first bool, old_used_keys []string, reserved_keys []string, prefix string) bool {
|
|
field_infos := encoder.cached_field_infos[T]()
|
|
mut is_first := was_first
|
|
mut used_keys := old_used_keys.clone()
|
|
mut i := 0
|
|
|
|
$for field in T.fields {
|
|
$if field.is_embed {
|
|
mut child_reserved_keys := reserved_keys.clone()
|
|
mut reserved_i := 0
|
|
$for reserved_field in T.fields {
|
|
reserved_field_info := field_infos[reserved_i]
|
|
$if !reserved_field.is_embed {
|
|
if !reserved_field_info.is_skip {
|
|
child_reserved_keys << reserved_field_info.key_name
|
|
}
|
|
}
|
|
reserved_i++
|
|
}
|
|
new_prefix := prefix + field.name + '.'
|
|
$if field.typ is $shared {
|
|
shared field_value := unsafe { val.$(field.name) }
|
|
rlock field_value {
|
|
is_first = encoder.encode_embedded_struct_fields(field_value, is_first,
|
|
used_keys, child_reserved_keys, new_prefix)
|
|
}
|
|
} $else {
|
|
is_first = encoder.encode_embedded_struct_fields(val.$(field.name), is_first,
|
|
used_keys, child_reserved_keys, new_prefix)
|
|
}
|
|
} $else {
|
|
field_info := field_infos[i]
|
|
mut write_field := true
|
|
$if field.typ is $shared {
|
|
shared field_value := unsafe { val.$(field.name) }
|
|
rlock field_value {
|
|
write_field = struct_field_should_encode(field_info, field_value)
|
|
if write_field {
|
|
should_prefix := field_info.key_name in used_keys
|
|
|| field_info.key_name in reserved_keys
|
|
json_key := if should_prefix {
|
|
prefix + field_info.key_name
|
|
} else {
|
|
field_info.key_name
|
|
}
|
|
is_first = encoder.encode_object_key(is_first, json_key)
|
|
if !should_prefix {
|
|
used_keys << field_info.key_name
|
|
}
|
|
encoder.encode_struct_field_value(field_value)
|
|
}
|
|
}
|
|
} $else {
|
|
write_field = struct_field_should_encode(field_info, val.$(field.name))
|
|
if write_field {
|
|
should_prefix := field_info.key_name in used_keys
|
|
|| field_info.key_name in reserved_keys
|
|
json_key := if should_prefix {
|
|
prefix + field_info.key_name
|
|
} else {
|
|
field_info.key_name
|
|
}
|
|
is_first = encoder.encode_object_key(is_first, json_key)
|
|
if !should_prefix {
|
|
used_keys << field_info.key_name
|
|
}
|
|
encoder.encode_struct_field_value(val.$(field.name))
|
|
}
|
|
}
|
|
}
|
|
i++
|
|
}
|
|
return is_first
|
|
}
|
|
|
|
fn (mut encoder Encoder) encode_custom[T](val T) {
|
|
integer_val := val.to_json()
|
|
unsafe { encoder.output.push_many(integer_val.str, integer_val.len) }
|
|
}
|
|
|
|
fn (mut encoder Encoder) encode_custom2[T](val T) {
|
|
integer_val := val.json_str()
|
|
unsafe { encoder.output.push_many(integer_val.str, integer_val.len) }
|
|
}
|
|
|
|
fn (mut encoder Encoder) increment_level() {
|
|
encoder.level++
|
|
encoder.prefix = encoder.newline_string + encoder.indent_string.repeat(encoder.level)
|
|
}
|
|
|
|
fn (mut encoder Encoder) decrement_level() {
|
|
encoder.level--
|
|
encoder.prefix = encoder.newline_string + encoder.indent_string.repeat(encoder.level)
|
|
}
|
|
|
|
fn (mut encoder Encoder) add_indent() {
|
|
unsafe { encoder.output.push_many(encoder.prefix.str, encoder.prefix.len) }
|
|
}
|