| 1 | // Copyright (c) 2019-2024 Alexander Medvednikov. All rights reserved. |
| 2 | // Use of this source code is governed by an MIT license |
| 3 | // that can be found in the LICENSE file. |
| 4 | module c |
| 5 | |
| 6 | import v.ast |
| 7 | import v.token |
| 8 | import v.util |
| 9 | import strings |
| 10 | |
| 11 | // TODO: replace with comptime code generation. |
| 12 | // TODO: remove cJSON dependency. |
| 13 | |
| 14 | // Old: |
| 15 | // `User decode_User(string js) {` |
| 16 | // now it's: |
| 17 | // ``` |
| 18 | // User decode_User(cJSON* root) { |
| 19 | // User res; |
| 20 | // res.name = decode_string(js_get(root, "name")); |
| 21 | // res.profile = decode_Profile(js_get(root, "profile")); |
| 22 | // return res; |
| 23 | // } |
| 24 | // ``` |
| 25 | |
| 26 | // Codegen json_decode/encode funcs |
| 27 | fn (mut g Gen) gen_json_for_type(typ ast.Type) { |
| 28 | utyp := g.unwrap_generic(typ) |
| 29 | sym := g.table.sym(utyp) |
| 30 | if is_js_prim(sym.name) && !utyp.has_flag(.option) && !typ.is_ptr() { |
| 31 | return |
| 32 | } |
| 33 | if g.json_gen_pos != token.Pos{} |
| 34 | && (utyp !in g.json_types_pos || g.json_types_pos[utyp] == token.Pos{}) { |
| 35 | g.json_types_pos[utyp] = g.json_gen_pos |
| 36 | } |
| 37 | g.json_types << utyp |
| 38 | } |
| 39 | |
| 40 | fn (mut g Gen) gen_json_for_type_with_pos(typ ast.Type, pos token.Pos) { |
| 41 | saved_json_gen_pos := g.json_gen_pos |
| 42 | g.json_gen_pos = pos |
| 43 | g.gen_json_for_type(typ) |
| 44 | g.json_gen_pos = saved_json_gen_pos |
| 45 | } |
| 46 | |
| 47 | fn (mut g Gen) json_error(typ ast.Type, s string) { |
| 48 | utyp := g.unwrap_generic(typ) |
| 49 | mut pos := g.json_gen_pos |
| 50 | if utyp in g.json_types_pos { |
| 51 | pos = g.json_types_pos[utyp] |
| 52 | } |
| 53 | if pos != token.Pos{} { |
| 54 | g.error(s, pos) |
| 55 | } |
| 56 | verror(s) |
| 57 | } |
| 58 | |
| 59 | fn (mut g Gen) gen_jsons() { |
| 60 | mut done := []ast.Type{} |
| 61 | for i := 0; i < g.json_types.len; i++ { |
| 62 | utyp := g.json_types[i] |
| 63 | if utyp in done { |
| 64 | continue |
| 65 | } |
| 66 | done << utyp |
| 67 | saved_json_gen_pos := g.json_gen_pos |
| 68 | g.json_gen_pos = g.json_types_pos[utyp] |
| 69 | mut dec := strings.new_builder(100) |
| 70 | mut enc := strings.new_builder(100) |
| 71 | sym := g.table.sym(utyp) |
| 72 | styp := g.styp(utyp) |
| 73 | ret_styp := styp.replace('*', '_ptr') |
| 74 | if utyp.is_ptr() && utyp.has_flag(.option) { |
| 75 | g.register_option(utyp.set_nr_muls(0)) |
| 76 | } |
| 77 | g.register_result(utyp) |
| 78 | |
| 79 | // decode_TYPE funcs receive an actual cJSON* object to decode |
| 80 | // cJSON_Parse(str) call is added by the compiler |
| 81 | // Codegen decoder |
| 82 | dec_fn_name := js_dec_name(styp) |
| 83 | dec_fn_dec := '${result_name}_${ret_styp} ${dec_fn_name}(cJSON* root)' |
| 84 | |
| 85 | mut init_styp := '${styp} res' |
| 86 | if utyp.has_flag(.option) { |
| 87 | none_str := g.expr_string(ast.None{}) |
| 88 | init_styp += ' = (${styp}){ .state=2, .err=${none_str}, .data={E_STRUCT} }' |
| 89 | } else { |
| 90 | if !utyp.is_ptr() { |
| 91 | init_styp += ' = ' |
| 92 | g.set_current_pos_as_last_stmt_pos() |
| 93 | pos := g.out.len |
| 94 | g.write(g.type_default(utyp)) |
| 95 | init_generated := g.out.cut_to(pos).trim_space() |
| 96 | if g.type_default_vars.len > 0 { |
| 97 | saved_init := init_styp |
| 98 | init_styp = g.type_default_vars.bytestr() |
| 99 | init_styp += '\n' |
| 100 | init_styp += saved_init |
| 101 | g.type_default_vars.clear() |
| 102 | } |
| 103 | init_styp += init_generated |
| 104 | } else if utyp.is_ptr() { |
| 105 | ptr_styp := g.styp(utyp.set_nr_muls(utyp.nr_muls() - 1)) |
| 106 | init_styp += ' = HEAP(${ptr_styp}, {0})' |
| 107 | } |
| 108 | } |
| 109 | |
| 110 | dec.writeln(' |
| 111 | ${dec_fn_dec} { |
| 112 | ${init_styp}; |
| 113 | if (!root) { |
| 114 | const char *error_ptr = cJSON_GetErrorPtr(); |
| 115 | if (error_ptr != NULL) { |
| 116 | const ${ast.int_type_name} error_pos = (${ast.int_type_name})cJSON_GetErrorPos(); |
| 117 | ${ast.int_type_name} maxcontext_chars = 30; |
| 118 | byte *buf = builtin__vcalloc_noscan(maxcontext_chars + 10); |
| 119 | if (error_pos > 0) { |
| 120 | ${ast.int_type_name} backlines = 1; |
| 121 | ${ast.int_type_name} backchars = error_pos < maxcontext_chars-7 ? (${ast.int_type_name})error_pos : maxcontext_chars-7 ; |
| 122 | char *prevline_ptr = (char*)error_ptr; |
| 123 | while(backchars--){ |
| 124 | char prevc = *(prevline_ptr - 1); |
| 125 | if(0==prevc){ |
| 126 | break; |
| 127 | } |
| 128 | if(10==prevc && !backlines--){ |
| 129 | break; |
| 130 | } |
| 131 | prevline_ptr--; |
| 132 | if(123==prevc) { |
| 133 | break; // stop at `{` too |
| 134 | } |
| 135 | } |
| 136 | ${ast.int_type_name} maxchars = builtin__vstrlen_char(prevline_ptr); |
| 137 | builtin__vmemcpy(buf, prevline_ptr, (maxchars < maxcontext_chars ? maxchars : maxcontext_chars)); |
| 138 | } |
| 139 | string msg; |
| 140 | msg = _S("failed to decode JSON string"); |
| 141 | if (buf[0] != \'\\0\') { |
| 142 | msg = builtin__string__plus(msg, _S(": ")); |
| 143 | } |
| 144 | return (${result_name}_${ret_styp}){.is_error = true,.err = builtin___v_error(builtin__string__plus(msg, builtin__tos2(buf))),.data = {0}}; |
| 145 | } |
| 146 | } |
| 147 | ') |
| 148 | |
| 149 | if utyp.has_flag(.option) { |
| 150 | dec.writeln('\tif (cJSON_IsNull(root)) {') |
| 151 | dec.writeln('\t${result_name}_${ret_styp} ret;') |
| 152 | dec.writeln('\tbuiltin___result_ok(&res, (${result_name}*)&ret, sizeof(res));') |
| 153 | dec.writeln('\treturn ret;') |
| 154 | dec.writeln('\t}') |
| 155 | |
| 156 | base_type := utyp.clear_flag(.option) |
| 157 | base_type_str := g.styp(base_type) |
| 158 | // Optional struct pointers need storage before field decoding writes through them. |
| 159 | base_value := if base_type.is_ptr() && sym.info is ast.Struct { |
| 160 | ptr_styp := g.styp(base_type.set_nr_muls(base_type.nr_muls() - 1)) |
| 161 | 'HEAP(${ptr_styp}, {0})' |
| 162 | } else { |
| 163 | g.type_default(base_type) |
| 164 | } |
| 165 | dec.writeln('\tbuiltin___option_ok(&(${base_type_str}[]){ ${base_value} }, (${styp}*)&res, sizeof(${base_type_str}));\n') |
| 166 | } |
| 167 | |
| 168 | extern_str := if g.pref.parallel_cc { 'extern ' } else { '' } |
| 169 | g.json_forward_decls.writeln('${extern_str}${dec_fn_dec};') |
| 170 | // Codegen encoder |
| 171 | // encode_TYPE funcs receive an object to encode |
| 172 | enc_fn_name := js_enc_name(styp) |
| 173 | enc_fn_dec := 'cJSON* ${enc_fn_name}(${styp} val)' |
| 174 | g.json_forward_decls.writeln('${extern_str}${enc_fn_dec};\n') |
| 175 | enc.writeln(' |
| 176 | ${enc_fn_dec} { |
| 177 | \tcJSON *o;') |
| 178 | if utyp.is_ptr() && !utyp.has_flag(.option) { |
| 179 | enc.writeln('\tif (val == 0) {') |
| 180 | enc.writeln('\t\treturn cJSON_CreateNull();') |
| 181 | enc.writeln('\t}') |
| 182 | } |
| 183 | if is_js_prim(sym.name) && utyp.is_ptr() { |
| 184 | g.gen_prim_enc_dec(utyp, mut enc, mut dec) |
| 185 | } else if sym.kind in [.array, .array_fixed] { |
| 186 | array_size := if sym.kind == .array_fixed { |
| 187 | (sym.info as ast.ArrayFixed).size |
| 188 | } else { |
| 189 | -1 |
| 190 | } |
| 191 | // Handle arrays |
| 192 | value_type := g.table.value_type(utyp) |
| 193 | // If we have `[]Profile`, have to register a Profile en(de)coder first |
| 194 | g.gen_json_for_type(value_type) |
| 195 | dec.writeln(g.decode_array(utyp, value_type, array_size, ret_styp)) |
| 196 | enc.writeln(g.encode_array(utyp, value_type, array_size)) |
| 197 | } else if sym.kind == .map { |
| 198 | // Handle maps |
| 199 | m := sym.info as ast.Map |
| 200 | g.gen_json_for_type(m.key_type) |
| 201 | g.gen_json_for_type(m.value_type) |
| 202 | dec.writeln(g.decode_map(utyp, m.key_type, m.value_type, ret_styp)) |
| 203 | enc.writeln(g.encode_map(utyp, m.key_type, m.value_type)) |
| 204 | } else if sym.kind == .alias { |
| 205 | a := sym.info as ast.Alias |
| 206 | parent_typ := a.parent_type |
| 207 | psym := g.table.sym(parent_typ) |
| 208 | if is_js_prim(g.styp(parent_typ)) { |
| 209 | if utyp.has_flag(.option) { |
| 210 | g.gen_json_for_type(parent_typ.set_flag(.option)) |
| 211 | g.gen_option_enc_dec(parent_typ.set_flag(.option), mut enc, mut dec) |
| 212 | } else { |
| 213 | g.gen_json_for_type(parent_typ) |
| 214 | g.gen_prim_enc_dec(parent_typ, mut enc, mut dec) |
| 215 | } |
| 216 | } else if psym.info is ast.Struct { |
| 217 | enc.writeln('\to = cJSON_CreateObject();') |
| 218 | gen_struct_root_validation(ret_styp, mut dec) |
| 219 | g.gen_struct_enc_dec(utyp, psym.info, ret_styp, mut enc, mut dec, '') |
| 220 | } else if psym.kind == .enum { |
| 221 | g.gen_enum_enc_dec(utyp, psym, mut enc, mut dec) |
| 222 | } else if psym.kind == .sum_type { |
| 223 | g.json_error(utyp, 'json: ${sym.name} aliased sumtypes does not work at the moment') |
| 224 | } else if psym.kind == .map { |
| 225 | m := psym.info as ast.Map |
| 226 | g.gen_json_for_type(m.key_type) |
| 227 | g.gen_json_for_type(m.value_type) |
| 228 | dec.writeln(g.decode_map(utyp, m.key_type, m.value_type, ret_styp)) |
| 229 | enc.writeln(g.encode_map(utyp, m.key_type, m.value_type)) |
| 230 | } else if utyp.has_flag(.option) { |
| 231 | g.gen_option_enc_dec(utyp, mut enc, mut dec) |
| 232 | } else { |
| 233 | g.json_error(utyp, 'json: ${sym.name} is not struct') |
| 234 | } |
| 235 | } else if sym.kind == .sum_type { |
| 236 | enc.writeln('\to = cJSON_CreateObject();') |
| 237 | // Sumtypes. Range through variants of sumtype |
| 238 | if sym.info !is ast.SumType { |
| 239 | g.json_error(utyp, 'json: ${sym.name} is not a sumtype') |
| 240 | } |
| 241 | g.gen_sumtype_enc_dec(utyp, sym, mut enc, mut dec, ret_styp) |
| 242 | } else if sym.kind == .enum { |
| 243 | g.gen_enum_enc_dec(utyp, sym, mut enc, mut dec) |
| 244 | } else if utyp.has_flag(.option) |
| 245 | && (is_js_prim(g.styp(utyp.clear_flag(.option))) || sym.info !is ast.Struct) { |
| 246 | g.gen_option_enc_dec(utyp, mut enc, mut dec) |
| 247 | } else { |
| 248 | enc.writeln('\to = cJSON_CreateObject();') |
| 249 | // Structs. Range through fields |
| 250 | if sym.info !is ast.Struct { |
| 251 | g.json_error(utyp, 'json: ${sym.name} is not struct') |
| 252 | } |
| 253 | gen_struct_root_validation(ret_styp, mut dec) |
| 254 | g.gen_struct_enc_dec(utyp, sym.info, ret_styp, mut enc, mut dec, '') |
| 255 | } |
| 256 | // cJSON_delete |
| 257 | dec.writeln('\t${result_name}_${ret_styp} ret;') |
| 258 | dec.writeln('\tbuiltin___result_ok(&res, (${result_name}*)&ret, sizeof(res));') |
| 259 | dec.writeln('\treturn ret;\n}') |
| 260 | enc.writeln('\treturn o;\n}') |
| 261 | g.gowrappers.writeln(dec.str()) |
| 262 | g.gowrappers.writeln(enc.str()) |
| 263 | g.json_gen_pos = saved_json_gen_pos |
| 264 | } |
| 265 | } |
| 266 | |
| 267 | @[inline] |
| 268 | fn (mut g Gen) gen_enum_to_str(utyp ast.Type, sym ast.TypeSymbol, enum_var string, result_var string, ident string, |
| 269 | mut enc strings.Builder) { |
| 270 | enum_prefix := g.gen_enum_prefix(utyp.clear_flag(.option)) |
| 271 | enc.writeln('${ident}switch (${enum_var}) {') |
| 272 | for val in (sym.info as ast.Enum).vals { |
| 273 | enc.write_string('${ident}\tcase ${enum_prefix}${val}:\t') |
| 274 | // read [json:] attr from the Enum value |
| 275 | attr := g.table.enum_decls[sym.name].fields.filter(it.name == val)[0].attrs.find_first('json') or { |
| 276 | ast.Attr{} |
| 277 | } |
| 278 | if attr.has_arg { |
| 279 | enc.writeln('${result_var} = json__encode_string(_S("${attr.arg}")); break;') |
| 280 | } else { |
| 281 | enc.writeln('${result_var} = json__encode_string(_S("${val}")); break;') |
| 282 | } |
| 283 | } |
| 284 | enc.writeln('${ident}}') |
| 285 | } |
| 286 | |
| 287 | @[inline] |
| 288 | fn (mut g Gen) gen_str_to_enum(utyp ast.Type, sym ast.TypeSymbol, val_var string, result_var string, ident string, |
| 289 | mut dec strings.Builder) { |
| 290 | enum_prefix := g.gen_enum_prefix(utyp.clear_flag(.option)) |
| 291 | is_option := utyp.has_flag(.option) |
| 292 | for k, val in (sym.info as ast.Enum).vals { |
| 293 | // read [json:] attr from the Enum value |
| 294 | attr := g.table.enum_decls[sym.name].fields.filter(it.name == val)[0].attrs.find_first('json') or { |
| 295 | ast.Attr{} |
| 296 | } |
| 297 | if k == 0 { |
| 298 | dec.write_string('${ident}if (builtin__string__eq(_S("${val}"), ${val_var})') |
| 299 | } else { |
| 300 | dec.write_string('${ident}else if (builtin__string__eq(_S("${val}"), ${val_var})') |
| 301 | } |
| 302 | if attr.has_arg { |
| 303 | dec.write_string(' || builtin__string__eq(_S("${attr.arg}"), ${val_var})') |
| 304 | } |
| 305 | dec.write_string(')\t') |
| 306 | if is_option { |
| 307 | base_typ := g.base_type(utyp) |
| 308 | dec.writeln('builtin___option_ok(&(${base_typ}[]){ ${enum_prefix}${val} }, (${option_name}*)${result_var}, sizeof(${base_typ}));') |
| 309 | } else { |
| 310 | dec.writeln('${result_var} = ${enum_prefix}${val};') |
| 311 | } |
| 312 | } |
| 313 | } |
| 314 | |
| 315 | @[inline] |
| 316 | fn (mut g Gen) is_enum_as_int(sym ast.TypeSymbol) bool { |
| 317 | if enum_decl := g.table.enum_decls[sym.name] { |
| 318 | if _ := enum_decl.attrs.find_first('json_as_number') { |
| 319 | return true |
| 320 | } |
| 321 | } |
| 322 | return false |
| 323 | } |
| 324 | |
| 325 | @[inline] |
| 326 | fn (mut g Gen) gen_enum_enc_dec(utyp ast.Type, sym ast.TypeSymbol, mut enc strings.Builder, mut dec strings.Builder) { |
| 327 | is_option := utyp.has_flag(.option) |
| 328 | |
| 329 | if g.is_enum_as_int(sym) { |
| 330 | if is_option { |
| 331 | base_typ := g.styp(utyp.clear_flag(.option)) |
| 332 | enc.writeln('\to = ${js_enc_name('u64')}(*val.data);') |
| 333 | dec.writeln('\tbuiltin___option_ok(&(${base_typ}[]){ ${js_dec_name('u64')}(root) }, (${option_name}*)&res, sizeof(${base_typ}));') |
| 334 | } else { |
| 335 | dec.writeln('\tres = ${js_dec_name('u64')}(root);') |
| 336 | enc.writeln('\to = ${js_enc_name('u64')}(val);') |
| 337 | } |
| 338 | } else { |
| 339 | tmp := g.new_tmp_var() |
| 340 | dec.writeln('\tstring ${tmp} = ${js_dec_name('string')}(root);') |
| 341 | if is_option { |
| 342 | g.gen_str_to_enum(utyp, sym, tmp, '&res', '\t', mut dec) |
| 343 | g.gen_enum_to_str(utyp, sym, '*(${g.base_type(utyp)}*)val.data', 'o', '\t\t', mut enc) |
| 344 | } else { |
| 345 | g.gen_str_to_enum(utyp, sym, tmp, 'res', '\t', mut dec) |
| 346 | g.gen_enum_to_str(utyp, sym, 'val', 'o', '\t', mut enc) |
| 347 | } |
| 348 | } |
| 349 | } |
| 350 | |
| 351 | @[inline] |
| 352 | fn (mut g Gen) gen_prim_enc_dec(typ ast.Type, mut enc strings.Builder, mut dec strings.Builder) { |
| 353 | if typ.is_ptr() { |
| 354 | type_str := g.styp(typ.clear_flag(.option).set_nr_muls(typ.nr_muls() - 1)) |
| 355 | type_str_0 := g.styp(typ.clear_flag(.option).set_nr_muls(0)) |
| 356 | encode_name := js_enc_name(type_str_0) |
| 357 | dec_name := js_dec_name(type_str) |
| 358 | if typ.has_flag(.option) { |
| 359 | enc.writeln('\to = ${encode_name}(${'*'.repeat(typ.nr_muls() + 1)}(${type_str_0}${'*'.repeat(typ.nr_muls())}*)&val.data);') |
| 360 | } else { |
| 361 | enc.writeln('\to = ${encode_name}(${'*'.repeat(typ.nr_muls())}val);') |
| 362 | } |
| 363 | |
| 364 | if typ.nr_muls() > 1 { |
| 365 | g.gen_json_for_type(typ.clear_flag(.option).set_nr_muls(typ.nr_muls() - 1)) |
| 366 | if typ.has_flag(.option) { |
| 367 | tmp_var := g.new_tmp_var() |
| 368 | dec.writeln('${type_str}* ${tmp_var} = HEAP(${type_str}, *(${type_str}*) ${dec_name}(root).data);') |
| 369 | dec.writeln('\tbuiltin___option_ok(&(${type_str}*[]) { &(*(${tmp_var})) }, (${option_name}*)&res, sizeof(${type_str}*));') |
| 370 | } else { |
| 371 | dec.writeln('\tres = HEAP(${type_str}, *(${type_str}*) ${dec_name}(root).data);') |
| 372 | } |
| 373 | } else { |
| 374 | if typ.has_flag(.option) { |
| 375 | tmp_var := g.new_tmp_var() |
| 376 | dec.writeln('${type_str}* ${tmp_var} = HEAP(${type_str}, ${dec_name}(root));') |
| 377 | dec.writeln('\tbuiltin___option_ok(&(${type_str}*[]) { &(*(${tmp_var})) }, (${option_name}*)&res, sizeof(${type_str}*));') |
| 378 | } else { |
| 379 | dec.writeln('\tres = HEAP(${type_str}, ${dec_name}(root));') |
| 380 | } |
| 381 | } |
| 382 | } else { |
| 383 | type_str := g.styp(typ.clear_flag(.option)) |
| 384 | encode_name := js_enc_name(type_str) |
| 385 | dec_name := js_dec_name(type_str) |
| 386 | enc.writeln('\to = ${encode_name}(val);') |
| 387 | dec.writeln('\tres = ${dec_name}(root);') |
| 388 | } |
| 389 | } |
| 390 | |
| 391 | fn gen_struct_root_validation(ret_styp string, mut dec strings.Builder) { |
| 392 | dec.writeln('\tif (!cJSON_IsObject(root) && !cJSON_IsNull(root) && !cJSON_IsArray(root)) {') |
| 393 | dec.writeln('\t\treturn (${result_name}_${ret_styp}){ .is_error = true, .err = builtin___v_error(builtin__string__plus(_S("Json element is not an object: "), json__json_print(root))), .data = {0} };') |
| 394 | dec.writeln('\t}') |
| 395 | } |
| 396 | |
| 397 | @[inline] |
| 398 | fn (mut g Gen) gen_option_enc_dec(typ ast.Type, mut enc strings.Builder, mut dec strings.Builder) { |
| 399 | enc.writeln('\tif (val.state == 2) {') |
| 400 | enc.writeln('\t\treturn NULL;') |
| 401 | enc.writeln('\t}') |
| 402 | type_str := g.styp(typ.clear_flag(.option)) |
| 403 | encode_name := js_enc_name(type_str) |
| 404 | enc.writeln('\to = ${encode_name}(*(${type_str}*)val.data);') |
| 405 | |
| 406 | dec_name := js_dec_name(type_str) |
| 407 | dec.writeln('\tif (!cJSON_IsNull(root)) {') |
| 408 | dec.writeln('\t\tbuiltin___option_ok(&(${type_str}[]){ ${dec_name}(root) }, (${option_name}*)&res, sizeof(${type_str}));') |
| 409 | dec.writeln('\t} else {') |
| 410 | default_init := if typ.is_int() || typ.is_float() || typ.is_bool() { '0' } else { '{0}' } |
| 411 | dec.writeln('\t\tbuiltin___option_none(&(${type_str}[]){ ${default_init} }, (${option_name}*)&res, sizeof(${type_str}));') |
| 412 | dec.writeln('\t}') |
| 413 | } |
| 414 | |
| 415 | @[inline] |
| 416 | fn (mut g Gen) gen_sumtype_variant_decode(variant_typ string, sumtype_cname string, ret_styp string, |
| 417 | prefix string, is_option bool, decoded_var string, indent string, mut dec strings.Builder) { |
| 418 | tmp := g.new_tmp_var() |
| 419 | dec.writeln('${indent}${result_name}_${variant_typ} ${tmp} = ${js_dec_name(variant_typ)}(root);') |
| 420 | dec.writeln('${indent}if (${tmp}.is_error) {') |
| 421 | dec.writeln('${indent}\treturn (${result_name}_${ret_styp}){ .is_error = true, .err = ${tmp}.err, .data = {0} };') |
| 422 | dec.writeln('${indent}}') |
| 423 | if is_option { |
| 424 | dec.writeln('${indent}builtin___option_ok(&(${sumtype_cname}[]){ ${variant_typ}_to_sumtype_${sumtype_cname}((${variant_typ}*)${tmp}.data, false) }, (${option_name}*)&res, sizeof(${sumtype_cname}));') |
| 425 | } else { |
| 426 | dec.writeln('${indent}${prefix}res = ${variant_typ}_to_sumtype_${sumtype_cname}((${variant_typ}*)${tmp}.data, false);') |
| 427 | } |
| 428 | dec.writeln('${indent}${decoded_var} = true;') |
| 429 | } |
| 430 | |
| 431 | @[inline] |
| 432 | fn (mut g Gen) gen_sumtype_enc_dec(utyp ast.Type, sym ast.TypeSymbol, mut enc strings.Builder, mut dec strings.Builder, |
| 433 | ret_styp string) { |
| 434 | info := sym.info as ast.SumType |
| 435 | type_var := g.new_tmp_var() |
| 436 | typ := ast.idx_to_type(g.table.type_idxs[sym.name]) |
| 437 | prefix := if utyp.is_ptr() { '*' } else { '' } |
| 438 | field_op := if utyp.is_ptr() { '->' } else { '.' } |
| 439 | is_option := utyp.has_flag(.option) |
| 440 | var_data := if is_option { '(*(${g.base_type(utyp)}*)val.data)' } else { 'val' } |
| 441 | decoded_var := g.new_tmp_var() |
| 442 | dec.writeln('\tbool ${decoded_var} = false;') |
| 443 | |
| 444 | // DECODING (inline) |
| 445 | $if !json_no_inline_sumtypes ? { |
| 446 | // Handle "key": null |
| 447 | // In this case the first variant must be used (something like InvalidExpr for example) |
| 448 | // An empty instance of the first variant is generated. |
| 449 | // This way the user can easily check if the sum type was not provided in json ("key":null): |
| 450 | // `if node.expr is InvalidExpr { ... }` |
| 451 | // (Do this only for structs) |
| 452 | mut null_option_variant_typ := '' |
| 453 | mut null_option_variant_count := 0 |
| 454 | for variant in info.variants { |
| 455 | if variant.has_flag(.option) { |
| 456 | null_option_variant_typ = g.styp(variant) |
| 457 | null_option_variant_count++ |
| 458 | } |
| 459 | } |
| 460 | type_tmp := g.new_tmp_var() |
| 461 | first_variant := info.variants[0] |
| 462 | variant_typ := g.styp(first_variant) |
| 463 | fv_sym := g.table.sym(first_variant) |
| 464 | first_variant_name := fv_sym.cname |
| 465 | // println('KIND=${fv_sym.kind}') |
| 466 | if null_option_variant_count == 1 { |
| 467 | null_option_tmp := g.new_tmp_var() |
| 468 | dec.writeln('\tif (root->type == cJSON_NULL) {') |
| 469 | dec.writeln('\t\t${result_name}_${null_option_variant_typ} ${null_option_tmp} = ${js_dec_name(null_option_variant_typ)}(root);') |
| 470 | dec.writeln('\t\tif (${null_option_tmp}.is_error) {') |
| 471 | dec.writeln('\t\t\treturn (${result_name}_${ret_styp}){ .is_error = true, .err = ${null_option_tmp}.err, .data = {0} };') |
| 472 | dec.writeln('\t\t}') |
| 473 | if is_option { |
| 474 | dec.writeln('\t\tbuiltin___option_ok(&(${sym.cname}[]){ ${null_option_variant_typ}_to_sumtype_${sym.cname}((${null_option_variant_typ}*)${null_option_tmp}.data, false) }, (${option_name}*)&res, sizeof(${sym.cname}));') |
| 475 | } else { |
| 476 | dec.writeln('\t\tres = ${null_option_variant_typ}_to_sumtype_${ret_styp}((${null_option_variant_typ}*)${null_option_tmp}.data, false);') |
| 477 | } |
| 478 | dec.writeln('\t\t${decoded_var} = true;') |
| 479 | dec.writeln('\t} \n else ') |
| 480 | } else if fv_sym.kind == .struct && !is_option && field_op != '->' { |
| 481 | dec.writeln('\tif (root->type == cJSON_NULL) { ') |
| 482 | dec.writeln('\t\tstruct ${first_variant_name} empty = {0};') |
| 483 | dec.writeln('\t\t${decoded_var} = true;') |
| 484 | dec.writeln('res = ${variant_typ}_to_sumtype_${ret_styp}(&empty, false); } \n else ') |
| 485 | // dec.writeln('res = ${variant_typ}_to_sumtype_${sym.cname}(&empty, false); } \n else ') |
| 486 | } |
| 487 | // |
| 488 | dec.writeln('if (cJSON_IsObject(root) || (cJSON_IsArray(root) && cJSON_IsObject(root->child))) {') |
| 489 | dec.writeln('\t\tcJSON* ${type_tmp} = cJSON_IsObject(root) ? js_get(root, "_type") : js_get(root->child, "_type");') |
| 490 | dec.writeln('\t\tif (${type_tmp} != 0) {') |
| 491 | dec.writeln('\t\t\tchar* ${type_var} = cJSON_GetStringValue(${type_tmp});') |
| 492 | // dec.writeln('\t\t\tcJSON_DeleteItemFromObjectCaseSensitive(root, "_type");') |
| 493 | } |
| 494 | |
| 495 | mut variant_types := []string{} |
| 496 | mut variant_symbols := []ast.TypeSymbol{} |
| 497 | mut at_least_one_prim := false |
| 498 | mut object_variant_count := 0 |
| 499 | mut fallback_object_variant_typ := '' |
| 500 | for variant in info.variants { |
| 501 | variant_typ := g.styp(variant) |
| 502 | variant_types << variant_typ |
| 503 | variant_sym := g.table.sym(variant) |
| 504 | variant_symbols << variant_sym |
| 505 | final_variant_sym := g.table.final_sym(variant) |
| 506 | at_least_one_prim = at_least_one_prim || is_js_prim(variant_typ) |
| 507 | || variant_sym.kind == .enum || variant_sym.name == 'time.Time' |
| 508 | if variant_sym.name != 'time.Time' && final_variant_sym.kind in [.struct, .map] { |
| 509 | object_variant_count++ |
| 510 | if object_variant_count == 1 { |
| 511 | fallback_object_variant_typ = variant_typ |
| 512 | } else { |
| 513 | fallback_object_variant_typ = '' |
| 514 | } |
| 515 | } |
| 516 | unmangled_variant_name := variant_sym.name.split('.').last() |
| 517 | |
| 518 | // TODO: Do not generate dec/enc for 'time.Time', because we handle it by saving it as u64 |
| 519 | g.gen_json_for_type(variant) |
| 520 | |
| 521 | // Helpers for decoding |
| 522 | g.get_sumtype_casting_fn(variant, typ) |
| 523 | g.definitions.writeln('static inline ${sym.cname} ${variant_typ}_to_sumtype_${sym.cname}(${variant_typ}* x, bool is_mut);') |
| 524 | |
| 525 | // ENCODING |
| 526 | enc.writeln('\tif (${var_data}${field_op}_typ == ${g.type_sidx(variant)}) {') |
| 527 | $if json_no_inline_sumtypes ? { |
| 528 | if variant_sym.kind == .enum { |
| 529 | enc.writeln('\t\tcJSON_AddItemToObject(o, "${unmangled_variant_name}", ${js_enc_name('u64')}(*${var_data}${field_op}_${variant_typ}));') |
| 530 | } else if variant_sym.name == 'time.Time' { |
| 531 | enc.writeln('\t\tcJSON_AddItemToObject(o, "${unmangled_variant_name}", ${js_enc_name('i64')}(time__Time_unix(*${var_data}${field_op}_${variant_typ})));') |
| 532 | } else { |
| 533 | enc.writeln('\t\tcJSON_AddItemToObject(o, "${unmangled_variant_name}", ${js_enc_name(variant_typ)}(*${var_data}${field_op}_${variant_typ}));') |
| 534 | } |
| 535 | } $else { |
| 536 | if is_js_prim(variant_typ) { |
| 537 | enc.writeln('\t\tcJSON_free(o); return ${js_enc_name(variant_typ)}(*${var_data}${field_op}_${variant_typ});') |
| 538 | } else if variant_sym.kind == .enum { |
| 539 | if g.is_enum_as_int(variant_sym) { |
| 540 | enc.writeln('\t\tcJSON_free(o); return ${js_enc_name('u64')}(*${var_data}${field_op}_${variant_typ});') |
| 541 | } else { |
| 542 | enc.writeln('\t\tcJSON_free(o);') |
| 543 | tmp2 := g.new_tmp_var() |
| 544 | if utyp.has_flag(.option) { |
| 545 | enc.writeln('\t\tu64 ${tmp2} = *${var_data}${field_op}_${variant_typ};') |
| 546 | g.gen_enum_to_str(variant, variant_sym, tmp2, 'o', '\t\t', mut enc) |
| 547 | } else { |
| 548 | enc.writeln('\t\tu64 ${tmp2} = *${var_data}${field_op}_${variant_typ};') |
| 549 | g.gen_enum_to_str(variant, variant_sym, tmp2, 'o', '\t\t', mut enc) |
| 550 | } |
| 551 | } |
| 552 | } else if variant_sym.name == 'time.Time' { |
| 553 | enc.writeln('\t\tcJSON_AddItemToObject(o, "_type", cJSON_CreateString("${unmangled_variant_name}"));') |
| 554 | enc.writeln('\t\tcJSON_AddItemToObject(o, "value", ${js_enc_name('i64')}(time__Time_unix(*${var_data}${field_op}_${variant_typ})));') |
| 555 | } else { |
| 556 | encoded_variant := g.new_tmp_var() |
| 557 | enc.writeln('\t\tcJSON* ${encoded_variant} = ${js_enc_name(variant_typ)}(*${var_data}${field_op}_${variant_typ});') |
| 558 | if variant.has_flag(.option) { |
| 559 | enc.writeln('\t\tif (${encoded_variant} != NULL) {') |
| 560 | enc.writeln('\t\t\tcJSON_free(o);') |
| 561 | enc.writeln('\t\t\to = ${encoded_variant};') |
| 562 | if variant_sym.kind == .array { |
| 563 | enc.writeln('\t\t\tif (cJSON_IsObject(o->child)) {') |
| 564 | enc.writeln('\t\t\t\tcJSON_AddItemToObject(o->child, "_type", cJSON_CreateString("${unmangled_variant_name}"));') |
| 565 | enc.writeln('\t\t\t}') |
| 566 | } else { |
| 567 | enc.writeln('\t\t\tcJSON_AddItemToObject(o, "_type", cJSON_CreateString("${unmangled_variant_name}"));') |
| 568 | } |
| 569 | enc.writeln('\t\t}') |
| 570 | } else { |
| 571 | enc.writeln('\t\tcJSON_free(o);') |
| 572 | enc.writeln('\t\to = ${encoded_variant};') |
| 573 | if variant_sym.kind == .array { |
| 574 | enc.writeln('\t\tif (cJSON_IsObject(o->child)) {') |
| 575 | enc.writeln('\t\t\tcJSON_AddItemToObject(o->child, "_type", cJSON_CreateString("${unmangled_variant_name}"));') |
| 576 | enc.writeln('\t\t}') |
| 577 | } else { |
| 578 | enc.writeln('\t\tcJSON_AddItemToObject(o, "_type", cJSON_CreateString("${unmangled_variant_name}"));') |
| 579 | } |
| 580 | } |
| 581 | } |
| 582 | } |
| 583 | enc.writeln('\t}') |
| 584 | |
| 585 | // DECODING |
| 586 | tmp := g.new_tmp_var() |
| 587 | $if json_no_inline_sumtypes ? { |
| 588 | dec.writeln('\tif (strcmp("${unmangled_variant_name}", root->child->string) == 0) {') |
| 589 | if is_js_prim(variant_typ) { |
| 590 | gen_js_get(ret_styp, tmp, unmangled_variant_name, mut dec, true) |
| 591 | dec.writeln('\t\t${variant_typ} value = ${js_dec_name(variant_typ)}(jsonroot_${tmp});') |
| 592 | } else if variant_sym.kind == .enum { |
| 593 | if g.is_enum_as_int(variant_sym) { |
| 594 | gen_js_get(ret_styp, tmp, unmangled_variant_name, mut dec, true) |
| 595 | dec.writeln('\t\t${variant_typ} value = ${js_dec_name('u64')}(jsonroot_${tmp});') |
| 596 | } else { |
| 597 | gen_js_get(ret_styp, tmp, unmangled_variant_name, mut dec, true) |
| 598 | dec.writeln('\t\t${variant_typ} value;') |
| 599 | tmp2 := g.new_tmp_var() |
| 600 | dec.writeln('\t\tstring ${tmp2} = json__decode_string(jsonroot_${tmp});') |
| 601 | g.gen_enum_to_str(variant, variant_sym, tmp2, 'value', '\t\t', mut dec) |
| 602 | } |
| 603 | } else if variant_sym.name == 'time.Time' { |
| 604 | gen_js_get(ret_styp, tmp, unmangled_variant_name, mut dec, true) |
| 605 | dec.writeln('\t\t${variant_typ} value = time__unix(${js_dec_name('i64')}(jsonroot_${tmp}));') |
| 606 | } else { |
| 607 | gen_js_get_opt(js_dec_name(variant_typ), variant_typ, ret_styp, tmp, |
| 608 | unmangled_variant_name, mut dec, true) |
| 609 | dec.writeln('\t\t${variant_typ} value = *(${variant_typ}*)(${tmp}.data);') |
| 610 | } |
| 611 | if is_option { |
| 612 | dec.writeln('\t\t\tbuiltin___option_ok(&(${sym.cname}[]){ ${variant_typ}_to_sumtype_${sym.cname}(&value, false) }, (${option_name}*)&res, sizeof(${sym.cname}));') |
| 613 | } else { |
| 614 | dec.writeln('\t\tres = ${variant_typ}_to_sumtype_${ret_styp}(&value, false);') |
| 615 | } |
| 616 | dec.writeln('\t\t${decoded_var} = true;') |
| 617 | dec.writeln('\t}') |
| 618 | } $else { |
| 619 | if variant_sym.name == 'time.Time' { |
| 620 | dec.writeln('\t\t\tif (strcmp("Time", ${type_var}) == 0) {') |
| 621 | gen_js_get(ret_styp, tmp, 'value', mut dec, true) |
| 622 | dec.writeln('\t\t\t\t${variant_typ} ${tmp} = time__unix(${js_dec_name('i64')}(jsonroot_${tmp}));') |
| 623 | if utyp.has_flag(.option) { |
| 624 | dec.writeln('\t\t\t\t${prefix}res.state = 0;') |
| 625 | tmp_time_var := g.new_tmp_var() |
| 626 | dec.writeln('\t\t\t\t${g.base_type(utyp)} ${tmp_time_var} = ${variant_typ}_to_sumtype_${sym.cname}(&${tmp}, false);') |
| 627 | dec.writeln('\t\t\t\tbuiltin__vmemcpy(&${prefix}res.data, ${tmp_time_var}._time__Time, sizeof(${variant_typ}));') |
| 628 | } else { |
| 629 | dec.writeln('\t\t\t\t${prefix}res = ${variant_typ}_to_sumtype_${sym.cname}(&${tmp}, false);') |
| 630 | } |
| 631 | dec.writeln('\t\t\t\t${decoded_var} = true;') |
| 632 | dec.writeln('\t\t\t}') |
| 633 | } else if !is_js_prim(variant_typ) && variant_sym.kind != .enum { |
| 634 | dec.writeln('\t\t\tif (strcmp("${unmangled_variant_name}", ${type_var}) == 0 && ${variant_sym.kind == .array} == cJSON_IsArray(root)) {') |
| 635 | g.gen_sumtype_variant_decode(variant_typ, sym.cname, ret_styp, prefix, is_option, |
| 636 | decoded_var, '\t\t\t\t', mut dec) |
| 637 | dec.writeln('\t\t\t}') |
| 638 | } |
| 639 | } |
| 640 | } |
| 641 | |
| 642 | // DECODING (inline) |
| 643 | $if !json_no_inline_sumtypes ? { |
| 644 | if object_variant_count == 1 && fallback_object_variant_typ != '' { |
| 645 | dec.writeln('\t\t} else if (cJSON_IsObject(root)) {') |
| 646 | g.gen_sumtype_variant_decode(fallback_object_variant_typ, sym.cname, ret_styp, prefix, |
| 647 | is_option, decoded_var, '\t\t\t', mut dec) |
| 648 | } |
| 649 | dec.writeln('\t\t}') |
| 650 | |
| 651 | mut number_is_met := false |
| 652 | mut string_is_met := false |
| 653 | mut last_number_type := '' |
| 654 | |
| 655 | if at_least_one_prim { |
| 656 | dec.writeln('\t} else {') |
| 657 | |
| 658 | if 'bool' in variant_types { |
| 659 | var_t := 'bool' |
| 660 | dec.writeln('\t\tif (cJSON_IsBool(root)) {') |
| 661 | dec.writeln('\t\t\t${var_t} value = ${js_dec_name(var_t)}(root);') |
| 662 | dec.writeln('\t\t\t${prefix}res = ${var_t}_to_sumtype_${sym.cname}(&value, false);') |
| 663 | dec.writeln('\t\t\t${decoded_var} = true;') |
| 664 | dec.writeln('\t\t}') |
| 665 | } |
| 666 | |
| 667 | for i, var_t in variant_types { |
| 668 | if variant_symbols[i].kind == .enum { |
| 669 | if number_is_met { |
| 670 | var_num := var_t.replace('__', '.') |
| 671 | last_num := last_number_type.replace('__', '.') |
| 672 | verror_suggest_json_no_inline_sumtypes(sym.name, last_num, var_num) |
| 673 | } |
| 674 | number_is_met = true |
| 675 | last_number_type = var_t |
| 676 | dec.writeln('\t\tif (cJSON_IsNumber(root)) {') |
| 677 | dec.writeln('\t\t\t${var_t} value = ${js_dec_name('u64')}(root);') |
| 678 | if utyp.has_flag(.option) { |
| 679 | dec.writeln('\t\t\tbuiltin___option_ok(&(${sym.cname}[]){ ${var_t}_to_sumtype_${sym.cname}(&value, false) }, (${option_name}*)&${prefix}res, sizeof(${sym.cname}));') |
| 680 | } else { |
| 681 | dec.writeln('\t\t\t${prefix}res = ${var_t}_to_sumtype_${sym.cname}(&value, false);') |
| 682 | } |
| 683 | dec.writeln('\t\t\t${decoded_var} = true;') |
| 684 | dec.writeln('\t\t}') |
| 685 | } |
| 686 | |
| 687 | if var_t in ['string', 'rune'] { |
| 688 | if string_is_met { |
| 689 | var_num := var_t.replace('__', '.') |
| 690 | verror_suggest_json_no_inline_sumtypes(sym.name, 'string', var_num) |
| 691 | } |
| 692 | string_is_met = true |
| 693 | dec.writeln('\t\tif (cJSON_IsString(root)) {') |
| 694 | dec.writeln('\t\t\t${var_t} value = ${js_dec_name(var_t)}(root);') |
| 695 | if utyp.has_flag(.option) { |
| 696 | dec.writeln('\t\t\tbuiltin___option_ok(&(${sym.cname}[]){ ${var_t}_to_sumtype_${sym.cname}(&value, false) }, (${option_name}*)&${prefix}res, sizeof(${sym.cname}));') |
| 697 | } else { |
| 698 | dec.writeln('\t\t\t${prefix}res = ${var_t}_to_sumtype_${sym.cname}(&value, false);') |
| 699 | } |
| 700 | dec.writeln('\t\t\t${decoded_var} = true;') |
| 701 | dec.writeln('\t\t}') |
| 702 | } |
| 703 | |
| 704 | if var_t.starts_with('Array_') { |
| 705 | tmp := g.new_tmp_var() |
| 706 | judge_elem_typ := if var_t.ends_with('string') { |
| 707 | 'cJSON_IsString(root->child)' |
| 708 | } else if var_t.ends_with('bool') { |
| 709 | 'cJSON_IsBool(root->child)' |
| 710 | } else if g.table.sym(g.table.value_type(ast.idx_to_type(variant_symbols[i].idx))).kind == .struct { |
| 711 | 'cJSON_IsObject(root->child)' |
| 712 | } else { |
| 713 | 'cJSON_IsNumber(root->child)' |
| 714 | } |
| 715 | dec.writeln('\t\tif (cJSON_IsArray(root) && ${judge_elem_typ}) {') |
| 716 | dec.writeln('\t\t\t${result_name}_${var_t} ${tmp} = ${js_dec_name(var_t)}(root);') |
| 717 | dec.writeln('\t\t\tif (${tmp}.is_error) {') |
| 718 | dec.writeln('\t\t\t\treturn (${result_name}_${ret_styp}){ .is_error = true, .err = ${tmp}.err, .data = {0} };') |
| 719 | dec.writeln('\t\t\t}') |
| 720 | if utyp.has_flag(.option) { |
| 721 | dec.writeln('\t\t\tbuiltin___option_ok(&(${sym.cname}[]){ ${var_t}_to_sumtype_${sym.cname}((${var_t}*)${tmp}.data, false) }, (${option_name}*)&${prefix}res, sizeof(${sym.cname}));') |
| 722 | } else { |
| 723 | dec.writeln('\t\t\t${prefix}res = ${var_t}_to_sumtype_${sym.cname}((${var_t}*)${tmp}.data, false);') |
| 724 | } |
| 725 | dec.writeln('\t\t\t${decoded_var} = true;') |
| 726 | dec.writeln('\t\t}') |
| 727 | } |
| 728 | |
| 729 | if var_t in ['i8', 'i16', 'i32', 'i64', ast.int_type_name, 'int', 'u8', 'u16', |
| 730 | 'u32', 'u64', 'byte', 'rune', 'f64', 'f32'] { |
| 731 | if number_is_met { |
| 732 | var_num := var_t.replace('__', '.') |
| 733 | last_num := last_number_type.replace('__', '.') |
| 734 | verror_suggest_json_no_inline_sumtypes(sym.name, last_num, var_num) |
| 735 | } |
| 736 | number_is_met = true |
| 737 | last_number_type = var_t |
| 738 | dec.writeln('\t\tif (cJSON_IsNumber(root)) {') |
| 739 | dec.writeln('\t\t\t${var_t} value = ${js_dec_name(var_t)}(root);') |
| 740 | if utyp.has_flag(.option) { |
| 741 | dec.writeln('\t\t\tbuiltin___option_ok(&(${sym.cname}[]){ ${var_t}_to_sumtype_${sym.cname}(&value, false) }, (${option_name}*)&${prefix}res, sizeof(${sym.cname}));') |
| 742 | } else { |
| 743 | dec.writeln('\t\t\t${prefix}res = ${var_t}_to_sumtype_${sym.cname}(&value, false);') |
| 744 | } |
| 745 | dec.writeln('\t\t\t${decoded_var} = true;') |
| 746 | dec.writeln('\t\t}') |
| 747 | } |
| 748 | } |
| 749 | } |
| 750 | dec.writeln('\t}') |
| 751 | } |
| 752 | dec.writeln('\tif (!${decoded_var}) {') |
| 753 | dec.writeln('\t\treturn (${result_name}_${ret_styp}){ .is_error = true, .err = builtin___v_error(builtin__string__plus(_S("cannot decode `${sym.name}` sum type from JSON value: "), json__json_print(root))), .data = {0} };') |
| 754 | dec.writeln('\t}') |
| 755 | } |
| 756 | |
| 757 | fn (mut g Gen) gen_prim_type_validation(name string, typ ast.Type, tmp string, is_required bool, ret_styp string, mut dec strings.Builder) { |
| 758 | none_check := if !is_required { 'cJSON_IsNull(jsonroot_${tmp}) || ' } else { '' } |
| 759 | type_check := if typ.is_int() || typ.is_float() { |
| 760 | '${none_check}cJSON_IsNumber(jsonroot_${tmp}) || (cJSON_IsString(jsonroot_${tmp}) && strlen(jsonroot_${tmp}->valuestring))' |
| 761 | } else if typ.is_string() { |
| 762 | '${none_check}(cJSON_IsString(jsonroot_${tmp}) || cJSON_IsObject(jsonroot_${tmp}) || cJSON_IsArray(jsonroot_${tmp}))' |
| 763 | } else if typ.is_bool() { |
| 764 | '${none_check}cJSON_IsBool(jsonroot_${tmp})' |
| 765 | } else { |
| 766 | '' |
| 767 | } |
| 768 | if type_check == '' { |
| 769 | return |
| 770 | } |
| 771 | dec.writeln('\t\tif (!(${type_check})) {') |
| 772 | dec.writeln('\t\t\treturn (${ret_styp}){ .is_error = true, .err = builtin___v_error(builtin__string__plus(_S("type mismatch for field \'${name}\', expecting `${g.table.type_to_str(typ)}` type, got: "), json__json_print(jsonroot_${tmp}))), .data = {0} };') |
| 773 | dec.writeln('\t\t}') |
| 774 | } |
| 775 | |
| 776 | @[inline] |
| 777 | fn (mut g Gen) gen_struct_enc_dec(utyp ast.Type, type_info ast.TypeInfo, styp string, mut enc strings.Builder, |
| 778 | mut dec strings.Builder, embed_prefix string) { |
| 779 | info := type_info as ast.Struct |
| 780 | for field in info.fields { |
| 781 | mut name := field.name |
| 782 | mut is_raw := false |
| 783 | mut is_skip := false |
| 784 | mut is_required := false |
| 785 | mut is_omit_empty := false |
| 786 | mut skip_embed := false |
| 787 | mut is_json_null := false |
| 788 | |
| 789 | for attr in field.attrs { |
| 790 | match attr.name { |
| 791 | 'json' { |
| 792 | if attr.arg == '-' { |
| 793 | // [json:'-'] |
| 794 | is_skip = true |
| 795 | } else { |
| 796 | name = attr.arg |
| 797 | } |
| 798 | } |
| 799 | 'skip' { |
| 800 | is_skip = true |
| 801 | } |
| 802 | 'raw' { |
| 803 | is_raw = true |
| 804 | } |
| 805 | 'required' { |
| 806 | is_required = true |
| 807 | } |
| 808 | 'omitempty' { |
| 809 | is_omit_empty = true |
| 810 | } |
| 811 | 'json_null' { |
| 812 | is_json_null = true |
| 813 | } |
| 814 | else {} |
| 815 | } |
| 816 | } |
| 817 | if is_skip { |
| 818 | continue |
| 819 | } |
| 820 | field_type := vint2int(g.styp(field.typ)) |
| 821 | field_sym := g.table.sym(field.typ) |
| 822 | op := if utyp.is_ptr() { '->' } else { '.' } |
| 823 | embed_member := if embed_prefix.len > 0 { '.${embed_prefix}' } else { '' } |
| 824 | prefix := if utyp.has_flag(.option) { |
| 825 | '(*(${g.base_type(utyp)}*)res${embed_member}.data)' |
| 826 | } else { |
| 827 | 'res${embed_member}' |
| 828 | } |
| 829 | // First generate decoding |
| 830 | if is_raw { |
| 831 | if field.typ.has_flag(.option) { |
| 832 | g.gen_json_for_type(field.typ) |
| 833 | base_typ := g.base_type(field.typ) |
| 834 | dec.writeln('\tif (js_get(root, "${name}") == NULL)') |
| 835 | default_init := if field.typ.is_int() || field.typ.is_float() || field.typ.is_bool() { |
| 836 | '0' |
| 837 | } else { |
| 838 | '{0}' |
| 839 | } |
| 840 | dec.writeln('\t\tbuiltin___option_none(&(${base_typ}[]) { ${default_init} }, (${option_name}*)&${prefix}${op}${c_name(field.name)}, sizeof(${base_typ}));') |
| 841 | dec.writeln('\telse') |
| 842 | dec.writeln('\t\tbuiltin___option_ok(&(${base_typ}[]) { json__json_print(js_get(root, "${name}")) }, (${option_name}*)&${prefix}${op}${c_name(field.name)}, sizeof(${base_typ}));') |
| 843 | } else { |
| 844 | dec.writeln('\t${prefix}${op}${c_name(field.name)} = json__json_print(js_get(root, "${name}"));') |
| 845 | } |
| 846 | } else { |
| 847 | // Now generate decoders for all field types in this struct |
| 848 | // need to do it here so that these functions are generated first |
| 849 | g.gen_json_for_type(field.typ) |
| 850 | dec_name := js_dec_name(field_type) |
| 851 | if is_js_prim(field_type) { |
| 852 | tmp := g.new_tmp_var() |
| 853 | gen_js_get(styp, tmp, name, mut dec, is_required) |
| 854 | dec.writeln('\tif (jsonroot_${tmp}) {') |
| 855 | if utyp.has_flag(.option) { |
| 856 | dec.writeln('\t\tres.state = 0;') |
| 857 | } |
| 858 | g.gen_prim_type_validation(field.name, field.typ, tmp, is_required, |
| 859 | '${result_name}_${styp}', mut dec) |
| 860 | dec.writeln('\t\t${prefix}${op}${c_name(field.name)} = ${dec_name}(jsonroot_${tmp});') |
| 861 | if field.has_default_expr { |
| 862 | dec.writeln('\t} else {') |
| 863 | dec.writeln('\t\t${prefix}${op}${c_name(field.name)} = ${g.expr_string_opt(field.typ, |
| 864 | field.default_expr)};') |
| 865 | } |
| 866 | dec.writeln('\t}') |
| 867 | } else if field_sym.kind == .enum { |
| 868 | tmp := g.new_tmp_var() |
| 869 | is_option_field := field.typ.has_flag(.option) |
| 870 | if field.typ.has_flag(.option) { |
| 871 | gen_js_get_opt(js_dec_name(field_type), field_type, styp, tmp, name, mut dec, |
| 872 | is_required) |
| 873 | dec.writeln('\tif (jsonroot_${tmp} && !cJSON_IsNull(jsonroot_${tmp})) {') |
| 874 | } else { |
| 875 | gen_js_get(styp, tmp, name, mut dec, is_required) |
| 876 | dec.writeln('\tif (jsonroot_${tmp}) {') |
| 877 | } |
| 878 | if g.is_enum_as_int(field_sym) { |
| 879 | if is_option_field { |
| 880 | base_typ := g.base_type(field.typ) |
| 881 | dec.writeln('\t\tbuiltin___option_ok(&(${base_typ}[]) { ${js_dec_name('u64')}(jsonroot_${tmp}) }, (${option_name}*)&${prefix}${op}${c_name(field.name)}, sizeof(${base_typ}));') |
| 882 | } else { |
| 883 | dec.writeln('\t\t${prefix}${op}${c_name(field.name)} = ${js_dec_name('u64')}(jsonroot_${tmp});') |
| 884 | } |
| 885 | } else { |
| 886 | if is_option_field { |
| 887 | base_typ := g.base_type(field.typ) |
| 888 | dec.writeln('\t\tbuiltin___option_ok(&(${base_typ}[]) { *(${base_typ}*)((${g.styp(field.typ)}*)${tmp}.data)->data }, (${option_name}*)&${prefix}${op}${c_name(field.name)}, sizeof(${base_typ}));') |
| 889 | } else { |
| 890 | tmp2 := g.new_tmp_var() |
| 891 | dec.writeln('\t\tstring ${tmp2} = json__decode_string(jsonroot_${tmp});') |
| 892 | g.gen_str_to_enum(field.typ, field_sym, tmp2, |
| 893 | '${prefix}${op}${c_name(field.name)}', '\t\t', mut dec) |
| 894 | } |
| 895 | } |
| 896 | if field.has_default_expr { |
| 897 | dec.writeln('\t} else {') |
| 898 | dec.writeln('\t\t${prefix}${op}${c_name(field.name)} = ${g.expr_string_opt(field.typ, |
| 899 | field.default_expr)};') |
| 900 | } |
| 901 | dec.writeln('\t}') |
| 902 | } else if field_sym.name == 'time.Time' { |
| 903 | // time struct requires special treatment |
| 904 | // it can be decoded from either a JSON string or number |
| 905 | tmp := g.new_tmp_var() |
| 906 | gen_js_get(styp, tmp, name, mut dec, is_required) |
| 907 | dec.writeln('\tif (jsonroot_${tmp}) {') |
| 908 | tmp_time_res := g.new_tmp_var() |
| 909 | dec.writeln('\t\t${result_name}_time__Time ${tmp_time_res} = json__decode_time(jsonroot_${tmp});') |
| 910 | dec.writeln('\t\tif (${tmp_time_res}.is_error) {') |
| 911 | dec.writeln('\t\t\treturn (${result_name}_${styp}){ .is_error = true, .err = ${tmp_time_res}.err, .data = {0} };') |
| 912 | dec.writeln('\t\t}') |
| 913 | if field.typ.has_flag(.option) { |
| 914 | dec.writeln('\t\tif (!(cJSON_IsNull(jsonroot_${tmp}))) {') |
| 915 | dec.writeln('\t\t\t${prefix}${op}${c_name(field.name)}.state = 0;') |
| 916 | tmp_time_var := g.new_tmp_var() |
| 917 | dec.writeln('\t\t\t${g.base_type(field.typ)} ${tmp_time_var} = *(time__Time*)${tmp_time_res}.data;') |
| 918 | dec.writeln('\t\t\tbuiltin__vmemcpy(&${prefix}${op}${c_name(field.name)}.data, &${tmp_time_var}, sizeof(${g.base_type(field.typ)}));') |
| 919 | dec.writeln('\t\t}') |
| 920 | } else { |
| 921 | dec.writeln('\t\t${prefix}${op}${c_name(field.name)} = *(time__Time*)${tmp_time_res}.data;') |
| 922 | if field.has_default_expr { |
| 923 | dec.writeln('\t} else {') |
| 924 | dec.writeln('\t\t${prefix}${op}${c_name(field.name)} = ${g.expr_string_opt(field.typ, |
| 925 | field.default_expr)};') |
| 926 | } |
| 927 | } |
| 928 | dec.writeln('\t}') |
| 929 | } else if field_sym.kind == .alias { |
| 930 | alias := field_sym.info as ast.Alias |
| 931 | parent_type := if field.typ.has_flag(.option) { |
| 932 | alias.parent_type.set_flag(.option) |
| 933 | } else { |
| 934 | alias.parent_type |
| 935 | } |
| 936 | sparent_type := g.styp(parent_type) |
| 937 | parent_dec_name := js_dec_name(sparent_type) |
| 938 | if is_js_prim(sparent_type) { |
| 939 | tmp := g.new_tmp_var() |
| 940 | gen_js_get(styp, tmp, name, mut dec, is_required) |
| 941 | dec.writeln('\tif (jsonroot_${tmp}) {') |
| 942 | g.gen_prim_type_validation(field.name, parent_type, tmp, is_required, |
| 943 | '${result_name}_${styp}', mut dec) |
| 944 | dec.writeln('\t\t${prefix}${op}${c_name(field.name)} = ${parent_dec_name} (jsonroot_${tmp});') |
| 945 | if field.has_default_expr { |
| 946 | dec.writeln('\t} else {') |
| 947 | dec.writeln('\t\t${prefix}${op}${c_name(field.name)} = ${g.expr_string_opt(field.typ, |
| 948 | field.default_expr)};') |
| 949 | } |
| 950 | dec.writeln('\t}') |
| 951 | } else { |
| 952 | g.gen_json_for_type(parent_type) |
| 953 | tmp := g.new_tmp_var() |
| 954 | gen_js_get_opt(dec_name, field_type, styp, tmp, name, mut dec, is_required) |
| 955 | dec.writeln('\tif (jsonroot_${tmp}) {') |
| 956 | dec.writeln('\t\t${prefix}${op}${c_name(field.name)} = *(${field_type}*) ${tmp}.data;') |
| 957 | if field.has_default_expr { |
| 958 | dec.writeln('\t} else {') |
| 959 | dec.writeln('\t\t${prefix}${op}${c_name(field.name)} = ${g.expr_string_opt(field.typ, |
| 960 | field.default_expr)};') |
| 961 | } |
| 962 | dec.writeln('\t}') |
| 963 | } |
| 964 | } else { |
| 965 | // embeded |
| 966 | if field.is_embed && field_sym.info is ast.Struct { |
| 967 | for embed in info.embeds { |
| 968 | if embed == int(field.typ) { |
| 969 | prefix_embed := if embed_prefix != '' { |
| 970 | '${embed_prefix}.${name}' |
| 971 | } else { |
| 972 | name |
| 973 | } |
| 974 | g.gen_struct_enc_dec(field.typ, g.table.sym(field.typ).info, styp, mut |
| 975 | enc, mut dec, prefix_embed) |
| 976 | skip_embed = true |
| 977 | break |
| 978 | } |
| 979 | } |
| 980 | } |
| 981 | tmp := g.new_tmp_var() |
| 982 | gen_js_get_opt(dec_name, field_type, styp, tmp, name, mut dec, is_required) |
| 983 | dec.writeln('\tif (jsonroot_${tmp}) {') |
| 984 | if is_js_prim(g.styp(field.typ.clear_option_and_result())) { |
| 985 | g.gen_prim_type_validation(field.name, field.typ, tmp, is_required, |
| 986 | '${result_name}_${styp}', mut dec) |
| 987 | } |
| 988 | if field.typ.has_flag(.option) { |
| 989 | dec.writeln('\t\tbuiltin__vmemcpy(&${prefix}${op}${c_name(field.name)}, (${field_type}*)${tmp}.data, sizeof(${field_type}));') |
| 990 | } else { |
| 991 | if field_sym.kind == .array_fixed { |
| 992 | dec.writeln('\t\tbuiltin__vmemcpy(${prefix}${op}${c_name(field.name)},*(${field_type}*)${tmp}.data,sizeof(${field_type}));') |
| 993 | } else { |
| 994 | dec.writeln('\t\t${prefix}${op}${c_name(field.name)} = *(${field_type}*) ${tmp}.data;') |
| 995 | } |
| 996 | } |
| 997 | if field.has_default_expr { |
| 998 | dec.writeln('\t} else {') |
| 999 | default_str := g.expr_string_opt(field.typ, field.default_expr) |
| 1000 | if default_str.count(';\n') > 1 { |
| 1001 | dec.writeln(default_str.all_before_last('\n')) |
| 1002 | dec.writeln('\t\t${prefix}${op}${c_name(field.name)} = ${default_str.all_after_last('\n')};') |
| 1003 | } else { |
| 1004 | dec.writeln('\t\t${prefix}${op}${c_name(field.name)} = ${default_str};') |
| 1005 | } |
| 1006 | } |
| 1007 | dec.writeln('\t}') |
| 1008 | } |
| 1009 | } |
| 1010 | if skip_embed { |
| 1011 | continue |
| 1012 | } |
| 1013 | // Encoding |
| 1014 | mut enc_name := js_enc_name(field_type) |
| 1015 | prefix_enc := if utyp.has_flag(.option) { |
| 1016 | '(*(${g.base_type(utyp)}*)val${embed_member}.data)' |
| 1017 | } else { |
| 1018 | 'val${embed_member}' |
| 1019 | } |
| 1020 | is_option := field.typ.has_flag(.option) |
| 1021 | indent := if is_option { '\t\t' } else { '\t' } |
| 1022 | if is_option { |
| 1023 | enc.writeln('\tif (${prefix_enc}${op}${c_name(field.name)}.state != 2) {') |
| 1024 | } |
| 1025 | if is_omit_empty { |
| 1026 | if field.typ.has_flag(.option) { |
| 1027 | enc.writeln('${indent}if (${prefix_enc}${op}${c_name(field.name)}.state != 2)') |
| 1028 | } else if field.typ == ast.string_type { |
| 1029 | enc.writeln('${indent}if (${prefix_enc}${op}${c_name(field.name)}.len != 0)') |
| 1030 | } else { |
| 1031 | if !field.typ.is_ptr() |
| 1032 | && field_sym.kind in [.alias, .sum_type, .map, .array, .struct] { |
| 1033 | ptr_typ := g.equality_fn(field.typ) |
| 1034 | if field_sym.kind == .alias { |
| 1035 | enc.writeln('${indent}if (!${ptr_typ}_alias_eq(${prefix_enc}${op}${c_name(field.name)}, ${g.type_default(field.typ)}))') |
| 1036 | } else if field_sym.kind == .sum_type { |
| 1037 | enc.writeln('${indent}if (${prefix_enc}${op}${c_name(field.name)}._typ != 0)') |
| 1038 | } else if field_sym.kind == .map { |
| 1039 | enc.writeln('${indent}if (!${ptr_typ}_map_eq(${prefix_enc}${op}${c_name(field.name)}, ${g.type_default(field.typ)}))') |
| 1040 | } else if field_sym.kind == .array { |
| 1041 | enc.writeln('${indent}if (!${ptr_typ}_arr_eq(${prefix_enc}${op}${c_name(field.name)}, ${g.type_default(field.typ)}))') |
| 1042 | } else if field_sym.kind == .struct { |
| 1043 | enc.writeln('${indent}if (!${ptr_typ}_struct_eq(${prefix_enc}${op}${c_name(field.name)}, ${g.type_default(field.typ)}))') |
| 1044 | } |
| 1045 | } else { |
| 1046 | enc.writeln('${indent}if (${prefix_enc}${op}${c_name(field.name)} != ${g.type_default(field.typ)})') |
| 1047 | } |
| 1048 | } |
| 1049 | } |
| 1050 | if !is_js_prim(field_type) { |
| 1051 | if field_sym.kind == .alias { |
| 1052 | ainfo := field_sym.info as ast.Alias |
| 1053 | if field.typ.has_flag(.option) { |
| 1054 | enc_name = js_enc_name(g.styp(ainfo.parent_type.set_flag(.option))) |
| 1055 | } else { |
| 1056 | enc_name = js_enc_name(g.styp(ainfo.parent_type)) |
| 1057 | } |
| 1058 | } |
| 1059 | } |
| 1060 | if field_sym.kind == .enum { |
| 1061 | if g.is_enum_as_int(field_sym) { |
| 1062 | if field.typ.has_flag(.option) { |
| 1063 | enc.writeln('${indent}\tcJSON_AddItemToObject(o, "${name}", json__encode_u64(*${prefix_enc}${op}${c_name(field.name)}.data));\n') |
| 1064 | } else { |
| 1065 | enc.writeln('${indent}\tcJSON_AddItemToObject(o, "${name}", json__encode_u64(${prefix_enc}${op}${c_name(field.name)}));\n') |
| 1066 | } |
| 1067 | } else { |
| 1068 | if field.typ.has_flag(.option) { |
| 1069 | enc.writeln('${indent}\t{') |
| 1070 | enc.writeln('${indent}\t\tcJSON *enum_val;') |
| 1071 | g.gen_enum_to_str(field.typ, field_sym, |
| 1072 | '*(${g.base_type(field.typ)}*)${prefix_enc}${op}${c_name(field.name)}.data', |
| 1073 | 'enum_val', '${indent}\t\t', mut enc) |
| 1074 | enc.writeln('${indent}\t\tcJSON_AddItemToObject(o, "${name}", enum_val);') |
| 1075 | enc.writeln('${indent}\t}') |
| 1076 | } else { |
| 1077 | enc.writeln('${indent}\t{') |
| 1078 | enc.writeln('${indent}\t\tcJSON *enum_val;') |
| 1079 | g.gen_enum_to_str(field.typ, field_sym, |
| 1080 | '${prefix_enc}${op}${c_name(field.name)}', 'enum_val', '${indent}\t\t', mut |
| 1081 | enc) |
| 1082 | enc.writeln('${indent}\t\tcJSON_AddItemToObject(o, "${name}", enum_val);') |
| 1083 | enc.writeln('${indent}\t}') |
| 1084 | } |
| 1085 | } |
| 1086 | } else { |
| 1087 | if field_sym.name == 'time.Time' { |
| 1088 | // time struct requires special treatment |
| 1089 | // it has to be encoded as a unix timestamp number |
| 1090 | if is_option { |
| 1091 | enc.writeln('${indent}cJSON_AddItemToObject(o, "${name}", json__encode_u64(time__Time_unix(*(${g.base_type(field.typ)}*)(${prefix_enc}${op}${c_name(field.name)}.data))));') |
| 1092 | } else { |
| 1093 | enc.writeln('${indent}cJSON_AddItemToObject(o, "${name}", json__encode_u64(time__Time_unix(${prefix_enc}${op}${c_name(field.name)})));') |
| 1094 | } |
| 1095 | } else { |
| 1096 | if !field.typ.is_any_kind_of_pointer() { |
| 1097 | if field_sym.kind == .alias && field.typ.has_flag(.option) { |
| 1098 | parent_type := g.table.unaliased_type(field.typ).set_flag(.option) |
| 1099 | enc.writeln('${indent}\tcJSON_AddItemToObject(o, "${name}", ${enc_name}(*(${g.styp(parent_type)}*)&${prefix_enc}${op}${c_name(field.name)}));') |
| 1100 | } else { |
| 1101 | enc.writeln('${indent}\tcJSON_AddItemToObject(o, "${name}", ${enc_name}(${prefix_enc}${op}${c_name(field.name)}));') |
| 1102 | } |
| 1103 | } else { |
| 1104 | arg_prefix := if field.typ.is_ptr() { '' } else { '*' } |
| 1105 | sptr_value := '${prefix_enc}${op}${c_name(field.name)}' |
| 1106 | if !field.typ.has_flag(.option) { |
| 1107 | enc.writeln('${indent}if (${sptr_value} != 0) {') |
| 1108 | enc.writeln('${indent}\tcJSON_AddItemToObject(o, "${name}", ${enc_name}(${arg_prefix}${sptr_value}));') |
| 1109 | enc.writeln('${indent}}\n') |
| 1110 | } else { |
| 1111 | enc.writeln('${indent}cJSON_AddItemToObject(o, "${name}", ${enc_name}(${arg_prefix}${sptr_value}));') |
| 1112 | } |
| 1113 | } |
| 1114 | } |
| 1115 | } |
| 1116 | |
| 1117 | if is_option { |
| 1118 | if is_json_null { |
| 1119 | enc.writeln('\t} else {') |
| 1120 | enc.writeln('\t\tcJSON_AddItemToObject(o, "${name}", cJSON_CreateNull());') |
| 1121 | } |
| 1122 | enc.writeln('\t}') |
| 1123 | } |
| 1124 | } |
| 1125 | } |
| 1126 | |
| 1127 | fn gen_js_get(styp string, tmp string, name string, mut dec strings.Builder, is_required bool) { |
| 1128 | dec.writeln('\tcJSON *jsonroot_${tmp} = js_get(root, "${name}");') |
| 1129 | if is_required { |
| 1130 | dec.writeln('\tif (jsonroot_${tmp} == 0) {') |
| 1131 | dec.writeln('\t\treturn (${result_name}_${styp}){ .is_error = true, .err = builtin___v_error(_S("expected field \'${name}\' is missing")), .data = {0} };') |
| 1132 | dec.writeln('\t}') |
| 1133 | } |
| 1134 | } |
| 1135 | |
| 1136 | fn gen_js_get_opt(dec_name string, field_type string, styp string, tmp string, name string, mut dec strings.Builder, |
| 1137 | is_required bool) { |
| 1138 | gen_js_get(styp, tmp, name, mut dec, is_required) |
| 1139 | value_field_type := field_type.replace('*', '_ptr') |
| 1140 | dec.writeln('\t${result_name}_${value_field_type.replace('*', '_ptr')} ${tmp} = {0};') |
| 1141 | dec.writeln('\tif (jsonroot_${tmp}) {') |
| 1142 | // dec.writeln('\t\tif (jsonroot_${tmp}->type == cJSON_NULL) { puts("${name} IS JSON_NULL"); }') |
| 1143 | dec.writeln('\t\t${tmp} = ${dec_name}(jsonroot_${tmp});') |
| 1144 | dec.writeln('\t\tif (${tmp}.is_error) {') |
| 1145 | dec.writeln('\t\t\treturn (${result_name}_${styp}){ .is_error = true, .err = ${tmp}.err, .data = {0} };') |
| 1146 | dec.writeln('\t\t}') |
| 1147 | dec.writeln('\t}') |
| 1148 | } |
| 1149 | |
| 1150 | fn js_enc_name(typ string) string { |
| 1151 | mut suffix := typ.replace('*', '_ptr') |
| 1152 | if typ == 'i32' { |
| 1153 | suffix = typ.replace('i32', 'int') |
| 1154 | } |
| 1155 | suffix = vint2int(suffix) |
| 1156 | name := 'json__encode_${suffix}' |
| 1157 | return util.no_dots(name) |
| 1158 | } |
| 1159 | |
| 1160 | fn js_dec_name(typ string) string { |
| 1161 | mut suffix := typ.replace('*', '_ptr') |
| 1162 | if typ == 'i32' { |
| 1163 | suffix = typ.replace('i32', 'int') |
| 1164 | } |
| 1165 | suffix = vint2int(suffix) |
| 1166 | name := 'json__decode_${suffix}' |
| 1167 | return util.no_dots(name) |
| 1168 | } |
| 1169 | |
| 1170 | fn is_js_prim(typ string) bool { |
| 1171 | return typ in [ast.int_type_name, 'int', 'rune', 'string', 'bool', 'f32', 'f64', 'i8', 'i16', |
| 1172 | 'i32', 'i64', 'u8', 'u16', 'u32', 'u64', 'byte'] |
| 1173 | } |
| 1174 | |
| 1175 | fn (mut g Gen) decode_array(utyp ast.Type, value_type ast.Type, fixed_array_size int, ret_styp string) string { |
| 1176 | styp := g.styp(value_type) |
| 1177 | fn_name := js_dec_name(styp) |
| 1178 | noscan := g.check_noscan(value_type) |
| 1179 | |
| 1180 | mut res_str := '' |
| 1181 | mut array_free_str := '' |
| 1182 | mut fixed_array_idx := '' |
| 1183 | mut fixed_array_idx_increment := '' |
| 1184 | mut array_element_assign := '' |
| 1185 | is_array_fixed_val := g.table.final_sym(value_type).kind == .array_fixed |
| 1186 | if utyp.has_flag(.option) { |
| 1187 | if fixed_array_size > -1 { |
| 1188 | fixed_array_idx += '${ast.int_type_name} fixed_array_idx = 0;' |
| 1189 | array_element_assign += '((${styp}*)res.data)[fixed_array_idx] = val;' |
| 1190 | fixed_array_idx_increment += 'fixed_array_idx++; res.state = 0;' |
| 1191 | } else { |
| 1192 | array_element_assign += 'builtin__array_push${noscan}((array*)&res.data, &val);' |
| 1193 | res_str += 'builtin___option_ok(&(${g.base_type(utyp)}[]) { builtin____new_array${noscan}(0, 0, sizeof(${styp})) }, (${option_name}*)&res, sizeof(${g.base_type(utyp)}));' |
| 1194 | array_free_str += 'builtin__array_free(&res.data);' |
| 1195 | } |
| 1196 | } else { |
| 1197 | if is_array_fixed_val { |
| 1198 | fixed_array_idx += '${ast.int_type_name} fixed_array_idx = 0;' |
| 1199 | array_element_assign += 'memcpy(res[fixed_array_idx], val, sizeof(${styp}));' |
| 1200 | fixed_array_idx_increment += 'fixed_array_idx++;' |
| 1201 | } else if fixed_array_size > -1 { |
| 1202 | fixed_array_idx += '${ast.int_type_name} fixed_array_idx = 0;' |
| 1203 | array_element_assign += 'res[fixed_array_idx] = val;' |
| 1204 | fixed_array_idx_increment += 'fixed_array_idx++;' |
| 1205 | } else { |
| 1206 | array_element_assign += 'builtin__array_push${noscan}((array*)&res, &val);' |
| 1207 | res_str += 'res = builtin____new_array${noscan}(0, 0, sizeof(${styp}));' |
| 1208 | array_free_str += 'builtin__array_free(&res);' |
| 1209 | } |
| 1210 | } |
| 1211 | |
| 1212 | mut s := '' |
| 1213 | if is_js_prim(styp) { |
| 1214 | s = '${styp} val = ${fn_name}((cJSON *)jsval); ' |
| 1215 | } else if is_array_fixed_val { |
| 1216 | s = ' |
| 1217 | ${result_name}_${styp.replace('*', '_ptr')} val2 = ${fn_name} ((cJSON *)jsval); |
| 1218 | if(val2.is_error) { |
| 1219 | ${array_free_str} |
| 1220 | return *(${result_name}_${ret_styp}*)&val2; |
| 1221 | } |
| 1222 | ${styp} val; |
| 1223 | memcpy(&val, (${styp}*)val2.data, sizeof(${styp}));' |
| 1224 | } else { |
| 1225 | s = ' |
| 1226 | ${result_name}_${styp.replace('*', '_ptr')} val2 = ${fn_name} ((cJSON *)jsval); |
| 1227 | if(val2.is_error) { |
| 1228 | ${array_free_str} |
| 1229 | return *(${result_name}_${ret_styp}*)&val2; |
| 1230 | } |
| 1231 | ${styp} val = *(${styp}*)val2.data; |
| 1232 | ' |
| 1233 | } |
| 1234 | |
| 1235 | return ' |
| 1236 | if(root && !cJSON_IsArray(root) && !cJSON_IsNull(root)) { |
| 1237 | return (${result_name}_${ret_styp}){.is_error = true, .err = builtin___v_error(builtin__string__plus(_S("Json element is not an array: "), json__json_print(root))), .data = {0}}; |
| 1238 | } |
| 1239 | ${res_str} |
| 1240 | const cJSON *jsval = NULL; |
| 1241 | ${fixed_array_idx} |
| 1242 | cJSON_ArrayForEach(jsval, root) |
| 1243 | { |
| 1244 | ${s} |
| 1245 | ${array_element_assign} |
| 1246 | ${fixed_array_idx_increment} |
| 1247 | } |
| 1248 | ' |
| 1249 | } |
| 1250 | |
| 1251 | fn (mut g Gen) encode_array(utyp ast.Type, value_type ast.Type, fixed_array_size int) string { |
| 1252 | styp := g.styp(value_type) |
| 1253 | fn_name := js_enc_name(styp) |
| 1254 | |
| 1255 | mut data_str := '' |
| 1256 | mut size_str := '' |
| 1257 | |
| 1258 | if utyp.has_flag(.option) { |
| 1259 | data_str, size_str = if fixed_array_size > -1 { |
| 1260 | // fixed array |
| 1261 | '(${styp}*)(*(${g.base_type(utyp)}*)val.data)', '${fixed_array_size}' |
| 1262 | } else { |
| 1263 | '(${styp}*)(*(${g.base_type(utyp)}*)val.data).data', '(*(${g.base_type(utyp)}*)val.data).len' |
| 1264 | } |
| 1265 | } else { |
| 1266 | data_str, size_str = if fixed_array_size > -1 { |
| 1267 | // fixed array |
| 1268 | '(${styp}*)val', '${fixed_array_size}' |
| 1269 | } else { |
| 1270 | '(${styp}*)val.data', 'val.len' |
| 1271 | } |
| 1272 | } |
| 1273 | |
| 1274 | return ' |
| 1275 | o = cJSON_CreateArray(); |
| 1276 | for (${ast.int_type_name} i = 0; i < ${size_str}; i++){ |
| 1277 | cJSON_AddItemToArray(o, ${fn_name}( (${data_str})[i] )); |
| 1278 | } |
| 1279 | ' |
| 1280 | } |
| 1281 | |
| 1282 | fn (mut g Gen) decode_map(utyp ast.Type, key_type ast.Type, value_type ast.Type, ustyp string) string { |
| 1283 | styp := g.styp(key_type) |
| 1284 | mut styp_v := g.styp(value_type) |
| 1285 | ret_styp := styp_v.replace('*', '_ptr') |
| 1286 | key_type_symbol := g.table.sym(key_type) |
| 1287 | hash_fn, key_eq_fn, clone_fn, free_fn := g.map_fn_ptrs(key_type_symbol) |
| 1288 | fn_name_v := js_dec_name(styp_v) |
| 1289 | ref, ptr := if utyp.is_ptr() { '', '*' } else { '&', '' } |
| 1290 | mut s := '' |
| 1291 | if is_js_prim(styp_v) { |
| 1292 | s = '${styp_v} val = ${fn_name_v} (js_get(root, jsval->string));' |
| 1293 | } else { |
| 1294 | s = ' |
| 1295 | ${result_name}_${ret_styp} val2 = ${fn_name_v} (js_get(root, jsval->string)); |
| 1296 | if(val2.is_error) { |
| 1297 | builtin__map_free(${ref}res); |
| 1298 | return *(${result_name}_${ustyp}*)&val2; |
| 1299 | } |
| 1300 | ${styp_v} val = *(${styp_v}*)val2.data; |
| 1301 | ' |
| 1302 | } |
| 1303 | |
| 1304 | if utyp.has_flag(.option) { |
| 1305 | return ' |
| 1306 | if(!cJSON_IsObject(root) && !cJSON_IsNull(root)) { |
| 1307 | return (${result_name}_${ustyp}){ .is_error = true, .err = builtin___v_error(builtin__string__plus(_S("Json element is not an object: "), json__json_print(root))), .data = {0}}; |
| 1308 | } |
| 1309 | builtin___option_ok(&(${g.base_type(utyp)}[]) { builtin__new_map(sizeof(${styp}), sizeof(${styp_v}), ${hash_fn}, ${key_eq_fn}, ${clone_fn}, ${free_fn}) }, (${option_name}*)&res, sizeof(${g.base_type(utyp)})); |
| 1310 | cJSON *jsval = NULL; |
| 1311 | cJSON_ArrayForEach(jsval, root) |
| 1312 | { |
| 1313 | ${s} |
| 1314 | string key = builtin__tos2((byteptr)jsval->string); |
| 1315 | builtin__map_set((map*)res.data, &key, &val); |
| 1316 | } |
| 1317 | ' |
| 1318 | } else { |
| 1319 | return ' |
| 1320 | if(!cJSON_IsObject(root) && !cJSON_IsNull(root)) { |
| 1321 | return (${result_name}_${ustyp}){ .is_error = true, .err = builtin___v_error(builtin__string__plus(_S("Json element is not an object: "), json__json_print(root))), .data = {0}}; |
| 1322 | } |
| 1323 | ${ptr}res = builtin__new_map(sizeof(${styp}), sizeof(${styp_v}), ${hash_fn}, ${key_eq_fn}, ${clone_fn}, ${free_fn}); |
| 1324 | cJSON *jsval = NULL; |
| 1325 | cJSON_ArrayForEach(jsval, root) |
| 1326 | { |
| 1327 | ${s} |
| 1328 | string key = builtin__tos2((byteptr)jsval->string); |
| 1329 | builtin__map_set(${ref}res, &key, &val); |
| 1330 | } |
| 1331 | ' |
| 1332 | } |
| 1333 | } |
| 1334 | |
| 1335 | fn (mut g Gen) encode_map(utyp ast.Type, key_type ast.Type, value_type ast.Type) string { |
| 1336 | styp := g.styp(key_type) |
| 1337 | styp_v := g.styp(value_type) |
| 1338 | key_array_type := ast.idx_to_type(g.table.find_or_register_array(key_type)) |
| 1339 | key_array_styp := g.styp(key_array_type) |
| 1340 | fn_name_v := js_enc_name(styp_v) |
| 1341 | zero := g.type_default(value_type) |
| 1342 | keys_tmp := g.new_tmp_var() |
| 1343 | mut key := 'string key = ' |
| 1344 | if key_type.is_string() { |
| 1345 | key += '((${styp}*)${keys_tmp}.data)[i];' |
| 1346 | } else { |
| 1347 | // key += '${styp}_str(((${styp}*)${keys_tmp}.data)[i]);' |
| 1348 | verror('json: encode only maps with string keys') |
| 1349 | } |
| 1350 | if utyp.has_flag(.option) { |
| 1351 | return ' |
| 1352 | o = cJSON_CreateObject(); |
| 1353 | ${key_array_styp} ${keys_tmp} = builtin__map_keys((map*)val.data); |
| 1354 | for (${ast.int_type_name} i = 0; i < ${keys_tmp}.len; ++i) { |
| 1355 | ${key} |
| 1356 | cJSON_AddItemToObject(o, (char*) key.str, ${fn_name_v} ( *(${styp_v}*) builtin__map_get((map*)val.data, &key, &(${styp_v}[]) { ${zero} } ) ) ); |
| 1357 | } |
| 1358 | builtin__array_free(&${keys_tmp}); |
| 1359 | ' |
| 1360 | } else { |
| 1361 | ref := if utyp.is_ptr() { '' } else { '&' } |
| 1362 | return ' |
| 1363 | o = cJSON_CreateObject(); |
| 1364 | ${key_array_styp} ${keys_tmp} = builtin__map_keys(${ref}val); |
| 1365 | for (${ast.int_type_name} i = 0; i < ${keys_tmp}.len; ++i) { |
| 1366 | ${key} |
| 1367 | cJSON_AddItemToObject(o, (char*) key.str, ${fn_name_v} ( *(${styp_v}*) builtin__map_get(${ref}val, &key, &(${styp_v}[]) { ${zero} } ) ) ); |
| 1368 | } |
| 1369 | builtin__array_free(&${keys_tmp}); |
| 1370 | ' |
| 1371 | } |
| 1372 | } |
| 1373 | |
| 1374 | @[noreturn] |
| 1375 | fn verror_suggest_json_no_inline_sumtypes(sumtype_name string, type_name1 string, type_name2 string) { |
| 1376 | verror('json: can not decode `${sumtype_name}` sumtype, too many numeric types (conflict of `${type_name1}` and `${type_name2}`), you can try to use alias for `${type_name2}` or compile v with `json_no_inline_sumtypes` flag') |
| 1377 | } |
| 1378 | |