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
}
}
for key, user in data.users {
assert data2.users[key].age == user.age
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() {
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}}}'

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*,
// i.e. it's always just Cat, not Cat_ptr:
fn (g &Gen) cc_type(t table.Type) string {
// TODO: merge cc_type and cc_type2
// cc_type but without the `struct` prefix
fn (g &Gen) cc_type2(t table.Type) string {
sym := g.table.get_type_symbol(g.unwrap_generic(t))
mut styp := util.no_dots(sym.name)
if sym.kind == .struct_ {
@ -501,6 +501,14 @@ fn (g &Gen) cc_type(t table.Type) string {
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__') {
styp = styp[3..]
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)
}
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 {
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.typ(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_ {
// Speaker_name_table[s._interface_idx].speak(s._object)
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)
if is_json_encode {
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`
encode_name := c_name(name) + '_' + util.no_dots(json_type_str)
g.writeln('// json.encode')
@ -507,7 +509,7 @@ fn (mut g Gen) fn_call(node ast.CallExpr) {
} else {
ast_type := node.args[0].expr as ast.Type
// `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
g.gen_json_for_type(ast_type.typ)
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
// cJSON_Parse(str) call is added by the compiler
// 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
g.register_optional(typ)
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;')
// Code gen encoder
// 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)'
g.json_forward_decls.writeln('$enc_fn_dec;\n')
enc.writeln('

View File

@ -237,6 +237,20 @@ fn test_generic_struct() {
// 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 }

View File

@ -122,3 +122,12 @@ pub fn (f Any) arr() []Any {
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 }
}
}