Compare commits

..

4 Commits

4 changed files with 136 additions and 44 deletions

View File

@ -11,8 +11,16 @@ pub const Options = struct {
dualstack: bool = false,
};
/// Using this constant may blow up build times. Recommed using Services()
/// function directly, e.g. const services = Services(.{.sts, .ec2, .s3, .ddb}){};
pub const services = servicemodel.services;
/// Get a service model by importing specific services only. As an example:
/// const services = Services(.{.sts, .ec2, .s3, .ddb}){};
///
/// This will give you a constant with service data for sts, ec2, s3 and ddb only
pub const Services = servicemodel.Services;
pub const Aws = struct {
allocator: *std.mem.Allocator,
aws_http: awshttp.AwsHttp,

View File

@ -20,10 +20,31 @@ pub const Smithy = struct {
pub fn deinit(self: Self) void {
for (self.shapes) |s| {
switch (s.shape) {
.string, .byte, .short, .integer, .long, .float, .double, .bigInteger, .bigDecimal => |v| self.allocator.free(v.traits),
.string,
.byte,
.short,
.integer,
.long,
.float,
.double,
.bigInteger,
.bigDecimal,
.blob,
.boolean,
.timestamp,
.document,
.member,
.resource,
=> |v| self.allocator.free(v.traits),
.structure => |v| {
for (v.members) |m| self.allocator.free(m.traits);
self.allocator.free(v.members);
self.allocator.free(v.traits);
},
.uniontype => |v| {
for (v.members) |m| self.allocator.free(m.traits);
self.allocator.free(v.members);
self.allocator.free(v.traits);
},
.service => |v| {
self.allocator.free(v.traits);
@ -33,7 +54,17 @@ pub const Smithy = struct {
if (v.errors) |e| self.allocator.free(e);
self.allocator.free(v.traits);
},
else => {},
.list => |v| {
self.allocator.free(v.traits);
},
.set => |v| {
self.allocator.free(v.traits);
},
.map => |v| {
self.allocator.free(v.key);
self.allocator.free(v.value);
self.allocator.free(v.traits);
},
}
}
self.allocator.free(self.shapes);
@ -212,6 +243,7 @@ pub fn parse(allocator: *std.mem.Allocator, json_model: []const u8) !Smithy {
// list must be deinitialized by caller
fn shapes(allocator: *std.mem.Allocator, map: anytype) ![]ShapeInfo {
var list = try std.ArrayList(ShapeInfo).initCapacity(allocator, map.count());
defer list.deinit();
var iterator = map.iterator();
while (iterator.next()) |kv| {
const id_info = try parseId(kv.key_ptr.*);
@ -392,6 +424,7 @@ fn parseMembers(allocator: *std.mem.Allocator, shape: ?std.json.Value) SmithyPar
const map = shape.?.Object;
var list = std.ArrayList(TypeMember).initCapacity(allocator, map.count()) catch return SmithyParseError.OutOfMemory;
defer list.deinit();
var iterator = map.iterator();
while (iterator.next()) |kv| {
try list.append(TypeMember{
@ -406,6 +439,7 @@ fn parseMembers(allocator: *std.mem.Allocator, shape: ?std.json.Value) SmithyPar
// ArrayList of std.Json.Value
fn parseTargetList(allocator: *std.mem.Allocator, list: anytype) SmithyParseError![][]const u8 {
var array_list = std.ArrayList([]const u8).initCapacity(allocator, list.items.len) catch return SmithyParseError.OutOfMemory;
defer array_list.deinit();
for (list.items) |i| {
try array_list.append(i.Object.get("target").?.String);
}
@ -424,6 +458,7 @@ fn parseTraits(allocator: *std.mem.Allocator, shape: ?std.json.Value) SmithyPars
const map = shape.?.Object;
var list = std.ArrayList(Trait).initCapacity(allocator, map.count()) catch return SmithyParseError.OutOfMemory;
defer list.deinit();
var iterator = map.iterator();
while (iterator.next()) |kv| {
if (try getTrait(kv.key_ptr.*, kv.value_ptr.*)) |t|
@ -622,6 +657,8 @@ fn read_file_to_string(allocator: *std.mem.Allocator, file_name: []const u8, max
return file.readToEndAlloc(allocator, max_bytes);
}
var test_data: ?[]const u8 = null;
const intrinsic_type_count: usize = 5; // 5 intrinsic types are added to every model
fn getTestData(allocator: *std.mem.Allocator) []const u8 {
if (test_data) |d| return d;
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
@ -660,13 +697,14 @@ test "parse string" {
const allocator = &gpa.allocator;
const model = try parse(allocator, test_string);
defer model.deinit();
expect(std.mem.eql(u8, model.version, "1.0"));
std.testing.expectEqual(@as(usize, 1), model.shapes.len);
std.testing.expectEqualStrings("com.amazonaws.sts#AWSSecurityTokenServiceV20110615", model.shapes[0].id);
std.testing.expectEqualStrings("com.amazonaws.sts", model.shapes[0].namespace);
std.testing.expectEqualStrings("AWSSecurityTokenServiceV20110615", model.shapes[0].name);
std.testing.expect(model.shapes[0].member == null);
std.testing.expectEqualStrings("2011-06-15", model.shapes[0].shape.service.version);
try expect(std.mem.eql(u8, model.version, "1.0"));
try std.testing.expectEqual(intrinsic_type_count + 1, model.shapes.len);
try std.testing.expectEqualStrings("com.amazonaws.sts#AWSSecurityTokenServiceV20110615", model.shapes[0].id);
try std.testing.expectEqualStrings("com.amazonaws.sts", model.shapes[0].namespace);
try std.testing.expectEqualStrings("AWSSecurityTokenServiceV20110615", model.shapes[0].name);
try std.testing.expect(model.shapes[0].member == null);
try std.testing.expectEqualStrings("2011-06-15", model.shapes[0].shape.service.version);
}
test "parse shape with member" {
const test_string =
@ -693,13 +731,13 @@ test "parse shape with member" {
const allocator = &gpa.allocator;
const model = try parse(allocator, test_string);
defer model.deinit();
expect(std.mem.eql(u8, model.version, "1.0"));
std.testing.expectEqual(@as(usize, 1), model.shapes.len);
std.testing.expectEqualStrings("com.amazonaws.sts#AWSSecurityTokenServiceV20110615$member", model.shapes[0].id);
std.testing.expectEqualStrings("com.amazonaws.sts", model.shapes[0].namespace);
std.testing.expectEqualStrings("AWSSecurityTokenServiceV20110615", model.shapes[0].name);
std.testing.expectEqualStrings("2011-06-15", model.shapes[0].shape.service.version);
std.testing.expectEqualStrings("member", model.shapes[0].member.?);
try expect(std.mem.eql(u8, model.version, "1.0"));
try std.testing.expectEqual(intrinsic_type_count + 1, model.shapes.len);
try std.testing.expectEqualStrings("com.amazonaws.sts#AWSSecurityTokenServiceV20110615$member", model.shapes[0].id);
try std.testing.expectEqualStrings("com.amazonaws.sts", model.shapes[0].namespace);
try std.testing.expectEqualStrings("AWSSecurityTokenServiceV20110615", model.shapes[0].name);
try std.testing.expectEqualStrings("2011-06-15", model.shapes[0].shape.service.version);
try std.testing.expectEqualStrings("member", model.shapes[0].member.?);
}
test "parse file" {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
@ -708,12 +746,12 @@ test "parse file" {
const test_string = getTestData(allocator);
const model = try parse(allocator, test_string);
defer model.deinit();
std.testing.expectEqualStrings(model.version, "1.0");
try std.testing.expectEqualStrings(model.version, "1.0");
// metadata expectations
// try expect(std.mem.eql(u8, model.version, "version 1.0"));
// shape expectations
std.testing.expectEqual(@as(usize, 81), model.shapes.len);
try std.testing.expectEqual(intrinsic_type_count + 81, model.shapes.len);
var optsvc: ?ShapeInfo = null;
for (model.shapes) |shape| {
if (std.mem.eql(u8, shape.id, "com.amazonaws.sts#AWSSecurityTokenServiceV20110615")) {
@ -721,13 +759,13 @@ test "parse file" {
break;
}
}
std.testing.expect(optsvc != null);
try std.testing.expect(optsvc != null);
const svc = optsvc.?;
std.testing.expectEqualStrings("com.amazonaws.sts#AWSSecurityTokenServiceV20110615", svc.id);
std.testing.expectEqualStrings("com.amazonaws.sts", svc.namespace);
std.testing.expectEqualStrings("AWSSecurityTokenServiceV20110615", svc.name);
std.testing.expectEqualStrings("2011-06-15", svc.shape.service.version);
try std.testing.expectEqualStrings("com.amazonaws.sts#AWSSecurityTokenServiceV20110615", svc.id);
try std.testing.expectEqualStrings("com.amazonaws.sts", svc.namespace);
try std.testing.expectEqualStrings("AWSSecurityTokenServiceV20110615", svc.name);
try std.testing.expectEqualStrings("2011-06-15", svc.shape.service.version);
// Should be 6, but we don't handle title or xml namespace
std.testing.expectEqual(@as(usize, 4), svc.shape.service.traits.len);
std.testing.expectEqual(@as(usize, 8), svc.shape.service.operations.len);
try std.testing.expectEqual(@as(usize, 4), svc.shape.service.traits.len);
try std.testing.expectEqual(@as(usize, 8), svc.shape.service.operations.len);
}

View File

@ -45,7 +45,8 @@ pub fn main() anyerror!void {
var client = aws.Aws.init(allocator);
defer client.deinit();
const resp = try client.call(aws.services.sts.get_caller_identity.Request{}, options);
const services = aws.Services(.{.sts}){};
const resp = try client.call(services.sts.get_caller_identity.Request{}, options);
// TODO: This is a bit wonky. Root cause is lack of declarations in
// comptime-generated types
defer resp.deinit();
@ -56,7 +57,7 @@ pub fn main() anyerror!void {
var client2 = aws.Aws.init(allocator);
defer client2.deinit();
const resp2 = try client2.call(aws.services.sts.get_caller_identity.Request{}, options); // catch here and try alloc?
const resp2 = try client2.call(services.sts.get_caller_identity.Request{}, options); // catch here and try alloc?
defer resp2.deinit();
}

View File

@ -1,15 +1,22 @@
const std = @import("std");
const expectEqualStrings = std.testing.expectEqualStrings;
// TODO: Make generic
fn Services() type {
pub fn Services(service_imports: anytype) type {
// This service list can be imported from a master file of all services
// provided by codegen
const service_list = @import("codegen/service_manifest.zig").services;
// From here, the fields of our structure can be generated at comptime...
var fields: [service_list.len]std.builtin.TypeInfo.StructField = undefined;
var fields: [serviceCount(service_list, service_imports)]std.builtin.TypeInfo.StructField = undefined;
// This is run at comptime with multiple nested loops and a large (267 at
// time of writing) number of services. 4 was chosen by trial and error,
// but otherwise the branch count will be the product of field length,
// service list length and the number of imports requested
@setEvalBranchQuota(4 * fields.len * service_list.len * std.math.min(service_imports.len, 1));
var inx = 0;
for (fields) |*item, i| {
if (service_imports.len == 0) {
const import = @field(@import("codegen/models/" ++ service_list[i].file_name), service_list[i].export_name);
item.* = .{
.name = service_list[i].name,
@ -18,6 +25,33 @@ fn Services() type {
.is_comptime = false,
.alignment = 0,
};
continue;
}
var found = false;
// we will loop through the big list and check each service
// against the list of desired imports
while (inx < service_list.len) {
for (service_imports) |si| {
if (std.mem.eql(u8, @tagName(si), service_list[inx].name)) {
const import = @field(@import("codegen/models/" ++ service_list[inx].file_name), service_list[inx].export_name);
item.* = .{
.name = service_list[inx].name,
.field_type = @TypeOf(import),
.default_value = import,
.is_comptime = false,
.alignment = 0,
};
found = true;
break;
}
}
inx = inx + 1; // service found or not in list - move to next service
if (found) break;
}
if (!found)
@compileError("imported service(s) not found");
}
// finally, generate the type
@ -30,21 +64,32 @@ fn Services() type {
},
});
}
// TODO: One constant to rule them all is a bit much. We can keep this
// in the back pocket, but let's move to a factory style getService("blah");
pub const services = Services(){};
fn serviceCount(service_list: anytype, desired_services: anytype) usize {
if (desired_services.len == 0) return service_list.len;
return desired_services.len;
}
/// Using this constant may blow up build times. Recommed using Services()
/// function directly, e.g. const services = Services(.{.sts, .ec2, .s3, .ddb}){};
pub const services = Services(.{}){};
test "services includes sts" {
expectEqualStrings("2011-06-15", services.sts.version);
try expectEqualStrings("2011-06-15", services.sts.version);
}
test "sts includes get_caller_identity" {
expectEqualStrings("GetCallerIdentity", services.sts.get_caller_identity.action_name);
try expectEqualStrings("GetCallerIdentity", services.sts.get_caller_identity.action_name);
}
test "can get service and action name from request" {
// get request object. This call doesn't have parameters
const req = services.sts.get_caller_identity.Request{};
// const metadata = @TypeOf(req).metaInfo();
const metadata = req.metaInfo();
expectEqualStrings("2011-06-15", metadata.service.version);
try expectEqualStrings("2011-06-15", metadata.service.version);
// expectEqualStrings("GetCallerIdentity", metadata.action.action_name);
}
test "can filter services" {
const filtered_services = Services(.{ .sts, .waf_v2 }){};
// const filtered_services = Services(.{.sts}){};
try expectEqualStrings("2011-06-15", filtered_services.sts.version);
}