json: fix struct field default value support (#14304)
parent
ca00b59b3f
commit
11bdb04d0c
|
@ -71,6 +71,11 @@ fn decode_i64(root &C.cJSON) i64 {
|
||||||
return i64(root.valuedouble) // i64 is double in C
|
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 {
|
fn decode_u8(root &C.cJSON) u8 {
|
||||||
if isnil(root) {
|
if isnil(root) {
|
||||||
return u8(0)
|
return u8(0)
|
||||||
|
@ -132,8 +137,6 @@ fn decode_string(root &C.cJSON) string {
|
||||||
if isnil(root.valuestring) {
|
if isnil(root.valuestring) {
|
||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
// println('decode string valuestring="$root.valuestring"')
|
|
||||||
// return tos(root.valuestring, _strlen(root.valuestring))
|
|
||||||
return unsafe { tos_clone(&u8(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 {
|
fn encode_int(val int) &C.cJSON {
|
||||||
return C.cJSON_CreateNumber(val)
|
return C.cJSON_CreateNumber(val)
|
||||||
}
|
}
|
||||||
|
@ -161,6 +165,11 @@ fn encode_i64(val i64) &C.cJSON {
|
||||||
return C.cJSON_CreateNumber(val)
|
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 {
|
fn encode_u8(val u8) &C.cJSON {
|
||||||
return C.cJSON_CreateNumber(val)
|
return C.cJSON_CreateNumber(val)
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,25 @@ fn test_simple() ? {
|
||||||
assert y.title == .worker
|
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() {
|
fn test_decode_top_level_array() {
|
||||||
s := '[{"name":"Peter", "age": 29}, {"name":"Bob", "age":31}]'
|
s := '[{"name":"Peter", "age": 29}, {"name":"Bob", "age":31}]'
|
||||||
x := json.decode([]Employee, s) or { panic(err) }
|
x := json.decode([]Employee, s) or { panic(err) }
|
||||||
|
@ -454,3 +473,11 @@ fn test_encode_sumtype_defined_ahead() {
|
||||||
println(ret)
|
println(ret)
|
||||||
assert ret == '{"value":0,"_type":"GPScale"}'
|
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]}'
|
||||||
|
}
|
||||||
|
|
|
@ -79,7 +79,6 @@ pub fn pref_arch_to_table_language(pref_arch pref.Arch) Language {
|
||||||
// * Table.type_kind(typ) not TypeSymbol.kind.
|
// * Table.type_kind(typ) not TypeSymbol.kind.
|
||||||
// Each TypeSymbol is entered into `Table.types`.
|
// Each TypeSymbol is entered into `Table.types`.
|
||||||
// See also: Table.sym.
|
// See also: Table.sym.
|
||||||
|
|
||||||
[minify]
|
[minify]
|
||||||
pub struct TypeSymbol {
|
pub struct TypeSymbol {
|
||||||
pub:
|
pub:
|
||||||
|
|
|
@ -7,16 +7,21 @@ import v.ast
|
||||||
import v.util
|
import v.util
|
||||||
import strings
|
import strings
|
||||||
|
|
||||||
// TODO replace with comptime code generation.
|
// TODO: replace with comptime code generation.
|
||||||
// TODO remove cJSON dependency.
|
// TODO: remove cJSON dependency.
|
||||||
// OLD: User decode_User(string js) {
|
|
||||||
// now it's
|
// Old:
|
||||||
|
// `User decode_User(string js) {`
|
||||||
|
// now it's:
|
||||||
|
// ```
|
||||||
// User decode_User(cJSON* root) {
|
// User decode_User(cJSON* root) {
|
||||||
// User res;
|
// User res;
|
||||||
// res.name = decode_string(js_get(root, "name"));
|
// res.name = decode_string(js_get(root, "name"));
|
||||||
// res.profile = decode_Profile(js_get(root, "profile"));
|
// res.profile = decode_Profile(js_get(root, "profile"));
|
||||||
// return res;
|
// return res;
|
||||||
// }
|
// }
|
||||||
|
// ```
|
||||||
|
|
||||||
// Codegen json_decode/encode funcs
|
// Codegen json_decode/encode funcs
|
||||||
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).set_nr_muls(0)
|
utyp := g.unwrap_generic(typ).set_nr_muls(0)
|
||||||
|
@ -40,10 +45,9 @@ fn (mut g Gen) gen_jsons() {
|
||||||
sym := g.table.sym(utyp)
|
sym := g.table.sym(utyp)
|
||||||
styp := g.typ(utyp)
|
styp := g.typ(utyp)
|
||||||
g.register_optional(utyp)
|
g.register_optional(utyp)
|
||||||
// println('gen_json_for_type($sym.name)')
|
|
||||||
// decode_TYPE funcs receive an actual cJSON* object to decode
|
// decode_TYPE funcs receive an actual cJSON* object to decode
|
||||||
// cJSON_Parse(str) call is added by the compiler
|
// cJSON_Parse(str) call is added by the compiler
|
||||||
// Code gen decoder
|
// Codegen decoder
|
||||||
dec_fn_name := js_dec_name(styp)
|
dec_fn_name := js_dec_name(styp)
|
||||||
dec_fn_dec := '${option_name}_$styp ${dec_fn_name}(cJSON* root)'
|
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;')
|
g.json_forward_decls.writeln('$dec_fn_dec;')
|
||||||
// Code gen encoder
|
// Codegen encoder
|
||||||
// encode_TYPE funcs receive an object to encode
|
// encode_TYPE funcs receive an object to encode
|
||||||
enc_fn_name := js_enc_name(styp)
|
enc_fn_name := js_enc_name(styp)
|
||||||
enc_fn_dec := 'cJSON* ${enc_fn_name}($styp val)'
|
enc_fn_dec := 'cJSON* ${enc_fn_name}($styp val)'
|
||||||
|
@ -116,7 +120,6 @@ $enc_fn_dec {
|
||||||
g.gen_json_for_type(value_type)
|
g.gen_json_for_type(value_type)
|
||||||
dec.writeln(g.decode_array(value_type))
|
dec.writeln(g.decode_array(value_type))
|
||||||
enc.writeln(g.encode_array(value_type))
|
enc.writeln(g.encode_array(value_type))
|
||||||
// enc += g.encode_array(t)
|
|
||||||
} else if sym.kind == .map {
|
} else if sym.kind == .map {
|
||||||
// Handle maps
|
// Handle maps
|
||||||
m := sym.info as ast.Map
|
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)
|
g.gen_struct_enc_dec(sym.info, styp, mut enc, mut dec)
|
||||||
}
|
}
|
||||||
// cJSON_delete
|
// cJSON_delete
|
||||||
// p.cgen.fns << '$dec return opt_ok(res); \n}'
|
|
||||||
dec.writeln('\t${option_name}_$styp ret;')
|
dec.writeln('\t${option_name}_$styp ret;')
|
||||||
dec.writeln('\topt_ok2(&res, ($option_name*)&ret, sizeof(res));')
|
dec.writeln('\topt_ok2(&res, ($option_name*)&ret, sizeof(res));')
|
||||||
dec.writeln('\treturn ret;\n}')
|
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) {
|
if is_js_prim(field_type) {
|
||||||
tmp := g.new_tmp_var()
|
tmp := g.new_tmp_var()
|
||||||
gen_js_get(styp, tmp, name, mut dec, is_required)
|
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);')
|
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_ {
|
} 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)
|
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);')
|
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' {
|
} else if field_sym.name == 'time.Time' {
|
||||||
// time struct requires special treatment
|
// time struct requires special treatment
|
||||||
// it has to be decoded from a unix timestamp number
|
// it has to be decoded from a unix timestamp number
|
||||||
tmp := g.new_tmp_var()
|
tmp := g.new_tmp_var()
|
||||||
gen_js_get(styp, tmp, name, mut dec, is_required)
|
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));')
|
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 {
|
} else if field_sym.kind == .alias {
|
||||||
alias := field_sym.info as ast.Alias
|
alias := field_sym.info as ast.Alias
|
||||||
parent_type := g.typ(alias.parent_type)
|
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) {
|
if is_js_prim(parent_type) {
|
||||||
tmp := g.new_tmp_var()
|
tmp := g.new_tmp_var()
|
||||||
gen_js_get(styp, tmp, name, mut dec, is_required)
|
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);')
|
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 {
|
} else {
|
||||||
g.gen_json_for_type(field.typ)
|
g.gen_json_for_type(field.typ)
|
||||||
tmp := g.new_tmp_var()
|
tmp := g.new_tmp_var()
|
||||||
gen_js_get_opt(dec_name, field_type, styp, tmp, name, mut dec, is_required)
|
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;')
|
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 {
|
} else {
|
||||||
tmp := g.new_tmp_var()
|
tmp := g.new_tmp_var()
|
||||||
gen_js_get_opt(dec_name, field_type, styp, tmp, name, mut dec, is_required)
|
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;')
|
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
|
// 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) {
|
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 {
|
if is_required {
|
||||||
dec.writeln('\tif(jsonroot_$tmp == 0) {')
|
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} };')
|
dec.writeln('\t\treturn (${option_name}_$styp){ .state = 2, .err = _v_error(_SLIT("expected field \'$name\' is missing")), .data = {0} };')
|
||||||
|
|
|
@ -40,11 +40,6 @@ fn (mut p Parser) struct_decl() ast.StructDecl {
|
||||||
return ast.StructDecl{}
|
return ast.StructDecl{}
|
||||||
}
|
}
|
||||||
mut name := p.check_name()
|
mut name := p.check_name()
|
||||||
// defer {
|
|
||||||
// if name.contains('App') {
|
|
||||||
// println('end of struct decl $name')
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
if name.len == 1 && name[0].is_capital() {
|
if name.len == 1 && name[0].is_capital() {
|
||||||
p.error_with_pos('single letter capital names are reserved for generic template types.',
|
p.error_with_pos('single letter capital names are reserved for generic template types.',
|
||||||
name_pos)
|
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)
|
p.error_with_pos('invalid recursive struct `$orig_name`', name_pos)
|
||||||
return ast.StructDecl{}
|
return ast.StructDecl{}
|
||||||
}
|
}
|
||||||
mut ret := 0
|
mut ret := p.table.register_sym(t)
|
||||||
// println('reg type symbol $name mod=$p.mod')
|
|
||||||
ret = p.table.register_sym(t)
|
|
||||||
// allow duplicate c struct declarations
|
// allow duplicate c struct declarations
|
||||||
if ret == -1 && language != .c {
|
if ret == -1 && language != .c {
|
||||||
p.error_with_pos('cannot register struct `$name`, another type with this name exists',
|
p.error_with_pos('cannot register struct `$name`, another type with this name exists',
|
||||||
|
|
Loading…
Reference in New Issue