From e43b8275767af9bc98f13080a98c017fdf9cb009 Mon Sep 17 00:00:00 2001 From: Simon Hartcher Date: Wed, 4 Jun 2025 12:35:32 +1000 Subject: [PATCH] chore: remove old stringify code --- codegen/src/main.zig | 28 --- lib/json/src/json.zig | 533 +----------------------------------------- 2 files changed, 1 insertion(+), 560 deletions(-) diff --git a/codegen/src/main.zig b/codegen/src/main.zig index 8190ae1..2982e55 100644 --- a/codegen/src/main.zig +++ b/codegen/src/main.zig @@ -1653,34 +1653,6 @@ fn generateComplexTypeFor(shape_id: []const u8, members: []smithy.TypeMember, ty _ = try writer.write("return @field(mappings, field_name);\n"); try outputIndent(child_state, writer); _ = try writer.write("}\n"); - try writeStringify(child_state, map_fields.items, writer); -} - -fn writeStringify(state: GenerationState, fields: [][]const u8, writer: anytype) !void { - if (fields.len > 0) { - // pub fn jsonStringifyField(self: @This(), comptime field_name: []const u8, options: anytype, out_stream: anytype) !bool { - // if (std.mem.eql(u8, "tags", field_name)) - // return try serializeMap(self.tags, self.jsonFieldNameFor("tags"), options, out_stream); - // return false; - // } - var child_state = state; - child_state.indent_level += 1; - try writer.writeByte('\n'); - try outputIndent(state, writer); - _ = try writer.write("pub fn jsonStringifyField(self: @This(), comptime field_name: []const u8, options: anytype, out_stream: anytype) !bool {\n"); - var return_state = child_state; - return_state.indent_level += 1; - for (fields) |field| { - try outputIndent(child_state, writer); - try writer.print("if (std.mem.eql(u8, \"{s}\", field_name))\n", .{field}); - try outputIndent(return_state, writer); - try writer.print("return try serializeMap(self.{s}, self.fieldNameFor(\"{s}\"), options, out_stream);\n", .{ field, field }); - } - try outputIndent(child_state, writer); - _ = try writer.write("return false;\n"); - try outputIndent(state, writer); - _ = try writer.write("}\n"); - } } fn writeMappings(state: GenerationState, @"pub": []const u8, mapping_name: []const u8, mappings: anytype, force_output: bool, writer: anytype) !void { diff --git a/lib/json/src/json.zig b/lib/json/src/json.zig index 8044f6b..4487faf 100644 --- a/lib/json/src/json.zig +++ b/lib/json/src/json.zig @@ -37,7 +37,7 @@ fn serializeMapKey(key: []const u8, options: anytype, out_stream: anytype) !void } } -pub fn serializeMapAsObject(map: anytype, options: anytype, out_stream: anytype) !void { +fn serializeMapAsObject(map: anytype, options: anytype, out_stream: anytype) !void { if (map.len == 0) { try out_stream.writeByte('{'); try out_stream.writeByte('}'); @@ -1371,137 +1371,8 @@ pub const Value = union(enum) { String: []const u8, Array: Array, Object: ObjectMap, - - pub fn jsonStringify( - value: @This(), - options: StringifyOptions, - out_stream: anytype, - ) @TypeOf(out_stream).Error!void { - switch (value) { - .Null => try stringify(null, options, out_stream), - .Bool => |inner| try stringify(inner, options, out_stream), - .Integer => |inner| try stringify(inner, options, out_stream), - .Float => |inner| try stringify(inner, options, out_stream), - .NumberString => |inner| try out_stream.writeAll(inner), - .String => |inner| try stringify(inner, options, out_stream), - .Array => |inner| try stringify(inner.items, options, out_stream), - .Object => |inner| { - try out_stream.writeByte('{'); - var field_output = false; - var child_options = options; - if (child_options.whitespace) |*child_whitespace| { - child_whitespace.indent_level += 1; - } - var it = inner.iterator(); - while (it.next()) |entry| { - if (!field_output) { - field_output = true; - } else { - try out_stream.writeByte(','); - } - if (child_options.whitespace) |child_whitespace| { - try out_stream.writeByte('\n'); - try child_whitespace.outputIndent(out_stream); - } - - try stringify(entry.key_ptr, options, out_stream); - try out_stream.writeByte(':'); - if (child_options.whitespace) |child_whitespace| { - if (child_whitespace.separator) { - try out_stream.writeByte(' '); - } - } - try stringify(entry.value_ptr, child_options, out_stream); - } - if (field_output) { - if (options.whitespace) |whitespace| { - try out_stream.writeByte('\n'); - try whitespace.outputIndent(out_stream); - } - } - try out_stream.writeByte('}'); - }, - } - } - - pub fn dump(self: Value) void { - std.debug.lockStdErr(); - defer std.debug.unlockStdErr(); - - const stderr = std.io.getStdErr().writer(); - stringify(self, StringifyOptions{ .whitespace = null }, stderr) catch return; - } }; -pub fn dump(value: anytype) void { - var held = std.debug.getStderrMutex().acquire(); - defer held.release(); - - const stderr = std.io.getStdErr().writer(); - stringify(value, StringifyOptions{ .whitespace = null }, stderr) catch return; -} - -test "Value.jsonStringify" { - { - var buffer: [10]u8 = undefined; - var fbs = std.io.fixedBufferStream(&buffer); - try @as(Value, .Null).jsonStringify(.{}, fbs.writer()); - try testing.expectEqualSlices(u8, fbs.getWritten(), "null"); - } - { - var buffer: [10]u8 = undefined; - var fbs = std.io.fixedBufferStream(&buffer); - try (Value{ .Bool = true }).jsonStringify(.{}, fbs.writer()); - try testing.expectEqualSlices(u8, fbs.getWritten(), "true"); - } - { - var buffer: [10]u8 = undefined; - var fbs = std.io.fixedBufferStream(&buffer); - try (Value{ .Integer = 42 }).jsonStringify(.{}, fbs.writer()); - try testing.expectEqualSlices(u8, fbs.getWritten(), "42"); - } - { - var buffer: [10]u8 = undefined; - var fbs = std.io.fixedBufferStream(&buffer); - try (Value{ .NumberString = "43" }).jsonStringify(.{}, fbs.writer()); - try testing.expectEqualSlices(u8, fbs.getWritten(), "43"); - } - { - var buffer: [10]u8 = undefined; - var fbs = std.io.fixedBufferStream(&buffer); - try (Value{ .Float = 42 }).jsonStringify(.{}, fbs.writer()); - try testing.expectEqualSlices(u8, fbs.getWritten(), "4.2e1"); - } - { - var buffer: [10]u8 = undefined; - var fbs = std.io.fixedBufferStream(&buffer); - try (Value{ .String = "weeee" }).jsonStringify(.{}, fbs.writer()); - try testing.expectEqualSlices(u8, fbs.getWritten(), "\"weeee\""); - } - { - var buffer: [10]u8 = undefined; - var fbs = std.io.fixedBufferStream(&buffer); - var vals = [_]Value{ - .{ .Integer = 1 }, - .{ .Integer = 2 }, - .{ .NumberString = "3" }, - }; - try (Value{ - .Array = Array.fromOwnedSlice(undefined, &vals), - }).jsonStringify(.{}, fbs.writer()); - try testing.expectEqualSlices(u8, fbs.getWritten(), "[1,2,3]"); - } - { - var buffer: [10]u8 = undefined; - var fbs = std.io.fixedBufferStream(&buffer); - var obj = ObjectMap.init(testing.allocator); - defer obj.deinit(); - try obj.putNoClobber("a", .{ .String = "b" }); - try (Value{ .Object = obj }).jsonStringify(.{}, fbs.writer()); - try testing.expectEqualSlices(u8, fbs.getWritten(), "{\"a\":\"b\"}"); - } -} - /// parse tokens from a stream, returning `false` if they do not decode to `value` fn parsesTo(comptime T: type, value: T, tokens: *TokenStream, options: ParseOptions) !bool { // TODO: should be able to write this function to not require an allocator @@ -2923,405 +2794,3 @@ fn outputUnicodeEscape( try std.fmt.formatIntValue(low, "x", std.fmt.FormatOptions{ .width = 4, .fill = '0' }, out_stream); } } - -pub fn stringify( - value: anytype, - options: StringifyOptions, - out_stream: anytype, -) !void { - const T = @TypeOf(value); - switch (@typeInfo(T)) { - .float, .comptime_float => { - return std.fmt.format(out_stream, "{e}", .{value}); - }, - .int, .comptime_int => { - return std.fmt.formatIntValue(value, "", std.fmt.FormatOptions{}, out_stream); - }, - .bool => { - return out_stream.writeAll(if (value) "true" else "false"); - }, - .null => { - return out_stream.writeAll("null"); - }, - .optional => { - if (value) |payload| { - return try stringify(payload, options, out_stream); - } else { - return try stringify(null, options, out_stream); - } - }, - .@"enum" => { - if (comptime std.meta.hasFn(T, "jsonStringify")) { - return value.jsonStringify(options, out_stream); - } - - @compileError("Unable to stringify enum '" ++ @typeName(T) ++ "'"); - }, - .@"union" => { - if (comptime std.meta.hasFn(T, "jsonStringify")) { - return value.jsonStringify(options, out_stream); - } - - const info = @typeInfo(T).@"union"; - if (info.tag_type) |UnionTagType| { - inline for (info.fields) |u_field| { - if (value == @field(UnionTagType, u_field.name)) { - return try stringify(@field(value, u_field.name), options, out_stream); - } - } - } else { - @compileError("Unable to stringify untagged union '" ++ @typeName(T) ++ "'"); - } - }, - .@"struct" => |S| { - if (comptime std.meta.hasFn(T, "jsonStringify")) { - return value.jsonStringify(options, out_stream); - } - - try out_stream.writeByte('{'); - var field_output = false; - var child_options = options; - if (child_options.whitespace) |*child_whitespace| { - child_whitespace.indent_level += 1; - } - inline for (S.fields) |field| { - // don't include void fields - if (field.type == void) continue; - - var output_this_field = true; - if (!options.emit_null and @typeInfo(field.type) == .optional and @field(value, field.name) == null) output_this_field = false; - - const final_name = if (comptime std.meta.hasFn(T, "fieldNameFor")) - value.fieldNameFor(field.name) - else - field.name; - if (options.exclude_fields) |exclude_fields| { - for (exclude_fields) |exclude_field| { - if (std.mem.eql(u8, final_name, exclude_field)) { - output_this_field = false; - } - } - } - - if (!field_output) { - field_output = output_this_field; - } else { - if (output_this_field) try out_stream.writeByte(','); - } - if (child_options.whitespace) |child_whitespace| { - if (output_this_field) try out_stream.writeByte('\n'); - if (output_this_field) try child_whitespace.outputIndent(out_stream); - } - var field_written = false; - if (comptime std.meta.hasFn(T, "jsonStringifyField")) { - if (output_this_field) field_written = try value.jsonStringifyField(field.name, child_options, out_stream); - } - - if (!field_written) { - if (output_this_field) { - try stringify(final_name, options, out_stream); - try out_stream.writeByte(':'); - } - if (child_options.whitespace) |child_whitespace| { - if (child_whitespace.separator) { - if (output_this_field) try out_stream.writeByte(' '); - } - } - if (output_this_field) { - try stringify(@field(value, field.name), child_options, out_stream); - } - } - } - if (field_output) { - if (options.whitespace) |whitespace| { - try out_stream.writeByte('\n'); - try whitespace.outputIndent(out_stream); - } - } - try out_stream.writeByte('}'); - return; - }, - .error_set => return stringify(@as([]const u8, @errorName(value)), options, out_stream), - .pointer => |ptr_info| switch (ptr_info.size) { - .one => switch (@typeInfo(ptr_info.child)) { - .array => { - const Slice = []const std.meta.Elem(ptr_info.child); - return stringify(@as(Slice, value), options, out_stream); - }, - else => { - // TODO: avoid loops? - return stringify(value.*, options, out_stream); - }, - }, - // TODO: .Many when there is a sentinel (waiting for https://github.com/ziglang/zig/pull/3972) - .slice => { - if (ptr_info.child == u8 and options.string == .String and std.unicode.utf8ValidateSlice(value)) { - try out_stream.writeByte('\"'); - var i: usize = 0; - while (i < value.len) : (i += 1) { - switch (value[i]) { - // normal ascii character - 0x20...0x21, 0x23...0x2E, 0x30...0x5B, 0x5D...0x7F => |c| try out_stream.writeByte(c), - // only 2 characters that *must* be escaped - '\\' => try out_stream.writeAll("\\\\"), - '\"' => try out_stream.writeAll("\\\""), - // solidus is optional to escape - '/' => { - if (options.string.String.escape_solidus) { - try out_stream.writeAll("\\/"); - } else { - try out_stream.writeByte('/'); - } - }, - // control characters with short escapes - // TODO: option to switch between unicode and 'short' forms? - 0x8 => try out_stream.writeAll("\\b"), - 0xC => try out_stream.writeAll("\\f"), - '\n' => try out_stream.writeAll("\\n"), - '\r' => try out_stream.writeAll("\\r"), - '\t' => try out_stream.writeAll("\\t"), - else => { - const ulen = std.unicode.utf8ByteSequenceLength(value[i]) catch unreachable; - // control characters (only things left with 1 byte length) should always be printed as unicode escapes - if (ulen == 1 or options.string.String.escape_unicode) { - const codepoint = std.unicode.utf8Decode(value[i .. i + ulen]) catch unreachable; - try outputUnicodeEscape(codepoint, out_stream); - } else { - try out_stream.writeAll(value[i .. i + ulen]); - } - i += ulen - 1; - }, - } - } - try out_stream.writeByte('\"'); - return; - } - - if (@typeInfo(ptr_info.child) == .@"struct" and @hasDecl(ptr_info.child, "is_map_type")) { - try serializeMapAsObject(value, options, out_stream); - return; - } - - var child_options = options; - if (child_options.whitespace) |*whitespace| { - whitespace.indent_level += 1; - } - - try out_stream.writeByte('['); - for (value, 0..) |x, i| { - if (i != 0) { - try out_stream.writeByte(','); - } - if (child_options.whitespace) |child_whitespace| { - try out_stream.writeByte('\n'); - try child_whitespace.outputIndent(out_stream); - } - try stringify(x, child_options, out_stream); - } - if (value.len != 0) { - if (options.whitespace) |whitespace| { - try out_stream.writeByte('\n'); - try whitespace.outputIndent(out_stream); - } - } - try out_stream.writeByte(']'); - return; - }, - else => @compileError("Unable to stringify type '" ++ @typeName(T) ++ "'"), - }, - .array => return stringify(&value, options, out_stream), - .vector => |info| { - const array: [info.len]info.child = value; - return stringify(&array, options, out_stream); - }, - else => @compileError("Unable to stringify type '" ++ @typeName(T) ++ "'"), - } - unreachable; -} - -fn teststringify(expected: []const u8, value: anytype, options: StringifyOptions) !void { - const ValidationWriter = struct { - const Self = @This(); - pub const Writer = std.io.Writer(*Self, Error, write); - pub const Error = error{ - TooMuchData, - DifferentData, - }; - - expected_remaining: []const u8, - - fn init(exp: []const u8) Self { - return .{ .expected_remaining = exp }; - } - - pub fn writer(self: *Self) Writer { - return .{ .context = self }; - } - - fn write(self: *Self, bytes: []const u8) Error!usize { - if (self.expected_remaining.len < bytes.len) { - std.log.warn( - \\====== expected this output: ========= - \\{s} - \\======== instead found this: ========= - \\{s} - \\====================================== - , .{ - self.expected_remaining, - bytes, - }); - return error.TooMuchData; - } - if (!mem.eql(u8, self.expected_remaining[0..bytes.len], bytes)) { - std.log.warn( - \\====== expected this output: ========= - \\{s} - \\======== instead found this: ========= - \\{s} - \\====================================== - , .{ - self.expected_remaining[0..bytes.len], - bytes, - }); - return error.DifferentData; - } - self.expected_remaining = self.expected_remaining[bytes.len..]; - return bytes.len; - } - }; - - var vos = ValidationWriter.init(expected); - try stringify(value, options, vos.writer()); - if (vos.expected_remaining.len > 0) return error.NotEnoughData; -} - -test "stringify basic types" { - try teststringify("false", false, StringifyOptions{}); - try teststringify("true", true, StringifyOptions{}); - try teststringify("null", @as(?u8, null), StringifyOptions{}); - try teststringify("null", @as(?*u32, null), StringifyOptions{}); - try teststringify("42", 42, StringifyOptions{}); - try teststringify("4.2e1", 42.0, StringifyOptions{}); - try teststringify("42", @as(u8, 42), StringifyOptions{}); - try teststringify("42", @as(u128, 42), StringifyOptions{}); - try teststringify("4.2e1", @as(f32, 42), StringifyOptions{}); - try teststringify("4.2e1", @as(f64, 42), StringifyOptions{}); - try teststringify("\"ItBroke\"", @as(anyerror, error.ItBroke), StringifyOptions{}); -} - -test "stringify string" { - try teststringify("\"hello\"", "hello", StringifyOptions{}); - try teststringify("\"with\\nescapes\\r\"", "with\nescapes\r", StringifyOptions{}); - try teststringify("\"with\\nescapes\\r\"", "with\nescapes\r", StringifyOptions{ .string = .{ .String = .{ .escape_unicode = true } } }); - try teststringify("\"with unicode\\u0001\"", "with unicode\u{1}", StringifyOptions{}); - try teststringify("\"with unicode\\u0001\"", "with unicode\u{1}", StringifyOptions{ .string = .{ .String = .{ .escape_unicode = true } } }); - try teststringify("\"with unicode\u{80}\"", "with unicode\u{80}", StringifyOptions{}); - try teststringify("\"with unicode\\u0080\"", "with unicode\u{80}", StringifyOptions{ .string = .{ .String = .{ .escape_unicode = true } } }); - try teststringify("\"with unicode\u{FF}\"", "with unicode\u{FF}", StringifyOptions{}); - try teststringify("\"with unicode\\u00ff\"", "with unicode\u{FF}", StringifyOptions{ .string = .{ .String = .{ .escape_unicode = true } } }); - try teststringify("\"with unicode\u{100}\"", "with unicode\u{100}", StringifyOptions{}); - try teststringify("\"with unicode\\u0100\"", "with unicode\u{100}", StringifyOptions{ .string = .{ .String = .{ .escape_unicode = true } } }); - try teststringify("\"with unicode\u{800}\"", "with unicode\u{800}", StringifyOptions{}); - try teststringify("\"with unicode\\u0800\"", "with unicode\u{800}", StringifyOptions{ .string = .{ .String = .{ .escape_unicode = true } } }); - try teststringify("\"with unicode\u{8000}\"", "with unicode\u{8000}", StringifyOptions{}); - try teststringify("\"with unicode\\u8000\"", "with unicode\u{8000}", StringifyOptions{ .string = .{ .String = .{ .escape_unicode = true } } }); - try teststringify("\"with unicode\u{D799}\"", "with unicode\u{D799}", StringifyOptions{}); - try teststringify("\"with unicode\\ud799\"", "with unicode\u{D799}", StringifyOptions{ .string = .{ .String = .{ .escape_unicode = true } } }); - try teststringify("\"with unicode\u{10000}\"", "with unicode\u{10000}", StringifyOptions{}); - try teststringify("\"with unicode\\ud800\\udc00\"", "with unicode\u{10000}", StringifyOptions{ .string = .{ .String = .{ .escape_unicode = true } } }); - try teststringify("\"with unicode\u{10FFFF}\"", "with unicode\u{10FFFF}", StringifyOptions{}); - try teststringify("\"with unicode\\udbff\\udfff\"", "with unicode\u{10FFFF}", StringifyOptions{ .string = .{ .String = .{ .escape_unicode = true } } }); - try teststringify("\"/\"", "/", StringifyOptions{}); - try teststringify("\"\\/\"", "/", StringifyOptions{ .string = .{ .String = .{ .escape_solidus = true } } }); -} - -test "stringify tagged unions" { - try teststringify("42", union(enum) { - Foo: u32, - Bar: bool, - }{ .Foo = 42 }, StringifyOptions{}); -} - -test "stringify struct" { - try teststringify("{\"foo\":42}", struct { - foo: u32, - }{ .foo = 42 }, StringifyOptions{}); -} - -test "stringify struct with indentation" { - try teststringify( - \\{ - \\ "foo": 42, - \\ "bar": [ - \\ 1, - \\ 2, - \\ 3 - \\ ] - \\} - , - struct { - foo: u32, - bar: [3]u32, - }{ - .foo = 42, - .bar = .{ 1, 2, 3 }, - }, - StringifyOptions{ - .whitespace = .{}, - }, - ); - try teststringify( - "{\n\t\"foo\":42,\n\t\"bar\":[\n\t\t1,\n\t\t2,\n\t\t3\n\t]\n}", - struct { - foo: u32, - bar: [3]u32, - }{ - .foo = 42, - .bar = .{ 1, 2, 3 }, - }, - StringifyOptions{ - .whitespace = .{ - .indent = .Tab, - .separator = false, - }, - }, - ); -} - -test "stringify struct with void field" { - try teststringify("{\"foo\":42}", struct { - foo: u32, - bar: void = {}, - }{ .foo = 42 }, StringifyOptions{}); -} - -test "stringify array of structs" { - const MyStruct = struct { - foo: u32, - }; - try teststringify("[{\"foo\":42},{\"foo\":100},{\"foo\":1000}]", [_]MyStruct{ - MyStruct{ .foo = 42 }, - MyStruct{ .foo = 100 }, - MyStruct{ .foo = 1000 }, - }, StringifyOptions{}); -} - -test "stringify struct with custom stringifier" { - try teststringify("[\"something special\",42]", struct { - foo: u32, - const Self = @This(); - pub fn jsonStringify( - _: Self, - options: StringifyOptions, - out_stream: anytype, - ) !void { - try out_stream.writeAll("[\"something special\","); - try stringify(42, options, out_stream); - try out_stream.writeByte(']'); - } - }{ .foo = 42 }, StringifyOptions{}); -} - -test "stringify vector" { - try teststringify("[1,1]", @as(@Vector(2, u32), @splat(@as(u32, 1))), StringifyOptions{}); -}