1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00

json: make enums work with json encode+decode (serialised as string names by default; the old integer one is supported too, using [json_as_number]) (#17696)

This commit is contained in:
Felipe Pena 2023-03-22 05:33:32 -03:00 committed by GitHub
parent c9345be6de
commit 278e747c7a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 340 additions and 34 deletions

View File

@ -0,0 +1,134 @@
import json
[json_as_number]
pub enum MessageType {
error = 1
warning = 2
info = 3
log = 4
}
pub enum MessageType2 {
error = 1
warning = 2
info = 3
log = 4
}
enum TestEnum {
one = 1
two
}
type TestAlias = TestEnum
type TestSum = TestEnum | string
type TestSum2 = MessageType | string
type TestAliasAttr = MessageType
struct TestStruct {
test []TestEnum
test2 TestEnum
test3 TestAlias
test4 TestSum
test5 MessageType
}
struct TestStruct2 {
a TestAliasAttr
b TestSum2
c TestSum2
}
struct Test {
ab ?int
a ?MessageType
}
struct Test2 {
a ?MessageType2
}
type TSum = MessageType | string
type TSum2 = MessageType2 | string
struct Test3 {
a ?TSum
}
struct Test4 {
a ?TSum2
}
fn test_encode_with_enum() {
out := json.encode(TestStruct{
test: [TestEnum.one, TestEnum.one]
test2: TestEnum.two
test3: TestEnum.one
test4: TestEnum.two
test5: .log
})
assert out == '{"test":["one","one"],"test2":"two","test3":"one","test4":"two","test5":4}'
}
fn test_encode_direct_enum() {
assert json.encode(TestEnum.one) == '"one"'
}
fn test_encode_alias_and_sumtype() {
assert json.decode(TestStruct, '{"test":["one","one"],"test2":"two","test3": "one", "test4": "two", "test5":4}')! == TestStruct{
test: [.one, .one]
test2: .two
test3: TestAlias(.one)
test4: TestSum('two')
test5: .log
}
}
fn test_enum_attr() {
assert dump(json.encode(MessageType.log)) == '4'
assert dump(json.encode(MessageType.error)) == '1'
}
fn test_enum_attr_decode() {
assert json.decode(TestStruct2, '{"a": 1, "b":4, "c": "test"}')! == TestStruct2{
a: .error
b: MessageType.log
c: 'test'
}
}
fn test_enum_attr_encode() {
assert json.encode(TestStruct2{
a: .error
b: MessageType.log
c: 'test'
}) == '{"a":1,"b":4,"c":"test"}'
}
fn test_option_enum() {
assert dump(json.encode(Test{none, none})) == '{}'
assert dump(json.encode(Test{none, MessageType.log})) == '{"a":4}'
t := dump(json.decode(Test, '{"a":4}')!)
assert t.ab == none
assert t.a? == .log
t2 := dump(json.decode(Test, '{"a":null}')!)
assert t2.a == none
assert json.encode(Test2{none}) == '{}'
assert dump(json.encode(Test2{MessageType2.log})) == '{"a":"log"}'
z := dump(json.decode(Test2, '{"a":"log"}')!)
assert z.a? == .log
a := dump(json.decode(Test2, '{"a": null}')!)
assert a.a == none
}
fn test_option_sumtype_enum() {
assert dump(json.encode(Test3{none})) == '{}'
assert dump(json.encode(Test3{ a: 'foo' })) == '{"a":"foo"}'
assert dump(json.encode(Test3{ a: MessageType.warning })) == '{"a":2}'
assert dump(json.encode(Test4{none})) == '{}'
assert dump(json.encode(Test4{ a: 'foo' })) == '{"a":"foo"}'
assert dump(json.encode(Test4{ a: MessageType2.warning })) == '{"a":"warning"}'
}

View File

@ -18,7 +18,7 @@ fn test_simple() {
x := Employee{'Peter', 28, 95000.5, .worker} x := Employee{'Peter', 28, 95000.5, .worker}
s := json.encode(x) s := json.encode(x)
// eprintln('Employee x: $s') // eprintln('Employee x: $s')
assert s == '{"name":"Peter","age":28,"salary":95000.5,"title":2}' assert s == '{"name":"Peter","age":28,"salary":95000.5,"title":"worker"}'
y := json.decode(Employee, s)! y := json.decode(Employee, s)!
// eprintln('Employee y: $y') // eprintln('Employee y: $y')
assert y.name == 'Peter' assert y.name == 'Peter'
@ -95,14 +95,15 @@ fn test_encode_decode_sumtype() {
enc := json.encode(game) enc := json.encode(game)
// eprintln('Encoded Game: $enc') // eprintln('Encoded Game: $enc')
assert enc == '{"title":"Super Mega Game","player":{"name":"Monke","_type":"Human"},"other":[{"tag":"Pen","_type":"Item"},{"tag":"Cookie","_type":"Item"},1,"Stool",{"_type":"Time","value":${t.unix_time()}}]}' assert enc == '{"title":"Super Mega Game","player":{"name":"Monke","_type":"Human"},"other":[{"tag":"Pen","_type":"Item"},{"tag":"Cookie","_type":"Item"},"cat","Stool",{"_type":"Time","value":${t.unix_time()}}]}'
dec := json.decode(SomeGame, enc)! dec := json.decode(SomeGame, enc)!
// eprintln('Decoded Game: $dec') // eprintln('Decoded Game: $dec')
assert game.title == dec.title assert game.title == dec.title
assert game.player == dec.player assert game.player == dec.player
assert (game.other[2] as Animal) == (dec.other[2] as Animal) assert (game.other[2] as Animal) == .cat
assert dec.other[2] == Entity('cat')
assert (game.other[4] as time.Time).unix_time() == (dec.other[4] as time.Time).unix_time() assert (game.other[4] as time.Time).unix_time() == (dec.other[4] as time.Time).unix_time()
} }

View File

@ -6144,16 +6144,21 @@ fn (mut g Gen) size_of(node ast.SizeOf) {
g.write('sizeof(${util.no_dots(styp)})') g.write('sizeof(${util.no_dots(styp)})')
} }
fn (mut g Gen) enum_val(node ast.EnumVal) { [inline]
styp := g.typ(g.table.unaliased_type(node.typ)) fn (mut g Gen) gen_enum_prefix(typ ast.Type) string {
if g.pref.translated && node.typ.is_number() { if g.pref.translated && typ.is_number() {
// Mostly in translated code, when C enums are used as ints in switches // Mostly in translated code, when C enums are used as ints in switches
g.write('_const_main__${node.val}') return '_const_main__'
} else { } else {
g.write('${styp}__${node.val}') styp := g.typ(g.table.unaliased_type(typ))
return '${styp}__'
} }
} }
fn (mut g Gen) enum_val(node ast.EnumVal) {
g.write('${g.gen_enum_prefix(node.typ)}${node.val}')
}
fn (mut g Gen) as_cast(node ast.AsCast) { fn (mut g Gen) as_cast(node ast.AsCast) {
// Make sure the sum type can be cast to this type (the types // Make sure the sum type can be cast to this type (the types
// are the same), otherwise panic. // are the same), otherwise panic.

View File

@ -26,7 +26,7 @@ import strings
fn (mut g Gen) gen_json_for_type(typ ast.Type) { fn (mut g Gen) gen_json_for_type(typ ast.Type) {
utyp := g.unwrap_generic(typ) utyp := g.unwrap_generic(typ)
sym := g.table.sym(utyp) sym := g.table.sym(utyp)
if (is_js_prim(sym.name) && !utyp.has_flag(.option)) || sym.kind == .enum_ { if is_js_prim(sym.name) && !utyp.has_flag(.option) {
return return
} }
g.json_types << utyp g.json_types << utyp
@ -145,9 +145,11 @@ ${enc_fn_dec} {
g.gen_json_for_type(parent_typ) g.gen_json_for_type(parent_typ)
continue continue
} }
enc.writeln('\to = cJSON_CreateObject();')
if psym.info is ast.Struct { if psym.info is ast.Struct {
enc.writeln('\to = cJSON_CreateObject();')
g.gen_struct_enc_dec(utyp, psym.info, ret_styp, mut enc, mut dec) g.gen_struct_enc_dec(utyp, psym.info, ret_styp, mut enc, mut dec)
} else if psym.kind == .enum_ {
g.gen_enum_enc_dec(utyp, psym, mut enc, mut dec)
} else if psym.kind == .sum_type { } else if psym.kind == .sum_type {
verror('json: ${sym.name} aliased sumtypes does not work at the moment') verror('json: ${sym.name} aliased sumtypes does not work at the moment')
} else { } else {
@ -160,6 +162,8 @@ ${enc_fn_dec} {
verror('json: ${sym.name} is not a sumtype') verror('json: ${sym.name} is not a sumtype')
} }
g.gen_sumtype_enc_dec(utyp, sym, mut enc, mut dec, ret_styp) g.gen_sumtype_enc_dec(utyp, sym, mut enc, mut dec, ret_styp)
} else if sym.kind == .enum_ {
g.gen_enum_enc_dec(utyp, sym, mut enc, mut dec)
} else if utyp.has_flag(.option) && sym.info !is ast.Struct { } else if utyp.has_flag(.option) && sym.info !is ast.Struct {
g.gen_option_enc_dec(utyp, mut enc, mut dec) g.gen_option_enc_dec(utyp, mut enc, mut dec)
} else { } else {
@ -183,6 +187,73 @@ ${enc_fn_dec} {
} }
} }
[inline]
fn (mut g Gen) gen_enum_to_str(utyp ast.Type, sym ast.TypeSymbol, enum_var string, result_var string, ident string, mut enc strings.Builder) {
enum_prefix := g.gen_enum_prefix(utyp.clear_flag(.option))
enc.writeln('${ident}switch (${enum_var}) {')
for val in (sym.info as ast.Enum).vals {
enc.write_string('${ident}\tcase ${enum_prefix}${val}:\t')
enc.writeln('${result_var} = json__encode_string(_SLIT("${val}")); break;')
}
enc.writeln('${ident}}')
}
[inline]
fn (mut g Gen) gen_str_to_enum(utyp ast.Type, sym ast.TypeSymbol, val_var string, result_var string, ident string, mut dec strings.Builder) {
enum_prefix := g.gen_enum_prefix(utyp.clear_flag(.option))
is_option := utyp.has_flag(.option)
for k, val in (sym.info as ast.Enum).vals {
if k == 0 {
dec.write_string('${ident}if (string__eq(_SLIT("${val}"), ${val_var}))\t')
} else {
dec.write_string('${ident}else if (string__eq(_SLIT("${val}"), ${val_var}))\t')
}
if is_option {
base_typ := g.base_type(utyp)
dec.writeln('_option_ok(&(${base_typ}[]){ ${enum_prefix}${val} }, ${result_var}, sizeof(${base_typ}));')
} else {
dec.writeln('${result_var} = ${enum_prefix}${val};')
}
}
}
[inline]
fn (mut g Gen) is_enum_as_int(sym ast.TypeSymbol) bool {
if enum_decl := g.table.enum_decls[sym.name] {
if _ := enum_decl.attrs.find_first('json_as_number') {
return true
}
}
return false
}
[inline]
fn (mut g Gen) gen_enum_enc_dec(utyp ast.Type, sym ast.TypeSymbol, mut enc strings.Builder, mut dec strings.Builder) {
is_option := utyp.has_flag(.option)
if g.is_enum_as_int(sym) {
if is_option {
base_typ := g.typ(utyp.clear_flag(.option))
enc.writeln('\to = ${js_enc_name('u64')}(*val.data);')
dec.writeln('\t_option_ok(&(${base_typ}[]){ ${js_dec_name('u64')}(root) }, &res, sizeof(${base_typ}));')
} else {
dec.writeln('\tres = ${js_dec_name('u64')}(root);')
enc.writeln('\to = ${js_enc_name('u64')}(val);')
}
} else {
tmp := g.new_tmp_var()
dec.writeln('\tstring ${tmp} = ${js_dec_name('string')}(root);')
if is_option {
g.gen_str_to_enum(utyp, sym, tmp, '&res', '\t', mut dec)
g.gen_enum_to_str(utyp, sym, '*(${g.base_type(utyp)}*)val.data', 'o', '\t\t', mut
enc)
} else {
g.gen_str_to_enum(utyp, sym, tmp, 'res', '\t', mut dec)
g.gen_enum_to_str(utyp, sym, 'val', 'o', '\t', mut enc)
}
}
}
[inline] [inline]
fn (mut g Gen) gen_option_enc_dec(typ ast.Type, mut enc strings.Builder, mut dec strings.Builder) { fn (mut g Gen) gen_option_enc_dec(typ ast.Type, mut enc strings.Builder, mut dec strings.Builder) {
enc.writeln('\tif (val.state == 2) {') enc.writeln('\tif (val.state == 2) {')
@ -191,6 +262,9 @@ fn (mut g Gen) gen_option_enc_dec(typ ast.Type, mut enc strings.Builder, mut dec
type_str := g.typ(typ.clear_flag(.option)) type_str := g.typ(typ.clear_flag(.option))
encode_name := js_enc_name(type_str) encode_name := js_enc_name(type_str)
enc.writeln('\to = ${encode_name}(*(${type_str}*)val.data);') enc.writeln('\to = ${encode_name}(*(${type_str}*)val.data);')
dec_name := js_dec_name(type_str)
dec.writeln('\t_option_ok(&(${type_str}[]){ ${dec_name}(root) }, &res, sizeof(${type_str}));')
} }
[inline] [inline]
@ -200,6 +274,8 @@ fn (mut g Gen) gen_sumtype_enc_dec(utyp ast.Type, sym ast.TypeSymbol, mut enc st
typ := g.table.type_idxs[sym.name] typ := g.table.type_idxs[sym.name]
prefix := if utyp.is_ptr() { '*' } else { '' } prefix := if utyp.is_ptr() { '*' } else { '' }
field_op := if utyp.is_ptr() { '->' } else { '.' } field_op := if utyp.is_ptr() { '->' } else { '.' }
is_option := utyp.has_flag(.option)
var_data := if is_option { '(*(${g.base_type(utyp)}*)val.data)' } else { 'val' }
// DECODING (inline) // DECODING (inline)
$if !json_no_inline_sumtypes ? { $if !json_no_inline_sumtypes ? {
@ -231,20 +307,38 @@ fn (mut g Gen) gen_sumtype_enc_dec(utyp ast.Type, sym ast.TypeSymbol, mut enc st
g.definitions.writeln('static inline ${sym.cname} ${variant_typ}_to_sumtype_${sym.cname}(${variant_typ}* x);') g.definitions.writeln('static inline ${sym.cname} ${variant_typ}_to_sumtype_${sym.cname}(${variant_typ}* x);')
// ENCODING // ENCODING
enc.writeln('\tif (val${field_op}_typ == ${variant.idx()}) {') if is_option {
enc.writeln('\tif (${var_data}${field_op}_typ == ${variant.idx()}) {')
} else {
enc.writeln('\tif (val${field_op}_typ == ${variant.idx()}) {')
}
$if json_no_inline_sumtypes ? { $if json_no_inline_sumtypes ? {
if variant_sym.kind == .enum_ { if variant_sym.kind == .enum_ {
enc.writeln('\t\tcJSON_AddItemToObject(o, "${unmangled_variant_name}", ${js_enc_name('u64')}(*val${field_op}_${variant_typ}));') enc.writeln('\t\tcJSON_AddItemToObject(o, "${unmangled_variant_name}", ${js_enc_name('u64')}(*${var_data}${field_op}_${variant_typ}));')
} else if variant_sym.name == 'time.Time' { } else if variant_sym.name == 'time.Time' {
enc.writeln('\t\tcJSON_AddItemToObject(o, "${unmangled_variant_name}", ${js_enc_name('i64')}(val${field_op}_${variant_typ}->_v_unix));') enc.writeln('\t\tcJSON_AddItemToObject(o, "${unmangled_variant_name}", ${js_enc_name('i64')}(${var_data}${field_op}_${variant_typ}->_v_unix));')
} else { } else {
enc.writeln('\t\tcJSON_AddItemToObject(o, "${unmangled_variant_name}", ${js_enc_name(variant_typ)}(*val${field_op}_${variant_typ}));') enc.writeln('\t\tcJSON_AddItemToObject(o, "${unmangled_variant_name}", ${js_enc_name(variant_typ)}(*${var_data}${field_op}_${variant_typ}));')
} }
} $else { } $else {
if is_js_prim(variant_typ) { if is_js_prim(variant_typ) {
enc.writeln('\t\to = ${js_enc_name(variant_typ)}(*val${field_op}_${variant_typ});') enc.writeln('\t\tcJSON_free(o); return ${js_enc_name(variant_typ)}(*${var_data}${field_op}_${variant_typ});')
} else if variant_sym.kind == .enum_ { } else if variant_sym.kind == .enum_ {
enc.writeln('\t\to = ${js_enc_name('u64')}(*val${field_op}_${variant_typ});') if g.is_enum_as_int(variant_sym) {
enc.writeln('\t\tcJSON_free(o); return ${js_enc_name('u64')}(*${var_data}${field_op}_${variant_typ});')
} else {
enc.writeln('\t\tcJSON_free(o);')
tmp2 := g.new_tmp_var()
if utyp.has_flag(.option) {
enc.writeln('\t\tu64 ${tmp2} = *${var_data}${field_op}_${variant_typ};')
g.gen_enum_to_str(variant, variant_sym, tmp2, 'o', '\t\t', mut
enc)
} else {
enc.writeln('\t\tu64 ${tmp2} = *${var_data}${field_op}_${variant_typ};')
g.gen_enum_to_str(variant, variant_sym, tmp2, 'o', '\t\t', mut
enc)
}
}
} else if variant_sym.name == 'time.Time' { } else if variant_sym.name == 'time.Time' {
enc.writeln('\t\tcJSON_AddItemToObject(o, "_type", cJSON_CreateString("${unmangled_variant_name}"));') enc.writeln('\t\tcJSON_AddItemToObject(o, "_type", cJSON_CreateString("${unmangled_variant_name}"));')
enc.writeln('\t\tcJSON_AddItemToObject(o, "value", ${js_enc_name('i64')}(val${field_op}_${variant_typ}->_v_unix));') enc.writeln('\t\tcJSON_AddItemToObject(o, "value", ${js_enc_name('i64')}(val${field_op}_${variant_typ}->_v_unix));')
@ -260,25 +354,38 @@ fn (mut g Gen) gen_sumtype_enc_dec(utyp ast.Type, sym ast.TypeSymbol, mut enc st
$if json_no_inline_sumtypes ? { $if json_no_inline_sumtypes ? {
dec.writeln('\tif (strcmp("${unmangled_variant_name}", root->child->string) == 0) {') dec.writeln('\tif (strcmp("${unmangled_variant_name}", root->child->string) == 0) {')
if is_js_prim(variant_typ) { if is_js_prim(variant_typ) {
gen_js_get(variant_typ, tmp, unmangled_variant_name, mut dec, true) gen_js_get(ret_styp, tmp, unmangled_variant_name, mut dec, true)
dec.writeln('\t\t${variant_typ} value = ${js_dec_name(variant_typ)}(jsonroot_${tmp});') dec.writeln('\t\t${variant_typ} value = ${js_dec_name(variant_typ)}(jsonroot_${tmp});')
} else if variant_sym.kind == .enum_ { } else if variant_sym.kind == .enum_ {
gen_js_get(variant_typ, tmp, unmangled_variant_name, mut dec, true) if g.is_enum_as_int(variant_sym) {
dec.writeln('\t\t${variant_typ} value = ${js_dec_name('u64')}(jsonroot_${tmp});') gen_js_get(ret_styp, tmp, unmangled_variant_name, mut dec, true)
dec.writeln('\t\t${variant_typ} value = ${js_dec_name('u64')}(jsonroot_${tmp});')
} else {
gen_js_get(ret_styp, tmp, unmangled_variant_name, mut dec, true)
dec.writeln('\t\t${variant_typ} value;')
tmp2 := g.new_tmp_var()
dec.writeln('\t\tstring ${tmp2} = json__decode_string(jsonroot_${tmp});')
g.gen_enum_to_str(variant, variant_sym, tmp2, 'value', '\t\t', mut
dec)
}
} else if variant_sym.name == 'time.Time' { } else if variant_sym.name == 'time.Time' {
gen_js_get(variant_typ, tmp, unmangled_variant_name, mut dec, true) gen_js_get(ret_styp, tmp, unmangled_variant_name, mut dec, true)
dec.writeln('\t\t${variant_typ} value = time__unix(${js_dec_name('i64')}(jsonroot_${tmp}));') dec.writeln('\t\t${variant_typ} value = time__unix(${js_dec_name('i64')}(jsonroot_${tmp}));')
} else { } else {
gen_js_get_opt(js_dec_name(variant_typ), variant_typ, sym.cname, tmp, gen_js_get_opt(js_dec_name(variant_typ), variant_typ, ret_styp, tmp, unmangled_variant_name, mut
unmangled_variant_name, mut dec, true) dec, true)
dec.writeln('\t\t${variant_typ} value = *(${variant_typ}*)(${tmp}.data);') dec.writeln('\t\t${variant_typ} value = *(${variant_typ}*)(${tmp}.data);')
} }
dec.writeln('\t\tres = ${variant_typ}_to_sumtype_${sym.cname}(&value);') if is_option {
dec.writeln('\t\t\t_option_ok(&(${sym.cname}[]){ ${variant_typ}_to_sumtype_${sym.cname}(&value) }, &res, sizeof(${sym.cname}));')
} else {
dec.writeln('\t\tres = ${variant_typ}_to_sumtype_${ret_styp}(&value);')
}
dec.writeln('\t}') dec.writeln('\t}')
} $else { } $else {
if variant_sym.name == 'time.Time' { if variant_sym.name == 'time.Time' {
dec.writeln('\t\t\tif (strcmp("Time", ${type_var}) == 0) {') dec.writeln('\t\t\tif (strcmp("Time", ${type_var}) == 0) {')
gen_js_get(sym.cname, tmp, 'value', mut dec, true) gen_js_get(ret_styp, tmp, 'value', mut dec, true)
dec.writeln('\t\t\t\t${variant_typ} ${tmp} = time__unix(${js_dec_name('i64')}(jsonroot_${tmp}));') dec.writeln('\t\t\t\t${variant_typ} ${tmp} = time__unix(${js_dec_name('i64')}(jsonroot_${tmp}));')
dec.writeln('\t\t\t\t${prefix}res = ${variant_typ}_to_sumtype_${sym.cname}(&${tmp});') dec.writeln('\t\t\t\t${prefix}res = ${variant_typ}_to_sumtype_${sym.cname}(&${tmp});')
dec.writeln('\t\t\t}') dec.writeln('\t\t\t}')
@ -325,7 +432,11 @@ fn (mut g Gen) gen_sumtype_enc_dec(utyp ast.Type, sym ast.TypeSymbol, mut enc st
last_number_type = var_t last_number_type = var_t
dec.writeln('\t\tif (cJSON_IsNumber(root)) {') dec.writeln('\t\tif (cJSON_IsNumber(root)) {')
dec.writeln('\t\t\t${var_t} value = ${js_dec_name('u64')}(root);') dec.writeln('\t\t\t${var_t} value = ${js_dec_name('u64')}(root);')
dec.writeln('\t\t\t${prefix}res = ${var_t}_to_sumtype_${sym.cname}(&value);') if utyp.has_flag(.option) {
dec.writeln('\t\t\t_option_ok(&(${sym.cname}[]){ ${var_t}_to_sumtype_${sym.cname}(&value) }, &${prefix}res, sizeof(${sym.cname}));')
} else {
dec.writeln('\t\t\t${prefix}res = ${var_t}_to_sumtype_${sym.cname}(&value);')
}
dec.writeln('\t\t}') dec.writeln('\t\t}')
} }
@ -337,7 +448,11 @@ fn (mut g Gen) gen_sumtype_enc_dec(utyp ast.Type, sym ast.TypeSymbol, mut enc st
string_is_met = true string_is_met = true
dec.writeln('\t\tif (cJSON_IsString(root)) {') dec.writeln('\t\tif (cJSON_IsString(root)) {')
dec.writeln('\t\t\t${var_t} value = ${js_dec_name(var_t)}(root);') dec.writeln('\t\t\t${var_t} value = ${js_dec_name(var_t)}(root);')
dec.writeln('\t\t\t${prefix}res = ${var_t}_to_sumtype_${sym.cname}(&value);') if utyp.has_flag(.option) {
dec.writeln('\t\t\t_option_ok(&(${sym.cname}[]){ ${var_t}_to_sumtype_${sym.cname}(&value) }, &${prefix}res, sizeof(${sym.cname}));')
} else {
dec.writeln('\t\t\t${prefix}res = ${var_t}_to_sumtype_${sym.cname}(&value);')
}
dec.writeln('\t\t}') dec.writeln('\t\t}')
} }
@ -437,12 +552,36 @@ fn (mut g Gen) gen_struct_enc_dec(utyp ast.Type, type_info ast.TypeInfo, styp st
dec.writeln('\t}') dec.writeln('\t}')
} else if field_sym.kind == .enum_ { } else if field_sym.kind == .enum_ {
tmp := g.new_tmp_var() tmp := g.new_tmp_var()
gen_js_get(styp, tmp, name, mut dec, is_required) is_option_field := field.typ.has_flag(.option)
dec.writeln('\tif (jsonroot_${tmp}) {') if field.typ.has_flag(.option) {
dec.writeln('\t\t${prefix}${op}${c_name(field.name)} = json__decode_u64(jsonroot_${tmp});') gen_js_get_opt(js_dec_name(field_type), field_type, styp, tmp, name, mut
dec, true)
dec.writeln('\tif (jsonroot_${tmp} && !cJSON_IsNull(jsonroot_${tmp})) {')
} else {
gen_js_get(styp, tmp, name, mut dec, is_required)
dec.writeln('\tif (jsonroot_${tmp}) {')
}
if g.is_enum_as_int(field_sym) {
if is_option_field {
base_typ := g.base_type(field.typ)
dec.writeln('\t\t_option_ok(&(${base_typ}[]) { ${js_dec_name('u64')}(jsonroot_${tmp}) }, &${prefix}${op}${c_name(field.name)}, sizeof(${base_typ}));')
} else {
dec.writeln('\t\t${prefix}${op}${c_name(field.name)} = ${js_dec_name('u64')}(jsonroot_${tmp});')
}
} else {
if is_option_field {
base_typ := g.base_type(field.typ)
dec.writeln('\t\t_option_ok(&(${base_typ}[]) { *(${base_typ}*)((${g.typ(field.typ)}*)${tmp}.data)->data }, &${prefix}${op}${c_name(field.name)}, sizeof(${base_typ}));')
} else {
tmp2 := g.new_tmp_var()
dec.writeln('\t\tstring ${tmp2} = json__decode_string(jsonroot_${tmp});')
g.gen_str_to_enum(field.typ, field_sym, tmp2, '${prefix}${op}${c_name(field.name)}',
'\t\t', mut dec)
}
}
if field.has_default_expr { if field.has_default_expr {
dec.writeln('\t} else {') dec.writeln('\t} else {')
dec.writeln('\t\t${prefix}.${c_name(field.name)} = ${g.expr_string(field.default_expr)};') dec.writeln('\t\t${prefix}${op}${c_name(field.name)} = ${g.expr_string(field.default_expr)};')
} }
dec.writeln('\t}') dec.writeln('\t}')
} else if field_sym.name == 'time.Time' { } else if field_sym.name == 'time.Time' {
@ -507,8 +646,9 @@ fn (mut g Gen) gen_struct_enc_dec(utyp ast.Type, type_info ast.TypeInfo, styp st
} else { } else {
'val' 'val'
} }
if field.typ.has_flag(.option) { is_option := field.typ.has_flag(.option)
enc.writeln('\tif (val${op}${c_name(field.name)}.state != 2)') if is_option {
enc.writeln('\tif (val${op}${c_name(field.name)}.state != 2) {')
} }
if is_omit_empty { if is_omit_empty {
enc.writeln('\t if (val${op}${c_name(field.name)} != ${g.type_default(field.typ)})') enc.writeln('\t if (val${op}${c_name(field.name)} != ${g.type_default(field.typ)})')
@ -520,7 +660,29 @@ fn (mut g Gen) gen_struct_enc_dec(utyp ast.Type, type_info ast.TypeInfo, styp st
} }
} }
if field_sym.kind == .enum_ { if field_sym.kind == .enum_ {
enc.writeln('\tcJSON_AddItemToObject(o, "${name}", json__encode_u64(${prefix_enc}${op}${c_name(field.name)}));\n') if g.is_enum_as_int(field_sym) {
if field.typ.has_flag(.option) {
enc.writeln('\tcJSON_AddItemToObject(o, "${name}", json__encode_u64(*${prefix_enc}${op}${c_name(field.name)}.data));\n')
} else {
enc.writeln('\tcJSON_AddItemToObject(o, "${name}", json__encode_u64(${prefix_enc}${op}${c_name(field.name)}));\n')
}
} else {
if field.typ.has_flag(.option) {
enc.writeln('\t{')
enc.writeln('\t\tcJSON *enum_val;')
g.gen_enum_to_str(field.typ, field_sym, '*(${g.base_type(field.typ)}*)${prefix_enc}${op}${c_name(field.name)}.data',
'enum_val', '\t\t', mut enc)
enc.writeln('\t\tcJSON_AddItemToObject(o, "${name}", enum_val);')
enc.writeln('\t}')
} else {
enc.writeln('\t{')
enc.writeln('\t\tcJSON *enum_val;')
g.gen_enum_to_str(field.typ, field_sym, '${prefix_enc}${op}${c_name(field.name)}',
'enum_val', '\t\t', mut enc)
enc.writeln('\t\tcJSON_AddItemToObject(o, "${name}", enum_val);')
enc.writeln('\t}')
}
}
} else { } else {
if field_sym.name == 'time.Time' { if field_sym.name == 'time.Time' {
// time struct requires special treatment // time struct requires special treatment
@ -542,6 +704,10 @@ fn (mut g Gen) gen_struct_enc_dec(utyp ast.Type, type_info ast.TypeInfo, styp st
} }
} }
} }
if is_option {
enc.writeln('\t} // !none')
}
} }
} }
@ -557,7 +723,7 @@ fn gen_js_get(styp string, tmp string, name string, mut dec strings.Builder, is_
fn gen_js_get_opt(dec_name string, field_type string, styp string, tmp string, name string, mut dec strings.Builder, is_required bool) { fn gen_js_get_opt(dec_name string, field_type string, styp string, tmp string, name string, mut dec strings.Builder, is_required bool) {
gen_js_get(styp, tmp, name, mut dec, is_required) gen_js_get(styp, tmp, name, mut dec, is_required)
value_field_type := field_type.replace('*', '_ptr') value_field_type := field_type.replace('*', '_ptr')
dec.writeln('\t${result_name}_${value_field_type.replace('*', '_ptr')} ${tmp};') dec.writeln('\t${result_name}_${value_field_type.replace('*', '_ptr')} ${tmp} = {0};')
dec.writeln('\tif (jsonroot_${tmp}) {') dec.writeln('\tif (jsonroot_${tmp}) {')
dec.writeln('\t\t${tmp} = ${dec_name}(jsonroot_${tmp});') dec.writeln('\t\t${tmp} = ${dec_name}(jsonroot_${tmp});')
dec.writeln('\t\tif (${tmp}.is_error) {') dec.writeln('\t\tif (${tmp}.is_error) {')