| ... | ... | @@ -1468,7 +1468,9 @@ pub const ParseOptions = struct { |
| 1468 | 1468 | allow_trailing_data: bool = false, |
| 1469 | 1469 | }; |
| 1470 | 1470 | |
| 1471 | | fn skipValue(tokens: *TokenStream) !void { |
| 1471 | const SkipValueError = error{UnexpectedJsonDepth} || TokenStream.Error; |
| 1472 | |
| 1473 | fn skipValue(tokens: *TokenStream) SkipValueError!void { |
| 1472 | 1474 | const original_depth = tokens.stackUsed(); |
| 1473 | 1475 | |
| 1474 | 1476 | // Return an error if no value is found |
| ... | ... | @@ -1530,7 +1532,84 @@ test "skipValue" { |
| 1530 | 1532 | } |
| 1531 | 1533 | } |
| 1532 | 1534 | |
| 1533 | | fn parseInternal(comptime T: type, token: Token, tokens: *TokenStream, options: ParseOptions) !T { |
| 1535 | fn ParseInternalError(comptime T: type) type { |
| 1536 | // `inferred_types` is used to avoid infinite recursion for recursive type definitions. |
| 1537 | const inferred_types = [_]type{}; |
| 1538 | return ParseInternalErrorImpl(T, &inferred_types); |
| 1539 | } |
| 1540 | |
| 1541 | fn ParseInternalErrorImpl(comptime T: type, comptime inferred_types: []const type) type { |
| 1542 | for (inferred_types) |ty| { |
| 1543 | if (T == ty) return error{}; |
| 1544 | } |
| 1545 | |
| 1546 | switch (@typeInfo(T)) { |
| 1547 | .Bool => return error{UnexpectedToken}, |
| 1548 | .Float, .ComptimeFloat => return error{UnexpectedToken} || std.fmt.ParseFloatError, |
| 1549 | .Int, .ComptimeInt => { |
| 1550 | return error{ UnexpectedToken, InvalidNumber, Overflow } || |
| 1551 | std.fmt.ParseIntError || std.fmt.ParseFloatError; |
| 1552 | }, |
| 1553 | .Optional => |optionalInfo| { |
| 1554 | return ParseInternalErrorImpl(optionalInfo.child, inferred_types ++ [_]type{T}); |
| 1555 | }, |
| 1556 | .Enum => return error{ UnexpectedToken, InvalidEnumTag } || std.fmt.ParseIntError || |
| 1557 | std.meta.IntToEnumError || std.meta.IntToEnumError, |
| 1558 | .Union => |unionInfo| { |
| 1559 | if (unionInfo.tag_type) |_| { |
| 1560 | var errors = error{NoUnionMembersMatched}; |
| 1561 | for (unionInfo.fields) |u_field| { |
| 1562 | errors = errors || ParseInternalErrorImpl(u_field.field_type, inferred_types ++ [_]type{T}); |
| 1563 | } |
| 1564 | return errors; |
| 1565 | } else { |
| 1566 | @compileError("Unable to parse into untagged union '" ++ @typeName(T) ++ "'"); |
| 1567 | } |
| 1568 | }, |
| 1569 | .Struct => |structInfo| { |
| 1570 | var errors = error{ |
| 1571 | DuplicateJSONField, |
| 1572 | UnexpectedEndOfJson, |
| 1573 | UnexpectedToken, |
| 1574 | UnexpectedValue, |
| 1575 | UnknownField, |
| 1576 | MissingField, |
| 1577 | } || SkipValueError || TokenStream.Error; |
| 1578 | for (structInfo.fields) |field| { |
| 1579 | errors = errors || ParseInternalErrorImpl(field.field_type, inferred_types ++ [_]type{T}); |
| 1580 | } |
| 1581 | return errors; |
| 1582 | }, |
| 1583 | .Array => |arrayInfo| { |
| 1584 | return error{ UnexpectedEndOfJson, UnexpectedToken } || TokenStream.Error || |
| 1585 | UnescapeValidStringError || |
| 1586 | ParseInternalErrorImpl(arrayInfo.child, inferred_types ++ [_]type{T}); |
| 1587 | }, |
| 1588 | .Pointer => |ptrInfo| { |
| 1589 | var errors = error{AllocatorRequired} || std.mem.Allocator.Error; |
| 1590 | switch (ptrInfo.size) { |
| 1591 | .One => { |
| 1592 | return errors || ParseInternalErrorImpl(ptrInfo.child, inferred_types ++ [_]type{T}); |
| 1593 | }, |
| 1594 | .Slice => { |
| 1595 | return errors || error{ UnexpectedEndOfJson, UnexpectedToken } || |
| 1596 | ParseInternalErrorImpl(ptrInfo.child, inferred_types ++ [_]type{T}) || |
| 1597 | UnescapeValidStringError || TokenStream.Error; |
| 1598 | }, |
| 1599 | else => @compileError("Unable to parse into type '" ++ @typeName(T) ++ "'"), |
| 1600 | } |
| 1601 | }, |
| 1602 | else => return error{}, |
| 1603 | } |
| 1604 | unreachable; |
| 1605 | } |
| 1606 | |
| 1607 | fn parseInternal( |
| 1608 | comptime T: type, |
| 1609 | token: Token, |
| 1610 | tokens: *TokenStream, |
| 1611 | options: ParseOptions, |
| 1612 | ) ParseInternalError(T)!T { |
| 1534 | 1613 | switch (@typeInfo(T)) { |
| 1535 | 1614 | .Bool => { |
| 1536 | 1615 | return switch (token) { |
| ... | ... | @@ -1794,7 +1873,11 @@ fn parseInternal(comptime T: type, token: Token, tokens: *TokenStream, options: |
| 1794 | 1873 | unreachable; |
| 1795 | 1874 | } |
| 1796 | 1875 | |
| 1797 | | pub fn parse(comptime T: type, tokens: *TokenStream, options: ParseOptions) !T { |
| 1876 | pub fn ParseError(comptime T: type) type { |
| 1877 | return ParseInternalError(T) || error{UnexpectedEndOfJson} || TokenStream.Error; |
| 1878 | } |
| 1879 | |
| 1880 | pub fn parse(comptime T: type, tokens: *TokenStream, options: ParseOptions) ParseError(T)!T { |
| 1798 | 1881 | const token = (try tokens.next()) orelse return error.UnexpectedEndOfJson; |
| 1799 | 1882 | const r = try parseInternal(T, token, tokens, options); |
| 1800 | 1883 | errdefer parseFree(T, r, options); |
| ... | ... | @@ -2181,6 +2264,45 @@ test "parse into struct ignoring unknown fields" { |
| 2181 | 2264 | try testing.expectEqualSlices(u8, "zig", r.language); |
| 2182 | 2265 | } |
| 2183 | 2266 | |
| 2267 | const ParseIntoRecursiveUnionDefinitionValue = union(enum) { |
| 2268 | integer: i64, |
| 2269 | array: []const ParseIntoRecursiveUnionDefinitionValue, |
| 2270 | }; |
| 2271 | |
| 2272 | test "parse into recursive union definition" { |
| 2273 | const T = struct { |
| 2274 | values: ParseIntoRecursiveUnionDefinitionValue, |
| 2275 | }; |
| 2276 | const ops = ParseOptions{ .allocator = testing.allocator }; |
| 2277 | |
| 2278 | const r = try parse(T, &std.json.TokenStream.init("{\"values\":[58]}"), ops); |
| 2279 | defer parseFree(T, r, ops); |
| 2280 | |
| 2281 | try testing.expectEqual(@as(i64, 58), r.values.array[0].integer); |
| 2282 | } |
| 2283 | |
| 2284 | const ParseIntoDoubleRecursiveUnionValueFirst = union(enum) { |
| 2285 | integer: i64, |
| 2286 | array: []const ParseIntoDoubleRecursiveUnionValueSecond, |
| 2287 | }; |
| 2288 | |
| 2289 | const ParseIntoDoubleRecursiveUnionValueSecond = union(enum) { |
| 2290 | boolean: bool, |
| 2291 | array: []const ParseIntoDoubleRecursiveUnionValueFirst, |
| 2292 | }; |
| 2293 | |
| 2294 | test "parse into double recursive union definition" { |
| 2295 | const T = struct { |
| 2296 | values: ParseIntoDoubleRecursiveUnionValueFirst, |
| 2297 | }; |
| 2298 | const ops = ParseOptions{ .allocator = testing.allocator }; |
| 2299 | |
| 2300 | const r = try parse(T, &std.json.TokenStream.init("{\"values\":[[58]]}"), ops); |
| 2301 | defer parseFree(T, r, ops); |
| 2302 | |
| 2303 | try testing.expectEqual(@as(i64, 58), r.values.array[0].array[0].integer); |
| 2304 | } |
| 2305 | |
| 2184 | 2306 | /// A non-stream JSON parser which constructs a tree of Value's. |
| 2185 | 2307 | pub const Parser = struct { |
| 2186 | 2308 | allocator: *Allocator, |
| ... | ... | @@ -2418,10 +2540,12 @@ pub const Parser = struct { |
| 2418 | 2540 | } |
| 2419 | 2541 | }; |
| 2420 | 2542 | |
| 2543 | pub const UnescapeValidStringError = error{InvalidUnicodeHexSymbol}; |
| 2544 | |
| 2421 | 2545 | /// Unescape a JSON string |
| 2422 | 2546 | /// Only to be used on strings already validated by the parser |
| 2423 | 2547 | /// (note the unreachable statements and lack of bounds checking) |
| 2424 | | pub fn unescapeValidString(output: []u8, input: []const u8) !void { |
| 2548 | pub fn unescapeValidString(output: []u8, input: []const u8) UnescapeValidStringError!void { |
| 2425 | 2549 | var inIndex: usize = 0; |
| 2426 | 2550 | var outIndex: usize = 0; |
| 2427 | 2551 | |