json: fix encode/decode support for generic structs (#6489)
							parent
							
								
									05dcdfd267
								
							
						
					
					
						commit
						1aec041371
					
				|  | @ -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}}}' | ||||||
|  |  | ||||||
|  | @ -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_ { | ||||||
|  |  | ||||||
|  | @ -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') | ||||||
|  |  | ||||||
|  | @ -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(' | ||||||
|  |  | ||||||
|  | @ -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 } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -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 } | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue