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/)
|
[![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
|
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
|
services only support XML, and more work is needed to parse and integrate
|
||||||
errors while incorporating the XML parser in conjunction with a process
|
type hydration from the base parsing. S3 also requires some plumbing tweaks
|
||||||
to fill the types. S3 also requires some plumbing tweaks in the signature
|
in the signature calculation. Examples of usage are in src/main.zig.
|
||||||
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)
|
Current executable size for the demo is 953k (90k of which is the AWS PEM file)
|
||||||
after compiling with -Drelease-safe and
|
after compiling with -Drelease-safe and
|
||||||
|
@ -53,11 +52,7 @@ implemented.
|
||||||
|
|
||||||
TODO List:
|
TODO List:
|
||||||
|
|
||||||
* To work around compiler issues, the best option may be to convert from
|
* Complete integration of Xml responses with remaining code base
|
||||||
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.
|
|
||||||
* Implement [AWS restXml protocol](https://awslabs.github.io/smithy/1.0/spec/aws/aws-restxml-protocol.html).
|
* 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.
|
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).
|
* 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 std = @import("std");
|
||||||
const xml = @import("xml.zig");
|
const xml = @import("xml.zig");
|
||||||
|
|
||||||
|
const log = std.log.scoped(.xml_shaper);
|
||||||
|
|
||||||
fn Parsed(comptime T: type) type {
|
fn Parsed(comptime T: type) type {
|
||||||
return struct {
|
return struct {
|
||||||
allocator: std.mem.Allocator,
|
allocator: std.mem.Allocator,
|
||||||
|
@ -42,6 +44,8 @@ fn Parsed(comptime T: type) type {
|
||||||
.Many => {},
|
.Many => {},
|
||||||
.C => {},
|
.C => {},
|
||||||
.Slice => {
|
.Slice => {
|
||||||
|
for (obj) |child|
|
||||||
|
deinitObject(allocator, child);
|
||||||
allocator.free(obj);
|
allocator.free(obj);
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -175,11 +179,13 @@ fn parseInternal(comptime T: type, element: *xml.Element, options: ParseOptions)
|
||||||
// return error.MoreElementsThanFields;
|
// return error.MoreElementsThanFields;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
log.debug("Processing fields in struct: {s}", .{@typeName(T)});
|
||||||
inline for (struct_info.fields) |field, i| {
|
inline for (struct_info.fields) |field, i| {
|
||||||
var name = field.name;
|
var name = field.name;
|
||||||
|
var found_value = false;
|
||||||
if (comptime std.meta.trait.hasFn("fieldNameFor")(T))
|
if (comptime std.meta.trait.hasFn("fieldNameFor")(T))
|
||||||
name = r.fieldNameFor(field.name);
|
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);
|
var iterator = element.findChildrenByTag(name);
|
||||||
if (options.match_predicate) |predicate| {
|
if (options.match_predicate) |predicate| {
|
||||||
iterator.predicate = predicate;
|
iterator.predicate = predicate;
|
||||||
|
@ -193,14 +199,17 @@ fn parseInternal(comptime T: type, element: *xml.Element, options: ParseOptions)
|
||||||
// return error.UnexpectedValue;
|
// return error.UnexpectedValue;
|
||||||
// }
|
// }
|
||||||
// } else {
|
// } else {
|
||||||
|
log.debug("Found child element {s}", .{child.tag});
|
||||||
@field(r, field.name) = try parseInternal(field.field_type, child, options);
|
@field(r, field.name) = try parseInternal(field.field_type, child, options);
|
||||||
fields_seen[i] = true;
|
fields_seen[i] = true;
|
||||||
fields_set = fields_set + 1;
|
fields_set = fields_set + 1;
|
||||||
// }
|
found_value = true;
|
||||||
|
|
||||||
} else {
|
|
||||||
return error.NoValueForField;
|
|
||||||
}
|
}
|
||||||
|
// 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)
|
if (fields_set != struct_info.fields.len)
|
||||||
return error.FieldElementMismatch; // see fields_seen for details
|
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>
|
// <Item>bar</Item>
|
||||||
// <Items>
|
// <Items>
|
||||||
if (ptr_info.child != u8) {
|
if (ptr_info.child != u8) {
|
||||||
std.log.debug("ptr_info.child == {s}", .{@typeName(ptr_info.child)});
|
log.debug("type = {s}, ptr_info.child == {s}, element = {s}", .{ @typeName(T), @typeName(ptr_info.child), element.tag });
|
||||||
const children = try allocator.alloc(ptr_info.child, element.children.items.len);
|
var iterator = element.elements();
|
||||||
var inx: usize = 0;
|
var children = std.ArrayList(ptr_info.child).init(allocator);
|
||||||
while (inx < children.len) {
|
defer children.deinit();
|
||||||
children[inx] = try parseInternal(ptr_info.child, element.children.items[inx].Element, options);
|
while (iterator.next()) |child_element| {
|
||||||
inx += 1;
|
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);
|
return try allocator.dupe(u8, element.children.items[0].CharData);
|
||||||
},
|
},
|
||||||
|
@ -390,13 +408,32 @@ test "can parse an optional boolean type" {
|
||||||
\\ <fooBar>true</fooBar>
|
\\ <fooBar>true</fooBar>
|
||||||
\\</Example>
|
\\</Example>
|
||||||
;
|
;
|
||||||
const Example = struct {
|
const ExampleDoesNotMatter = struct {
|
||||||
foo_bar: ?bool = null,
|
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();
|
defer parsed_data.deinit();
|
||||||
try testing.expectEqual(@as(?bool, true), parsed_data.parsed_value.foo_bar);
|
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" {
|
test "can parse a nested type" {
|
||||||
const allocator = std.testing.allocator;
|
const allocator = std.testing.allocator;
|
||||||
const data =
|
const data =
|
||||||
|
@ -416,6 +453,28 @@ test "can parse a nested type" {
|
||||||
defer parsed_data.deinit();
|
defer parsed_data.deinit();
|
||||||
try testing.expectEqualStrings("baz", parsed_data.parsed_value.foo.bar);
|
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 {
|
const service_metadata: struct {
|
||||||
version: []const u8 = "2016-11-15",
|
version: []const u8 = "2016-11-15",
|
||||||
|
@ -450,9 +509,11 @@ const describe_regions: struct {
|
||||||
},
|
},
|
||||||
Response: type = struct {
|
Response: type = struct {
|
||||||
regions: ?[]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,
|
endpoint: ?[]const u8 = null,
|
||||||
region_name: ?[]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 {
|
pub fn fieldNameFor(_: @This(), comptime field_name: []const u8) []const u8 {
|
||||||
const mappings = .{
|
const mappings = .{
|
||||||
|
@ -473,35 +534,31 @@ const describe_regions: struct {
|
||||||
},
|
},
|
||||||
} = .{};
|
} = .{};
|
||||||
|
|
||||||
// This test results in "broken LLVM module found: Operand is null"
|
test "can parse something serious" {
|
||||||
// br i1 %120, label %ErrRetReturn12, <null operand!>, !dbg !10637
|
|
||||||
//
|
|
||||||
// This is a bug in the Zig compiler.
|
|
||||||
// test "can parse something serious" {
|
|
||||||
// std.testing.log_level = .debug;
|
// std.testing.log_level = .debug;
|
||||||
// std.log.debug("", .{});
|
log.debug("", .{});
|
||||||
//
|
|
||||||
// const allocator = std.testing.allocator;
|
const allocator = std.testing.allocator;
|
||||||
// const data =
|
const data =
|
||||||
// \\<?xml version="1.0" encoding="UTF-8"?>
|
\\<?xml version="1.0" encoding="UTF-8"?>
|
||||||
// \\<DescribeRegionsResponse xmlns="http://ec2.amazonaws.com/doc/2016-11-15/">
|
\\<DescribeRegionsResponse xmlns="http://ec2.amazonaws.com/doc/2016-11-15/">
|
||||||
// \\ <requestId>8d6bfc99-978b-4146-ba23-2e5fe5b65406</requestId>
|
\\ <requestId>8d6bfc99-978b-4146-ba23-2e5fe5b65406</requestId>
|
||||||
// \\ <regionInfo>
|
\\ <regionInfo>
|
||||||
// \\ <item>
|
\\ <item>
|
||||||
// \\ <regionName>eu-north-1</regionName>
|
\\ <regionName>eu-north-1</regionName>
|
||||||
// \\ <regionEndpoint>ec2.eu-north-1.amazonaws.com</regionEndpoint>
|
\\ <regionEndpoint>ec2.eu-north-1.amazonaws.com</regionEndpoint>
|
||||||
// \\ <optInStatus>opt-in-not-required</optInStatus>
|
\\ </item>
|
||||||
// \\ </item>
|
\\ <item>
|
||||||
// \\ <item>
|
\\ <regionName>ap-south-1</regionName>
|
||||||
// \\ <regionName>ap-south-1</regionName>
|
\\ <regionEndpoint>ec2.ap-south-1.amazonaws.com</regionEndpoint>
|
||||||
// \\ <regionEndpoint>ec2.ap-south-1.amazonaws.com</regionEndpoint>
|
\\ </item>
|
||||||
// \\ <optInStatus>opt-in-not-required</optInStatus>
|
\\ </regionInfo>
|
||||||
// \\ </item>
|
\\</DescribeRegionsResponse>
|
||||||
// \\ </regionInfo>
|
;
|
||||||
// \\</DescribeRegionsResponse>
|
// const ServerResponse = struct { DescribeRegionsResponse: describe_regions.Response, };
|
||||||
// ;
|
const parsed_data = try parse(describe_regions.Response, data, .{ .allocator = allocator });
|
||||||
// const parsed_data = try parse(describe_regions.Response, data, .{ .allocator = allocator });
|
defer parsed_data.deinit();
|
||||||
// defer parsed_data.deinit();
|
try testing.expect(parsed_data.parsed_value.regions != null);
|
||||||
// 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("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