add jsonStringify implementation
This commit is contained in:
parent
f612b3798a
commit
80a76b0998
|
@ -3,6 +3,7 @@ const std = @import("std");
|
||||||
// specifically call this out
|
// specifically call this out
|
||||||
pub fn serializeMap(map: anytype, key: []const u8, options: anytype, out_stream: anytype) !bool {
|
pub fn serializeMap(map: anytype, key: []const u8, options: anytype, out_stream: anytype) !bool {
|
||||||
if (map.len == 0) return true;
|
if (map.len == 0) return true;
|
||||||
|
// TODO: Map might be [][]struct{key, value} rather than []struct{key, value}
|
||||||
var child_options = options;
|
var child_options = options;
|
||||||
if (child_options.whitespace) |*child_ws|
|
if (child_options.whitespace) |*child_ws|
|
||||||
child_ws.indent_level += 1;
|
child_ws.indent_level += 1;
|
||||||
|
|
|
@ -163,7 +163,7 @@ fn constantName(allocator: *std.mem.Allocator, id: []const u8) ![]const u8 {
|
||||||
|
|
||||||
const GenerationState = struct {
|
const GenerationState = struct {
|
||||||
type_stack: *std.ArrayList(*const smithy.ShapeInfo),
|
type_stack: *std.ArrayList(*const smithy.ShapeInfo),
|
||||||
map_fields: *std.StringArrayHashMap(std.ArrayList([]const u8)),
|
// we will need some sort of "type decls needed" for recursive structures
|
||||||
allocator: *std.mem.Allocator,
|
allocator: *std.mem.Allocator,
|
||||||
indent_level: u64,
|
indent_level: u64,
|
||||||
all_required: bool,
|
all_required: bool,
|
||||||
|
@ -179,15 +179,8 @@ fn generateOperation(allocator: *std.mem.Allocator, operation: smithy.ShapeInfo,
|
||||||
|
|
||||||
var type_stack = std.ArrayList(*const smithy.ShapeInfo).init(allocator);
|
var type_stack = std.ArrayList(*const smithy.ShapeInfo).init(allocator);
|
||||||
defer type_stack.deinit();
|
defer type_stack.deinit();
|
||||||
var map_fields = std.StringArrayHashMap(std.ArrayList([]const u8)).init(allocator);
|
|
||||||
defer {
|
|
||||||
for (map_fields.values()) |v|
|
|
||||||
v.deinit();
|
|
||||||
map_fields.deinit();
|
|
||||||
}
|
|
||||||
const state = GenerationState{
|
const state = GenerationState{
|
||||||
.type_stack = &type_stack,
|
.type_stack = &type_stack,
|
||||||
.map_fields = &map_fields,
|
|
||||||
.allocator = allocator,
|
.allocator = allocator,
|
||||||
.indent_level = 1,
|
.indent_level = 1,
|
||||||
.all_required = false,
|
.all_required = false,
|
||||||
|
@ -218,7 +211,7 @@ fn generateOperation(allocator: *std.mem.Allocator, operation: smithy.ShapeInfo,
|
||||||
try outputIndent(state, writer);
|
try outputIndent(state, writer);
|
||||||
_ = try writer.write("Request: type = ");
|
_ = try writer.write("Request: type = ");
|
||||||
if (operation.shape.operation.input) |member| {
|
if (operation.shape.operation.input) |member| {
|
||||||
try generateTypeFor(member, shapes, writer, state, false);
|
if (try generateTypeFor(member, shapes, writer, state, false)) unreachable; // we expect only structs here
|
||||||
_ = try writer.write("\n");
|
_ = try writer.write("\n");
|
||||||
try generateMetadataFunction(operation_name, state, writer);
|
try generateMetadataFunction(operation_name, state, writer);
|
||||||
} else {
|
} else {
|
||||||
|
@ -229,7 +222,7 @@ fn generateOperation(allocator: *std.mem.Allocator, operation: smithy.ShapeInfo,
|
||||||
try outputIndent(state, writer);
|
try outputIndent(state, writer);
|
||||||
_ = try writer.write("Response: type = ");
|
_ = try writer.write("Response: type = ");
|
||||||
if (operation.shape.operation.output) |member| {
|
if (operation.shape.operation.output) |member| {
|
||||||
try generateTypeFor(member, shapes, writer, state, true);
|
if (try generateTypeFor(member, shapes, writer, state, true)) unreachable; // we expect only structs here
|
||||||
} else _ = try writer.write("struct {}"); // we want to maintain consistency with other ops
|
} else _ = try writer.write("struct {}"); // we want to maintain consistency with other ops
|
||||||
_ = try writer.write(",\n");
|
_ = try writer.write(",\n");
|
||||||
|
|
||||||
|
@ -284,7 +277,8 @@ fn endsWith(item: []const u8, str: []const u8) bool {
|
||||||
return std.mem.eql(u8, item, str[str.len - item.len ..]);
|
return std.mem.eql(u8, item, str[str.len - item.len ..]);
|
||||||
}
|
}
|
||||||
/// return type is anyerror!void as this is a recursive function, so the compiler cannot properly infer error types
|
/// return type is anyerror!void as this is a recursive function, so the compiler cannot properly infer error types
|
||||||
fn generateTypeFor(shape_id: []const u8, shapes: std.StringHashMap(smithy.ShapeInfo), writer: anytype, state: GenerationState, end_structure: bool) anyerror!void {
|
fn generateTypeFor(shape_id: []const u8, shapes: std.StringHashMap(smithy.ShapeInfo), writer: anytype, state: GenerationState, end_structure: bool) anyerror!bool {
|
||||||
|
var rc = false;
|
||||||
if (shapes.get(shape_id) == null) {
|
if (shapes.get(shape_id) == null) {
|
||||||
std.debug.print("Shape ID not found. This is most likely a bug. Shape ID: {s}\n", .{shape_id});
|
std.debug.print("Shape ID not found. This is most likely a bug. Shape ID: {s}\n", .{shape_id});
|
||||||
return error.InvalidType;
|
return error.InvalidType;
|
||||||
|
@ -319,12 +313,15 @@ fn generateTypeFor(shape_id: []const u8, shapes: std.StringHashMap(smithy.ShapeI
|
||||||
// DOS attack
|
// DOS attack
|
||||||
try generateSimpleTypeFor("nothing", "[]const u8", writer);
|
try generateSimpleTypeFor("nothing", "[]const u8", writer);
|
||||||
std.log.warn("Type cycle detected, limiting depth. Type: {s}", .{shape_id});
|
std.log.warn("Type cycle detected, limiting depth. Type: {s}", .{shape_id});
|
||||||
|
// if (std.mem.eql(u8, "com.amazonaws.workmail#Timestamp", shape_id)) {
|
||||||
// std.log.info(" Type stack:\n", .{});
|
// std.log.info(" Type stack:\n", .{});
|
||||||
// for (type_stack.items) |i|
|
// for (state.type_stack.items) |i|
|
||||||
// std.log.info(" {s}", .{i.*.id});
|
// std.log.info(" {s}", .{i.*.id});
|
||||||
return;
|
// }
|
||||||
|
return false; // not a map
|
||||||
}
|
}
|
||||||
try state.type_stack.append(&shape_info);
|
try state.type_stack.append(&shape_info);
|
||||||
|
defer _ = state.type_stack.pop();
|
||||||
switch (shape) {
|
switch (shape) {
|
||||||
.structure => {
|
.structure => {
|
||||||
try generateComplexTypeFor(shape_id, shape.structure.members, "struct", shapes, writer, state);
|
try generateComplexTypeFor(shape_id, shape.structure.members, "struct", shapes, writer, state);
|
||||||
|
@ -344,11 +341,13 @@ fn generateTypeFor(shape_id: []const u8, shapes: std.StringHashMap(smithy.ShapeI
|
||||||
.integer => |s| try generateSimpleTypeFor(s, "i64", writer),
|
.integer => |s| try generateSimpleTypeFor(s, "i64", writer),
|
||||||
.list => {
|
.list => {
|
||||||
_ = try writer.write("[]");
|
_ = try writer.write("[]");
|
||||||
try generateTypeFor(shape.list.member_target, shapes, writer, state, true);
|
// The serializer will have to deal with the idea we might be an array
|
||||||
|
return try generateTypeFor(shape.list.member_target, shapes, writer, state, true);
|
||||||
},
|
},
|
||||||
.set => {
|
.set => {
|
||||||
_ = try writer.write("[]");
|
_ = try writer.write("[]");
|
||||||
try generateTypeFor(shape.set.member_target, shapes, writer, state, true);
|
// The serializer will have to deal with the idea we might be an array
|
||||||
|
return try generateTypeFor(shape.set.member_target, shapes, writer, state, true);
|
||||||
},
|
},
|
||||||
.timestamp => |s| try generateSimpleTypeFor(s, "i64", writer),
|
.timestamp => |s| try generateSimpleTypeFor(s, "i64", writer),
|
||||||
.blob => |s| try generateSimpleTypeFor(s, "[]const u8", writer),
|
.blob => |s| try generateSimpleTypeFor(s, "[]const u8", writer),
|
||||||
|
@ -357,30 +356,33 @@ fn generateTypeFor(shape_id: []const u8, shapes: std.StringHashMap(smithy.ShapeI
|
||||||
.float => |s| try generateSimpleTypeFor(s, "f32", writer),
|
.float => |s| try generateSimpleTypeFor(s, "f32", writer),
|
||||||
.long => |s| try generateSimpleTypeFor(s, "i64", writer),
|
.long => |s| try generateSimpleTypeFor(s, "i64", writer),
|
||||||
.map => {
|
.map => {
|
||||||
// TODO: We need this:
|
|
||||||
//
|
|
||||||
// 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;
|
|
||||||
// }
|
|
||||||
_ = try writer.write("[]struct {\n");
|
_ = try writer.write("[]struct {\n");
|
||||||
var child_state = state;
|
var child_state = state;
|
||||||
child_state.indent_level += 1;
|
child_state.indent_level += 1;
|
||||||
try outputIndent(child_state, writer);
|
try outputIndent(child_state, writer);
|
||||||
_ = try writer.write("key: ");
|
_ = try writer.write("key: ");
|
||||||
try writeOptional(shape.map.traits, writer, null);
|
try writeOptional(shape.map.traits, writer, null);
|
||||||
try generateTypeFor(shape.map.key, shapes, writer, state, true);
|
var sub_maps = std.ArrayList([]const u8).init(state.allocator);
|
||||||
|
defer sub_maps.deinit();
|
||||||
|
if (try generateTypeFor(shape.map.key, shapes, writer, child_state, true))
|
||||||
|
try sub_maps.append("key");
|
||||||
try writeOptional(shape.map.traits, writer, " = null");
|
try writeOptional(shape.map.traits, writer, " = null");
|
||||||
_ = try writer.write(",\n");
|
_ = try writer.write(",\n");
|
||||||
try outputIndent(child_state, writer);
|
try outputIndent(child_state, writer);
|
||||||
_ = try writer.write("value: ");
|
_ = try writer.write("value: ");
|
||||||
try writeOptional(shape.map.traits, writer, null);
|
try writeOptional(shape.map.traits, writer, null);
|
||||||
try generateTypeFor(shape.map.key, shapes, writer, state, true);
|
if (try generateTypeFor(shape.map.value, shapes, writer, child_state, true))
|
||||||
|
try sub_maps.append("value");
|
||||||
try writeOptional(shape.map.traits, writer, " = null");
|
try writeOptional(shape.map.traits, writer, " = null");
|
||||||
_ = try writer.write(",\n");
|
_ = try writer.write(",\n");
|
||||||
|
if (sub_maps.items.len > 0) {
|
||||||
|
_ = try writer.write("\n");
|
||||||
|
try writeStringify(state, sub_maps.items, writer);
|
||||||
|
}
|
||||||
try outputIndent(state, writer);
|
try outputIndent(state, writer);
|
||||||
_ = try writer.write("}");
|
_ = try writer.write("}");
|
||||||
|
|
||||||
|
rc = true;
|
||||||
},
|
},
|
||||||
else => {
|
else => {
|
||||||
std.log.err("encountered unimplemented shape type {s} for shape_id {s}. Generated code will not compile", .{ @tagName(shape), shape_id });
|
std.log.err("encountered unimplemented shape type {s} for shape_id {s}. Generated code will not compile", .{ @tagName(shape), shape_id });
|
||||||
|
@ -388,7 +390,7 @@ fn generateTypeFor(shape_id: []const u8, shapes: std.StringHashMap(smithy.ShapeI
|
||||||
// return error{UnimplementedShapeType}.UnimplementedShapeType;
|
// return error{UnimplementedShapeType}.UnimplementedShapeType;
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
_ = state.type_stack.pop();
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generateSimpleTypeFor(_: anytype, type_name: []const u8, writer: anytype) !void {
|
fn generateSimpleTypeFor(_: anytype, type_name: []const u8, writer: anytype) !void {
|
||||||
|
@ -421,6 +423,11 @@ fn generateComplexTypeFor(shape_id: []const u8, members: []smithy.TypeMember, ty
|
||||||
state.allocator.free(mapping.snake);
|
state.allocator.free(mapping.snake);
|
||||||
http_header_mappings.deinit();
|
http_header_mappings.deinit();
|
||||||
}
|
}
|
||||||
|
var map_fields = std.ArrayList([]const u8).init(state.allocator);
|
||||||
|
defer {
|
||||||
|
for (map_fields.items) |f| state.allocator.free(f);
|
||||||
|
map_fields.deinit();
|
||||||
|
}
|
||||||
// prolog. We'll rely on caller to get the spacing correct here
|
// prolog. We'll rely on caller to get the spacing correct here
|
||||||
_ = try writer.write(type_type_name);
|
_ = try writer.write(type_type_name);
|
||||||
_ = try writer.write(" {\n");
|
_ = try writer.write(" {\n");
|
||||||
|
@ -449,9 +456,12 @@ fn generateComplexTypeFor(shape_id: []const u8, members: []smithy.TypeMember, ty
|
||||||
json_field_name_mappings.appendAssumeCapacity(.{ .snake = try state.allocator.dupe(u8, snake_case_member), .json = member.name });
|
json_field_name_mappings.appendAssumeCapacity(.{ .snake = try state.allocator.dupe(u8, snake_case_member), .json = member.name });
|
||||||
defer state.allocator.free(snake_case_member);
|
defer state.allocator.free(snake_case_member);
|
||||||
try outputIndent(child_state, writer);
|
try outputIndent(child_state, writer);
|
||||||
try writer.print("{s}: ", .{avoidReserved(snake_case_member)});
|
const member_name = avoidReserved(snake_case_member);
|
||||||
|
try writer.print("{s}: ", .{member_name});
|
||||||
try writeOptional(member.traits, writer, null);
|
try writeOptional(member.traits, writer, null);
|
||||||
try generateTypeFor(member.target, shapes, writer, child_state, true);
|
if (try generateTypeFor(member.target, shapes, writer, child_state, true))
|
||||||
|
try map_fields.append(try std.fmt.allocPrint(state.allocator, "{s}", .{member_name}));
|
||||||
|
|
||||||
if (!std.mem.eql(u8, "union", type_type_name))
|
if (!std.mem.eql(u8, "union", type_type_name))
|
||||||
try writeOptional(member.traits, writer, " = null");
|
try writeOptional(member.traits, writer, " = null");
|
||||||
_ = try writer.write(",\n");
|
_ = try writer.write(",\n");
|
||||||
|
@ -481,8 +491,7 @@ fn generateComplexTypeFor(shape_id: []const u8, members: []smithy.TypeMember, ty
|
||||||
//
|
//
|
||||||
try writer.writeByte('\n');
|
try writer.writeByte('\n');
|
||||||
try outputIndent(child_state, writer);
|
try outputIndent(child_state, writer);
|
||||||
_ = try writer.write("pub fn jsonFieldNameFor(_: @This(), comptime field_name: []const u8) []const u8 ");
|
_ = try writer.write("pub fn jsonFieldNameFor(_: @This(), comptime field_name: []const u8) []const u8 {\n");
|
||||||
_ = try writer.write("{\n");
|
|
||||||
var grandchild_state = child_state;
|
var grandchild_state = child_state;
|
||||||
grandchild_state.indent_level += 1;
|
grandchild_state.indent_level += 1;
|
||||||
// We need to force output here becaseu we're referencing the field in the return statement below
|
// We need to force output here becaseu we're referencing the field in the return statement below
|
||||||
|
@ -491,8 +500,34 @@ fn generateComplexTypeFor(shape_id: []const u8, members: []smithy.TypeMember, ty
|
||||||
_ = try writer.write("return @field(mappings, field_name);\n");
|
_ = try writer.write("return @field(mappings, field_name);\n");
|
||||||
try outputIndent(child_state, writer);
|
try outputIndent(child_state, writer);
|
||||||
_ = try writer.write("}\n");
|
_ = try writer.write("}\n");
|
||||||
|
try writeStringify(child_state, map_fields.items, writer);
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Deal with the jsonStringifyField stuff
|
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.jsonFieldNameFor(\"{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 {
|
fn writeMappings(state: GenerationState, @"pub": []const u8, mapping_name: []const u8, mappings: anytype, force_output: bool, writer: anytype) !void {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user