| ... | @@ -1468,7 +1468,9 @@ pub const ParseOptions = struct { | ... | @@ -1468,7 +1468,9 @@ pub const ParseOptions = struct { |
| 1468 | allow_trailing_data: bool = false, | 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 | const original_depth = tokens.stackUsed(); | 1474 | const original_depth = tokens.stackUsed(); |
| 1473 | | 1475 | |
| 1474 | // Return an error if no value is found | 1476 | // Return an error if no value is found |
| ... | @@ -1530,7 +1532,84 @@ test "skipValue" { | ... | @@ -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 | switch (@typeInfo(T)) { | 1613 | switch (@typeInfo(T)) { |
| 1535 | .Bool => { | 1614 | .Bool => { |
| 1536 | return switch (token) { | 1615 | return switch (token) { |
| ... | @@ -1794,7 +1873,11 @@ fn parseInternal(comptime T: type, token: Token, tokens: *TokenStream, options: | ... | @@ -1794,7 +1873,11 @@ fn parseInternal(comptime T: type, token: Token, tokens: *TokenStream, options: |
| 1794 | unreachable; | 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 | const token = (try tokens.next()) orelse return error.UnexpectedEndOfJson; | 1881 | const token = (try tokens.next()) orelse return error.UnexpectedEndOfJson; |
| 1799 | const r = try parseInternal(T, token, tokens, options); | 1882 | const r = try parseInternal(T, token, tokens, options); |
| 1800 | errdefer parseFree(T, r, options); | 1883 | errdefer parseFree(T, r, options); |
| ... | @@ -2181,6 +2264,45 @@ test "parse into struct ignoring unknown fields" { | ... | @@ -2181,6 +2264,45 @@ test "parse into struct ignoring unknown fields" { |
| 2181 | try testing.expectEqualSlices(u8, "zig", r.language); | 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 | /// A non-stream JSON parser which constructs a tree of Value's. | 2306 | /// A non-stream JSON parser which constructs a tree of Value's. |
| 2185 | pub const Parser = struct { | 2307 | pub const Parser = struct { |
| 2186 | allocator: *Allocator, | 2308 | allocator: *Allocator, |
| ... | @@ -2418,10 +2540,12 @@ pub const Parser = struct { | ... | @@ -2418,10 +2540,12 @@ pub const Parser = struct { |
| 2418 | } | 2540 | } |
| 2419 | }; | 2541 | }; |
| 2420 | | 2542 | |
| | 2543 | pub const UnescapeValidStringError = error{InvalidUnicodeHexSymbol}; |
| | 2544 | |
| 2421 | /// Unescape a JSON string | 2545 | /// Unescape a JSON string |
| 2422 | /// Only to be used on strings already validated by the parser | 2546 | /// Only to be used on strings already validated by the parser |
| 2423 | /// (note the unreachable statements and lack of bounds checking) | 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 | var inIndex: usize = 0; | 2549 | var inIndex: usize = 0; |
| 2426 | var outIndex: usize = 0; | 2550 | var outIndex: usize = 0; |
| 2427 | | 2551 | |