| ... | ... | @@ -286,7 +286,10 @@ pub fn innerParse( |
| 286 | 286 | var name_token: ?Token = try source.nextAllocMax(allocator, .alloc_if_needed, options.max_value_len.?); |
| 287 | 287 | const field_name = switch (name_token.?) { |
| 288 | 288 | inline .string, .allocated_string => |slice| slice, |
| 289 | | else => return error.MissingField, |
| 289 | else => { |
| 290 | if (options.diagnostics) |diag| diag.recordContext("tagged union requires a field to identify the active tag"); |
| 291 | return error.MissingField; |
| 292 | }, |
| 290 | 293 | }; |
| 291 | 294 | |
| 292 | 295 | inline for (unionInfo.fields) |u_field| { |
| ... | ... | @@ -301,7 +304,10 @@ pub fn innerParse( |
| 301 | 304 | .object_begin => {}, |
| 302 | 305 | else => |t| return typeError(options.diagnostics, t, "void payload ('{}')"), |
| 303 | 306 | } |
| 304 | | if (.object_end != try source.next()) return error.UnknownField; |
| 307 | if (.object_end != try source.next()) { |
| 308 | if (options.diagnostics) |diag| diag.recordContext("void payload '{}' should have no fields"); |
| 309 | return error.UnknownField; |
| 310 | } |
| 305 | 311 | result = @unionInit(T, u_field.name, {}); |
| 306 | 312 | } else { |
| 307 | 313 | // Recurse. |
| ... | ... | @@ -311,10 +317,14 @@ pub fn innerParse( |
| 311 | 317 | } |
| 312 | 318 | } else { |
| 313 | 319 | // Didn't match anything. |
| 320 | if (options.diagnostics) |diag| diag.recordContext("unrecognized tag name"); |
| 314 | 321 | return error.UnknownField; |
| 315 | 322 | } |
| 316 | 323 | |
| 317 | | if (.object_end != try source.next()) return error.UnknownField; |
| 324 | if (.object_end != try source.next()) { |
| 325 | if (options.diagnostics) |diag| diag.recordContext("tagged union requires only one field"); |
| 326 | return error.UnknownField; |
| 327 | } |
| 318 | 328 | |
| 319 | 329 | return result.?; |
| 320 | 330 | }, |
| ... | ... | @@ -328,11 +338,23 @@ pub fn innerParse( |
| 328 | 338 | |
| 329 | 339 | var r: T = undefined; |
| 330 | 340 | inline for (0..structInfo.fields.len) |i| { |
| 331 | | if (.array_end == try source.peekNextTokenType()) return error.LengthMismatch; |
| 341 | if (.array_end == try source.peekNextTokenType()) { |
| 342 | if (options.diagnostics) |diag| diag.recordContext(std.fmt.comptimePrint( |
| 343 | "tuple too short. expected length: {}", |
| 344 | .{structInfo.fields.len}, |
| 345 | )); |
| 346 | return error.LengthMismatch; |
| 347 | } |
| 332 | 348 | r[i] = try innerParse(structInfo.fields[i].type, allocator, source, options); |
| 333 | 349 | } |
| 334 | 350 | |
| 335 | | if (.array_end != try source.next()) return error.LengthMismatch; |
| 351 | if (.array_end != try source.next()) { |
| 352 | if (options.diagnostics) |diag| diag.recordContext(std.fmt.comptimePrint( |
| 353 | "tuple too long. expected length: {}", |
| 354 | .{structInfo.fields.len}, |
| 355 | )); |
| 356 | return error.LengthMismatch; |
| 357 | } |
| 336 | 358 | |
| 337 | 359 | return r; |
| 338 | 360 | } |
| ... | ... | @@ -375,7 +397,10 @@ pub fn innerParse( |
| 375 | 397 | _ = try innerParse(field.type, allocator, source, options); |
| 376 | 398 | break; |
| 377 | 399 | }, |
| 378 | | .@"error" => return error.DuplicateField, |
| 400 | .@"error" => { |
| 401 | if (options.diagnostics) |diag| diag.recordContext("duplicate field name: " ++ field.name); |
| 402 | return error.DuplicateField; |
| 403 | }, |
| 379 | 404 | .use_last => {}, |
| 380 | 405 | } |
| 381 | 406 | } |
| ... | ... | @@ -389,11 +414,12 @@ pub fn innerParse( |
| 389 | 414 | if (options.ignore_unknown_fields) { |
| 390 | 415 | try source.skipValue(); |
| 391 | 416 | } else { |
| 417 | if (options.diagnostics) |diag| diag.recordContext("unrecognized field name"); |
| 392 | 418 | return error.UnknownField; |
| 393 | 419 | } |
| 394 | 420 | } |
| 395 | 421 | } |
| 396 | | try fillDefaultStructValues(T, &r, &fields_seen); |
| 422 | try fillDefaultStructValues(T, options, &r, &fields_seen); |
| 397 | 423 | return r; |
| 398 | 424 | }, |
| 399 | 425 | |
| ... | ... | @@ -591,6 +617,7 @@ fn typeError(diagnostics: ?*Diagnostics, token: anytype, comptime expected: []co |
| 591 | 617 | .null => prefix ++ "null", |
| 592 | 618 | .number => prefix ++ "number", |
| 593 | 619 | .string => prefix ++ "string", |
| 620 | |
| 594 | 621 | .object_end => unreachable, // type errors happen at the start of a value. |
| 595 | 622 | .array_end => unreachable, // type errors happen at the start of a value. |
| 596 | 623 | .end_of_document => unreachable, // type errors happen at the start of a value. |
| ... | ... | @@ -732,7 +759,7 @@ pub fn innerParseFromValue( |
| 732 | 759 | if (!options.ignore_unknown_fields) return error.UnknownField; |
| 733 | 760 | } |
| 734 | 761 | } |
| 735 | | try fillDefaultStructValues(T, &r, &fields_seen); |
| 762 | try fillDefaultStructValues(T, options, &r, &fields_seen); |
| 736 | 763 | return r; |
| 737 | 764 | }, |
| 738 | 765 | |
| ... | ... | @@ -846,13 +873,14 @@ fn sliceToEnum(comptime T: type, slice: []const u8) !T { |
| 846 | 873 | return std.meta.intToEnum(T, n); |
| 847 | 874 | } |
| 848 | 875 | |
| 849 | | fn fillDefaultStructValues(comptime T: type, r: *T, fields_seen: *[@typeInfo(T).Struct.fields.len]bool) !void { |
| 876 | fn fillDefaultStructValues(comptime T: type, options: ParseOptions, r: *T, fields_seen: *[@typeInfo(T).Struct.fields.len]bool) !void { |
| 850 | 877 | inline for (@typeInfo(T).Struct.fields, 0..) |field, i| { |
| 851 | 878 | if (!fields_seen[i]) { |
| 852 | 879 | if (field.default_value) |default_ptr| { |
| 853 | 880 | const default = @as(*align(1) const field.type, @ptrCast(default_ptr)).*; |
| 854 | 881 | @field(r, field.name) = default; |
| 855 | 882 | } else { |
| 883 | if (options.diagnostics) |diag| diag.recordContext("missing field: " ++ @typeName(T) ++ "." ++ field.name); |
| 856 | 884 | return error.MissingField; |
| 857 | 885 | } |
| 858 | 886 | } |