json: fix encode/decode support for generic structs (#6489)

pull/6507/head
Ned Palacios 2020-09-29 09:15:00 +08:00 committed by GitHub
parent 05dcdfd267
commit 1aec041371
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 62 additions and 10 deletions

View File

@ -218,7 +218,7 @@ fn test_nested_type() {
assert data2.countries[i].cities[j].name == data.countries[i].cities[j].name assert data2.countries[i].cities[j].name == data.countries[i].cities[j].name
} }
} }
for key, user in data.users { for key, user in data.users {
assert data2.users[key].age == user.age assert data2.users[key].age == user.age
assert data2.users[key].nums == user.nums assert data2.users[key].nums == user.nums
@ -235,6 +235,25 @@ fn test_nested_type() {
} }
} }
struct Foo<T> {
pub:
name string
data T
}
fn test_generic_struct() {
foo_int := Foo<int>{'bar', 12}
foo_enc := json.encode(foo_int)
assert foo_enc == '{"name":"bar","data":12}'
foo_dec := json.decode(Foo<int>, foo_enc) or {
exit(1)
}
assert foo_dec.name == 'bar'
assert foo_dec.data == 12
}
fn test_errors() { fn test_errors() {
invalid_array := fn () { invalid_array := fn () {
data := '{"countries":[{"cities":[{"name":"London"},{"name":"Manchester"}],"name":"UK"},{"cities":{"name":"Donlon"},"name":"KU"}],"users":{"Foo":{"age":10,"nums":[1,2,3],"lastName":"Johnson","IsRegistered":true,"type":0,"pet_animals":"little foo"},"Boo":{"age":20,"nums":[5,3,1],"lastName":"Smith","IsRegistered":false,"type":4,"pet_animals":"little boo"}},"extra":{"2":{"n1":2,"n2":4,"n3":8,"n4":16},"3":{"n1":3,"n2":9,"n3":27,"n4":81}}}' data := '{"countries":[{"cities":[{"name":"London"},{"name":"Manchester"}],"name":"UK"},{"cities":{"name":"Donlon"},"name":"KU"}],"users":{"Foo":{"age":10,"nums":[1,2,3],"lastName":"Johnson","IsRegistered":true,"type":0,"pet_animals":"little foo"},"Boo":{"age":20,"nums":[5,3,1],"lastName":"Smith","IsRegistered":false,"type":4,"pet_animals":"little boo"}},"extra":{"2":{"n1":2,"n2":4,"n3":8,"n4":16},"3":{"n1":3,"n2":9,"n3":27,"n4":81}}}'

View File

@ -482,9 +482,9 @@ static inline $opt_el_type __Option_${styp}_popval($styp ch) {
} }
} }
// cc_type returns the Cleaned Concrete Type name, *without ptr*, // TODO: merge cc_type and cc_type2
// i.e. it's always just Cat, not Cat_ptr: // cc_type but without the `struct` prefix
fn (g &Gen) cc_type(t table.Type) string { fn (g &Gen) cc_type2(t table.Type) string {
sym := g.table.get_type_symbol(g.unwrap_generic(t)) sym := g.table.get_type_symbol(g.unwrap_generic(t))
mut styp := util.no_dots(sym.name) mut styp := util.no_dots(sym.name)
if sym.kind == .struct_ { if sym.kind == .struct_ {
@ -501,6 +501,14 @@ fn (g &Gen) cc_type(t table.Type) string {
styp = styp.replace('<', '_T_').replace('>', '').replace(',', '_') styp = styp.replace('<', '_T_').replace('>', '').replace(',', '_')
} }
} }
return styp
}
// cc_type returns the Cleaned Concrete Type name, *without ptr*,
// i.e. it's always just Cat, not Cat_ptr:
fn (g &Gen) cc_type(t table.Type) string {
sym := g.table.get_type_symbol(g.unwrap_generic(t))
mut styp := g.cc_type2(t)
if styp.starts_with('C__') { if styp.starts_with('C__') {
styp = styp[3..] styp = styp[3..]
if sym.kind == .struct_ { if sym.kind == .struct_ {

View File

@ -50,7 +50,8 @@ fn (mut g Gen) gen_fn_decl(it ast.FnDecl, skip bool) {
name = util.replace_op(name) name = util.replace_op(name)
} }
if it.is_method { if it.is_method {
name = g.table.get_type_symbol(it.receiver.typ).name + '_' + name name = g.cc_type2(it.receiver.typ) + '_' + name
// name = g.table.get_type_symbol(it.receiver.typ).name + '_' + name
} }
if it.language == .c { if it.language == .c {
name = util.no_dots(name) name = util.no_dots(name)
@ -336,7 +337,8 @@ fn (mut g Gen) method_call(node ast.CallExpr) {
// mut receiver_type_name := g.cc_type(node.receiver_type) // mut receiver_type_name := g.cc_type(node.receiver_type)
// mut receiver_type_name := g.typ(node.receiver_type) // mut receiver_type_name := g.typ(node.receiver_type)
typ_sym := g.table.get_type_symbol(g.unwrap_generic(node.receiver_type)) typ_sym := g.table.get_type_symbol(g.unwrap_generic(node.receiver_type))
mut receiver_type_name := util.no_dots(typ_sym.name) // mut receiver_type_name := util.no_dots(typ_sym.name)
mut receiver_type_name := util.no_dots(g.cc_type2(g.unwrap_generic(node.receiver_type)))
if typ_sym.kind == .interface_ { if typ_sym.kind == .interface_ {
// Speaker_name_table[s._interface_idx].speak(s._object) // Speaker_name_table[s._interface_idx].speak(s._object)
g.write('${c_name(receiver_type_name)}_name_table[') g.write('${c_name(receiver_type_name)}_name_table[')
@ -494,7 +496,7 @@ fn (mut g Gen) fn_call(node ast.CallExpr) {
cur_line := g.go_before_stmt(0) cur_line := g.go_before_stmt(0)
if is_json_encode { if is_json_encode {
g.gen_json_for_type(node.args[0].typ) g.gen_json_for_type(node.args[0].typ)
json_type_str = g.table.get_type_symbol(node.args[0].typ).name json_type_str = g.typ(node.args[0].typ)
// `json__encode` => `json__encode_User` // `json__encode` => `json__encode_User`
encode_name := c_name(name) + '_' + util.no_dots(json_type_str) encode_name := c_name(name) + '_' + util.no_dots(json_type_str)
g.writeln('// json.encode') g.writeln('// json.encode')
@ -507,7 +509,7 @@ fn (mut g Gen) fn_call(node ast.CallExpr) {
} else { } else {
ast_type := node.args[0].expr as ast.Type ast_type := node.args[0].expr as ast.Type
// `json.decode(User, s)` => json.decode_User(s) // `json.decode(User, s)` => json.decode_User(s)
typ := c_name(g.table.get_type_symbol(ast_type.typ).name) typ := c_name(g.typ(ast_type.typ))
fn_name := c_name(name) + '_' + typ fn_name := c_name(name) + '_' + typ
g.gen_json_for_type(ast_type.typ) g.gen_json_for_type(ast_type.typ)
g.writeln('// json.decode') g.writeln('// json.decode')

View File

@ -37,7 +37,7 @@ fn (mut g Gen) gen_json_for_type(typ table.Type) {
// 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 // Code gen decoder
dec_fn_name := js_dec_name(sym.name) dec_fn_name := js_dec_name(styp)
// Make sure that this optional type actually exists // Make sure that this optional type actually exists
g.register_optional(typ) g.register_optional(typ)
dec_fn_dec := 'Option_$styp ${dec_fn_name}(cJSON* root)' dec_fn_dec := 'Option_$styp ${dec_fn_name}(cJSON* root)'
@ -58,7 +58,7 @@ $dec_fn_dec {
g.json_forward_decls.writeln('$dec_fn_dec;') g.json_forward_decls.writeln('$dec_fn_dec;')
// Code gen encoder // Code gen encoder
// encode_TYPE funcs receive an object to encode // encode_TYPE funcs receive an object to encode
enc_fn_name := js_enc_name(sym.name) 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)'
g.json_forward_decls.writeln('$enc_fn_dec;\n') g.json_forward_decls.writeln('$enc_fn_dec;\n')
enc.writeln(' enc.writeln('

View File

@ -237,6 +237,20 @@ fn test_generic_struct() {
// println(x.model.name) // println(x.model.name)
} }
struct Foo<T> {
pub:
data T
}
fn (f Foo<int>) value() string {
return f.data.str()
}
fn test_generic_struct_method() {
foo_int := Foo<int>{2}
assert foo_int.value() == '2'
}
/* /*
struct Abc{ x int y int z int } struct Abc{ x int y int z int }

View File

@ -122,3 +122,12 @@ pub fn (f Any) arr() []Any {
return [f] return [f]
} }
// Use `Any` as a bool
pub fn (f Any) bool() bool {
match f {
bool { return *f }
string { return (*f).bool() }
else { return false }
}
}