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) { pub fn call(self: Self, comptime request: anytype, options: Options) !FullResponse(request) {
const action_info = actionForRequest(request); // every codegenned request object includes a metaInfo function to get
// This is true weirdness, but we are running into compiler bugs. Touch only if // pointers to service and action
// prepared... const meta_info = request.metaInfo();
const service = @field(services, action_info.service); const service = meta_info.service;
const action = @field(service, action_info.action); const action = meta_info.action;
const R = Response(request); const R = Response(request);
const FullR = FullResponse(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("version {s}", .{service.version});
log.debug("action {s}", .{action.action_name}); log.debug("action {s}", .{action.action_name});
const response = try self.aws_http.callApi(action_info.service, service.version, action.action_name, options); 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 { fn ServerResponse(comptime request: anytype) type {
const T = Response(request); const T = Response(request);
const action_info = actionForRequest(request); const action = request.metaInfo().action;
const service = @field(services, action_info.service);
const action = @field(service, action_info.action);
// NOTE: The non-standard capitalization here is used as a performance // NOTE: The non-standard capitalization here is used as a performance
// enhancement and to reduce allocations in json.zig. These fields are // enhancement and to reduce allocations in json.zig. These fields are
// not (nor are they ever intended to be) exposed outside this codebase // 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 { fn Response(comptime request: anytype) type {
const action_info = actionForRequest(request); return request.metaInfo().action.Response;
const service = @field(services, action_info.service);
const action = @field(service, action_info.action);
return action.Response;
} }

View File

@ -1,108 +1,50 @@
const std = @import("std"); const std = @import("std");
const models = @import("models.zig"); const expectEqualStrings = std.testing.expectEqualStrings;
const json = @import("json.zig");
// TODO: Make generic // TODO: Make generic
fn Services() type { fn Services() type {
const types = [_]type{ // This service list can be imported from a master file of all services
Service("sts"), // 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(.{ return @Type(.{
.Struct = .{ .Struct = .{
.layout = .Auto, .layout = .Auto,
.fields = &[_]std.builtin.TypeInfo.StructField{ .fields = &fields,
.{
.name = "sts",
.field_type = types[0],
.default_value = new(types[0]),
.is_comptime = false,
.alignment = 0,
},
},
.decls = &[_]std.builtin.TypeInfo.Declaration{}, .decls = &[_]std.builtin.TypeInfo.Declaration{},
.is_tuple = false, .is_tuple = false,
}, },
}); });
} }
// TODO: One constant to rule them all is a bit much. We can keep this
fn ServiceActionResponse(comptime service: []const u8, comptime action: []const u8) type { // in the back pocket, but let's move to a factory style getService("blah");
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;
}
pub const services = Services(){}; pub const services = Services(){};
fn new(comptime T: type) T { test "services includes sts" {
return T{}; expectEqualStrings("2011-06-15", services.sts.version);
} }
fn Service(comptime service: []const u8) type { test "sts includes get_caller_identity" {
if (std.mem.eql(u8, "sts", service)) { expectEqualStrings("GetCallerIdentity", services.sts.get_caller_identity.action_name);
return @Type(.{ }
.Struct = .{ test "can get service and action name from request" {
.layout = .Auto, // get request object. This call doesn't have parameters
.fields = &[_]std.builtin.TypeInfo.StructField{ const req = services.sts.get_caller_identity.Request{};
.{ // const metadata = @TypeOf(req).metaInfo();
.name = "version", const metadata = req.metaInfo();
.field_type = @TypeOf("2011-06-15"), expectEqualStrings("2011-06-15", metadata.service.version);
.default_value = "2011-06-15", // expectEqualStrings("GetCallerIdentity", metadata.action.action_name);
.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,
},
});
}
unreachable;
} }
// End code "generation" prototype