Compare commits

...

3 Commits

2 changed files with 39 additions and 10 deletions

View File

@ -30,9 +30,9 @@ pub fn build(b: *std.Build) void {
b.installArtifact(lib); b.installArtifact(lib);
const module = b.addModule("smithy", .{ const module = b.addModule("smithy", .{
.source_file = .{ .path = "src/smithy.zig" }, .root_source_file = .{ .path = "src/smithy.zig" },
}); });
lib.addModule("smithy", module); lib.root_module.addImport("smithy", module);
// Creates a step for unit testing. This only builds the test executable // Creates a step for unit testing. This only builds the test executable
// but does not run it. // but does not run it.

View File

@ -7,14 +7,16 @@ pub const Smithy = struct {
metadata: ModelMetadata, metadata: ModelMetadata,
shapes: []ShapeInfo, shapes: []ShapeInfo,
allocator: std.mem.Allocator, allocator: std.mem.Allocator,
json_source: std.json.Parsed(std.json.Value),
const Self = @This(); const Self = @This();
pub fn init(allocator: std.mem.Allocator, version: []const u8, metadata: ModelMetadata, shapeinfo: []ShapeInfo) Smithy { pub fn init(allocator: std.mem.Allocator, version: []const u8, metadata: ModelMetadata, shapeinfo: []ShapeInfo, json_source: std.json.Parsed(std.json.Value)) Smithy {
return .{ return .{
.version = version, .version = version,
.metadata = metadata, .metadata = metadata,
.shapes = shapeinfo, .shapes = shapeinfo,
.allocator = allocator, .allocator = allocator,
.json_source = json_source,
}; };
} }
pub fn deinit(self: Self) void { pub fn deinit(self: Self) void {
@ -76,6 +78,7 @@ pub const Smithy = struct {
} }
} }
self.allocator.free(self.shapes); self.allocator.free(self.shapes);
self.json_source.deinit();
} }
}; };
pub const ShapeInfo = struct { pub const ShapeInfo = struct {
@ -99,6 +102,8 @@ pub const TraitType = enum {
aws_auth_sigv4, aws_auth_sigv4,
aws_protocol, aws_protocol,
ec2_query_name, ec2_query_name,
json_name,
xml_name,
http, http,
http_header, http_header,
http_label, http_label,
@ -106,13 +111,16 @@ pub const TraitType = enum {
http_payload, http_payload,
json_name, json_name,
xml_name, xml_name,
required, required, // required on the server
client_optional, // optional as far as the client is concerned
documentation, documentation,
pattern, pattern,
range, range,
length, length,
box, box,
sparse, sparse,
enum_value,
aws_query_error,
}; };
pub const Trait = union(TraitType) { pub const Trait = union(TraitType) {
aws_api_service: struct { aws_api_service: struct {
@ -139,6 +147,7 @@ pub const Trait = union(TraitType) {
http_query: []const u8, http_query: []const u8,
http_payload: struct {}, http_payload: struct {},
required: struct {}, required: struct {},
client_optional: void,
documentation: []const u8, documentation: []const u8,
pattern: []const u8, pattern: []const u8,
range: struct { // most data is actually integers, but as some are floats, we'll use that here range: struct { // most data is actually integers, but as some are floats, we'll use that here
@ -151,6 +160,11 @@ pub const Trait = union(TraitType) {
}, },
box: struct {}, box: struct {},
sparse: struct {}, sparse: struct {},
enum_value: []const u8,
aws_query_error: struct {
http_response_code: i64,
code: []const u8,
},
}; };
const ShapeType = enum { const ShapeType = enum {
blob, blob,
@ -253,10 +267,10 @@ pub const AwsProtocol = enum {
}; };
pub fn parse(allocator: std.mem.Allocator, json_model: []const u8) !Smithy { pub fn parse(allocator: std.mem.Allocator, json_model: []const u8) !Smithy {
// construct a parser. We're not copying strings here, but that may // construct a parser. We're not copying strings here
// be a poor decision // Instead, we keep the original json string around
// This might be bad if we only need a small fraction of the original json source
var vt = try std.json.parseFromSlice(std.json.Value, allocator, json_model, .{}); var vt = try std.json.parseFromSlice(std.json.Value, allocator, json_model, .{});
defer vt.deinit();
return Smithy.init( return Smithy.init(
allocator, allocator,
vt.value.object.get("smithy").?.string, vt.value.object.get("smithy").?.string,
@ -265,6 +279,7 @@ pub fn parse(allocator: std.mem.Allocator, json_model: []const u8) !Smithy {
.suppressions = &.{}, .suppressions = &.{},
}, },
try shapes(allocator, vt.value.object.get("shapes").?.object), try shapes(allocator, vt.value.object.get("shapes").?.object),
vt,
); );
} }
@ -572,7 +587,7 @@ fn getShape(allocator: std.mem.Allocator, shape: std.json.Value) SmithyParseErro
} }
fn parseMembers(allocator: std.mem.Allocator, shape: ?std.json.Value) SmithyParseError![]TypeMember { fn parseMembers(allocator: std.mem.Allocator, shape: ?std.json.Value) SmithyParseError![]TypeMember {
var rc: []TypeMember = &.{}; const rc: []TypeMember = &.{};
if (shape == null) if (shape == null)
return rc; return rc;
@ -606,7 +621,7 @@ fn parseTraitsOnly(allocator: std.mem.Allocator, shape: std.json.Value) SmithyPa
} }
fn parseTraits(allocator: std.mem.Allocator, shape: ?std.json.Value) SmithyParseError![]Trait { fn parseTraits(allocator: std.mem.Allocator, shape: ?std.json.Value) SmithyParseError![]Trait {
var rc: []Trait = &.{}; const rc: []Trait = &.{};
if (shape == null) if (shape == null)
return rc; return rc;
@ -641,6 +656,8 @@ fn getTrait(trait_type: []const u8, value: std.json.Value) SmithyParseError!?Tra
}; };
if (std.mem.eql(u8, trait_type, "smithy.api#required")) if (std.mem.eql(u8, trait_type, "smithy.api#required"))
return Trait{ .required = .{} }; return Trait{ .required = .{} };
if (std.mem.eql(u8, trait_type, "smithy.api#clientOptional"))
return Trait{ .client_optional = {} };
if (std.mem.eql(u8, trait_type, "smithy.api#sparse")) if (std.mem.eql(u8, trait_type, "smithy.api#sparse"))
return Trait{ .sparse = .{} }; return Trait{ .sparse = .{} };
if (std.mem.eql(u8, trait_type, "smithy.api#box")) if (std.mem.eql(u8, trait_type, "smithy.api#box"))
@ -705,6 +722,14 @@ fn getTrait(trait_type: []const u8, value: std.json.Value) SmithyParseError!?Tra
.code = code, .code = code,
} }; } };
} }
if (std.mem.eql(u8, trait_type, "aws.protocols#awsQueryError")) {
return Trait{
.aws_query_error = .{
.code = value.object.get("code").?.string, // code is required
.http_response_code = value.object.get("httpResponseCode").?.integer,
},
};
}
if (std.mem.eql(u8, trait_type, "smithy.api#jsonName")) if (std.mem.eql(u8, trait_type, "smithy.api#jsonName"))
return Trait{ .json_name = value.string }; return Trait{ .json_name = value.string };
if (std.mem.eql(u8, trait_type, "smithy.api#xmlName")) if (std.mem.eql(u8, trait_type, "smithy.api#xmlName"))
@ -722,6 +747,10 @@ fn getTrait(trait_type: []const u8, value: std.json.Value) SmithyParseError!?Tra
if (std.mem.eql(u8, trait_type, "smithy.api#xmlNamespace")) if (std.mem.eql(u8, trait_type, "smithy.api#xmlNamespace"))
return null; return null;
if (std.mem.eql(u8, trait_type, "smithy.api#enumValue"))
return Trait{ .enum_value = value.string };
// TODO: win argument with compiler to get this comptime // TODO: win argument with compiler to get this comptime
const list = const list =
\\aws.api#arnReference \\aws.api#arnReference
@ -864,7 +893,7 @@ fn read_file_to_string(allocator: std.mem.Allocator, file_name: []const u8, max_
return file.readToEndAlloc(allocator, max_bytes); return file.readToEndAlloc(allocator, max_bytes);
} }
const test_data: []const u8 = @embedFile("test.json"); const test_data: []const u8 = @embedFile("test.json");
const intrinsic_type_count: usize = 5; // 5 intrinsic types are added to every model const intrinsic_type_count: usize = 15; // 15 intrinsic types are added to every model (see shapes() function)
test "parse string" { test "parse string" {
const test_string = const test_string =