diff --git a/codegen/src/main.zig b/codegen/src/main.zig index e1ee765..80a26ac 100644 --- a/codegen/src/main.zig +++ b/codegen/src/main.zig @@ -135,7 +135,7 @@ fn generateOperation(allocator: *std.mem.Allocator, operation: smithy.ShapeInfo, try writer.print(" action_name: []const u8 = \"{s}\",\n", .{operation.name}); _ = try writer.write(" Request: type = "); if (operation.shape.operation.input) |member| { - try generateTypeFor(allocator, member, shapes, writer, " ", true, &type_stack); + try generateTypeFor(allocator, member, shapes, writer, " ", false, &type_stack); } else _ = try writer.write("struct {}"); // we want to maintain consistency with other ops _ = try writer.write(",\n"); _ = try writer.write(" Response: type = "); @@ -210,38 +210,37 @@ fn generateTypeFor(allocator: *std.mem.Allocator, shape_id: []const u8, shapes: } try type_stack.append(&shape_info); switch (shape) { - .structure => try generateComplexTypeFor(allocator, shape.structure.members, "struct", shapes, writer, prefix, all_required, type_stack), - .uniontype => try generateComplexTypeFor(allocator, shape.uniontype.members, "union", shapes, writer, prefix, all_required, type_stack), - .string => _ = try writer.write("[]const u8"), - .integer => _ = try writer.write("i64"), - .list => { + .structure => |s| try generateComplexTypeFor(allocator, shape.structure.members, "struct", shapes, writer, prefix, all_required, type_stack), + .uniontype => |s| try generateComplexTypeFor(allocator, shape.uniontype.members, "union", shapes, writer, prefix, all_required, type_stack), + .string => |s| try generateSimpleTypeFor(s, "[]const u8", writer, all_required), + .integer => |s| try generateSimpleTypeFor(s, "i64", writer, all_required), + .list => |s| { _ = try writer.write("[]"); try generateTypeFor(allocator, shape.list.member_target, shapes, writer, prefix, all_required, type_stack); }, - .set => { + .set => |s| { _ = try writer.write("[]"); try generateTypeFor(allocator, shape.set.member_target, shapes, writer, prefix, all_required, type_stack); }, - .timestamp => _ = try writer.write("i64"), - .blob => _ = try writer.write("[]const u8"), - .boolean => _ = try writer.write("bool"), - .double => _ = try writer.write("f64"), - .float => _ = try writer.write("f32"), - .long => _ = try writer.write("i64"), + .timestamp => |s| try generateSimpleTypeFor(s, "i64", writer, all_required), + .blob => |s| try generateSimpleTypeFor(s, "[]const u8", writer, all_required), + .boolean => |s| try generateSimpleTypeFor(s, "bool", writer, all_required), + .double => |s| try generateSimpleTypeFor(s, "f64", writer, all_required), + .float => |s| try generateSimpleTypeFor(s, "f32", writer, all_required), + .long => |s| try generateSimpleTypeFor(s, "i64", writer, all_required), .map => { _ = try writer.write("[]struct {\n"); const new_prefix = try std.fmt.allocPrint(allocator, " {s}", .{prefix}); defer allocator.free(new_prefix); try writer.print("{s} key: ", .{prefix}); - // this doesn't have traits, but I expect it will - // if (!all_required) try writeOptional(shape.map.traits, writer, null); + if (!all_required) try writeOptional(shape.map.traits, writer, null); try generateTypeFor(allocator, shape.map.key, shapes, writer, prefix, all_required, type_stack); - // if (!all_required) try writeOptional(shape.map.traits, writer, " = null"); + if (!all_required) try writeOptional(shape.map.traits, writer, " = null"); _ = try writer.write(",\n"); try writer.print("{s} value: ", .{prefix}); - // if (!all_required) try writeOptional(shape.map.traits, writer, null); + if (!all_required) try writeOptional(shape.map.traits, writer, null); try generateTypeFor(allocator, shape.map.key, shapes, writer, prefix, all_required, type_stack); - // if (!all_required) try writeOptional(shape.map.traits, writer, " = null"); + if (!all_required) try writeOptional(shape.map.traits, writer, " = null"); _ = try writer.write(",\n"); _ = try writer.write(prefix); _ = try writer.write("}"); @@ -255,9 +254,14 @@ fn generateTypeFor(allocator: *std.mem.Allocator, shape_id: []const u8, shapes: _ = type_stack.pop(); } +fn generateSimpleTypeFor(shape: anytype, type_name: []const u8, writer: anytype, all_required: bool) !void { + _ = try writer.write(type_name); // This had required stuff but the problem was elsewhere. Better to leave as function just in case +} + fn generateComplexTypeFor(allocator: *std.mem.Allocator, members: []smithy.TypeMember, type_type_name: []const u8, shapes: anytype, writer: anytype, prefix: []const u8, all_required: bool, type_stack: anytype) anyerror!void { // prolog. We'll rely on caller to get the spacing correct here - _ = try writer.write("struct {\n"); + _ = try writer.write(type_type_name); + _ = try writer.write(" {\n"); for (members) |member| { const new_prefix = try std.fmt.allocPrint(allocator, " {s}", .{prefix}); defer allocator.free(new_prefix); @@ -278,13 +282,14 @@ fn generateComplexTypeFor(allocator: *std.mem.Allocator, members: []smithy.TypeM fn writeOptional(traits: ?[]smithy.Trait, writer: anytype, value: ?[]const u8) !void { if (traits) |ts| { for (ts) |t| - if (t == smithy.TraitType.required) return; - // not required - if (value) |v| { - _ = try writer.write(v); - } else - _ = try writer.write("?"); + if (t == .required) return; } + + // not required + if (value) |v| { + _ = try writer.write(v); + } else + _ = try writer.write("?"); } fn camelCase(allocator: *std.mem.Allocator, name: []const u8) ![]const u8 { const first_letter = name[0] + ('a' - 'A'); diff --git a/codegen/src/smithy.zig b/codegen/src/smithy.zig index 576f01d..57a0095 100644 --- a/codegen/src/smithy.zig +++ b/codegen/src/smithy.zig @@ -59,6 +59,8 @@ pub const TraitType = enum { aws_api_service, aws_auth_sigv4, aws_protocol, + ec2_query_name, + json_name, required, documentation, pattern, @@ -79,6 +81,8 @@ pub const Trait = union(TraitType) { name: []const u8, }, aws_protocol: AwsProtocol, + ec2_query_name: []const u8, + json_name: []const u8, required: struct {}, documentation: []const u8, pattern: []const u8, @@ -126,7 +130,7 @@ pub const TypeMember = struct { traits: []Trait, }; const Shape = union(ShapeType) { - blob: struct {}, + blob: TraitsOnly, boolean: TraitsOnly, string: TraitsOnly, byte: TraitsOnly, @@ -138,23 +142,28 @@ const Shape = union(ShapeType) { bigInteger: TraitsOnly, bigDecimal: TraitsOnly, timestamp: TraitsOnly, - document: struct {}, - member: struct {}, + document: TraitsOnly, + member: TraitsOnly, list: struct { member_target: []const u8, + traits: []Trait, }, set: struct { member_target: []const u8, + traits: []Trait, }, map: struct { key: []const u8, value: []const u8, + traits: []Trait, }, structure: struct { members: []TypeMember, + traits: []Trait, }, uniontype: struct { members: []TypeMember, + traits: []Trait, }, service: struct { version: []const u8, @@ -168,7 +177,7 @@ const Shape = union(ShapeType) { errors: ?[][]const u8, traits: []Trait, }, - resource: struct {}, + resource: TraitsOnly, }; // https://awslabs.github.io/smithy/1.0/spec/aws/index.html @@ -295,12 +304,14 @@ fn getShape(allocator: *std.mem.Allocator, shape: std.json.Value) SmithyParseErr return Shape{ .structure = .{ .members = try parseMembers(allocator, shape.Object.get("members")), + .traits = try parseTraits(allocator, shape.Object.get("traits")), }, }; if (std.mem.eql(u8, shape_type, "union")) return Shape{ .uniontype = .{ .members = try parseMembers(allocator, shape.Object.get("members")), + .traits = try parseTraits(allocator, shape.Object.get("traits")), }, }; if (std.mem.eql(u8, shape_type, "operation")) @@ -321,12 +332,14 @@ fn getShape(allocator: *std.mem.Allocator, shape: std.json.Value) SmithyParseErr return Shape{ .list = .{ .member_target = shape.Object.get("member").?.Object.get("target").?.String, + .traits = try parseTraits(allocator, shape.Object.get("traits")), }, }; if (std.mem.eql(u8, shape_type, "set")) return Shape{ .set = .{ .member_target = shape.Object.get("member").?.Object.get("target").?.String, + .traits = try parseTraits(allocator, shape.Object.get("traits")), }, }; if (std.mem.eql(u8, shape_type, "map")) @@ -334,6 +347,7 @@ fn getShape(allocator: *std.mem.Allocator, shape: std.json.Value) SmithyParseErr .map = .{ .key = shape.Object.get("key").?.Object.get("target").?.String, .value = shape.Object.get("value").?.Object.get("target").?.String, + .traits = try parseTraits(allocator, shape.Object.get("traits")), }, }; if (std.mem.eql(u8, shape_type, "string")) @@ -356,12 +370,16 @@ fn getShape(allocator: *std.mem.Allocator, shape: std.json.Value) SmithyParseErr return Shape{ .bigDecimal = try parseTraitsOnly(allocator, shape) }; if (std.mem.eql(u8, shape_type, "boolean")) return Shape{ .boolean = try parseTraitsOnly(allocator, shape) }; - if (std.mem.eql(u8, shape_type, "blob")) return Shape{ .blob = .{} }; + if (std.mem.eql(u8, shape_type, "blob")) + return Shape{ .blob = try parseTraitsOnly(allocator, shape) }; if (std.mem.eql(u8, shape_type, "timestamp")) return Shape{ .timestamp = try parseTraitsOnly(allocator, shape) }; - if (std.mem.eql(u8, shape_type, "document")) return Shape{ .document = .{} }; - if (std.mem.eql(u8, shape_type, "member")) return Shape{ .member = .{} }; - if (std.mem.eql(u8, shape_type, "resource")) return Shape{ .resource = .{} }; + if (std.mem.eql(u8, shape_type, "document")) + return Shape{ .document = try parseTraitsOnly(allocator, shape) }; + if (std.mem.eql(u8, shape_type, "member")) + return Shape{ .member = try parseTraitsOnly(allocator, shape) }; + if (std.mem.eql(u8, shape_type, "resource")) + return Shape{ .resource = try parseTraitsOnly(allocator, shape) }; std.debug.print("Invalid Type: {s}", .{shape_type}); return SmithyParseError.InvalidType; @@ -483,6 +501,12 @@ fn getTrait(trait_type: []const u8, value: std.json.Value) SmithyParseError!?Tra if (std.mem.eql(u8, trait_type, "smithy.api#pattern")) return Trait{ .pattern = value.String }; + if (std.mem.eql(u8, trait_type, "aws.protocols#ec2QueryName")) + return Trait{ .ec2_query_name = value.String }; + + if (std.mem.eql(u8, trait_type, "smithy.api#jsonName")) + return Trait{ .json_name = value.String }; + // TODO: Maybe care about these traits? if (std.mem.eql(u8, trait_type, "smithy.api#title")) return null; @@ -490,22 +514,24 @@ fn getTrait(trait_type: []const u8, value: std.json.Value) SmithyParseError!?Tra if (std.mem.eql(u8, trait_type, "smithy.api#xmlNamespace")) return null; // TODO: win argument with compiler to get this comptime - // aws.protocols#ec2QueryName looks important const list = \\aws.api#arnReference \\aws.api#clientDiscoveredEndpoint \\aws.api#clientEndpointDiscovery + \\aws.api#arn \\aws.auth#unsignedPayload - \\aws.protocols#ec2QueryName + \\aws.iam#disableConditionKeyInference \\smithy.api#auth \\smithy.api#cors \\smithy.api#deprecated \\smithy.api#endpoint \\smithy.api#enum + \\smithy.api#error \\smithy.api#eventPayload \\smithy.api#externalDocumentation \\smithy.api#hostLabel \\smithy.api#http + \\smithy.api#httpError \\smithy.api#httpChecksumRequired \\smithy.api#httpHeader \\smithy.api#httpLabel @@ -516,12 +542,16 @@ fn getTrait(trait_type: []const u8, value: std.json.Value) SmithyParseError!?Tra \\smithy.api#httpResponseCode \\smithy.api#idempotencyToken \\smithy.api#idempotent - \\smithy.api#jsonName \\smithy.api#mediaType + \\smithy.api#noReplace \\smithy.api#optionalAuth \\smithy.api#paginated \\smithy.api#readonly + \\smithy.api#references + \\smithy.api#requiresLength + \\smithy.api#retryable \\smithy.api#sensitive + \\smithy.api#streaming \\smithy.api#suppress \\smithy.api#tags \\smithy.api#timestampFormat @@ -537,7 +567,7 @@ fn getTrait(trait_type: []const u8, value: std.json.Value) SmithyParseError!?Tra } // Totally unknown type - std.debug.print("Invalid Trait Type: {s}\n", .{trait_type}); + std.log.err("Invalid Trait Type: {s}", .{trait_type}); return null; } fn getOptionalNumber(value: std.json.Value, key: []const u8) ?f64 {