json: fix struct field default value support (#14304)

StunxFS 2022-05-20 04:22:17 -04:00 committed by Jef Roosens
parent 59d4e59ac6
commit b15f50e9b1
Signed by: Jef Roosens
GPG Key ID: B75D4F293C7052DB
5 changed files with 103 additions and 25 deletions

View File

@ -71,6 +71,11 @@ fn decode_i64(root &C.cJSON) i64 {
return i64(root.valuedouble) // i64 is double in C
}
// TODO: remove when `byte` is removed
fn decode_byte(root &C.cJSON) byte {
return byte(decode_u8(root))
}
fn decode_u8(root &C.cJSON) u8 {
if isnil(root) {
return u8(0)
@ -132,8 +137,6 @@ fn decode_string(root &C.cJSON) string {
if isnil(root.valuestring) {
return ''
}
// println('decode string valuestring="$root.valuestring"')
// return tos(root.valuestring, _strlen(root.valuestring))
return unsafe { tos_clone(&u8(root.valuestring)) } // , _strlen(root.valuestring))
}
@ -145,6 +148,7 @@ fn decode_bool(root &C.cJSON) bool {
}
// ///////////////////
fn encode_int(val int) &C.cJSON {
return C.cJSON_CreateNumber(val)
}
@ -161,6 +165,11 @@ fn encode_i64(val i64) &C.cJSON {
return C.cJSON_CreateNumber(val)
}
// TODO: remove when `byte` is removed
fn encode_byte(root byte) &C.cJSON {
return encode_u8(u8(root))
}
fn encode_u8(val u8) &C.cJSON {
return C.cJSON_CreateNumber(val)
}

View File

@ -27,6 +27,25 @@ fn test_simple() ? {
assert y.title == .worker
}
const currency_id = 'cconst'
struct Price {
net f64
currency_id string [json: currencyId] = currency_id
}
fn test_field_with_default_expr() ? {
data := '[{"net":1},{"net":2,"currencyId":"cjson"}]'
prices := json.decode([]Price, data)?
assert prices == [Price{
net: 1
currency_id: 'cconst'
}, Price{
net: 2
currency_id: 'cjson'
}]
}
fn test_decode_top_level_array() {
s := '[{"name":"Peter", "age": 29}, {"name":"Bob", "age":31}]'
x := json.decode([]Employee, s) or { panic(err) }
@ -454,3 +473,11 @@ fn test_encode_sumtype_defined_ahead() {
println(ret)
assert ret == '{"value":0,"_type":"GPScale"}'
}
struct StByteArray {
ba []byte
}
fn test_byte_array() {
assert json.encode(StByteArray{ ba: [byte(1), 2, 3, 4, 5] }) == '{"ba":[1,2,3,4,5]}'
}

View File

@ -79,7 +79,6 @@ pub fn pref_arch_to_table_language(pref_arch pref.Arch) Language {
// * Table.type_kind(typ) not TypeSymbol.kind.
// Each TypeSymbol is entered into `Table.types`.
// See also: Table.sym.
[minify]
pub struct TypeSymbol {
pub:

View File

@ -7,16 +7,21 @@ import v.ast
import v.util
import strings
// TODO replace with comptime code generation.
// TODO remove cJSON dependency.
// OLD: User decode_User(string js) {
// now it's
// TODO: replace with comptime code generation.
// TODO: remove cJSON dependency.
// Old:
// `User decode_User(string js) {`
// now it's:
// ```
// User decode_User(cJSON* root) {
// User res;
// res.name = decode_string(js_get(root, "name"));
// res.profile = decode_Profile(js_get(root, "profile"));
// return res;
// User res;
// res.name = decode_string(js_get(root, "name"));
// res.profile = decode_Profile(js_get(root, "profile"));
// return res;
// }
// ```
// Codegen json_decode/encode funcs
fn (mut g Gen) gen_json_for_type(typ ast.Type) {
utyp := g.unwrap_generic(typ).set_nr_muls(0)
@ -40,10 +45,9 @@ fn (mut g Gen) gen_jsons() {
sym := g.table.sym(utyp)
styp := g.typ(utyp)
g.register_optional(utyp)
// println('gen_json_for_type($sym.name)')
// decode_TYPE funcs receive an actual cJSON* object to decode
// cJSON_Parse(str) call is added by the compiler
// Code gen decoder
// Codegen decoder
dec_fn_name := js_dec_name(styp)
dec_fn_dec := '${option_name}_$styp ${dec_fn_name}(cJSON* root)'
@ -101,7 +105,7 @@ $dec_fn_dec {
}
')
g.json_forward_decls.writeln('$dec_fn_dec;')
// Code gen encoder
// Codegen encoder
// encode_TYPE funcs receive an object to encode
enc_fn_name := js_enc_name(styp)
enc_fn_dec := 'cJSON* ${enc_fn_name}($styp val)'
@ -116,7 +120,6 @@ $enc_fn_dec {
g.gen_json_for_type(value_type)
dec.writeln(g.decode_array(value_type))
enc.writeln(g.encode_array(value_type))
// enc += g.encode_array(t)
} else if sym.kind == .map {
// Handle maps
m := sym.info as ast.Map
@ -156,7 +159,6 @@ $enc_fn_dec {
g.gen_struct_enc_dec(sym.info, styp, mut enc, mut dec)
}
// cJSON_delete
// p.cgen.fns << '$dec return opt_ok(res); \n}'
dec.writeln('\t${option_name}_$styp ret;')
dec.writeln('\topt_ok2(&res, ($option_name*)&ret, sizeof(res));')
dec.writeln('\treturn ret;\n}')
@ -396,17 +398,41 @@ fn (mut g Gen) gen_struct_enc_dec(type_info ast.TypeInfo, styp string, mut enc s
if is_js_prim(field_type) {
tmp := g.new_tmp_var()
gen_js_get(styp, tmp, name, mut dec, is_required)
if field.has_default_expr {
dec.writeln('\tif (jsonroot_$tmp) {')
}
dec.writeln('\tres.${c_name(field.name)} = $dec_name (jsonroot_$tmp);')
if field.has_default_expr {
dec.writeln('\t} else {')
dec.writeln('\tres.${c_name(field.name)} = ${g.expr_string(field.default_expr)};')
dec.writeln('\t}')
}
} else if field_sym.kind == .enum_ {
tmp := g.new_tmp_var()
gen_js_get(styp, tmp, name, mut dec, is_required)
if field.has_default_expr {
dec.writeln('\tif (jsonroot_$tmp) {')
}
dec.writeln('\tres.${c_name(field.name)} = json__decode_u64(jsonroot_$tmp);')
if field.has_default_expr {
dec.writeln('\t} else {')
dec.writeln('\tres.${c_name(field.name)} = ${g.expr_string(field.default_expr)};')
dec.writeln('\t}')
}
} else if field_sym.name == 'time.Time' {
// time struct requires special treatment
// it has to be decoded from a unix timestamp number
tmp := g.new_tmp_var()
gen_js_get(styp, tmp, name, mut dec, is_required)
if field.has_default_expr {
dec.writeln('\tif (jsonroot_$tmp) {')
}
dec.writeln('\tres.${c_name(field.name)} = time__unix(json__decode_u64(jsonroot_$tmp));')
if field.has_default_expr {
dec.writeln('\t} else {')
dec.writeln('\tres.${c_name(field.name)} = ${g.expr_string(field.default_expr)};')
dec.writeln('\t}')
}
} else if field_sym.kind == .alias {
alias := field_sym.info as ast.Alias
parent_type := g.typ(alias.parent_type)
@ -414,17 +440,41 @@ fn (mut g Gen) gen_struct_enc_dec(type_info ast.TypeInfo, styp string, mut enc s
if is_js_prim(parent_type) {
tmp := g.new_tmp_var()
gen_js_get(styp, tmp, name, mut dec, is_required)
if field.has_default_expr {
dec.writeln('\tif (jsonroot_$tmp) {')
}
dec.writeln('\tres.${c_name(field.name)} = $parent_dec_name (jsonroot_$tmp);')
if field.has_default_expr {
dec.writeln('\t} else {')
dec.writeln('\tres.${c_name(field.name)} = ${g.expr_string(field.default_expr)};')
dec.writeln('\t}')
}
} else {
g.gen_json_for_type(field.typ)
tmp := g.new_tmp_var()
gen_js_get_opt(dec_name, field_type, styp, tmp, name, mut dec, is_required)
if field.has_default_expr {
dec.writeln('\tif (jsonroot_$tmp) {')
}
dec.writeln('\tres.${c_name(field.name)} = *($field_type*) ${tmp}.data;')
if field.has_default_expr {
dec.writeln('\t} else {')
dec.writeln('\tres.${c_name(field.name)} = ${g.expr_string(field.default_expr)};')
dec.writeln('\t}')
}
}
} else {
tmp := g.new_tmp_var()
gen_js_get_opt(dec_name, field_type, styp, tmp, name, mut dec, is_required)
if field.has_default_expr {
dec.writeln('\tif (jsonroot_$tmp) {')
}
dec.writeln('\tres.${c_name(field.name)} = *($field_type*) ${tmp}.data;')
if field.has_default_expr {
dec.writeln('\t} else {')
dec.writeln('\tres.${c_name(field.name)} = ${g.expr_string(field.default_expr)};')
dec.writeln('\t}')
}
}
}
// Encoding
@ -453,7 +503,7 @@ fn (mut g Gen) gen_struct_enc_dec(type_info ast.TypeInfo, styp string, mut enc s
}
fn gen_js_get(styp string, tmp string, name string, mut dec strings.Builder, is_required bool) {
dec.writeln('\tcJSON *jsonroot_$tmp = js_get(root,"$name");')
dec.writeln('\tcJSON *jsonroot_$tmp = js_get(root, "$name");')
if is_required {
dec.writeln('\tif(jsonroot_$tmp == 0) {')
dec.writeln('\t\treturn (${option_name}_$styp){ .state = 2, .err = _v_error(_SLIT("expected field \'$name\' is missing")), .data = {0} };')

View File

@ -40,11 +40,6 @@ fn (mut p Parser) struct_decl() ast.StructDecl {
return ast.StructDecl{}
}
mut name := p.check_name()
// defer {
// if name.contains('App') {
// println('end of struct decl $name')
// }
// }
if name.len == 1 && name[0].is_capital() {
p.error_with_pos('single letter capital names are reserved for generic template types.',
name_pos)
@ -328,9 +323,7 @@ fn (mut p Parser) struct_decl() ast.StructDecl {
p.error_with_pos('invalid recursive struct `$orig_name`', name_pos)
return ast.StructDecl{}
}
mut ret := 0
// println('reg type symbol $name mod=$p.mod')
ret = p.table.register_sym(t)
mut ret := p.table.register_sym(t)
// allow duplicate c struct declarations
if ret == -1 && language != .c {
p.error_with_pos('cannot register struct `$name`, another type with this name exists',