refactor: create shared lib for json
This commit is contained in:
parent
631d014215
commit
8007a910dd
9 changed files with 170 additions and 159 deletions
11
build.zig
11
build.zig
|
@ -81,11 +81,12 @@ pub fn build(b: *Builder) !void {
|
||||||
// End External dependencies
|
// End External dependencies
|
||||||
|
|
||||||
// Private modules/dependencies
|
// Private modules/dependencies
|
||||||
const mod_json = b.createModule(.{
|
const dep_json = b.dependency("json", .{
|
||||||
.root_source_file = b.path("codegen/src/json.zig"),
|
|
||||||
.target = target,
|
.target = target,
|
||||||
.optimize = optimize,
|
.optimize = optimize,
|
||||||
});
|
});
|
||||||
|
const mod_json = dep_json.module("json");
|
||||||
|
mod_exe.addImport("json", mod_json);
|
||||||
|
|
||||||
const dep_date = b.dependency("date", .{
|
const dep_date = b.dependency("date", .{
|
||||||
.target = target,
|
.target = target,
|
||||||
|
@ -114,6 +115,7 @@ pub fn build(b: *Builder) !void {
|
||||||
});
|
});
|
||||||
cg_mod.addImport("smithy", mod_smithy);
|
cg_mod.addImport("smithy", mod_smithy);
|
||||||
cg_mod.addImport("date", mod_date);
|
cg_mod.addImport("date", mod_date);
|
||||||
|
cg_mod.addImport("json", mod_json);
|
||||||
|
|
||||||
const cg_exe = b.addExecutable(.{
|
const cg_exe = b.addExecutable(.{
|
||||||
.name = "codegen",
|
.name = "codegen",
|
||||||
|
@ -163,7 +165,7 @@ pub fn build(b: *Builder) !void {
|
||||||
service_manifest_module.addImport("json", mod_json);
|
service_manifest_module.addImport("json", mod_json);
|
||||||
service_manifest_module.addImport("zeit", mod_zeit);
|
service_manifest_module.addImport("zeit", mod_zeit);
|
||||||
|
|
||||||
exe.root_module.addImport("service_manifest", service_manifest_module);
|
mod_exe.addImport("service_manifest", service_manifest_module);
|
||||||
|
|
||||||
// Expose module to others
|
// Expose module to others
|
||||||
const mod_aws = b.addModule("aws", .{
|
const mod_aws = b.addModule("aws", .{
|
||||||
|
@ -174,6 +176,7 @@ pub fn build(b: *Builder) !void {
|
||||||
mod_aws.addImport("smithy", mod_smithy);
|
mod_aws.addImport("smithy", mod_smithy);
|
||||||
mod_aws.addImport("service_manifest", service_manifest_module);
|
mod_aws.addImport("service_manifest", service_manifest_module);
|
||||||
mod_aws.addImport("date", mod_date);
|
mod_aws.addImport("date", mod_date);
|
||||||
|
mod_aws.addImport("json", mod_json);
|
||||||
mod_aws.addImport("zeit", mod_zeit);
|
mod_aws.addImport("zeit", mod_zeit);
|
||||||
|
|
||||||
// Expose module to others
|
// Expose module to others
|
||||||
|
@ -182,6 +185,7 @@ pub fn build(b: *Builder) !void {
|
||||||
});
|
});
|
||||||
mod_aws_signing.addImport("date", mod_date);
|
mod_aws_signing.addImport("date", mod_date);
|
||||||
mod_aws_signing.addImport("smithy", mod_smithy);
|
mod_aws_signing.addImport("smithy", mod_smithy);
|
||||||
|
mod_aws_signing.addImport("json", mod_json);
|
||||||
|
|
||||||
// Similar to creating the run step earlier, this exposes a `test` step to
|
// Similar to creating the run step earlier, this exposes a `test` step to
|
||||||
// the `zig build --help` menu, providing a way for the user to request
|
// the `zig build --help` menu, providing a way for the user to request
|
||||||
|
@ -214,6 +218,7 @@ pub fn build(b: *Builder) !void {
|
||||||
mod_unit_tests.addImport("service_manifest", service_manifest_module);
|
mod_unit_tests.addImport("service_manifest", service_manifest_module);
|
||||||
mod_unit_tests.addImport("date", mod_date);
|
mod_unit_tests.addImport("date", mod_date);
|
||||||
mod_unit_tests.addImport("zeit", mod_zeit);
|
mod_unit_tests.addImport("zeit", mod_zeit);
|
||||||
|
mod_unit_tests.addImport("json", mod_json);
|
||||||
|
|
||||||
// Creates a step for unit testing. This only builds the test executable
|
// Creates a step for unit testing. This only builds the test executable
|
||||||
// but does not run it.
|
// but does not run it.
|
||||||
|
|
|
@ -28,5 +28,8 @@
|
||||||
.date = .{
|
.date = .{
|
||||||
.path = "lib/date",
|
.path = "lib/date",
|
||||||
},
|
},
|
||||||
|
.json = .{
|
||||||
|
.path = "lib/json",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,150 +0,0 @@
|
||||||
const std = @import("std");
|
|
||||||
// options is a json.Options, but since we're using our hacked json.zig we don't want to
|
|
||||||
// specifically call this out
|
|
||||||
pub fn serializeMap(map: anytype, key: []const u8, options: anytype, out_stream: anytype) !bool {
|
|
||||||
if (@typeInfo(@TypeOf(map)) == .optional) {
|
|
||||||
if (map == null)
|
|
||||||
return false
|
|
||||||
else
|
|
||||||
return serializeMapInternal(map.?, key, options, out_stream);
|
|
||||||
}
|
|
||||||
return serializeMapInternal(map, key, options, out_stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn serializeMapInternal(map: anytype, key: []const u8, options: anytype, out_stream: anytype) !bool {
|
|
||||||
if (map.len == 0) {
|
|
||||||
var child_options = options;
|
|
||||||
if (child_options.whitespace) |*child_ws|
|
|
||||||
child_ws.indent_level += 1;
|
|
||||||
|
|
||||||
try out_stream.writeByte('"');
|
|
||||||
try out_stream.writeAll(key);
|
|
||||||
_ = try out_stream.write("\":");
|
|
||||||
if (options.whitespace) |ws| {
|
|
||||||
if (ws.separator) {
|
|
||||||
try out_stream.writeByte(' ');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
try out_stream.writeByte('{');
|
|
||||||
try out_stream.writeByte('}');
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// TODO: Map might be [][]struct{key, value} rather than []struct{key, value}
|
|
||||||
var child_options = options;
|
|
||||||
if (child_options.whitespace) |*child_ws|
|
|
||||||
child_ws.indent_level += 1;
|
|
||||||
|
|
||||||
try out_stream.writeByte('"');
|
|
||||||
try out_stream.writeAll(key);
|
|
||||||
_ = try out_stream.write("\":");
|
|
||||||
if (options.whitespace) |ws| {
|
|
||||||
if (ws.separator) {
|
|
||||||
try out_stream.writeByte(' ');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
try out_stream.writeByte('{');
|
|
||||||
if (options.whitespace) |_|
|
|
||||||
try out_stream.writeByte('\n');
|
|
||||||
for (map, 0..) |tag, i| {
|
|
||||||
if (tag.key == null or tag.value == null) continue;
|
|
||||||
// TODO: Deal with escaping and general "json.stringify" the values...
|
|
||||||
if (child_options.whitespace) |ws|
|
|
||||||
try ws.outputIndent(out_stream);
|
|
||||||
try out_stream.writeByte('"');
|
|
||||||
try jsonEscape(tag.key.?, child_options, out_stream);
|
|
||||||
_ = try out_stream.write("\":");
|
|
||||||
if (child_options.whitespace) |ws| {
|
|
||||||
if (ws.separator) {
|
|
||||||
try out_stream.writeByte(' ');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
try out_stream.writeByte('"');
|
|
||||||
try jsonEscape(tag.value.?, child_options, out_stream);
|
|
||||||
try out_stream.writeByte('"');
|
|
||||||
if (i < map.len - 1) {
|
|
||||||
try out_stream.writeByte(',');
|
|
||||||
}
|
|
||||||
if (child_options.whitespace) |_|
|
|
||||||
try out_stream.writeByte('\n');
|
|
||||||
}
|
|
||||||
if (options.whitespace) |ws|
|
|
||||||
try ws.outputIndent(out_stream);
|
|
||||||
try out_stream.writeByte('}');
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// code within jsonEscape lifted from json.zig in stdlib
|
|
||||||
fn jsonEscape(value: []const u8, options: anytype, out_stream: anytype) !void {
|
|
||||||
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;
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// outputUnicodeEscape and assert lifted from json.zig in stdlib
|
|
||||||
fn outputUnicodeEscape(
|
|
||||||
codepoint: u21,
|
|
||||||
out_stream: anytype,
|
|
||||||
) !void {
|
|
||||||
if (codepoint <= 0xFFFF) {
|
|
||||||
// If the character is in the Basic Multilingual Plane (U+0000 through U+FFFF),
|
|
||||||
// then it may be represented as a six-character sequence: a reverse solidus, followed
|
|
||||||
// by the lowercase letter u, followed by four hexadecimal digits that encode the character's code point.
|
|
||||||
try out_stream.writeAll("\\u");
|
|
||||||
try std.fmt.formatIntValue(codepoint, "x", std.fmt.FormatOptions{ .width = 4, .fill = '0' }, out_stream);
|
|
||||||
} else {
|
|
||||||
assert(codepoint <= 0x10FFFF);
|
|
||||||
// To escape an extended character that is not in the Basic Multilingual Plane,
|
|
||||||
// the character is represented as a 12-character sequence, encoding the UTF-16 surrogate pair.
|
|
||||||
const high = @as(u16, @intCast((codepoint - 0x10000) >> 10)) + 0xD800;
|
|
||||||
const low = @as(u16, @intCast(codepoint & 0x3FF)) + 0xDC00;
|
|
||||||
try out_stream.writeAll("\\u");
|
|
||||||
try std.fmt.formatIntValue(high, "x", std.fmt.FormatOptions{ .width = 4, .fill = '0' }, out_stream);
|
|
||||||
try out_stream.writeAll("\\u");
|
|
||||||
try std.fmt.formatIntValue(low, "x", std.fmt.FormatOptions{ .width = 4, .fill = '0' }, out_stream);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This function invokes undefined behavior when `ok` is `false`.
|
|
||||||
/// In Debug and ReleaseSafe modes, calls to this function are always
|
|
||||||
/// generated, and the `unreachable` statement triggers a panic.
|
|
||||||
/// In ReleaseFast and ReleaseSmall modes, calls to this function are
|
|
||||||
/// optimized away, and in fact the optimizer is able to use the assertion
|
|
||||||
/// in its heuristics.
|
|
||||||
/// Inside a test block, it is best to use the `std.testing` module rather
|
|
||||||
/// than this function, because this function may not detect a test failure
|
|
||||||
/// in ReleaseFast and ReleaseSmall mode. Outside of a test block, this assert
|
|
||||||
/// function is the correct function to use.
|
|
||||||
pub fn assert(ok: bool) void {
|
|
||||||
if (!ok) unreachable; // assertion failure
|
|
||||||
}
|
|
29
lib/json/build.zig
Normal file
29
lib/json/build.zig
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
pub fn build(b: *std.Build) void {
|
||||||
|
const target = b.standardTargetOptions(.{});
|
||||||
|
const optimize = b.standardOptimizeOption(.{});
|
||||||
|
|
||||||
|
const lib_mod = b.addModule("json", .{
|
||||||
|
.root_source_file = b.path("src/root.zig"),
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
});
|
||||||
|
|
||||||
|
const lib = b.addLibrary(.{
|
||||||
|
.linkage = .static,
|
||||||
|
.name = "json",
|
||||||
|
.root_module = lib_mod,
|
||||||
|
});
|
||||||
|
|
||||||
|
b.installArtifact(lib);
|
||||||
|
|
||||||
|
const lib_unit_tests = b.addTest(.{
|
||||||
|
.root_module = lib_mod,
|
||||||
|
});
|
||||||
|
|
||||||
|
const run_lib_unit_tests = b.addRunArtifact(lib_unit_tests);
|
||||||
|
|
||||||
|
const test_step = b.step("test", "Run unit tests");
|
||||||
|
test_step.dependOn(&run_lib_unit_tests.step);
|
||||||
|
}
|
12
lib/json/build.zig.zon
Normal file
12
lib/json/build.zig.zon
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
.{
|
||||||
|
.name = .json,
|
||||||
|
.version = "0.0.0",
|
||||||
|
.fingerprint = 0x6b0725452065211c, // Changing this has security and trust implications.
|
||||||
|
.minimum_zig_version = "0.14.0",
|
||||||
|
.dependencies = .{},
|
||||||
|
.paths = .{
|
||||||
|
"build.zig",
|
||||||
|
"build.zig.zon",
|
||||||
|
"src",
|
||||||
|
},
|
||||||
|
}
|
|
@ -14,8 +14,116 @@ const testing = std.testing;
|
||||||
const mem = std.mem;
|
const mem = std.mem;
|
||||||
const maxInt = std.math.maxInt;
|
const maxInt = std.math.maxInt;
|
||||||
|
|
||||||
// pub const WriteStream = @import("json/write_stream.zig").WriteStream;
|
pub fn serializeMap(map: anytype, key: []const u8, options: anytype, out_stream: anytype) !bool {
|
||||||
// pub const writeStream = @import("json/write_stream.zig").writeStream;
|
if (@typeInfo(@TypeOf(map)) == .optional) {
|
||||||
|
if (map == null)
|
||||||
|
return false
|
||||||
|
else
|
||||||
|
return serializeMapInternal(map.?, key, options, out_stream);
|
||||||
|
}
|
||||||
|
return serializeMapInternal(map, key, options, out_stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serializeMapInternal(map: anytype, key: []const u8, options: anytype, out_stream: anytype) !bool {
|
||||||
|
if (map.len == 0) {
|
||||||
|
var child_options = options;
|
||||||
|
if (child_options.whitespace) |*child_ws|
|
||||||
|
child_ws.indent_level += 1;
|
||||||
|
|
||||||
|
try out_stream.writeByte('"');
|
||||||
|
try out_stream.writeAll(key);
|
||||||
|
_ = try out_stream.write("\":");
|
||||||
|
if (options.whitespace) |ws| {
|
||||||
|
if (ws.separator) {
|
||||||
|
try out_stream.writeByte(' ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try out_stream.writeByte('{');
|
||||||
|
try out_stream.writeByte('}');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// TODO: Map might be [][]struct{key, value} rather than []struct{key, value}
|
||||||
|
var child_options = options;
|
||||||
|
if (child_options.whitespace) |*child_ws|
|
||||||
|
child_ws.indent_level += 1;
|
||||||
|
|
||||||
|
try out_stream.writeByte('"');
|
||||||
|
try out_stream.writeAll(key);
|
||||||
|
_ = try out_stream.write("\":");
|
||||||
|
if (options.whitespace) |ws| {
|
||||||
|
if (ws.separator) {
|
||||||
|
try out_stream.writeByte(' ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try out_stream.writeByte('{');
|
||||||
|
if (options.whitespace) |_|
|
||||||
|
try out_stream.writeByte('\n');
|
||||||
|
for (map, 0..) |tag, i| {
|
||||||
|
if (tag.key == null or tag.value == null) continue;
|
||||||
|
// TODO: Deal with escaping and general "json.stringify" the values...
|
||||||
|
if (child_options.whitespace) |ws|
|
||||||
|
try ws.outputIndent(out_stream);
|
||||||
|
try out_stream.writeByte('"');
|
||||||
|
try jsonEscape(tag.key.?, child_options, out_stream);
|
||||||
|
_ = try out_stream.write("\":");
|
||||||
|
if (child_options.whitespace) |ws| {
|
||||||
|
if (ws.separator) {
|
||||||
|
try out_stream.writeByte(' ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try out_stream.writeByte('"');
|
||||||
|
try jsonEscape(tag.value.?, child_options, out_stream);
|
||||||
|
try out_stream.writeByte('"');
|
||||||
|
if (i < map.len - 1) {
|
||||||
|
try out_stream.writeByte(',');
|
||||||
|
}
|
||||||
|
if (child_options.whitespace) |_|
|
||||||
|
try out_stream.writeByte('\n');
|
||||||
|
}
|
||||||
|
if (options.whitespace) |ws|
|
||||||
|
try ws.outputIndent(out_stream);
|
||||||
|
try out_stream.writeByte('}');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// code within jsonEscape lifted from json.zig in stdlib
|
||||||
|
fn jsonEscape(value: []const u8, options: anytype, out_stream: anytype) !void {
|
||||||
|
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;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const StringEscapes = union(enum) {
|
const StringEscapes = union(enum) {
|
||||||
None,
|
None,
|
||||||
|
@ -1316,8 +1424,8 @@ pub const Value = union(enum) {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dump(self: Value) void {
|
pub fn dump(self: Value) void {
|
||||||
var held = std.debug.getStderrMutex().acquire();
|
std.debug.lockStdErr();
|
||||||
defer held.release();
|
defer std.debug.unlockStdErr();
|
||||||
|
|
||||||
const stderr = std.io.getStdErr().writer();
|
const stderr = std.io.getStdErr().writer();
|
||||||
stringify(self, StringifyOptions{ .whitespace = null }, stderr) catch return;
|
stringify(self, StringifyOptions{ .whitespace = null }, stderr) catch return;
|
4
lib/json/src/root.zig
Normal file
4
lib/json/src/root.zig
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const testing = std.testing;
|
||||||
|
|
||||||
|
pub usingnamespace @import("json.zig");
|
|
@ -3,7 +3,7 @@ const std = @import("std");
|
||||||
const zeit = @import("zeit");
|
const zeit = @import("zeit");
|
||||||
|
|
||||||
const awshttp = @import("aws_http.zig");
|
const awshttp = @import("aws_http.zig");
|
||||||
const json = @import("json.zig");
|
const json = @import("json");
|
||||||
const url = @import("url.zig");
|
const url = @import("url.zig");
|
||||||
const case = @import("case.zig");
|
const case = @import("case.zig");
|
||||||
const date = @import("date");
|
const date = @import("date");
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const aws = @import("aws.zig");
|
const aws = @import("aws.zig");
|
||||||
const json = @import("json.zig");
|
const json = @import("json");
|
||||||
|
|
||||||
var verbose: u8 = 0;
|
var verbose: u8 = 0;
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue