392 lines
14 KiB
Zig
392 lines
14 KiB
Zig
const std = @import("std");
|
|
const smithy = @import("smithy");
|
|
const smithy_tools = @import("../smithy_tools.zig");
|
|
const support = @import("../support.zig");
|
|
|
|
const GenerationState = @import("../GenerationState.zig");
|
|
const GenerateTypeOptions = @import("../GenerateTypeOptions.zig");
|
|
const Allocator = std.mem.Allocator;
|
|
|
|
const Shape = smithy_tools.Shape;
|
|
|
|
const JsonMember = struct {
|
|
field_name: []const u8,
|
|
json_key: []const u8,
|
|
target: []const u8,
|
|
type_member: smithy.TypeMember,
|
|
shape_info: smithy.ShapeInfo,
|
|
};
|
|
|
|
pub fn generateToJsonFunction(shape_id: []const u8, writer: *std.Io.Writer, state: GenerationState, comptime options: GenerateTypeOptions) !void {
|
|
_ = options;
|
|
const allocator = state.allocator;
|
|
|
|
const shape_info = try smithy_tools.getShapeInfo(shape_id, state.file_state.shapes);
|
|
const shape = shape_info.shape;
|
|
|
|
if (try getJsonMembers(allocator, shape, state)) |json_members| {
|
|
if (json_members.items.len > 0) {
|
|
try writer.writeAll("pub fn jsonStringify(self: @This(), jw: anytype) !void {\n");
|
|
try writer.writeAll("try jw.beginObject();\n");
|
|
try writer.writeAll("{\n");
|
|
|
|
for (json_members.items) |member| {
|
|
const member_value = try getMemberValueJson(allocator, "self", member);
|
|
defer allocator.free(member_value);
|
|
|
|
try writer.print("try jw.objectField(\"{s}\");\n", .{member.json_key});
|
|
try writeMemberJson(
|
|
.{
|
|
.shape_id = member.target,
|
|
.field_name = member.field_name,
|
|
.field_value = member_value,
|
|
.state = state.indent(),
|
|
.member = member.type_member,
|
|
},
|
|
writer,
|
|
);
|
|
}
|
|
|
|
try writer.writeAll("}\n");
|
|
try writer.writeAll("try jw.endObject();\n");
|
|
try writer.writeAll("}\n\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
fn getJsonMembers(allocator: Allocator, shape: Shape, state: GenerationState) !?std.ArrayListUnmanaged(JsonMember) {
|
|
const is_json_shape = switch (state.file_state.protocol) {
|
|
.json_1_0, .json_1_1, .rest_json_1 => true,
|
|
else => false,
|
|
};
|
|
|
|
if (!is_json_shape) {
|
|
return null;
|
|
}
|
|
|
|
var hash_map = std.StringHashMapUnmanaged(smithy.TypeMember){};
|
|
|
|
const shape_members = smithy_tools.getShapeMembers(shape);
|
|
for (shape_members) |member| {
|
|
try hash_map.putNoClobber(state.allocator, member.name, member);
|
|
}
|
|
|
|
for (shape_members) |member| {
|
|
for (member.traits) |trait| {
|
|
switch (trait) {
|
|
.http_header, .http_query => {
|
|
std.debug.assert(hash_map.remove(member.name));
|
|
break;
|
|
},
|
|
else => continue,
|
|
}
|
|
}
|
|
}
|
|
|
|
if (hash_map.count() == 0) {
|
|
return null;
|
|
}
|
|
|
|
var json_members = std.ArrayListUnmanaged(JsonMember){};
|
|
|
|
var iter = hash_map.iterator();
|
|
while (iter.next()) |kvp| {
|
|
const member = kvp.value_ptr.*;
|
|
|
|
const key = blk: {
|
|
if (smithy_tools.findTrait(.json_name, member.traits)) |trait| {
|
|
break :blk trait.json_name;
|
|
}
|
|
|
|
break :blk member.name;
|
|
};
|
|
|
|
try json_members.append(allocator, .{
|
|
.field_name = try support.constantName(allocator, member.name, .snake),
|
|
.json_key = key,
|
|
.target = member.target,
|
|
.type_member = member,
|
|
.shape_info = try smithy_tools.getShapeInfo(member.target, state.file_state.shapes),
|
|
});
|
|
}
|
|
|
|
return json_members;
|
|
}
|
|
|
|
fn getMemberValueJson(allocator: std.mem.Allocator, source: []const u8, member: JsonMember) ![]const u8 {
|
|
const member_value = try std.fmt.allocPrint(allocator, "@field({s}, \"{s}\")", .{ source, member.field_name });
|
|
defer allocator.free(member_value);
|
|
|
|
var output_block = std.Io.Writer.Allocating.init(allocator);
|
|
defer output_block.deinit();
|
|
|
|
try writeMemberValue(
|
|
&output_block.writer,
|
|
member_value,
|
|
);
|
|
|
|
return output_block.toOwnedSlice();
|
|
}
|
|
|
|
fn getShapeJsonValueType(shape: Shape) []const u8 {
|
|
return switch (shape) {
|
|
.string, .@"enum", .blob, .document, .timestamp => ".string",
|
|
.boolean => ".bool",
|
|
.integer, .bigInteger, .short, .long => ".integer",
|
|
.float, .double, .bigDecimal => ".float",
|
|
else => std.debug.panic("Unexpected shape: {}", .{shape}),
|
|
};
|
|
}
|
|
|
|
fn writeMemberValue(
|
|
writer: *std.Io.Writer,
|
|
member_value: []const u8,
|
|
) !void {
|
|
try writer.writeAll(member_value);
|
|
}
|
|
|
|
const WriteMemberJsonParams = struct {
|
|
shape_id: []const u8,
|
|
field_name: []const u8,
|
|
field_value: []const u8,
|
|
state: GenerationState,
|
|
member: smithy.TypeMember,
|
|
};
|
|
|
|
fn writeStructureJson(params: WriteMemberJsonParams, writer: *std.Io.Writer) !void {
|
|
const shape_type = "structure";
|
|
const allocator = params.state.allocator;
|
|
const state = params.state;
|
|
|
|
const shape_info = try smithy_tools.getShapeInfo(params.shape_id, state.file_state.shapes);
|
|
const shape = shape_info.shape;
|
|
|
|
const structure_name = try std.fmt.allocPrint(params.state.allocator, "{s}_{s}_{d}", .{ params.field_name, shape_type, state.indent_level });
|
|
defer params.state.allocator.free(structure_name);
|
|
|
|
const object_value_capture = try std.fmt.allocPrint(allocator, "{s}_capture", .{structure_name});
|
|
defer allocator.free(object_value_capture);
|
|
|
|
try writer.print("\n// start {s}: {s}\n", .{ shape_type, structure_name });
|
|
defer writer.print("// end {s}: {s}\n", .{ shape_type, structure_name }) catch std.debug.panic("Unreachable", .{});
|
|
|
|
if (try getJsonMembers(allocator, shape, state)) |json_members| {
|
|
if (json_members.items.len > 0) {
|
|
const is_optional = smithy_tools.shapeIsOptional(params.member.traits);
|
|
|
|
var object_value = params.field_value;
|
|
|
|
if (is_optional) {
|
|
object_value = object_value_capture;
|
|
|
|
try writer.print("if ({s}) |{s}|", .{ params.field_value, object_value_capture });
|
|
try writer.writeAll("{\n");
|
|
}
|
|
|
|
try writer.writeAll("try jw.beginObject();\n");
|
|
try writer.writeAll("{\n");
|
|
|
|
// this is a workaround in case a child structure doesn't have any fields
|
|
// and therefore doesn't use the structure variable so we capture it here.
|
|
// the compiler should optimize this away
|
|
try writer.print("const unused_capture_{s} = {s};\n", .{ structure_name, object_value });
|
|
try writer.print("_ = unused_capture_{s};\n", .{structure_name});
|
|
|
|
for (json_members.items) |member| {
|
|
const member_value = try getMemberValueJson(allocator, object_value, member);
|
|
defer allocator.free(member_value);
|
|
|
|
try writer.print("try jw.objectField(\"{s}\");\n", .{member.json_key});
|
|
try writeMemberJson(
|
|
.{
|
|
.shape_id = member.target,
|
|
.field_name = member.field_name,
|
|
.field_value = member_value,
|
|
.state = state.indent(),
|
|
.member = member.type_member,
|
|
},
|
|
writer,
|
|
);
|
|
}
|
|
|
|
try writer.writeAll("}\n");
|
|
try writer.writeAll("try jw.endObject();\n");
|
|
|
|
if (is_optional) {
|
|
try writer.writeAll("} else {\n");
|
|
try writer.writeAll("try jw.write(null);\n");
|
|
try writer.writeAll("}\n");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn writeListJson(list: smithy_tools.ListShape, params: WriteMemberJsonParams, writer: *std.Io.Writer) anyerror!void {
|
|
const state = params.state;
|
|
const allocator = state.allocator;
|
|
|
|
const list_name = try std.fmt.allocPrint(allocator, "{s}_list_{d}", .{ params.field_name, state.indent_level });
|
|
defer state.allocator.free(list_name);
|
|
|
|
try writer.print("\n// start list: {s}\n", .{list_name});
|
|
defer writer.print("// end list: {s}\n", .{list_name}) catch std.debug.panic("Unreachable", .{});
|
|
|
|
const list_each_value = try std.fmt.allocPrint(allocator, "{s}_value", .{list_name});
|
|
defer allocator.free(list_each_value);
|
|
|
|
const list_capture = try std.fmt.allocPrint(allocator, "{s}_capture", .{list_name});
|
|
defer allocator.free(list_capture);
|
|
|
|
{
|
|
const list_is_optional = smithy_tools.shapeIsOptional(list.traits);
|
|
|
|
var list_value = params.field_value;
|
|
|
|
if (list_is_optional) {
|
|
list_value = list_capture;
|
|
|
|
try writer.print("if ({s}) |{s}| ", .{
|
|
params.field_value,
|
|
list_capture,
|
|
});
|
|
try writer.writeAll("{\n");
|
|
}
|
|
|
|
// start loop
|
|
try writer.writeAll("try jw.beginArray();\n");
|
|
try writer.print("for ({s}) |{s}|", .{ list_value, list_each_value });
|
|
try writer.writeAll("{\n");
|
|
try writer.writeAll("try jw.write(");
|
|
try writeMemberValue(
|
|
writer,
|
|
list_each_value,
|
|
);
|
|
try writer.writeAll(");\n");
|
|
try writer.writeAll("}\n");
|
|
try writer.writeAll("try jw.endArray();\n");
|
|
// end loop
|
|
|
|
if (list_is_optional) {
|
|
try writer.writeAll("} else {\n");
|
|
try writer.writeAll("try jw.write(null);\n");
|
|
try writer.writeAll("}\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
fn writeMapJson(map: smithy_tools.MapShape, params: WriteMemberJsonParams, writer: *std.Io.Writer) anyerror!void {
|
|
const state = params.state;
|
|
const name = params.field_name;
|
|
const value = params.field_value;
|
|
const allocator = state.allocator;
|
|
|
|
const map_name = try std.fmt.allocPrint(allocator, "{s}_object_map_{d}", .{ name, state.indent_level });
|
|
defer allocator.free(map_name);
|
|
|
|
try writer.print("\n// start map: {s}\n", .{map_name});
|
|
defer writer.print("// end map: {s}\n", .{map_name}) catch std.debug.panic("Unreachable", .{});
|
|
|
|
const map_value_capture = try std.fmt.allocPrint(allocator, "{s}_kvp", .{map_name});
|
|
defer allocator.free(map_value_capture);
|
|
|
|
const map_capture_key = try std.fmt.allocPrint(allocator, "{s}.key", .{map_value_capture});
|
|
defer allocator.free(map_capture_key);
|
|
|
|
const map_capture_value = try std.fmt.allocPrint(allocator, "{s}.value", .{map_value_capture});
|
|
defer allocator.free(map_capture_value);
|
|
|
|
const value_shape_info = try smithy_tools.getShapeInfo(map.value, state.file_state.shapes);
|
|
|
|
const value_member = smithy.TypeMember{
|
|
.name = "value",
|
|
.target = map.value,
|
|
.traits = smithy_tools.getShapeTraits(value_shape_info.shape),
|
|
};
|
|
|
|
const map_capture = try std.fmt.allocPrint(state.allocator, "{s}_capture", .{map_name});
|
|
|
|
{
|
|
const map_member = params.member;
|
|
const map_is_optional = !smithy_tools.hasTrait(.required, map_member.traits);
|
|
|
|
var map_value = value;
|
|
|
|
if (map_is_optional) {
|
|
map_value = map_capture;
|
|
|
|
try writer.print("if ({s}) |{s}| ", .{
|
|
value,
|
|
map_capture,
|
|
});
|
|
try writer.writeAll("{\n");
|
|
}
|
|
|
|
try writer.writeAll("try jw.beginObject();\n");
|
|
try writer.writeAll("{\n");
|
|
|
|
// start loop
|
|
try writer.print("for ({s}) |{s}|", .{ map_value, map_value_capture });
|
|
try writer.writeAll("{\n");
|
|
try writer.print("try jw.objectField({s});\n", .{map_capture_key});
|
|
|
|
try writeMemberJson(.{
|
|
.shape_id = map.value,
|
|
.field_name = "value",
|
|
.field_value = map_capture_value,
|
|
.state = state.indent(),
|
|
.member = value_member,
|
|
}, writer);
|
|
|
|
try writer.writeAll("}\n");
|
|
// end loop
|
|
|
|
try writer.writeAll("}\n");
|
|
try writer.writeAll("try jw.endObject();\n");
|
|
|
|
if (map_is_optional) {
|
|
try writer.writeAll("} else {\n");
|
|
try writer.writeAll("try jw.write(null);\n");
|
|
try writer.writeAll("}\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
fn writeScalarJson(comment: []const u8, params: WriteMemberJsonParams, writer: *std.Io.Writer) anyerror!void {
|
|
try writer.print("try jw.write({s}); // {s}\n\n", .{ params.field_value, comment });
|
|
}
|
|
|
|
fn writeMemberJson(params: WriteMemberJsonParams, writer: *std.Io.Writer) anyerror!void {
|
|
const shape_id = params.shape_id;
|
|
const state = params.state;
|
|
const shape_info = try smithy_tools.getShapeInfo(shape_id, state.file_state.shapes);
|
|
const shape = shape_info.shape;
|
|
|
|
if (state.getTypeRecurrenceCount(shape_id) > 2) {
|
|
return;
|
|
}
|
|
|
|
try state.appendToTypeStack(&shape_info);
|
|
defer state.popFromTypeStack();
|
|
|
|
switch (shape) {
|
|
.structure, .uniontype => try writeStructureJson(params, writer),
|
|
.list => |l| try writeListJson(l, params, writer),
|
|
.map => |m| try writeMapJson(m, params, writer),
|
|
.timestamp => try writeScalarJson("timestamp", params, writer),
|
|
.string => try writeScalarJson("string", params, writer),
|
|
.@"enum" => try writeScalarJson("enum", params, writer),
|
|
.document => try writeScalarJson("document", params, writer),
|
|
.blob => try writeScalarJson("blob", params, writer),
|
|
.boolean => try writeScalarJson("bool", params, writer),
|
|
.float => try writeScalarJson("float", params, writer),
|
|
.integer => try writeScalarJson("integer", params, writer),
|
|
.long => try writeScalarJson("long", params, writer),
|
|
.double => try writeScalarJson("double", params, writer),
|
|
.bigDecimal => try writeScalarJson("bigDecimal", params, writer),
|
|
.bigInteger => try writeScalarJson("bigInteger", params, writer),
|
|
.unit => try writeScalarJson("unit", params, writer),
|
|
.byte => try writeScalarJson("byte", params, writer),
|
|
.short => try writeScalarJson("short", params, writer),
|
|
.service, .resource, .operation, .member, .set => std.debug.panic("Shape type not supported: {}", .{shape}),
|
|
}
|
|
}
|