update servicemodel for generated model

This increases compilation time significantly as all 260+ services
need to be analyzed by zig during compilation. I plan to change
the model from a single "services" constant to a function
that will import only services that plan to be used. This
might be in addition to the single constant to allow
consumers to choose short compile times or all services
support
This commit is contained in:
Emil Lerch 2021-06-09 16:22:44 -07:00
parent 88632df671
commit bd5d509665
Signed by: lobo
GPG Key ID: A7B62D657EF764F8
2 changed files with 43 additions and 134 deletions

View File

@ -27,15 +27,16 @@ pub const Aws = struct {
}
pub fn call(self: Self, comptime request: anytype, options: Options) !FullResponse(request) {
const action_info = actionForRequest(request);
// This is true weirdness, but we are running into compiler bugs. Touch only if
// prepared...
const service = @field(services, action_info.service);
const action = @field(service, action_info.action);
// every codegenned request object includes a metaInfo function to get
// pointers to service and action
const meta_info = request.metaInfo();
const service = meta_info.service;
const action = meta_info.action;
const R = Response(request);
const FullR = FullResponse(request);
log.debug("service {s}", .{action_info.service});
log.debug("service endpoint {s}", .{service.endpoint_prefix});
log.debug("service sigv4 name {s}", .{service.sigv4_name});
log.debug("version {s}", .{service.version});
log.debug("action {s}", .{action.action_name});
const response = try self.aws_http.callApi(action_info.service, service.version, action.action_name, options);
@ -73,40 +74,9 @@ pub const Aws = struct {
}
};
fn actionForRequest(comptime request: anytype) struct { service: []const u8, action: []const u8, service_obj: anytype } {
const type_name = @typeName(@TypeOf(request));
var service_start: usize = 0;
var service_end: usize = 0;
var action_start: usize = 0;
var action_end: usize = 0;
for (type_name) |ch, i| {
switch (ch) {
'(' => service_start = i + 2,
')' => action_end = i - 1,
',' => {
service_end = i - 1;
action_start = i + 2;
},
else => continue,
}
}
// const zero: usize = 0;
// TODO: Figure out why if statement isn't working
// if (serviceStart == zero or serviceEnd == zero or actionStart == zero or actionEnd == zero) {
// @compileLog("Type must be a function with two parameters \"service\" and \"action\". Found: " ++ type_name);
// // @compileError("Type must be a function with two parameters \"service\" and \"action\". Found: " ++ type_name);
// }
return .{
.service = type_name[service_start..service_end],
.action = type_name[action_start..action_end],
.service_obj = @field(services, type_name[service_start..service_end]),
};
}
fn ServerResponse(comptime request: anytype) type {
const T = Response(request);
const action_info = actionForRequest(request);
const service = @field(services, action_info.service);
const action = @field(service, action_info.action);
const action = request.metaInfo().action;
// NOTE: The non-standard capitalization here is used as a performance
// enhancement and to reduce allocations in json.zig. These fields are
// not (nor are they ever intended to be) exposed outside this codebase
@ -169,8 +139,5 @@ fn FullResponse(comptime request: anytype) type {
};
}
fn Response(comptime request: anytype) type {
const action_info = actionForRequest(request);
const service = @field(services, action_info.service);
const action = @field(service, action_info.action);
return action.Response;
return request.metaInfo().action.Response;
}

View File

@ -1,108 +1,50 @@
const std = @import("std");
const models = @import("models.zig");
const json = @import("json.zig");
const expectEqualStrings = std.testing.expectEqualStrings;
// TODO: Make generic
fn Services() type {
const types = [_]type{
Service("sts"),
// 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;
for (fields) |*item, i| {
const import = @field(@import("codegen/models/" ++ service_list[i].file_name), service_list[i].export_name);
item.* = .{
.name = service_list[i].name,
.field_type = @TypeOf(import),
.default_value = import,
.is_comptime = false,
.alignment = 0,
};
}
// finally, generate the type
return @Type(.{
.Struct = .{
.layout = .Auto,
.fields = &[_]std.builtin.TypeInfo.StructField{
.{
.name = "sts",
.field_type = types[0],
.default_value = new(types[0]),
.is_comptime = false,
.alignment = 0,
},
},
.fields = &fields,
.decls = &[_]std.builtin.TypeInfo.Declaration{},
.is_tuple = false,
},
});
}
fn ServiceActionResponse(comptime service: []const u8, comptime action: []const u8) type {
if (std.mem.eql(u8, service, "sts") and std.mem.eql(u8, action, "get_caller_identity")) {
return struct {
arn: []const u8,
user_id: []const u8,
account: []const u8,
};
}
unreachable;
}
fn ServiceAction(comptime service: []const u8, comptime action: []const u8) type {
if (std.mem.eql(u8, service, "sts") and std.mem.eql(u8, action, "get_caller_identity")) {
return @Type(.{
.Struct = .{
.layout = .Auto,
.fields = &[_]std.builtin.TypeInfo.StructField{
.{
.name = "Request",
.field_type = type,
.default_value = struct {},
.is_comptime = false,
.alignment = 0,
},
.{
.name = "action_name",
.field_type = @TypeOf("GetCallerIdentity"),
.default_value = "GetCallerIdentity",
.is_comptime = false,
.alignment = 0,
},
.{
.name = "Response",
.field_type = type,
.default_value = ServiceActionResponse("sts", "get_caller_identity"),
.is_comptime = false,
.alignment = 0,
},
},
.decls = &[_]std.builtin.TypeInfo.Declaration{},
.is_tuple = false,
},
});
}
unreachable;
}
// 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 new(comptime T: type) T {
return T{};
test "services includes sts" {
expectEqualStrings("2011-06-15", services.sts.version);
}
fn Service(comptime service: []const u8) type {
if (std.mem.eql(u8, "sts", service)) {
return @Type(.{
.Struct = .{
.layout = .Auto,
.fields = &[_]std.builtin.TypeInfo.StructField{
.{
.name = "version",
.field_type = @TypeOf("2011-06-15"),
.default_value = "2011-06-15",
.is_comptime = false,
.alignment = 0,
},
.{
.name = "get_caller_identity",
.field_type = ServiceAction("sts", "get_caller_identity"),
.default_value = new(ServiceAction("sts", "get_caller_identity")),
.is_comptime = false,
.alignment = 0,
},
},
.decls = &[_]std.builtin.TypeInfo.Declaration{},
.is_tuple = false,
},
});
test "sts includes get_caller_identity" {
expectEqualStrings("GetCallerIdentity", services.sts.get_caller_identity.action_name);
}
unreachable;
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);
// expectEqualStrings("GetCallerIdentity", metadata.action.action_name);
}
// End code "generation" prototype