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 | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 
 | ||||
| 	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}}}' | ||||
|  |  | |||
|  | @ -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_ { | ||||
|  |  | |||
|  | @ -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') | ||||
|  |  | |||
|  | @ -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(' | ||||
|  |  | |||
|  | @ -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 } | ||||
| 
 | ||||
|  |  | |||
|  | @ -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 } | ||||
| 	} | ||||
| } | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue