json: support sumtypes (#11549)
							parent
							
								
									7bd145d88a
								
							
						
					
					
						commit
						2534946ead
					
				| 
						 | 
				
			
			@ -37,6 +37,48 @@ fn test_decode_top_level_array() {
 | 
			
		|||
	assert x[1].age == 31
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct Usr {
 | 
			
		||||
	name string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct Item {
 | 
			
		||||
	tag string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
enum Animal {
 | 
			
		||||
	dog // Will be encoded as `0`
 | 
			
		||||
	cat
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Entity = Animal | Item | Usr | string | time.Time
 | 
			
		||||
 | 
			
		||||
struct SomeGame {
 | 
			
		||||
	title  string
 | 
			
		||||
	player Entity
 | 
			
		||||
	other  []Entity
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn test_encode_decode_sumtype() ? {
 | 
			
		||||
	game := SomeGame{
 | 
			
		||||
		title: 'Super Mega Game'
 | 
			
		||||
		player: Usr{'PoopLord69'}
 | 
			
		||||
		other: [
 | 
			
		||||
			Entity(Item{'Pen'}),
 | 
			
		||||
			Item{'Cookie'},
 | 
			
		||||
			Animal.dog,
 | 
			
		||||
			'Stool',
 | 
			
		||||
			time.now(),
 | 
			
		||||
		]
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	enc := json.encode(game)
 | 
			
		||||
	dec := json.decode(SomeGame, enc) ?
 | 
			
		||||
 | 
			
		||||
	assert game.title == dec.title
 | 
			
		||||
	assert game.player == dec.player
 | 
			
		||||
	assert (game.other[4] as time.Time).unix_time() == (dec.other[4] as time.Time).unix_time()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn bar<T>(payload string) ?Bar { // ?T doesn't work currently
 | 
			
		||||
	result := json.decode(T, payload) ?
 | 
			
		||||
	return result
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -24,6 +24,7 @@ fn (mut g Gen) gen_json_for_type(typ ast.Type) {
 | 
			
		|||
	mut enc := strings.new_builder(100)
 | 
			
		||||
	sym := g.table.get_type_symbol(utyp)
 | 
			
		||||
	styp := g.typ(utyp)
 | 
			
		||||
	g.register_optional(utyp)
 | 
			
		||||
	if is_js_prim(sym.name) || sym.kind == .enum_ {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -39,8 +40,6 @@ fn (mut g Gen) gen_json_for_type(typ ast.Type) {
 | 
			
		|||
	// cJSON_Parse(str) call is added by the compiler
 | 
			
		||||
	// Code gen decoder
 | 
			
		||||
	dec_fn_name := js_dec_name(styp)
 | 
			
		||||
	// Make sure that this optional type actually exists
 | 
			
		||||
	g.register_optional(utyp)
 | 
			
		||||
	dec_fn_dec := 'Option_$styp ${dec_fn_name}(cJSON* root)'
 | 
			
		||||
	dec.writeln('
 | 
			
		||||
$dec_fn_dec {
 | 
			
		||||
| 
						 | 
				
			
			@ -91,6 +90,13 @@ $enc_fn_dec {
 | 
			
		|||
			verror('json: $sym.name is not struct')
 | 
			
		||||
		}
 | 
			
		||||
		g.gen_struct_enc_dec(psym.info, styp, mut enc, mut dec)
 | 
			
		||||
	} else if sym.kind == .sum_type {
 | 
			
		||||
		enc.writeln('\to = cJSON_CreateObject();')
 | 
			
		||||
		// Structs. Range through fields
 | 
			
		||||
		if sym.info !is ast.SumType {
 | 
			
		||||
			verror('json: $sym.name is not a sumtype')
 | 
			
		||||
		}
 | 
			
		||||
		g.gen_sumtype_enc_dec(sym, mut enc, mut dec)
 | 
			
		||||
	} else {
 | 
			
		||||
		enc.writeln('\to = cJSON_CreateObject();')
 | 
			
		||||
		// Structs. Range through fields
 | 
			
		||||
| 
						 | 
				
			
			@ -109,6 +115,53 @@ $enc_fn_dec {
 | 
			
		|||
	g.gowrappers.writeln(enc.str())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
[inline]
 | 
			
		||||
fn (mut g Gen) gen_sumtype_enc_dec(sym ast.TypeSymbol, mut enc strings.Builder, mut dec strings.Builder) {
 | 
			
		||||
	info := sym.info as ast.SumType
 | 
			
		||||
	typ := sym.idx
 | 
			
		||||
 | 
			
		||||
	for variant in info.variants {
 | 
			
		||||
		variant_typ := g.typ(variant)
 | 
			
		||||
		variant_sym := g.table.get_type_symbol(variant)
 | 
			
		||||
 | 
			
		||||
		g.gen_json_for_type(variant)
 | 
			
		||||
		g.write_sumtype_casting_fn(variant, typ)
 | 
			
		||||
		g.definitions.writeln('static inline $sym.cname ${variant_typ}_to_sumtype_${sym.cname}($variant_typ* x);')
 | 
			
		||||
 | 
			
		||||
		// ENCODING
 | 
			
		||||
		enc.writeln('\tif (val._typ == $variant) {')
 | 
			
		||||
		if variant_sym.kind == .enum_ {
 | 
			
		||||
			enc.writeln('\t\tcJSON_AddItemToObject(o, "$variant_sym.name", json__encode_u64(*val._$variant_typ));')
 | 
			
		||||
		} else if variant_sym.name == 'time.Time' {
 | 
			
		||||
			enc.writeln('\t\tcJSON_AddItemToObject(o, "$variant_sym.name", json__encode_i64(val._$variant_typ->_v_unix));')
 | 
			
		||||
		} else {
 | 
			
		||||
			enc.writeln('\t\tcJSON_AddItemToObject(o, "$variant_sym.name", json__encode_${variant_typ}(*val._$variant_typ));')
 | 
			
		||||
		}
 | 
			
		||||
		enc.writeln('\t}')
 | 
			
		||||
 | 
			
		||||
		// DECODING
 | 
			
		||||
		dec.writeln('\tif (strcmp("$variant_sym.name", root->child->string) == 0) {')
 | 
			
		||||
		tmp := g.new_tmp_var()
 | 
			
		||||
		if is_js_prim(variant_typ) {
 | 
			
		||||
			gen_js_get(variant_typ, tmp, variant_sym.name, mut dec, false)
 | 
			
		||||
			dec.writeln('\t\t$variant_typ value = (${js_dec_name(variant_sym.name)})(jsonroot_$tmp);')
 | 
			
		||||
		} else if variant_sym.kind == .enum_ {
 | 
			
		||||
			gen_js_get(variant_typ, tmp, variant_sym.name, mut dec, false)
 | 
			
		||||
			dec.writeln('\t\t$variant_typ value = json__decode_u64(jsonroot_$tmp);')
 | 
			
		||||
		} else if variant_sym.name == 'time.Time' {
 | 
			
		||||
			gen_js_get(variant_typ, tmp, variant_sym.name, mut dec, false)
 | 
			
		||||
			dec.writeln('\t\t$variant_typ value = time__unix(json__decode_i64(jsonroot_$tmp));')
 | 
			
		||||
		} else {
 | 
			
		||||
			gen_js_get_opt(js_dec_name(variant_sym.cname), variant_typ, sym.cname, tmp,
 | 
			
		||||
				variant_sym.name, mut dec, false)
 | 
			
		||||
			// dec.writeln('\t\tOption_${variant_typ} $tmp = json__decode_${variant_typ}(js_get(root, "$variant_sym.name"));')
 | 
			
		||||
			dec.writeln('\t\t$variant_typ value = *($variant_typ*)(${tmp}.data);')
 | 
			
		||||
		}
 | 
			
		||||
		dec.writeln('\t\tres = ${variant_typ}_to_sumtype_${sym.cname}(&value);')
 | 
			
		||||
		dec.writeln('\t}')
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
[inline]
 | 
			
		||||
fn (mut g Gen) gen_struct_enc_dec(type_info ast.TypeInfo, styp string, mut enc strings.Builder, mut dec strings.Builder) {
 | 
			
		||||
	info := type_info as ast.Struct
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue