update codegen to 0.15.1

This commit is contained in:
Emil Lerch 2025-08-22 11:23:26 -07:00
parent 9ed9c9b447
commit b865285b24
Signed by: lobo
GPG key ID: A7B62D657EF764F8
5 changed files with 137 additions and 102 deletions

View file

@ -1,11 +1,19 @@
.{ .{
.name = "aws-zig-codegen", .name = .codegen,
.version = "0.0.1", .version = "0.0.1",
.paths = .{
"build.zig",
"build.zig.zon",
"src",
"README.md",
"LICENSE",
},
.fingerprint = 0x41c2ec2d551fe279,
.dependencies = .{ .dependencies = .{
.smithy = .{ .smithy = .{
.url = "https://git.lerch.org/lobo/smithy/archive/41b61745d25a65817209dd5dddbb5f9b66896a99.tar.gz", .url = "git+https://git.lerch.org/lobo/smithy.git#09c0a618877ebaf8e15fbfc505983876f4e063d5",
.hash = "122087deb0ae309b2258d59b40d82fe5921fdfc35b420bb59033244851f7f276fa34", .hash = "smithy-1.0.0-uAyBgTnTAgBp2v6vypGcK5-YOCtxs2iEqR-4LfC5FTlS",
}, },
}, },
} }

View file

@ -12,7 +12,7 @@ allocator: std.mem.Allocator,
indent_level: u64, indent_level: u64,
pub fn appendToTypeStack(self: @This(), shape_info: *const smithy.ShapeInfo) !void { pub fn appendToTypeStack(self: @This(), shape_info: *const smithy.ShapeInfo) !void {
try self.type_stack.append(shape_info); try self.type_stack.append(self.allocator, shape_info);
} }
pub fn popFromTypeStack(self: @This()) void { pub fn popFromTypeStack(self: @This()) void {

View file

@ -107,8 +107,9 @@ pub fn computeDirectoryHash(
const arena = arena_instance.allocator(); const arena = arena_instance.allocator();
// Collect all files, recursively, then sort. // Collect all files, recursively, then sort.
var all_files = std.ArrayList(*HashedFile).init(gpa); // Normally we're looking at around 300 model files
defer all_files.deinit(); var all_files = try std.ArrayList(*HashedFile).initCapacity(gpa, 300);
defer all_files.deinit(gpa);
var walker = try dir.walk(gpa); var walker = try dir.walk(gpa);
defer walker.deinit(); defer walker.deinit();
@ -139,7 +140,7 @@ pub fn computeDirectoryHash(
wait_group.start(); wait_group.start();
try thread_pool.spawn(workerHashFile, .{ dir, hashed_file, &wait_group }); try thread_pool.spawn(workerHashFile, .{ dir, hashed_file, &wait_group });
try all_files.append(hashed_file); try all_files.append(gpa, hashed_file);
} }
} }
@ -155,7 +156,7 @@ pub fn computeDirectoryHash(
hasher.update(&hashed_file.hash); hasher.update(&hashed_file.hash);
} }
if (any_failures) return error.DirectoryHashUnavailable; if (any_failures) return error.DirectoryHashUnavailable;
if (options.needFileHashes) options.fileHashes = try all_files.toOwnedSlice(); if (options.needFileHashes) options.fileHashes = try all_files.toOwnedSlice(gpa);
return hasher.finalResult(); return hasher.finalResult();
} }
fn workerHashFile(dir: std.fs.Dir, hashed_file: *HashedFile, wg: *std.Thread.WaitGroup) void { fn workerHashFile(dir: std.fs.Dir, hashed_file: *HashedFile, wg: *std.Thread.WaitGroup) void {

View file

@ -17,6 +17,9 @@ const ServiceShape = smt.ServiceShape;
const ListShape = smt.ListShape; const ListShape = smt.ListShape;
const MapShape = smt.MapShape; const MapShape = smt.MapShape;
// manifest file 21k currently, but unbounded
var manifest_buf: [1024 * 32]u8 = undefined;
pub fn main() anyerror!void { pub fn main() anyerror!void {
const root_progress_node = std.Progress.start(.{}); const root_progress_node = std.Progress.start(.{});
defer root_progress_node.end(); defer root_progress_node.end();
@ -27,7 +30,8 @@ pub fn main() anyerror!void {
const args = try std.process.argsAlloc(allocator); const args = try std.process.argsAlloc(allocator);
defer std.process.argsFree(allocator, args); defer std.process.argsFree(allocator, args);
const stdout = std.io.getStdOut().writer(); var stdout_writer = std.fs.File.stdout().writer(&.{});
const stdout = &stdout_writer.interface;
var output_dir = std.fs.cwd(); var output_dir = std.fs.cwd();
defer if (output_dir.fd > 0) output_dir.close(); defer if (output_dir.fd > 0) output_dir.close();
@ -48,11 +52,10 @@ pub fn main() anyerror!void {
models_dir = try std.fs.cwd().openDir(args[i + 1], .{ .iterate = true }); models_dir = try std.fs.cwd().openDir(args[i + 1], .{ .iterate = true });
} }
// TODO: We need a different way to handle this file... var manifest_file = try output_dir.createFile("service_manifest.zig", .{});
const manifest_file_started = false; defer manifest_file.close();
var manifest_file: std.fs.File = undefined; var manifest = manifest_file.writer(&manifest_buf).interface;
defer if (manifest_file_started) manifest_file.close(); defer manifest.flush() catch @panic("Could not flush service manifest");
var manifest: std.fs.File.Writer = undefined;
var files_processed: usize = 0; var files_processed: usize = 0;
var skip_next = true; var skip_next = true;
for (args) |arg| { for (args) |arg| {
@ -71,11 +74,7 @@ pub fn main() anyerror!void {
skip_next = true; skip_next = true;
continue; continue;
} }
if (!manifest_file_started) { try processFile(arg, output_dir, &manifest);
manifest_file = try output_dir.createFile("service_manifest.zig", .{});
manifest = manifest_file.writer();
}
try processFile(arg, output_dir, manifest);
files_processed += 1; files_processed += 1;
} }
if (files_processed == 0) { if (files_processed == 0) {
@ -94,7 +93,7 @@ pub fn main() anyerror!void {
} }
if (args.len == 0) if (args.len == 0)
_ = try generateServices(allocator, ";", std.io.getStdIn(), stdout); _ = try generateServices(allocator, ";", std.fs.File.stdin(), stdout);
if (verbose) { if (verbose) {
const output_path = try output_dir.realpathAlloc(allocator, "."); const output_path = try output_dir.realpathAlloc(allocator, ".");
@ -133,7 +132,7 @@ fn processDirectories(models_dir: std.fs.Dir, output_dir: std.fs.Dir, parent_pro
// Do this in a brain dead fashion from here, no optimization // Do this in a brain dead fashion from here, no optimization
const manifest_file = try output_dir.createFile("service_manifest.zig", .{}); const manifest_file = try output_dir.createFile("service_manifest.zig", .{});
defer manifest_file.close(); defer manifest_file.close();
const manifest = manifest_file.writer(); var manifest = manifest_file.writer(&manifest_buf);
var mi = models_dir.iterate(); var mi = models_dir.iterate();
const generating_models_progress = parent_progress.start("generating models", count); const generating_models_progress = parent_progress.start("generating models", count);
@ -141,18 +140,15 @@ fn processDirectories(models_dir: std.fs.Dir, output_dir: std.fs.Dir, parent_pro
while (try mi.next()) |e| { while (try mi.next()) |e| {
if ((e.kind == .file or e.kind == .sym_link) and std.mem.endsWith(u8, e.name, ".json")) { if ((e.kind == .file or e.kind == .sym_link) and std.mem.endsWith(u8, e.name, ".json")) {
try processFile(e.name, output_dir, manifest); try processFile(e.name, output_dir, &manifest.interface);
generating_models_progress.completeOne(); generating_models_progress.completeOne();
} }
} }
// re-calculate so we can store the manifest // re-calculate so we can store the manifest
model_digest = calculated_manifest.model_dir_hash_digest; model_digest = calculated_manifest.model_dir_hash_digest;
_, calculated_manifest = try calculateDigests(models_dir, output_dir, &thread_pool); _, calculated_manifest = try calculateDigests(models_dir, output_dir, &thread_pool);
try output_dir.writeFile(.{ .sub_path = "output_manifest.json", .data = try std.json.stringifyAlloc( const data = try std.fmt.allocPrint(allocator, "{f}", .{std.json.fmt(calculated_manifest, .{ .whitespace = .indent_2 })});
allocator, try output_dir.writeFile(.{ .sub_path = "output_manifest.json", .data = data });
calculated_manifest,
.{ .whitespace = .indent_2 },
) });
} }
var model_digest: ?[Hasher.hex_multihash_len]u8 = null; var model_digest: ?[Hasher.hex_multihash_len]u8 = null;
@ -200,7 +196,7 @@ fn calculateDigests(models_dir: std.fs.Dir, output_dir: std.fs.Dir, thread_pool:
}, },
}; };
} }
fn processFile(file_name: []const u8, output_dir: std.fs.Dir, manifest: anytype) !void { fn processFile(file_name: []const u8, output_dir: std.fs.Dir, manifest: *std.Io.Writer) !void {
// It's probably best to create our own allocator here so we can deint at the end and // It's probably best to create our own allocator here so we can deint at the end and
// toss all allocations related to the services in this file // toss all allocations related to the services in this file
// I can't guarantee we're not leaking something, and at the end of the // I can't guarantee we're not leaking something, and at the end of the
@ -209,11 +205,10 @@ fn processFile(file_name: []const u8, output_dir: std.fs.Dir, manifest: anytype)
defer arena.deinit(); defer arena.deinit();
const allocator = arena.allocator(); const allocator = arena.allocator();
var output = try std.ArrayListUnmanaged(u8).initCapacity(allocator, 1024 * 1024 * 2); var output = try std.Io.Writer.Allocating.initCapacity(allocator, 1024 * 1024 * 2);
defer output.deinit(allocator); defer output.deinit();
var counting_writer = std.io.countingWriter(output.writer(allocator)); var writer = output.writer;
var writer = counting_writer.writer();
_ = try writer.write("const std = @import(\"std\");\n"); _ = try writer.write("const std = @import(\"std\");\n");
_ = try writer.write("const smithy = @import(\"smithy\");\n"); _ = try writer.write("const smithy = @import(\"smithy\");\n");
@ -226,7 +221,12 @@ fn processFile(file_name: []const u8, output_dir: std.fs.Dir, manifest: anytype)
if (verbose) std.log.info("Processing file: {s}", .{file_name}); if (verbose) std.log.info("Processing file: {s}", .{file_name});
const service_names = generateServicesForFilePath(allocator, ";", file_name, writer) catch |err| { const service_names = generateServicesForFilePath(
allocator,
";",
file_name,
&writer,
) catch |err| {
std.log.err("Error processing file: {s}", .{file_name}); std.log.err("Error processing file: {s}", .{file_name});
return err; return err;
}; };
@ -249,7 +249,7 @@ fn processFile(file_name: []const u8, output_dir: std.fs.Dir, manifest: anytype)
output_file_name = new_output_file_name; output_file_name = new_output_file_name;
} }
const unformatted: [:0]const u8 = try output.toOwnedSliceSentinel(allocator, 0); const unformatted: [:0]const u8 = try output.toOwnedSliceSentinel(0);
const formatted = try zigFmt(allocator, unformatted); const formatted = try zigFmt(allocator, unformatted);
// Dump our buffer out to disk // Dump our buffer out to disk
@ -266,14 +266,17 @@ fn zigFmt(allocator: std.mem.Allocator, buffer: [:0]const u8) ![]const u8 {
var tree = try std.zig.Ast.parse(allocator, buffer, .zig); var tree = try std.zig.Ast.parse(allocator, buffer, .zig);
defer tree.deinit(allocator); defer tree.deinit(allocator);
return try tree.render(allocator); var aw = try std.Io.Writer.Allocating.initCapacity(allocator, buffer.len);
defer aw.deinit();
try tree.render(allocator, &aw.writer, .{});
return aw.toOwnedSlice();
} }
fn generateServicesForFilePath( fn generateServicesForFilePath(
allocator: std.mem.Allocator, allocator: std.mem.Allocator,
comptime terminator: []const u8, comptime terminator: []const u8,
path: []const u8, path: []const u8,
writer: anytype, writer: *std.Io.Writer,
) ![][]const u8 { ) ![][]const u8 {
const file = try std.fs.cwd().openFile(path, .{}); const file = try std.fs.cwd().openFile(path, .{});
defer file.close(); defer file.close();
@ -288,28 +291,34 @@ fn addReference(id: []const u8, map: *std.StringHashMap(u64)) !void {
res.value_ptr.* = 1; res.value_ptr.* = 1;
} }
} }
fn countAllReferences(shape_ids: [][]const u8, shapes: std.StringHashMap(smithy.ShapeInfo), shape_references: *std.StringHashMap(u64), stack: *std.ArrayList([]const u8)) anyerror!void { fn countAllReferences(allocator: std.mem.Allocator, shape_ids: [][]const u8, shapes: std.StringHashMap(smithy.ShapeInfo), shape_references: *std.StringHashMap(u64), stack: *std.ArrayList([]const u8)) anyerror!void {
for (shape_ids) |id| { for (shape_ids) |id| {
const shape = shapes.get(id); const shape = shapes.get(id);
if (shape == null) { if (shape == null) {
std.log.err("Error - could not find shape with id {s}", .{id}); std.log.err("Error - could not find shape with id {s}", .{id});
return error.ShapeNotFound; return error.ShapeNotFound;
} }
try countReferences(shape.?, shapes, shape_references, stack); try countReferences(allocator, shape.?, shapes, shape_references, stack);
} }
} }
fn countTypeMembersReferences(type_members: []smithy.TypeMember, shapes: std.StringHashMap(smithy.ShapeInfo), shape_references: *std.StringHashMap(u64), stack: *std.ArrayList([]const u8)) anyerror!void { fn countTypeMembersReferences(allocator: std.mem.Allocator, type_members: []smithy.TypeMember, shapes: std.StringHashMap(smithy.ShapeInfo), shape_references: *std.StringHashMap(u64), stack: *std.ArrayList([]const u8)) anyerror!void {
for (type_members) |m| { for (type_members) |m| {
const target = shapes.get(m.target); const target = shapes.get(m.target);
if (target == null) { if (target == null) {
std.log.err("Error - could not find target {s}", .{m.target}); std.log.err("Error - could not find target {s}", .{m.target});
return error.TargetNotFound; return error.TargetNotFound;
} }
try countReferences(target.?, shapes, shape_references, stack); try countReferences(allocator, target.?, shapes, shape_references, stack);
} }
} }
fn countReferences(shape: smithy.ShapeInfo, shapes: std.StringHashMap(smithy.ShapeInfo), shape_references: *std.StringHashMap(u64), stack: *std.ArrayList([]const u8)) anyerror!void { fn countReferences(
allocator: std.mem.Allocator,
shape: smithy.ShapeInfo,
shapes: std.StringHashMap(smithy.ShapeInfo),
shape_references: *std.StringHashMap(u64),
stack: *std.ArrayList([]const u8),
) anyerror!void {
// Add ourselves as a reference, then we will continue down the tree // Add ourselves as a reference, then we will continue down the tree
try addReference(shape.id, shape_references); try addReference(shape.id, shape_references);
// Put ourselves on the stack. If we come back to ourselves, we want to end. // Put ourselves on the stack. If we come back to ourselves, we want to end.
@ -317,7 +326,7 @@ fn countReferences(shape: smithy.ShapeInfo, shapes: std.StringHashMap(smithy.Sha
if (std.mem.eql(u8, shape.id, i)) if (std.mem.eql(u8, shape.id, i))
return; return;
} }
try stack.append(shape.id); try stack.append(allocator, shape.id);
defer _ = stack.pop(); defer _ = stack.pop();
// Well, this is a fun read: https://awslabs.github.io/smithy/1.0/spec/core/model.html#recursive-shape-definitions // Well, this is a fun read: https://awslabs.github.io/smithy/1.0/spec/core/model.html#recursive-shape-definitions
// Looks like recursion has special rules in the spec to accomodate Java. // Looks like recursion has special rules in the spec to accomodate Java.
@ -339,15 +348,15 @@ fn countReferences(shape: smithy.ShapeInfo, shapes: std.StringHashMap(smithy.Sha
.unit, .unit,
=> {}, => {},
.document, .member, .resource => {}, // less sure about these? .document, .member, .resource => {}, // less sure about these?
.list => |i| try countReferences(shapes.get(i.member_target).?, shapes, shape_references, stack), .list => |i| try countReferences(allocator, shapes.get(i.member_target).?, shapes, shape_references, stack),
.set => |i| try countReferences(shapes.get(i.member_target).?, shapes, shape_references, stack), .set => |i| try countReferences(allocator, shapes.get(i.member_target).?, shapes, shape_references, stack),
.map => |i| { .map => |i| {
try countReferences(shapes.get(i.key).?, shapes, shape_references, stack); try countReferences(allocator, shapes.get(i.key).?, shapes, shape_references, stack);
try countReferences(shapes.get(i.value).?, shapes, shape_references, stack); try countReferences(allocator, shapes.get(i.value).?, shapes, shape_references, stack);
}, },
.structure => |m| try countTypeMembersReferences(m.members, shapes, shape_references, stack), .structure => |m| try countTypeMembersReferences(allocator, m.members, shapes, shape_references, stack),
.uniontype => |m| try countTypeMembersReferences(m.members, shapes, shape_references, stack), .uniontype => |m| try countTypeMembersReferences(allocator, m.members, shapes, shape_references, stack),
.service => |i| try countAllReferences(i.operations, shapes, shape_references, stack), .service => |i| try countAllReferences(allocator, i.operations, shapes, shape_references, stack),
.operation => |op| { .operation => |op| {
if (op.input) |i| { if (op.input) |i| {
const val = shapes.get(i); const val = shapes.get(i);
@ -355,7 +364,7 @@ fn countReferences(shape: smithy.ShapeInfo, shapes: std.StringHashMap(smithy.Sha
std.log.err("Error processing shape with id \"{s}\". Input shape \"{s}\" was not found", .{ shape.id, i }); std.log.err("Error processing shape with id \"{s}\". Input shape \"{s}\" was not found", .{ shape.id, i });
return error.ShapeNotFound; return error.ShapeNotFound;
} }
try countReferences(val.?, shapes, shape_references, stack); try countReferences(allocator, val.?, shapes, shape_references, stack);
} }
if (op.output) |i| { if (op.output) |i| {
const val = shapes.get(i); const val = shapes.get(i);
@ -363,27 +372,31 @@ fn countReferences(shape: smithy.ShapeInfo, shapes: std.StringHashMap(smithy.Sha
std.log.err("Error processing shape with id \"{s}\". Output shape \"{s}\" was not found", .{ shape.id, i }); std.log.err("Error processing shape with id \"{s}\". Output shape \"{s}\" was not found", .{ shape.id, i });
return error.ShapeNotFound; return error.ShapeNotFound;
} }
try countReferences(val.?, shapes, shape_references, stack); try countReferences(allocator, val.?, shapes, shape_references, stack);
} }
if (op.errors) |i| try countAllReferences(i, shapes, shape_references, stack); if (op.errors) |i| try countAllReferences(allocator, i, shapes, shape_references, stack);
}, },
.@"enum" => |m| try countTypeMembersReferences(m.members, shapes, shape_references, stack), .@"enum" => |m| try countTypeMembersReferences(allocator, m.members, shapes, shape_references, stack),
} }
} }
fn generateServices(allocator: std.mem.Allocator, comptime _: []const u8, file: std.fs.File, writer: anytype) ![][]const u8 { fn generateServices(
allocator: std.mem.Allocator,
comptime _: []const u8,
file: std.fs.File,
writer: *std.Io.Writer,
) ![][]const u8 {
const json = try file.readToEndAlloc(allocator, 1024 * 1024 * 1024); const json = try file.readToEndAlloc(allocator, 1024 * 1024 * 1024);
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();
var shapes = std.StringHashMap(smithy.ShapeInfo).init(allocator); var shapes = std.StringHashMap(smithy.ShapeInfo).init(allocator);
defer shapes.deinit(); defer shapes.deinit();
var services = std.ArrayList(smithy.ShapeInfo).init(allocator); var services = try std.ArrayList(smithy.ShapeInfo).initCapacity(allocator, model.shapes.len);
defer services.deinit();
for (model.shapes) |shape| { for (model.shapes) |shape| {
try shapes.put(shape.id, shape); try shapes.put(shape.id, shape);
switch (shape.shape) { switch (shape.shape) {
.service => try services.append(shape), .service => services.appendAssumeCapacity(shape),
else => {}, else => {},
} }
} }
@ -392,15 +405,15 @@ fn generateServices(allocator: std.mem.Allocator, comptime _: []const u8, file:
// a reference count in case there are recursive data structures // a reference count in case there are recursive data structures
var shape_references = std.StringHashMap(u64).init(allocator); var shape_references = std.StringHashMap(u64).init(allocator);
defer shape_references.deinit(); defer shape_references.deinit();
var stack = std.ArrayList([]const u8).init(allocator); var stack: std.ArrayList([]const u8) = .{};
defer stack.deinit(); defer stack.deinit(allocator);
for (services.items) |service| for (services.items) |service|
try countReferences(service, shapes, &shape_references, &stack); try countReferences(allocator, service, shapes, &shape_references, &stack);
var constant_names = std.ArrayList([]const u8).init(allocator); var constant_names = try std.ArrayList([]const u8).initCapacity(allocator, services.items.len);
defer constant_names.deinit(); defer constant_names.deinit(allocator);
var unresolved = std.ArrayList(smithy.ShapeInfo).init(allocator); var unresolved: std.ArrayList(smithy.ShapeInfo) = .{};
defer unresolved.deinit(); defer unresolved.deinit(allocator);
var generated = std.StringHashMap(void).init(allocator); var generated = std.StringHashMap(void).init(allocator);
defer generated.deinit(); defer generated.deinit();
@ -445,7 +458,7 @@ fn generateServices(allocator: std.mem.Allocator, comptime _: []const u8, file:
// name of the field will be snake_case of whatever comes in from // name of the field will be snake_case of whatever comes in from
// sdk_id. Not sure this will simple... // sdk_id. Not sure this will simple...
const constant_name = try support.constantName(allocator, sdk_id, .snake); const constant_name = try support.constantName(allocator, sdk_id, .snake);
try constant_names.append(constant_name); constant_names.appendAssumeCapacity(constant_name);
try writer.print("const Self = @This();\n", .{}); try writer.print("const Self = @This();\n", .{});
if (version) |v| if (version) |v|
try writer.print("pub const version: ?[]const u8 = \"{s}\";\n", .{v}) try writer.print("pub const version: ?[]const u8 = \"{s}\";\n", .{v})
@ -481,16 +494,16 @@ fn generateServices(allocator: std.mem.Allocator, comptime _: []const u8, file:
try generateOperation(allocator, shapes.get(op).?, state, writer); try generateOperation(allocator, shapes.get(op).?, state, writer);
} }
try generateAdditionalTypes(allocator, state, writer); try generateAdditionalTypes(allocator, state, writer);
return constant_names.toOwnedSlice(); return constant_names.toOwnedSlice(allocator);
} }
fn generateAdditionalTypes(allocator: std.mem.Allocator, file_state: FileGenerationState, writer: anytype) !void { fn generateAdditionalTypes(allocator: std.mem.Allocator, file_state: FileGenerationState, writer: *std.Io.Writer) !void {
// More types may be added during processing // More types may be added during processing
while (file_state.additional_types_to_generate.pop()) |t| { while (file_state.additional_types_to_generate.pop()) |t| {
if (file_state.additional_types_generated.getEntry(t.name) != null) continue; if (file_state.additional_types_generated.getEntry(t.name) != null) continue;
// std.log.info("\t\t{s}", .{t.name}); // std.log.info("\t\t{s}", .{t.name});
var type_stack = std.ArrayList(*const smithy.ShapeInfo).init(allocator); var type_stack: std.ArrayList(*const smithy.ShapeInfo) = .{};
defer type_stack.deinit(); defer type_stack.deinit(allocator);
const state = GenerationState{ const state = GenerationState{
.type_stack = &type_stack, .type_stack = &type_stack,
.file_state = file_state, .file_state = file_state,
@ -510,9 +523,9 @@ fn generateAdditionalTypes(allocator: std.mem.Allocator, file_state: FileGenerat
} }
} }
fn outputIndent(state: GenerationState, writer: anytype) !void { fn outputIndent(state: GenerationState, writer: *std.Io.Writer) !void {
const n_chars = 4 * state.indent_level; const n_chars = 4 * state.indent_level;
try writer.writeByteNTimes(' ', n_chars); try writer.splatBytesAll(" ", n_chars);
} }
const StructType = enum { const StructType = enum {
@ -536,12 +549,12 @@ const operation_sub_types = [_]OperationSubTypeInfo{
}, },
}; };
fn generateOperation(allocator: std.mem.Allocator, operation: smithy.ShapeInfo, file_state: FileGenerationState, writer: anytype) !void { fn generateOperation(allocator: std.mem.Allocator, operation: smithy.ShapeInfo, file_state: FileGenerationState, writer: *std.Io.Writer) !void {
const snake_case_name = try support.constantName(allocator, operation.name, .snake); const snake_case_name = try support.constantName(allocator, operation.name, .snake);
defer allocator.free(snake_case_name); defer allocator.free(snake_case_name);
var type_stack = std.ArrayList(*const smithy.ShapeInfo).init(allocator); var type_stack: std.ArrayList(*const smithy.ShapeInfo) = .{};
defer type_stack.deinit(); defer type_stack.deinit(allocator);
const state = GenerationState{ const state = GenerationState{
.type_stack = &type_stack, .type_stack = &type_stack,
.file_state = file_state, .file_state = file_state,
@ -586,7 +599,12 @@ fn generateOperation(allocator: std.mem.Allocator, operation: smithy.ShapeInfo,
new_state.indent_level = 0; new_state.indent_level = 0;
std.debug.assert(new_state.type_stack.items.len == 0); std.debug.assert(new_state.type_stack.items.len == 0);
try serialization.json.generateToJsonFunction(shape_id, writer.any(), new_state, generate_type_options.keyCase(.pascal)); try serialization.json.generateToJsonFunction(
shape_id,
writer,
new_state,
generate_type_options.keyCase(.pascal),
);
try writer.writeAll("\n"); try writer.writeAll("\n");
}, },
@ -638,7 +656,7 @@ fn generateOperation(allocator: std.mem.Allocator, operation: smithy.ShapeInfo,
_ = try writer.write("} = .{};\n"); _ = try writer.write("} = .{};\n");
} }
fn generateMetadataFunction(operation_name: []const u8, state: GenerationState, writer: anytype, options: GenerateTypeOptions) !void { fn generateMetadataFunction(operation_name: []const u8, state: GenerationState, writer: *std.Io.Writer, options: GenerateTypeOptions) !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 };
@ -699,7 +717,7 @@ fn getTypeName(allocator: std.mem.Allocator, shape: smithy.ShapeInfo) ![]const u
} }
} }
fn reuseCommonType(shape: smithy.ShapeInfo, writer: anytype, state: GenerationState) !bool { fn reuseCommonType(shape: smithy.ShapeInfo, writer: *std.Io.Writer, state: GenerationState) !bool {
// We want to return if we're at the top level of the stack. There are three // We want to return if we're at the top level of the stack. There are three
// reasons for this: // reasons for this:
// 1. For operations, we have a request that includes a metadata function // 1. For operations, we have a request that includes a metadata function
@ -729,14 +747,14 @@ fn reuseCommonType(shape: smithy.ShapeInfo, writer: anytype, state: GenerationSt
rc = true; rc = true;
_ = try writer.write(type_name); // This can't possibly be this easy... _ = try writer.write(type_name); // This can't possibly be this easy...
if (state.file_state.additional_types_generated.getEntry(shape.name) == null) if (state.file_state.additional_types_generated.getEntry(shape.name) == null)
try state.file_state.additional_types_to_generate.append(shape); try state.file_state.additional_types_to_generate.append(state.allocator, shape);
} }
} }
return rc; return rc;
} }
/// 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, writer: anytype, state: GenerationState, comptime options: GenerateTypeOptions) anyerror!bool { fn generateTypeFor(shape_id: []const u8, writer: *std.Io.Writer, state: GenerationState, comptime options: GenerateTypeOptions) anyerror!bool {
const end_structure = options.end_structure; const end_structure = options.end_structure;
var rc = false; var rc = false;
@ -808,7 +826,8 @@ fn generateTypeFor(shape_id: []const u8, writer: anytype, state: GenerationState
.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 => |m| { .map => |m| {
if (!try reuseCommonType(shape_info, std.io.null_writer, state)) { var null_writer = std.Io.Writer.Discarding.init(&.{}).writer;
if (!try reuseCommonType(shape_info, &null_writer, state)) {
try generateMapTypeFor(m, writer, state, options); try generateMapTypeFor(m, writer, state, options);
rc = true; rc = true;
} else { } else {
@ -825,7 +844,7 @@ fn generateTypeFor(shape_id: []const u8, writer: anytype, state: GenerationState
return rc; return rc;
} }
fn generateMapTypeFor(map: anytype, writer: anytype, state: GenerationState, comptime options: GenerateTypeOptions) anyerror!void { fn generateMapTypeFor(map: anytype, writer: *std.Io.Writer, state: GenerationState, comptime options: GenerateTypeOptions) anyerror!void {
_ = try writer.write("struct {\n"); _ = try writer.write("struct {\n");
try writer.writeAll("pub const is_map_type = true;\n\n"); try writer.writeAll("pub const is_map_type = true;\n\n");
@ -848,12 +867,12 @@ fn generateMapTypeFor(map: anytype, writer: anytype, state: GenerationState, com
_ = try writer.write("}"); _ = try writer.write("}");
} }
fn generateSimpleTypeFor(_: anytype, type_name: []const u8, writer: anytype) !void { fn generateSimpleTypeFor(_: anytype, type_name: []const u8, writer: *std.Io.Writer) !void {
_ = 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
} }
const Mapping = struct { snake: []const u8, original: []const u8 }; const Mapping = struct { snake: []const u8, original: []const u8 };
fn generateComplexTypeFor(shape_id: []const u8, members: []smithy.TypeMember, type_type_name: []const u8, writer: anytype, state: GenerationState, comptime options: GenerateTypeOptions) anyerror!void { fn generateComplexTypeFor(shape_id: []const u8, members: []smithy.TypeMember, type_type_name: []const u8, writer: *std.Io.Writer, state: GenerationState, comptime options: GenerateTypeOptions) anyerror!void {
_ = shape_id; _ = shape_id;
var arena = std.heap.ArenaAllocator.init(state.allocator); var arena = std.heap.ArenaAllocator.init(state.allocator);
@ -861,7 +880,7 @@ fn generateComplexTypeFor(shape_id: []const u8, members: []smithy.TypeMember, ty
const allocator = arena.allocator(); const allocator = arena.allocator();
var field_name_mappings = try std.ArrayList(Mapping).initCapacity(allocator, members.len); var field_name_mappings = try std.ArrayList(Mapping).initCapacity(allocator, members.len);
defer field_name_mappings.deinit(); defer field_name_mappings.deinit(allocator);
// 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
// pretends to, but it's an empty map // pretends to, but it's an empty map
// //
@ -869,13 +888,13 @@ fn generateComplexTypeFor(shape_id: []const u8, members: []smithy.TypeMember, ty
// //
// 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(allocator, members.len);
defer http_query_mappings.deinit(); defer http_query_mappings.deinit(allocator);
var http_header_mappings = try std.ArrayList(Mapping).initCapacity(allocator, members.len); var http_header_mappings = try std.ArrayList(Mapping).initCapacity(allocator, members.len);
defer http_header_mappings.deinit(); defer http_header_mappings.deinit(allocator);
var map_fields = std.ArrayList([]const u8).init(allocator); var map_fields = try std.ArrayList([]const u8).initCapacity(allocator, members.len);
defer map_fields.deinit(); defer map_fields.deinit(allocator);
// 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);
@ -930,7 +949,7 @@ fn generateComplexTypeFor(shape_id: []const u8, members: []smithy.TypeMember, ty
try writer.print("{s}: ", .{member_name}); try writer.print("{s}: ", .{member_name});
try writeOptional(member.traits, writer, null); try writeOptional(member.traits, writer, null);
if (try generateTypeFor(member.target, writer, child_state, options.endStructure(true))) if (try generateTypeFor(member.target, writer, child_state, options.endStructure(true)))
try map_fields.append(try std.fmt.allocPrint(allocator, "{s}", .{member_name})); map_fields.appendAssumeCapacity(try std.fmt.allocPrint(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");
@ -978,7 +997,14 @@ fn generateComplexTypeFor(shape_id: []const u8, members: []smithy.TypeMember, ty
_ = try writer.write("}\n"); _ = 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: *std.Io.Writer,
) !void {
if (mappings.items.len == 0 and !force_output) return; if (mappings.items.len == 0 and !force_output) return;
try outputIndent(state, writer); try outputIndent(state, writer);
if (mappings.items.len == 0) { if (mappings.items.len == 0) {
@ -998,7 +1024,7 @@ fn writeMappings(state: GenerationState, @"pub": []const u8, mapping_name: []con
_ = try writer.write("};\n"); _ = try writer.write("};\n");
} }
fn writeOptional(traits: ?[]smithy.Trait, writer: anytype, value: ?[]const u8) !void { fn writeOptional(traits: ?[]smithy.Trait, writer: *std.Io.Writer, value: ?[]const u8) !void {
if (traits) |ts| if (smt.hasTrait(.required, ts)) return; if (traits) |ts| if (smt.hasTrait(.required, ts)) return;
try writer.writeAll(value orelse "?"); try writer.writeAll(value orelse "?");
} }

View file

@ -17,7 +17,7 @@ const JsonMember = struct {
shape_info: smithy.ShapeInfo, shape_info: smithy.ShapeInfo,
}; };
pub fn generateToJsonFunction(shape_id: []const u8, writer: std.io.AnyWriter, state: GenerationState, comptime options: GenerateTypeOptions) !void { pub fn generateToJsonFunction(shape_id: []const u8, writer: *std.Io.Writer, state: GenerationState, comptime options: GenerateTypeOptions) !void {
_ = options; _ = options;
const allocator = state.allocator; const allocator = state.allocator;
@ -117,15 +117,15 @@ fn getMemberValueJson(allocator: std.mem.Allocator, source: []const u8, member:
const member_value = try std.fmt.allocPrint(allocator, "@field({s}, \"{s}\")", .{ source, member.field_name }); const member_value = try std.fmt.allocPrint(allocator, "@field({s}, \"{s}\")", .{ source, member.field_name });
defer allocator.free(member_value); defer allocator.free(member_value);
var output_block = std.ArrayListUnmanaged(u8){}; var output_block = std.Io.Writer.Allocating.init(allocator);
const writer = output_block.writer(allocator); defer output_block.deinit();
try writeMemberValue( try writeMemberValue(
writer, &output_block.writer,
member_value, member_value,
); );
return output_block.toOwnedSlice(allocator); return output_block.toOwnedSlice();
} }
fn getShapeJsonValueType(shape: Shape) []const u8 { fn getShapeJsonValueType(shape: Shape) []const u8 {
@ -139,7 +139,7 @@ fn getShapeJsonValueType(shape: Shape) []const u8 {
} }
fn writeMemberValue( fn writeMemberValue(
writer: anytype, writer: *std.Io.Writer,
member_value: []const u8, member_value: []const u8,
) !void { ) !void {
try writer.writeAll(member_value); try writer.writeAll(member_value);
@ -153,7 +153,7 @@ const WriteMemberJsonParams = struct {
member: smithy.TypeMember, member: smithy.TypeMember,
}; };
fn writeStructureJson(params: WriteMemberJsonParams, writer: std.io.AnyWriter) !void { fn writeStructureJson(params: WriteMemberJsonParams, writer: *std.Io.Writer) !void {
const shape_type = "structure"; const shape_type = "structure";
const allocator = params.state.allocator; const allocator = params.state.allocator;
const state = params.state; const state = params.state;
@ -221,7 +221,7 @@ fn writeStructureJson(params: WriteMemberJsonParams, writer: std.io.AnyWriter) !
} }
} }
fn writeListJson(list: smithy_tools.ListShape, params: WriteMemberJsonParams, writer: std.io.AnyWriter) anyerror!void { fn writeListJson(list: smithy_tools.ListShape, params: WriteMemberJsonParams, writer: *std.Io.Writer) anyerror!void {
const state = params.state; const state = params.state;
const allocator = state.allocator; const allocator = state.allocator;
@ -274,7 +274,7 @@ fn writeListJson(list: smithy_tools.ListShape, params: WriteMemberJsonParams, wr
} }
} }
fn writeMapJson(map: smithy_tools.MapShape, params: WriteMemberJsonParams, writer: std.io.AnyWriter) anyerror!void { fn writeMapJson(map: smithy_tools.MapShape, params: WriteMemberJsonParams, writer: *std.Io.Writer) anyerror!void {
const state = params.state; const state = params.state;
const name = params.field_name; const name = params.field_name;
const value = params.field_value; const value = params.field_value;
@ -351,11 +351,11 @@ fn writeMapJson(map: smithy_tools.MapShape, params: WriteMemberJsonParams, write
} }
} }
fn writeScalarJson(comment: []const u8, params: WriteMemberJsonParams, writer: std.io.AnyWriter) anyerror!void { 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 }); try writer.print("try jw.write({s}); // {s}\n\n", .{ params.field_value, comment });
} }
fn writeMemberJson(params: WriteMemberJsonParams, writer: std.io.AnyWriter) anyerror!void { fn writeMemberJson(params: WriteMemberJsonParams, writer: *std.Io.Writer) anyerror!void {
const shape_id = params.shape_id; const shape_id = params.shape_id;
const state = params.state; const state = params.state;
const shape_info = try smithy_tools.getShapeInfo(shape_id, state.file_state.shapes); const shape_info = try smithy_tools.getShapeInfo(shape_id, state.file_state.shapes);