json: fix struct field default value support (#14304)
							parent
							
								
									ca00b59b3f
								
							
						
					
					
						commit
						11bdb04d0c
					
				|  | @ -71,6 +71,11 @@ fn decode_i64(root &C.cJSON) i64 { | |||
| 	return i64(root.valuedouble) // i64 is double in C
 | ||||
| } | ||||
| 
 | ||||
| // TODO: remove when `byte` is removed
 | ||||
| fn decode_byte(root &C.cJSON) byte { | ||||
| 	return byte(decode_u8(root)) | ||||
| } | ||||
| 
 | ||||
| fn decode_u8(root &C.cJSON) u8 { | ||||
| 	if isnil(root) { | ||||
| 		return u8(0) | ||||
|  | @ -132,8 +137,6 @@ fn decode_string(root &C.cJSON) string { | |||
| 	if isnil(root.valuestring) { | ||||
| 		return '' | ||||
| 	} | ||||
| 	// println('decode string valuestring="$root.valuestring"')
 | ||||
| 	// return tos(root.valuestring, _strlen(root.valuestring))
 | ||||
| 	return unsafe { tos_clone(&u8(root.valuestring)) } // , _strlen(root.valuestring))
 | ||||
| } | ||||
| 
 | ||||
|  | @ -145,6 +148,7 @@ fn decode_bool(root &C.cJSON) bool { | |||
| } | ||||
| 
 | ||||
| // ///////////////////
 | ||||
| 
 | ||||
| fn encode_int(val int) &C.cJSON { | ||||
| 	return C.cJSON_CreateNumber(val) | ||||
| } | ||||
|  | @ -161,6 +165,11 @@ fn encode_i64(val i64) &C.cJSON { | |||
| 	return C.cJSON_CreateNumber(val) | ||||
| } | ||||
| 
 | ||||
| // TODO: remove when `byte` is removed
 | ||||
| fn encode_byte(root byte) &C.cJSON { | ||||
| 	return encode_u8(u8(root)) | ||||
| } | ||||
| 
 | ||||
| fn encode_u8(val u8) &C.cJSON { | ||||
| 	return C.cJSON_CreateNumber(val) | ||||
| } | ||||
|  |  | |||
|  | @ -27,6 +27,25 @@ fn test_simple() ? { | |||
| 	assert y.title == .worker | ||||
| } | ||||
| 
 | ||||
| const currency_id = 'cconst' | ||||
| 
 | ||||
| struct Price { | ||||
| 	net         f64 | ||||
| 	currency_id string [json: currencyId] = currency_id | ||||
| } | ||||
| 
 | ||||
| fn test_field_with_default_expr() ? { | ||||
| 	data := '[{"net":1},{"net":2,"currencyId":"cjson"}]' | ||||
| 	prices := json.decode([]Price, data)? | ||||
| 	assert prices == [Price{ | ||||
| 		net: 1 | ||||
| 		currency_id: 'cconst' | ||||
| 	}, Price{ | ||||
| 		net: 2 | ||||
| 		currency_id: 'cjson' | ||||
| 	}] | ||||
| } | ||||
| 
 | ||||
| fn test_decode_top_level_array() { | ||||
| 	s := '[{"name":"Peter", "age": 29}, {"name":"Bob", "age":31}]' | ||||
| 	x := json.decode([]Employee, s) or { panic(err) } | ||||
|  | @ -454,3 +473,11 @@ fn test_encode_sumtype_defined_ahead() { | |||
| 	println(ret) | ||||
| 	assert ret == '{"value":0,"_type":"GPScale"}' | ||||
| } | ||||
| 
 | ||||
| struct StByteArray { | ||||
| 	ba []byte | ||||
| } | ||||
| 
 | ||||
| fn test_byte_array() { | ||||
| 	assert json.encode(StByteArray{ ba: [byte(1), 2, 3, 4, 5] }) == '{"ba":[1,2,3,4,5]}' | ||||
| } | ||||
|  |  | |||
|  | @ -79,7 +79,6 @@ pub fn pref_arch_to_table_language(pref_arch pref.Arch) Language { | |||
| // * Table.type_kind(typ) not TypeSymbol.kind.
 | ||||
| // Each TypeSymbol is entered into `Table.types`.
 | ||||
| // See also: Table.sym.
 | ||||
| 
 | ||||
| [minify] | ||||
| pub struct TypeSymbol { | ||||
| pub: | ||||
|  |  | |||
|  | @ -7,16 +7,21 @@ import v.ast | |||
| import v.util | ||||
| import strings | ||||
| 
 | ||||
| // TODO replace with comptime code generation.
 | ||||
| // TODO remove cJSON dependency.
 | ||||
| // OLD: User decode_User(string js) {
 | ||||
| // now it's
 | ||||
| // TODO: replace with comptime code generation.
 | ||||
| // TODO: remove cJSON dependency.
 | ||||
| 
 | ||||
| // Old:
 | ||||
| // `User decode_User(string js) {`
 | ||||
| // now it's:
 | ||||
| // ```
 | ||||
| // User decode_User(cJSON* root) {
 | ||||
| // User res;
 | ||||
| // res.name = decode_string(js_get(root, "name"));
 | ||||
| // res.profile = decode_Profile(js_get(root, "profile"));
 | ||||
| // return res;
 | ||||
| //     User res;
 | ||||
| //     res.name = decode_string(js_get(root, "name"));
 | ||||
| //     res.profile = decode_Profile(js_get(root, "profile"));
 | ||||
| //     return res;
 | ||||
| // }
 | ||||
| // ```
 | ||||
| 
 | ||||
| // Codegen json_decode/encode funcs
 | ||||
| fn (mut g Gen) gen_json_for_type(typ ast.Type) { | ||||
| 	utyp := g.unwrap_generic(typ).set_nr_muls(0) | ||||
|  | @ -40,10 +45,9 @@ fn (mut g Gen) gen_jsons() { | |||
| 		sym := g.table.sym(utyp) | ||||
| 		styp := g.typ(utyp) | ||||
| 		g.register_optional(utyp) | ||||
| 		// println('gen_json_for_type($sym.name)')
 | ||||
| 		// decode_TYPE funcs receive an actual cJSON* object to decode
 | ||||
| 		// cJSON_Parse(str) call is added by the compiler
 | ||||
| 		// Code gen decoder
 | ||||
| 		// Codegen decoder
 | ||||
| 		dec_fn_name := js_dec_name(styp) | ||||
| 		dec_fn_dec := '${option_name}_$styp ${dec_fn_name}(cJSON* root)' | ||||
| 
 | ||||
|  | @ -101,7 +105,7 @@ $dec_fn_dec { | |||
| 	} | ||||
| ') | ||||
| 		g.json_forward_decls.writeln('$dec_fn_dec;') | ||||
| 		// Code gen encoder
 | ||||
| 		// Codegen encoder
 | ||||
| 		// encode_TYPE funcs receive an object to encode
 | ||||
| 		enc_fn_name := js_enc_name(styp) | ||||
| 		enc_fn_dec := 'cJSON* ${enc_fn_name}($styp val)' | ||||
|  | @ -116,7 +120,6 @@ $enc_fn_dec { | |||
| 			g.gen_json_for_type(value_type) | ||||
| 			dec.writeln(g.decode_array(value_type)) | ||||
| 			enc.writeln(g.encode_array(value_type)) | ||||
| 			// enc += g.encode_array(t)
 | ||||
| 		} else if sym.kind == .map { | ||||
| 			// Handle maps
 | ||||
| 			m := sym.info as ast.Map | ||||
|  | @ -156,7 +159,6 @@ $enc_fn_dec { | |||
| 			g.gen_struct_enc_dec(sym.info, styp, mut enc, mut dec) | ||||
| 		} | ||||
| 		// cJSON_delete
 | ||||
| 		// p.cgen.fns << '$dec return opt_ok(res); \n}'
 | ||||
| 		dec.writeln('\t${option_name}_$styp ret;') | ||||
| 		dec.writeln('\topt_ok2(&res, ($option_name*)&ret, sizeof(res));') | ||||
| 		dec.writeln('\treturn ret;\n}') | ||||
|  | @ -396,17 +398,41 @@ fn (mut g Gen) gen_struct_enc_dec(type_info ast.TypeInfo, styp string, mut enc s | |||
| 			if is_js_prim(field_type) { | ||||
| 				tmp := g.new_tmp_var() | ||||
| 				gen_js_get(styp, tmp, name, mut dec, is_required) | ||||
| 				if field.has_default_expr { | ||||
| 					dec.writeln('\tif (jsonroot_$tmp) {') | ||||
| 				} | ||||
| 				dec.writeln('\tres.${c_name(field.name)} = $dec_name (jsonroot_$tmp);') | ||||
| 				if field.has_default_expr { | ||||
| 					dec.writeln('\t} else {') | ||||
| 					dec.writeln('\tres.${c_name(field.name)} = ${g.expr_string(field.default_expr)};') | ||||
| 					dec.writeln('\t}') | ||||
| 				} | ||||
| 			} else if field_sym.kind == .enum_ { | ||||
| 				tmp := g.new_tmp_var() | ||||
| 				gen_js_get(styp, tmp, name, mut dec, is_required) | ||||
| 				if field.has_default_expr { | ||||
| 					dec.writeln('\tif (jsonroot_$tmp) {') | ||||
| 				} | ||||
| 				dec.writeln('\tres.${c_name(field.name)} = json__decode_u64(jsonroot_$tmp);') | ||||
| 				if field.has_default_expr { | ||||
| 					dec.writeln('\t} else {') | ||||
| 					dec.writeln('\tres.${c_name(field.name)} = ${g.expr_string(field.default_expr)};') | ||||
| 					dec.writeln('\t}') | ||||
| 				} | ||||
| 			} else if field_sym.name == 'time.Time' { | ||||
| 				// time struct requires special treatment
 | ||||
| 				// it has to be decoded from a unix timestamp number
 | ||||
| 				tmp := g.new_tmp_var() | ||||
| 				gen_js_get(styp, tmp, name, mut dec, is_required) | ||||
| 				if field.has_default_expr { | ||||
| 					dec.writeln('\tif (jsonroot_$tmp) {') | ||||
| 				} | ||||
| 				dec.writeln('\tres.${c_name(field.name)} = time__unix(json__decode_u64(jsonroot_$tmp));') | ||||
| 				if field.has_default_expr { | ||||
| 					dec.writeln('\t} else {') | ||||
| 					dec.writeln('\tres.${c_name(field.name)} = ${g.expr_string(field.default_expr)};') | ||||
| 					dec.writeln('\t}') | ||||
| 				} | ||||
| 			} else if field_sym.kind == .alias { | ||||
| 				alias := field_sym.info as ast.Alias | ||||
| 				parent_type := g.typ(alias.parent_type) | ||||
|  | @ -414,17 +440,41 @@ fn (mut g Gen) gen_struct_enc_dec(type_info ast.TypeInfo, styp string, mut enc s | |||
| 				if is_js_prim(parent_type) { | ||||
| 					tmp := g.new_tmp_var() | ||||
| 					gen_js_get(styp, tmp, name, mut dec, is_required) | ||||
| 					if field.has_default_expr { | ||||
| 						dec.writeln('\tif (jsonroot_$tmp) {') | ||||
| 					} | ||||
| 					dec.writeln('\tres.${c_name(field.name)} = $parent_dec_name (jsonroot_$tmp);') | ||||
| 					if field.has_default_expr { | ||||
| 						dec.writeln('\t} else {') | ||||
| 						dec.writeln('\tres.${c_name(field.name)} = ${g.expr_string(field.default_expr)};') | ||||
| 						dec.writeln('\t}') | ||||
| 					} | ||||
| 				} else { | ||||
| 					g.gen_json_for_type(field.typ) | ||||
| 					tmp := g.new_tmp_var() | ||||
| 					gen_js_get_opt(dec_name, field_type, styp, tmp, name, mut dec, is_required) | ||||
| 					if field.has_default_expr { | ||||
| 						dec.writeln('\tif (jsonroot_$tmp) {') | ||||
| 					} | ||||
| 					dec.writeln('\tres.${c_name(field.name)} = *($field_type*) ${tmp}.data;') | ||||
| 					if field.has_default_expr { | ||||
| 						dec.writeln('\t} else {') | ||||
| 						dec.writeln('\tres.${c_name(field.name)} = ${g.expr_string(field.default_expr)};') | ||||
| 						dec.writeln('\t}') | ||||
| 					} | ||||
| 				} | ||||
| 			} else { | ||||
| 				tmp := g.new_tmp_var() | ||||
| 				gen_js_get_opt(dec_name, field_type, styp, tmp, name, mut dec, is_required) | ||||
| 				if field.has_default_expr { | ||||
| 					dec.writeln('\tif (jsonroot_$tmp) {') | ||||
| 				} | ||||
| 				dec.writeln('\tres.${c_name(field.name)} = *($field_type*) ${tmp}.data;') | ||||
| 				if field.has_default_expr { | ||||
| 					dec.writeln('\t} else {') | ||||
| 					dec.writeln('\tres.${c_name(field.name)} = ${g.expr_string(field.default_expr)};') | ||||
| 					dec.writeln('\t}') | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		// Encoding
 | ||||
|  | @ -453,7 +503,7 @@ fn (mut g Gen) gen_struct_enc_dec(type_info ast.TypeInfo, styp string, mut enc s | |||
| } | ||||
| 
 | ||||
| fn gen_js_get(styp string, tmp string, name string, mut dec strings.Builder, is_required bool) { | ||||
| 	dec.writeln('\tcJSON *jsonroot_$tmp = js_get(root,"$name");') | ||||
| 	dec.writeln('\tcJSON *jsonroot_$tmp = js_get(root, "$name");') | ||||
| 	if is_required { | ||||
| 		dec.writeln('\tif(jsonroot_$tmp == 0) {') | ||||
| 		dec.writeln('\t\treturn (${option_name}_$styp){ .state = 2, .err = _v_error(_SLIT("expected field \'$name\' is missing")), .data = {0} };') | ||||
|  |  | |||
|  | @ -40,11 +40,6 @@ fn (mut p Parser) struct_decl() ast.StructDecl { | |||
| 		return ast.StructDecl{} | ||||
| 	} | ||||
| 	mut name := p.check_name() | ||||
| 	// defer {
 | ||||
| 	// if name.contains('App') {
 | ||||
| 	// println('end of struct decl $name')
 | ||||
| 	// }
 | ||||
| 	// }
 | ||||
| 	if name.len == 1 && name[0].is_capital() { | ||||
| 		p.error_with_pos('single letter capital names are reserved for generic template types.', | ||||
| 			name_pos) | ||||
|  | @ -328,9 +323,7 @@ fn (mut p Parser) struct_decl() ast.StructDecl { | |||
| 		p.error_with_pos('invalid recursive struct `$orig_name`', name_pos) | ||||
| 		return ast.StructDecl{} | ||||
| 	} | ||||
| 	mut ret := 0 | ||||
| 	// println('reg type symbol $name mod=$p.mod')
 | ||||
| 	ret = p.table.register_sym(t) | ||||
| 	mut ret := p.table.register_sym(t) | ||||
| 	// allow duplicate c struct declarations
 | ||||
| 	if ret == -1 && language != .c { | ||||
| 		p.error_with_pos('cannot register struct `$name`, another type with this name exists', | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue