move to state parameter and index level rather than prefix
This also includes a small change to force output of mappings even if empty New zig correctly detects that without any field, we end up trying to use a constant that does not exist (this is in the generated code)
This commit is contained in:
		
							parent
							
								
									2d10c6e356
								
							
						
					
					
						commit
						0368c27c2c
					
				
					 1 changed files with 168 additions and 105 deletions
				
			
		|  | @ -68,10 +68,9 @@ fn generateServices(allocator: *std.mem.Allocator, comptime _: []const u8, file: | ||||||
|     defer allocator.free(json); |     defer allocator.free(json); | ||||||
|     const model = try smithy.parse(allocator, json); |     const model = try smithy.parse(allocator, json); | ||||||
|     defer model.deinit(); |     defer model.deinit(); | ||||||
|     const ShapeInfo = @TypeOf(model.shapes[0]); // assume we have at least one shape |     var shapes = std.StringHashMap(smithy.ShapeInfo).init(allocator); | ||||||
|     var shapes = std.StringHashMap(ShapeInfo).init(allocator); |  | ||||||
|     defer shapes.deinit(); |     defer shapes.deinit(); | ||||||
|     var services = std.ArrayList(ShapeInfo).init(allocator); |     var services = std.ArrayList(smithy.ShapeInfo).init(allocator); | ||||||
|     defer services.deinit(); |     defer services.deinit(); | ||||||
|     for (model.shapes) |shape| { |     for (model.shapes) |shape| { | ||||||
|         try shapes.put(shape.id, shape); |         try shapes.put(shape.id, shape); | ||||||
|  | @ -131,7 +130,7 @@ fn generateServices(allocator: *std.mem.Allocator, comptime _: []const u8, file: | ||||||
| 
 | 
 | ||||||
|         // Operations |         // Operations | ||||||
|         for (service.shape.service.operations) |op| |         for (service.shape.service.operations) |op| | ||||||
|             try generateOperation(allocator, shapes.get(op).?, shapes, writer, constant_name); |             try generateOperation(allocator, shapes.get(op).?, shapes, writer); | ||||||
|     } |     } | ||||||
|     return constant_names.toOwnedSlice(); |     return constant_names.toOwnedSlice(); | ||||||
| } | } | ||||||
|  | @ -155,67 +154,115 @@ fn constantName(allocator: *std.mem.Allocator, id: []const u8) ![]const u8 { | ||||||
|     // Not a special case - just snake it |     // Not a special case - just snake it | ||||||
|     return try snake.fromPascalCase(allocator, id); |     return try snake.fromPascalCase(allocator, id); | ||||||
| } | } | ||||||
| fn generateOperation(allocator: *std.mem.Allocator, operation: smithy.ShapeInfo, shapes: anytype, writer: anytype, service: []const u8) !void { | 
 | ||||||
|  | const GenerationState = struct { | ||||||
|  |     type_stack: *std.ArrayList(*const smithy.ShapeInfo), | ||||||
|  |     map_fields: *std.StringArrayHashMap(std.ArrayList([]const u8)), | ||||||
|  |     allocator: *std.mem.Allocator, | ||||||
|  |     indent_level: u64, | ||||||
|  |     all_required: bool, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | fn outputIndent(state: GenerationState, writer: anytype) !void { | ||||||
|  |     const n_chars = 4 * state.indent_level; | ||||||
|  |     try writer.writeByteNTimes(' ', n_chars); | ||||||
|  | } | ||||||
|  | fn generateOperation(allocator: *std.mem.Allocator, operation: smithy.ShapeInfo, shapes: std.StringHashMap(smithy.ShapeInfo), writer: anytype) !void { | ||||||
|     const snake_case_name = try snake.fromPascalCase(allocator, operation.name); |     const snake_case_name = try snake.fromPascalCase(allocator, operation.name); | ||||||
|     defer allocator.free(snake_case_name); |     defer allocator.free(snake_case_name); | ||||||
| 
 | 
 | ||||||
|     const prefix = "    "; |  | ||||||
|     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{ | ||||||
|  |         .type_stack = &type_stack, | ||||||
|  |         .map_fields = &map_fields, | ||||||
|  |         .allocator = allocator, | ||||||
|  |         .indent_level = 1, | ||||||
|  |         .all_required = false, | ||||||
|  |     }; | ||||||
|  |     var child_state = state; | ||||||
|  |     child_state.indent_level += 1; | ||||||
|     // indent should start at 4 spaces here |     // indent should start at 4 spaces here | ||||||
|     const operation_name = avoidReserved(snake_case_name); |     const operation_name = avoidReserved(snake_case_name); | ||||||
|     try writer.print("pub const {s}: struct ", .{operation_name}); |     try writer.print("pub const {s}: struct ", .{operation_name}); | ||||||
|     _ = try writer.write("{\n"); |     _ = try writer.write("{\n"); | ||||||
|     for (operation.shape.operation.traits) |trait| { |     for (operation.shape.operation.traits) |trait| { | ||||||
|         if (trait == .http) { |         if (trait == .http) { | ||||||
|             _ = try writer.write("    pub const http_config = .{\n"); |             try outputIndent(state, writer); | ||||||
|             try writer.print("        .method = \"{s}\",\n", .{trait.http.method}); |             _ = try writer.write("pub const http_config = .{\n"); | ||||||
|             try writer.print("        .uri = \"{s}\",\n", .{trait.http.uri}); |             try outputIndent(child_state, writer); | ||||||
|             try writer.print("        .success_code = {d},\n", .{trait.http.code}); |             try writer.print(".method = \"{s}\",\n", .{trait.http.method}); | ||||||
|             _ = try writer.write("    };\n\n"); |             try outputIndent(child_state, writer); | ||||||
|  |             try writer.print(".uri = \"{s}\",\n", .{trait.http.uri}); | ||||||
|  |             try outputIndent(child_state, writer); | ||||||
|  |             try writer.print(".success_code = {d},\n", .{trait.http.code}); | ||||||
|  |             try outputIndent(state, writer); | ||||||
|  |             _ = try writer.write("};\n\n"); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     try writer.print("    action_name: []const u8 = \"{s}\",\n", .{operation.name}); |     try outputIndent(state, writer); | ||||||
|     _ = try writer.write("    Request: type = "); |     try writer.print("action_name: []const u8 = \"{s}\",\n", .{operation.name}); | ||||||
|  |     try outputIndent(state, writer); | ||||||
|  |     _ = try writer.write("Request: type = "); | ||||||
|     if (operation.shape.operation.input) |member| { |     if (operation.shape.operation.input) |member| { | ||||||
|         try generateTypeFor(allocator, member, shapes, writer, prefix, false, &type_stack, false); |         try generateTypeFor(member, shapes, writer, state, false); | ||||||
|         _ = try writer.write("\n"); |         _ = try writer.write("\n"); | ||||||
|         try generateMetadataFunction(service, operation_name, prefix, writer); |         try generateMetadataFunction(operation_name, state, writer); | ||||||
|     } else { |     } else { | ||||||
|         _ = try writer.write("struct {\n"); |         _ = try writer.write("struct {\n"); | ||||||
|         try generateMetadataFunction(service, operation_name, prefix, writer); |         try generateMetadataFunction(operation_name, state, writer); | ||||||
|     } |     } | ||||||
|     _ = try writer.write(",\n"); |     _ = try writer.write(",\n"); | ||||||
|     _ = try writer.write("    Response: type = "); |     try outputIndent(state, writer); | ||||||
|  |     _ = try writer.write("Response: type = "); | ||||||
|     if (operation.shape.operation.output) |member| { |     if (operation.shape.operation.output) |member| { | ||||||
|         try generateTypeFor(allocator, member, shapes, writer, "    ", false, &type_stack, true); |         try generateTypeFor(member, shapes, writer, state, true); | ||||||
|     } 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"); | ||||||
| 
 | 
 | ||||||
|     if (operation.shape.operation.errors) |errors| { |     if (operation.shape.operation.errors) |errors| { | ||||||
|         _ = try writer.write("    ServiceError: type = error{\n"); |         try outputIndent(state, writer); | ||||||
|  |         _ = try writer.write("ServiceError: type = error{\n"); | ||||||
|         for (errors) |err| { |         for (errors) |err| { | ||||||
|             const err_name = getErrorName(shapes.get(err).?.name); // need to remove "exception" |             const err_name = getErrorName(shapes.get(err).?.name); // need to remove "exception" | ||||||
|             try writer.print("        {s},\n", .{err_name}); |             try outputIndent(child_state, writer); | ||||||
|  |             try writer.print("{s},\n", .{err_name}); | ||||||
|         } |         } | ||||||
|         _ = try writer.write("    },\n"); |         try outputIndent(state, writer); | ||||||
|  |         _ = try writer.write("},\n"); | ||||||
|     } |     } | ||||||
|     _ = try writer.write("} = .{};\n"); |     _ = try writer.write("} = .{};\n"); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fn generateMetadataFunction(_: []const u8, operation_name: []const u8, comptime prefix: []const u8, writer: anytype) !void { | fn generateMetadataFunction(operation_name: []const u8, state: GenerationState, writer: anytype) !void { | ||||||
|     // TODO: Shove these lines in here, and also the else portion |     // TODO: Shove these lines in here, and also the else portion | ||||||
|     // pub fn metaInfo(self: @This()) struct { service: @TypeOf(sts), action: @TypeOf(sts.get_caller_identity) } { |     // pub fn metaInfo(self: @This()) struct { service: @TypeOf(sts), action: @TypeOf(sts.get_caller_identity) } { | ||||||
|     //     return .{ .service = sts, .action = sts.get_caller_identity }; |     //     return .{ .service = sts, .action = sts.get_caller_identity }; | ||||||
|     // } |     // } | ||||||
|     // We want to add a short "get my parents" function into the response |     // We want to add a short "get my parents" function into the response | ||||||
|     try writer.print("{s}    ", .{prefix}); |     var child_state = state; | ||||||
|  |     child_state.indent_level += 1; | ||||||
|  |     try outputIndent(child_state, writer); | ||||||
|     _ = try writer.write("pub fn metaInfo() struct { "); |     _ = try writer.write("pub fn metaInfo() struct { "); | ||||||
|     try writer.print("service_metadata: @TypeOf(service_metadata), action: @TypeOf({s})", .{operation_name}); |     try writer.print("service_metadata: @TypeOf(service_metadata), action: @TypeOf({s})", .{operation_name}); | ||||||
|     _ = try writer.write(" } {\n" ++ prefix ++ "        return .{ .service_metadata = service_metadata, "); |     _ = try writer.write(" } {\n"); | ||||||
|  |     child_state.indent_level += 1; | ||||||
|  |     try outputIndent(child_state, writer); | ||||||
|  |     _ = try writer.write("return .{ .service_metadata = service_metadata, "); | ||||||
|     try writer.print(".action = {s}", .{operation_name}); |     try writer.print(".action = {s}", .{operation_name}); | ||||||
|     _ = try writer.write(" };\n" ++ prefix ++ "    }\n" ++ prefix ++ "}"); |     _ = try writer.write(" };\n"); | ||||||
|  |     child_state.indent_level -= 1; | ||||||
|  |     try outputIndent(child_state, writer); | ||||||
|  |     _ = try writer.write("}\n"); | ||||||
|  |     try outputIndent(state, writer); | ||||||
|  |     try writer.writeByte('}'); | ||||||
| } | } | ||||||
| fn getErrorName(err_name: []const u8) []const u8 { | fn getErrorName(err_name: []const u8) []const u8 { | ||||||
|     if (endsWith("Exception", err_name)) |     if (endsWith("Exception", err_name)) | ||||||
|  | @ -231,7 +278,7 @@ 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(allocator: *std.mem.Allocator, shape_id: []const u8, shapes: anytype, writer: anytype, prefix: []const u8, all_required: bool, type_stack: anytype, end_structure: bool) anyerror!void { | fn generateTypeFor(shape_id: []const u8, shapes: std.StringHashMap(smithy.ShapeInfo), writer: anytype, state: GenerationState, end_structure: bool) anyerror!void { | ||||||
|     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; | ||||||
|  | @ -242,7 +289,7 @@ fn generateTypeFor(allocator: *std.mem.Allocator, shape_id: []const u8, shapes: | ||||||
|     const shape = shape_info.shape; |     const shape = shape_info.shape; | ||||||
|     // Check for ourselves up the stack |     // Check for ourselves up the stack | ||||||
|     var self_occurences: u8 = 0; |     var self_occurences: u8 = 0; | ||||||
|     for (type_stack.items) |i| { |     for (state.type_stack.items) |i| { | ||||||
|         // NOTE: shapes.get isn't providing a consistent pointer - is it allocating each time? |         // NOTE: shapes.get isn't providing a consistent pointer - is it allocating each time? | ||||||
|         // we will therefore need to compare ids |         // we will therefore need to compare ids | ||||||
|         if (std.mem.eql(u8, i.*.id, shape_info.id)) |         if (std.mem.eql(u8, i.*.id, shape_info.id)) | ||||||
|  | @ -264,60 +311,69 @@ fn generateTypeFor(allocator: *std.mem.Allocator, shape_id: []const u8, shapes: | ||||||
|         // type to properly reference. Realistically, AWS or the service |         // type to properly reference. Realistically, AWS or the service | ||||||
|         // must be blocking deep recursion somewhere or this would be a great |         // must be blocking deep recursion somewhere or this would be a great | ||||||
|         // DOS attack |         // DOS attack | ||||||
|         try generateSimpleTypeFor("nothing", "[]const u8", writer, all_required); |         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}); | ||||||
|         // std.log.info("  Type stack:\n", .{}); |         // std.log.info("  Type stack:\n", .{}); | ||||||
|         // for (type_stack.items) |i| |         // for (type_stack.items) |i| | ||||||
|         //     std.log.info("  {s}", .{i.*.id}); |         //     std.log.info("  {s}", .{i.*.id}); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|     try type_stack.append(&shape_info); |     try state.type_stack.append(&shape_info); | ||||||
|     switch (shape) { |     switch (shape) { | ||||||
|         .structure => { |         .structure => { | ||||||
|             try generateComplexTypeFor(allocator, shape.structure.members, "struct", shapes, writer, prefix, all_required, type_stack); |             try generateComplexTypeFor(shape_id, shape.structure.members, "struct", shapes, writer, state); | ||||||
|             if (end_structure) { |             if (end_structure) { | ||||||
|                 // epilog |                 // epilog | ||||||
|                 try writer.print("{s}", .{prefix}); |                 try outputIndent(state, writer); | ||||||
|                 _ = try writer.write("}"); |                 _ = try writer.write("}"); | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|         .uniontype => { |         .uniontype => { | ||||||
|             try generateComplexTypeFor(allocator, shape.uniontype.members, "union", shapes, writer, prefix, all_required, type_stack); |             try generateComplexTypeFor(shape_id, shape.uniontype.members, "union", shapes, writer, state); | ||||||
|             // epilog |             // epilog | ||||||
|             try writer.print("{s}", .{prefix}); |             try outputIndent(state, writer); | ||||||
|             _ = try writer.write("}"); |             _ = try writer.write("}"); | ||||||
|         }, |         }, | ||||||
|         .string => |s| try generateSimpleTypeFor(s, "[]const u8", writer, all_required), |         .string => |s| try generateSimpleTypeFor(s, "[]const u8", writer), | ||||||
|         .integer => |s| try generateSimpleTypeFor(s, "i64", writer, all_required), |         .integer => |s| try generateSimpleTypeFor(s, "i64", writer), | ||||||
|         .list => { |         .list => { | ||||||
|             _ = try writer.write("[]"); |             _ = try writer.write("[]"); | ||||||
|             try generateTypeFor(allocator, shape.list.member_target, shapes, writer, prefix, all_required, type_stack, true); |             try generateTypeFor(shape.list.member_target, shapes, writer, state, true); | ||||||
|         }, |         }, | ||||||
|         .set => { |         .set => { | ||||||
|             _ = try writer.write("[]"); |             _ = try writer.write("[]"); | ||||||
|             try generateTypeFor(allocator, shape.set.member_target, shapes, writer, prefix, all_required, type_stack, true); |             try generateTypeFor(shape.set.member_target, shapes, writer, state, true); | ||||||
|         }, |         }, | ||||||
|         .timestamp => |s| try generateSimpleTypeFor(s, "i64", writer, all_required), |         .timestamp => |s| try generateSimpleTypeFor(s, "i64", writer), | ||||||
|         .blob => |s| try generateSimpleTypeFor(s, "[]const u8", writer, all_required), |         .blob => |s| try generateSimpleTypeFor(s, "[]const u8", writer), | ||||||
|         .boolean => |s| try generateSimpleTypeFor(s, "bool", writer, all_required), |         .boolean => |s| try generateSimpleTypeFor(s, "bool", writer), | ||||||
|         .double => |s| try generateSimpleTypeFor(s, "f64", writer, all_required), |         .double => |s| try generateSimpleTypeFor(s, "f64", writer), | ||||||
|         .float => |s| try generateSimpleTypeFor(s, "f32", writer, all_required), |         .float => |s| try generateSimpleTypeFor(s, "f32", writer), | ||||||
|         .long => |s| try generateSimpleTypeFor(s, "i64", writer, all_required), |         .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"); | ||||||
|             const new_prefix = try std.fmt.allocPrint(allocator, "    {s}", .{prefix}); |             var child_state = state; | ||||||
|             defer allocator.free(new_prefix); |             child_state.indent_level += 1; | ||||||
|             try writer.print("{s}    key: ", .{prefix}); |             try outputIndent(child_state, writer); | ||||||
|             if (!all_required) try writeOptional(shape.map.traits, writer, null); |             _ = try writer.write("key: "); | ||||||
|             try generateTypeFor(allocator, shape.map.key, shapes, writer, prefix, all_required, type_stack, true); |             try writeOptional(shape.map.traits, writer, null); | ||||||
|             if (!all_required) try writeOptional(shape.map.traits, writer, " = null"); |             try generateTypeFor(shape.map.key, shapes, writer, state, true); | ||||||
|  |             try writeOptional(shape.map.traits, writer, " = null"); | ||||||
|             _ = try writer.write(",\n"); |             _ = try writer.write(",\n"); | ||||||
|             try writer.print("{s}    value: ", .{prefix}); |             try outputIndent(child_state, writer); | ||||||
|             if (!all_required) try writeOptional(shape.map.traits, writer, null); |             _ = try writer.write("value: "); | ||||||
|             try generateTypeFor(allocator, shape.map.key, shapes, writer, prefix, all_required, type_stack, true); |             try writeOptional(shape.map.traits, writer, null); | ||||||
|             if (!all_required) try writeOptional(shape.map.traits, writer, " = null"); |             try generateTypeFor(shape.map.key, shapes, writer, state, true); | ||||||
|  |             try writeOptional(shape.map.traits, writer, " = null"); | ||||||
|             _ = try writer.write(",\n"); |             _ = try writer.write(",\n"); | ||||||
|             _ = try writer.write(prefix); |             try outputIndent(state, writer); | ||||||
|             _ = try writer.write("}"); |             _ = try writer.write("}"); | ||||||
|         }, |         }, | ||||||
|         else => { |         else => { | ||||||
|  | @ -326,25 +382,19 @@ fn generateTypeFor(allocator: *std.mem.Allocator, shape_id: []const u8, shapes: | ||||||
|             // return error{UnimplementedShapeType}.UnimplementedShapeType; |             // return error{UnimplementedShapeType}.UnimplementedShapeType; | ||||||
|         }, |         }, | ||||||
|     } |     } | ||||||
|     _ = type_stack.pop(); |     _ = state.type_stack.pop(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // fn generateSimpleTypeFor(shape: anytype, type_name: []const u8, writer: anytype, _: bool) !void { | fn generateSimpleTypeFor(_: anytype, type_name: []const u8, writer: anytype) !void { | ||||||
| fn generateSimpleTypeFor(_: anytype, type_name: []const u8, writer: anytype, all_required: bool) !void { |  | ||||||
|     // current compiler checks unused variables, but can't handle multiple unused |  | ||||||
|     // function parameters. We don't want to change the signature in case we need to work with |  | ||||||
|     // these in the future, so this stupid code is only here to trick the compiler |  | ||||||
|     if (all_required or !all_required) |  | ||||||
|     _ = try writer.write(type_name); // This had required stuff but the problem was elsewhere. Better to leave as function just in case |     _ = try writer.write(type_name); // This had required stuff but the problem was elsewhere. Better to leave as function just in case | ||||||
| } | } | ||||||
| 
 | fn generateComplexTypeFor(shape_id: []const u8, members: []smithy.TypeMember, type_type_name: []const u8, shapes: std.StringHashMap(smithy.ShapeInfo), writer: anytype, state: GenerationState) anyerror!void { | ||||||
| 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 { |     _ = shape_id; | ||||||
|     const Mapping = struct { snake: []const u8, json: []const u8 }; |     const Mapping = struct { snake: []const u8, json: []const u8 }; | ||||||
|     var json_field_name_mappings = try std.ArrayList(Mapping).initCapacity(allocator, members.len); |     var json_field_name_mappings = try std.ArrayList(Mapping).initCapacity(state.allocator, members.len); | ||||||
|     defer { |     defer { | ||||||
|         for (json_field_name_mappings.items) |mapping| { |         for (json_field_name_mappings.items) |mapping| | ||||||
|             allocator.free(mapping.snake); |             state.allocator.free(mapping.snake); | ||||||
|         } |  | ||||||
|         json_field_name_mappings.deinit(); |         json_field_name_mappings.deinit(); | ||||||
|     } |     } | ||||||
|     // There is an httpQueryParams trait as well, but nobody is using it. API GW |     // There is an httpQueryParams trait as well, but nobody is using it. API GW | ||||||
|  | @ -353,28 +403,26 @@ fn generateComplexTypeFor(allocator: *std.mem.Allocator, members: []smithy.TypeM | ||||||
|     // Same with httpPayload |     // Same with httpPayload | ||||||
|     // |     // | ||||||
|     // httpLabel is interesting - right now we just assume anything can be used - do we need to track this? |     // httpLabel is interesting - right now we just assume anything can be used - do we need to track this? | ||||||
|     var http_query_mappings = try std.ArrayList(Mapping).initCapacity(allocator, members.len); |     var http_query_mappings = try std.ArrayList(Mapping).initCapacity(state.allocator, members.len); | ||||||
|     defer { |     defer { | ||||||
|         for (http_query_mappings.items) |mapping| { |         for (http_query_mappings.items) |mapping| | ||||||
|             allocator.free(mapping.snake); |             state.allocator.free(mapping.snake); | ||||||
|         } |  | ||||||
|         http_query_mappings.deinit(); |         http_query_mappings.deinit(); | ||||||
|     } |     } | ||||||
|     var http_header_mappings = try std.ArrayList(Mapping).initCapacity(allocator, members.len); |     var http_header_mappings = try std.ArrayList(Mapping).initCapacity(state.allocator, members.len); | ||||||
|     defer { |     defer { | ||||||
|         for (http_header_mappings.items) |mapping| { |         for (http_header_mappings.items) |mapping| | ||||||
|             allocator.free(mapping.snake); |             state.allocator.free(mapping.snake); | ||||||
|         } |  | ||||||
|         http_header_mappings.deinit(); |         http_header_mappings.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"); | ||||||
|  |     var child_state = state; | ||||||
|  |     child_state.indent_level += 1; | ||||||
|     for (members) |member| { |     for (members) |member| { | ||||||
|         const new_prefix = try std.fmt.allocPrint(allocator, "    {s}", .{prefix}); |  | ||||||
|         defer allocator.free(new_prefix); |  | ||||||
|         // This is our mapping |         // This is our mapping | ||||||
|         const snake_case_member = try snake.fromPascalCase(allocator, member.name); |         const snake_case_member = try snake.fromPascalCase(state.allocator, member.name); | ||||||
|         // So it looks like some services have duplicate names?! Check out "httpMethod" |         // So it looks like some services have duplicate names?! Check out "httpMethod" | ||||||
|         // in API Gateway. Not sure what we're supposed to do there. Checking the go |         // in API Gateway. Not sure what we're supposed to do there. Checking the go | ||||||
|         // sdk, they move this particular duplicate to 'http_method' - not sure yet |         // sdk, they move this particular duplicate to 'http_method' - not sure yet | ||||||
|  | @ -384,20 +432,21 @@ fn generateComplexTypeFor(allocator: *std.mem.Allocator, members: []smithy.TypeM | ||||||
|             switch (trait) { |             switch (trait) { | ||||||
|                 .json_name => { |                 .json_name => { | ||||||
|                     found_name_trait = true; |                     found_name_trait = true; | ||||||
|                     json_field_name_mappings.appendAssumeCapacity(.{ .snake = try allocator.dupe(u8, snake_case_member), .json = trait.json_name }); |                     json_field_name_mappings.appendAssumeCapacity(.{ .snake = try state.allocator.dupe(u8, snake_case_member), .json = trait.json_name }); | ||||||
|                 }, |                 }, | ||||||
|                 .http_query => http_query_mappings.appendAssumeCapacity(.{ .snake = try allocator.dupe(u8, snake_case_member), .json = trait.http_query }), |                 .http_query => http_query_mappings.appendAssumeCapacity(.{ .snake = try state.allocator.dupe(u8, snake_case_member), .json = trait.http_query }), | ||||||
|                 .http_header => http_header_mappings.appendAssumeCapacity(.{ .snake = try allocator.dupe(u8, snake_case_member), .json = trait.http_header }), |                 .http_header => http_header_mappings.appendAssumeCapacity(.{ .snake = try state.allocator.dupe(u8, snake_case_member), .json = trait.http_header }), | ||||||
|                 else => {}, |                 else => {}, | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         if (!found_name_trait) |         if (!found_name_trait) | ||||||
|             json_field_name_mappings.appendAssumeCapacity(.{ .snake = try 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 allocator.free(snake_case_member); |         defer state.allocator.free(snake_case_member); | ||||||
|         try writer.print("{s}    {s}: ", .{ prefix, avoidReserved(snake_case_member) }); |         try outputIndent(child_state, writer); | ||||||
|         if (!all_required) try writeOptional(member.traits, writer, null); |         try writer.print("{s}: ", .{avoidReserved(snake_case_member)}); | ||||||
|         try generateTypeFor(allocator, member.target, shapes, writer, new_prefix, all_required, type_stack, true); |         try writeOptional(member.traits, writer, null); | ||||||
|         if (!all_required and !std.mem.eql(u8, "union", type_type_name)) |         try generateTypeFor(member.target, shapes, writer, child_state, true); | ||||||
|  |         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"); | ||||||
|     } |     } | ||||||
|  | @ -409,12 +458,10 @@ fn generateComplexTypeFor(allocator: *std.mem.Allocator, members: []smithy.TypeM | ||||||
|     //     .marker = "Marker", |     //     .marker = "Marker", | ||||||
|     //     .max_items = "MaxItems", |     //     .max_items = "MaxItems", | ||||||
|     // }; |     // }; | ||||||
|     var constprefix = try std.fmt.allocPrint(allocator, "{s}    ", .{prefix}); |  | ||||||
|     defer allocator.free(constprefix); |  | ||||||
|     if (http_query_mappings.items.len > 0) _ = try writer.write("\n"); |     if (http_query_mappings.items.len > 0) _ = try writer.write("\n"); | ||||||
|     try writeMappings(constprefix, "pub ", "http_query", http_query_mappings, writer); |     try writeMappings(child_state, "pub ", "http_query", http_query_mappings, false, writer); | ||||||
|     if (http_query_mappings.items.len > 0 and http_header_mappings.items.len > 0) _ = try writer.write("\n"); |     if (http_query_mappings.items.len > 0 and http_header_mappings.items.len > 0) _ = try writer.write("\n"); | ||||||
|     try writeMappings(constprefix, "pub ", "http_header", http_header_mappings, writer); |     try writeMappings(child_state, "pub ", "http_header", http_header_mappings, false, writer); | ||||||
| 
 | 
 | ||||||
|     // Add in json mappings. The function looks like this: |     // Add in json mappings. The function looks like this: | ||||||
|     // |     // | ||||||
|  | @ -426,23 +473,39 @@ fn generateComplexTypeFor(allocator: *std.mem.Allocator, members: []smithy.TypeM | ||||||
|     //     return @field(mappings, field_name); |     //     return @field(mappings, field_name); | ||||||
|     // } |     // } | ||||||
|     // |     // | ||||||
|     var fieldnameprefix = try std.fmt.allocPrint(allocator, "{s}        ", .{prefix}); |     try writer.writeByte('\n'); | ||||||
|     defer allocator.free(fieldnameprefix); |     try outputIndent(child_state, writer); | ||||||
|     try writer.print("\n{s}    pub fn jsonFieldNameFor(_: @This(), comptime field_name: []const u8) []const u8 ", .{prefix}); |     _ = try writer.write("pub fn jsonFieldNameFor(_: @This(), comptime field_name: []const u8) []const u8 "); | ||||||
|     _ = try writer.write("{\n"); |     _ = try writer.write("{\n"); | ||||||
|     try writeMappings(fieldnameprefix, "", "mappings", json_field_name_mappings, writer); |     var grandchild_state = child_state; | ||||||
|     try writer.print("{s}        return @field(mappings, field_name);\n{s}", .{ prefix, prefix }); |     grandchild_state.indent_level += 1; | ||||||
|     _ = try writer.write("    }\n"); |     // We need to force output here becaseu we're referencing the field in the return statement below | ||||||
|  |     try writeMappings(grandchild_state, "", "mappings", json_field_name_mappings, true, writer); | ||||||
|  |     try outputIndent(grandchild_state, writer); | ||||||
|  |     _ = try writer.write("return @field(mappings, field_name);\n"); | ||||||
|  |     try outputIndent(child_state, writer); | ||||||
|  |     _ = try writer.write("}\n"); | ||||||
|  | 
 | ||||||
|  |     // TODO: Deal with the jsonStringifyField stuff | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fn writeMappings(prefix: []const u8, @"pub": []const u8, mapping_name: []const u8, mappings: anytype, writer: anytype) !void { | fn writeMappings(state: GenerationState, @"pub": []const u8, mapping_name: []const u8, mappings: anytype, force_output: bool, writer: anytype) !void { | ||||||
|     if (mappings.items.len == 0) return; |     if (mappings.items.len == 0 and !force_output) return; | ||||||
|     try writer.print("{s}{s}const {s} = .", .{ prefix, @"pub", mapping_name }); |     try outputIndent(state, writer); | ||||||
|     _ = try writer.write("{\n"); |     if (mappings.items.len == 0) { | ||||||
|     for (mappings.items) |mapping| { |         try writer.print("{s}const {s} = ", .{ @"pub", mapping_name }); | ||||||
|         try writer.print("{s}    .{s} = \"{s}\",\n", .{ prefix, avoidReserved(mapping.snake), mapping.json }); |         _ = try writer.write(".{};\n"); | ||||||
|  |         return; | ||||||
|     } |     } | ||||||
|     _ = try writer.write(prefix); |     try writer.print("{s}const {s} = .", .{ @"pub", mapping_name }); | ||||||
|  |     _ = try writer.write("{\n"); | ||||||
|  |     var child_state = state; | ||||||
|  |     child_state.indent_level += 1; | ||||||
|  |     for (mappings.items) |mapping| { | ||||||
|  |         try outputIndent(child_state, writer); | ||||||
|  |         try writer.print(".{s} = \"{s}\",\n", .{ avoidReserved(mapping.snake), mapping.json }); | ||||||
|  |     } | ||||||
|  |     try outputIndent(state, writer); | ||||||
|     _ = try writer.write("};\n"); |     _ = try writer.write("};\n"); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		
		Reference in a new issue