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

master
StunxFS 2022-05-20 04:22:17 -04:00 committed by GitHub
parent ca00b59b3f
commit 11bdb04d0c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
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;
// }
// ```
// 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,7 +45,6 @@ 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
// Codegen decoder
@ -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

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',