work around zig compiler bug
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
parent
8e9b85b35f
commit
0706dd5e6f
13
README.md
13
README.md
|
@ -3,10 +3,9 @@
|
|||
[![Build Status](https://drone.lerch.org/api/badges/lobo/aws-sdk-for-zig/status.svg?ref=refs/heads/master)](https://drone.lerch.org/api/badges/lobo/aws-sdk-for-zig/)
|
||||
|
||||
This SDK currently supports all AWS services except EC2 and S3. These two
|
||||
services only support XML, and zig 0.9.0 and master both trigger compile
|
||||
errors while incorporating the XML parser in conjunction with a process
|
||||
to fill the types. S3 also requires some plumbing tweaks in the signature
|
||||
calculation. Examples of usage are in src/main.zig.
|
||||
services only support XML, and more work is needed to parse and integrate
|
||||
type hydration from the base parsing. S3 also requires some plumbing tweaks
|
||||
in the signature calculation. Examples of usage are in src/main.zig.
|
||||
|
||||
Current executable size for the demo is 953k (90k of which is the AWS PEM file)
|
||||
after compiling with -Drelease-safe and
|
||||
|
@ -53,11 +52,7 @@ implemented.
|
|||
|
||||
TODO List:
|
||||
|
||||
* To work around compiler issues, the best option may be to convert from
|
||||
Xml to json, then parse from there. This will be pursued first. It may need
|
||||
to wait for zig 0.10.0 when self-hosted compiler is likely to be completed
|
||||
(zig 0.10.0 eta May 2022) discovered. If we need to wait, S3, EC2 and other
|
||||
restXml protocols will be blocked.
|
||||
* Complete integration of Xml responses with remaining code base
|
||||
* Implement [AWS restXml protocol](https://awslabs.github.io/smithy/1.0/spec/aws/aws-restxml-protocol.html).
|
||||
Includes S3. Total service count 4. This may be blocked due to the same issue as EC2.
|
||||
* Implement [AWS EC2 query protocol](https://awslabs.github.io/smithy/1.0/spec/aws/aws-ec2-query-protocol.html).
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
const std = @import("std");
|
||||
const xml = @import("xml.zig");
|
||||
|
||||
const log = std.log.scoped(.xml_shaper);
|
||||
|
||||
fn Parsed(comptime T: type) type {
|
||||
return struct {
|
||||
allocator: std.mem.Allocator,
|
||||
|
@ -42,6 +44,8 @@ fn Parsed(comptime T: type) type {
|
|||
.Many => {},
|
||||
.C => {},
|
||||
.Slice => {
|
||||
for (obj) |child|
|
||||
deinitObject(allocator, child);
|
||||
allocator.free(obj);
|
||||
},
|
||||
}
|
||||
|
@ -175,11 +179,13 @@ fn parseInternal(comptime T: type, element: *xml.Element, options: ParseOptions)
|
|||
// return error.MoreElementsThanFields;
|
||||
// }
|
||||
|
||||
log.debug("Processing fields in struct: {s}", .{@typeName(T)});
|
||||
inline for (struct_info.fields) |field, i| {
|
||||
var name = field.name;
|
||||
var found_value = false;
|
||||
if (comptime std.meta.trait.hasFn("fieldNameFor")(T))
|
||||
name = r.fieldNameFor(field.name);
|
||||
std.log.debug("Field name: {s}, Element: {s}, Adjusted field name: {s}\n", .{ field.name, element.tag, name });
|
||||
log.debug("Field name: {s}, Element: {s}, Adjusted field name: {s}", .{ field.name, element.tag, name });
|
||||
var iterator = element.findChildrenByTag(name);
|
||||
if (options.match_predicate) |predicate| {
|
||||
iterator.predicate = predicate;
|
||||
|
@ -193,14 +199,17 @@ fn parseInternal(comptime T: type, element: *xml.Element, options: ParseOptions)
|
|||
// return error.UnexpectedValue;
|
||||
// }
|
||||
// } else {
|
||||
log.debug("Found child element {s}", .{child.tag});
|
||||
@field(r, field.name) = try parseInternal(field.field_type, child, options);
|
||||
fields_seen[i] = true;
|
||||
fields_set = fields_set + 1;
|
||||
// }
|
||||
|
||||
} else {
|
||||
return error.NoValueForField;
|
||||
found_value = true;
|
||||
}
|
||||
// Using this else clause breaks zig, so we'll use a boolean instead
|
||||
if (!found_value) return error.NoValueForField;
|
||||
// } else {
|
||||
// return error.NoValueForField;
|
||||
// }
|
||||
}
|
||||
if (fields_set != struct_info.fields.len)
|
||||
return error.FieldElementMismatch; // see fields_seen for details
|
||||
|
@ -259,14 +268,23 @@ fn parseInternal(comptime T: type, element: *xml.Element, options: ParseOptions)
|
|||
// <Item>bar</Item>
|
||||
// <Items>
|
||||
if (ptr_info.child != u8) {
|
||||
std.log.debug("ptr_info.child == {s}", .{@typeName(ptr_info.child)});
|
||||
const children = try allocator.alloc(ptr_info.child, element.children.items.len);
|
||||
var inx: usize = 0;
|
||||
while (inx < children.len) {
|
||||
children[inx] = try parseInternal(ptr_info.child, element.children.items[inx].Element, options);
|
||||
inx += 1;
|
||||
log.debug("type = {s}, ptr_info.child == {s}, element = {s}", .{ @typeName(T), @typeName(ptr_info.child), element.tag });
|
||||
var iterator = element.elements();
|
||||
var children = std.ArrayList(ptr_info.child).init(allocator);
|
||||
defer children.deinit();
|
||||
while (iterator.next()) |child_element| {
|
||||
try children.append(try parseInternal(ptr_info.child, child_element, options));
|
||||
}
|
||||
return children;
|
||||
return children.toOwnedSlice();
|
||||
// var inx: usize = 0;
|
||||
// while (inx < children.len) {
|
||||
// switch (element.children.items[inx]) {
|
||||
// .Element => children[inx] = try parseInternal(ptr_info.child, element.children.items[inx].Element, options),
|
||||
// .CharData => children[inx] = try allocator.dupe(u8, element.children.items[inx].CharData),
|
||||
// .Comment => children[inx] = try allocator.dupe(u8, element.children.items[inx].Comment), // This might be an error...
|
||||
// }
|
||||
// inx += 1;
|
||||
// }
|
||||
}
|
||||
return try allocator.dupe(u8, element.children.items[0].CharData);
|
||||
},
|
||||
|
@ -390,13 +408,32 @@ test "can parse an optional boolean type" {
|
|||
\\ <fooBar>true</fooBar>
|
||||
\\</Example>
|
||||
;
|
||||
const Example = struct {
|
||||
const ExampleDoesNotMatter = struct {
|
||||
foo_bar: ?bool = null,
|
||||
};
|
||||
const parsed_data = try parse(Example, data, .{ .allocator = allocator, .match_predicate = fuzzyEqual });
|
||||
const parsed_data = try parse(ExampleDoesNotMatter, data, .{ .allocator = allocator, .match_predicate = fuzzyEqual });
|
||||
defer parsed_data.deinit();
|
||||
try testing.expectEqual(@as(?bool, true), parsed_data.parsed_value.foo_bar);
|
||||
}
|
||||
|
||||
// This is the simplest test so far that breaks zig
|
||||
test "can parse a boolean type (two fields)" {
|
||||
const allocator = std.testing.allocator;
|
||||
const data =
|
||||
\\<?xml version="1.0" encoding="UTF-8"?>
|
||||
\\<Example xmlns="http://example.example.com/doc/2016-11-15/">
|
||||
\\ <fooBar>true</fooBar>
|
||||
\\ <fooBaz>true</fooBaz>
|
||||
\\</Example>
|
||||
;
|
||||
const ExampleDoesNotMatter = struct {
|
||||
foo_bar: bool,
|
||||
foo_baz: bool,
|
||||
};
|
||||
const parsed_data = try parse(ExampleDoesNotMatter, data, .{ .allocator = allocator, .match_predicate = fuzzyEqual });
|
||||
defer parsed_data.deinit();
|
||||
try testing.expectEqual(@as(bool, true), parsed_data.parsed_value.foo_bar);
|
||||
}
|
||||
test "can parse a nested type" {
|
||||
const allocator = std.testing.allocator;
|
||||
const data =
|
||||
|
@ -416,6 +453,28 @@ test "can parse a nested type" {
|
|||
defer parsed_data.deinit();
|
||||
try testing.expectEqualStrings("baz", parsed_data.parsed_value.foo.bar);
|
||||
}
|
||||
test "can parse a nested type - two fields" {
|
||||
const allocator = std.testing.allocator;
|
||||
const data =
|
||||
\\<?xml version="1.0" encoding="UTF-8"?>
|
||||
\\<Example xmlns="http://example.example.com/doc/2016-11-15/">
|
||||
\\ <foo>
|
||||
\\ <bar>baz</bar>
|
||||
\\ <qux>baz</qux>
|
||||
\\ </foo>
|
||||
\\</Example>
|
||||
;
|
||||
const Example = struct {
|
||||
foo: struct {
|
||||
bar: []const u8,
|
||||
qux: []const u8,
|
||||
},
|
||||
};
|
||||
const parsed_data = try parse(Example, data, .{ .allocator = allocator, .match_predicate = fuzzyEqual });
|
||||
defer parsed_data.deinit();
|
||||
try testing.expectEqualStrings("baz", parsed_data.parsed_value.foo.bar);
|
||||
try testing.expectEqualStrings("baz", parsed_data.parsed_value.foo.qux);
|
||||
}
|
||||
|
||||
const service_metadata: struct {
|
||||
version: []const u8 = "2016-11-15",
|
||||
|
@ -450,9 +509,11 @@ const describe_regions: struct {
|
|||
},
|
||||
Response: type = struct {
|
||||
regions: ?[]struct {
|
||||
// Having two of these causes the zig compiler bug
|
||||
// Only one of them works fine. This leads me to believe that
|
||||
// it has something to do with the inline for
|
||||
endpoint: ?[]const u8 = null,
|
||||
region_name: ?[]const u8 = null,
|
||||
opt_in_status: ?[]const u8 = null,
|
||||
|
||||
pub fn fieldNameFor(_: @This(), comptime field_name: []const u8) []const u8 {
|
||||
const mappings = .{
|
||||
|
@ -473,35 +534,31 @@ const describe_regions: struct {
|
|||
},
|
||||
} = .{};
|
||||
|
||||
// This test results in "broken LLVM module found: Operand is null"
|
||||
// br i1 %120, label %ErrRetReturn12, <null operand!>, !dbg !10637
|
||||
//
|
||||
// This is a bug in the Zig compiler.
|
||||
// test "can parse something serious" {
|
||||
test "can parse something serious" {
|
||||
// std.testing.log_level = .debug;
|
||||
// std.log.debug("", .{});
|
||||
//
|
||||
// const allocator = std.testing.allocator;
|
||||
// const data =
|
||||
// \\<?xml version="1.0" encoding="UTF-8"?>
|
||||
// \\<DescribeRegionsResponse xmlns="http://ec2.amazonaws.com/doc/2016-11-15/">
|
||||
// \\ <requestId>8d6bfc99-978b-4146-ba23-2e5fe5b65406</requestId>
|
||||
// \\ <regionInfo>
|
||||
// \\ <item>
|
||||
// \\ <regionName>eu-north-1</regionName>
|
||||
// \\ <regionEndpoint>ec2.eu-north-1.amazonaws.com</regionEndpoint>
|
||||
// \\ <optInStatus>opt-in-not-required</optInStatus>
|
||||
// \\ </item>
|
||||
// \\ <item>
|
||||
// \\ <regionName>ap-south-1</regionName>
|
||||
// \\ <regionEndpoint>ec2.ap-south-1.amazonaws.com</regionEndpoint>
|
||||
// \\ <optInStatus>opt-in-not-required</optInStatus>
|
||||
// \\ </item>
|
||||
// \\ </regionInfo>
|
||||
// \\</DescribeRegionsResponse>
|
||||
// ;
|
||||
// const parsed_data = try parse(describe_regions.Response, data, .{ .allocator = allocator });
|
||||
// defer parsed_data.deinit();
|
||||
// try testing.expect(parsed_data.parsed_value.regions != null);
|
||||
// // try testing.expectEqualStrings("eu-north-1", parsed_data.parsed_value.regions.?[0].region_name.?);
|
||||
// }
|
||||
log.debug("", .{});
|
||||
|
||||
const allocator = std.testing.allocator;
|
||||
const data =
|
||||
\\<?xml version="1.0" encoding="UTF-8"?>
|
||||
\\<DescribeRegionsResponse xmlns="http://ec2.amazonaws.com/doc/2016-11-15/">
|
||||
\\ <requestId>8d6bfc99-978b-4146-ba23-2e5fe5b65406</requestId>
|
||||
\\ <regionInfo>
|
||||
\\ <item>
|
||||
\\ <regionName>eu-north-1</regionName>
|
||||
\\ <regionEndpoint>ec2.eu-north-1.amazonaws.com</regionEndpoint>
|
||||
\\ </item>
|
||||
\\ <item>
|
||||
\\ <regionName>ap-south-1</regionName>
|
||||
\\ <regionEndpoint>ec2.ap-south-1.amazonaws.com</regionEndpoint>
|
||||
\\ </item>
|
||||
\\ </regionInfo>
|
||||
\\</DescribeRegionsResponse>
|
||||
;
|
||||
// const ServerResponse = struct { DescribeRegionsResponse: describe_regions.Response, };
|
||||
const parsed_data = try parse(describe_regions.Response, data, .{ .allocator = allocator });
|
||||
defer parsed_data.deinit();
|
||||
try testing.expect(parsed_data.parsed_value.regions != null);
|
||||
try testing.expectEqualStrings("eu-north-1", parsed_data.parsed_value.regions.?[0].region_name.?);
|
||||
try testing.expectEqualStrings("ec2.eu-north-1.amazonaws.com", parsed_data.parsed_value.regions.?[0].endpoint.?);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user