Compare commits

..

No commits in common. "master" and "zig-develop" have entirely different histories.

7 changed files with 60 additions and 116 deletions

8
.envrc
View file

@ -1,8 +0,0 @@
# vi: ft=sh
# shellcheck shell=bash
if ! has zvm_direnv_version || ! zvm_direnv_version 2.0.0; then
source_url "https://git.lerch.org/lobo/zvm-direnv/raw/tag/2.0.0/direnvrc" "sha256-8Umzxj32hFU6G0a7Wrq0KTNDQ8XEuje2A3s2ljh/hFY="
fi
use zig 0.14.0

View file

@ -9,20 +9,17 @@ jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Check out repository code - uses: actions/checkout@v4
uses: actions/checkout@v4 - uses: elerch/setup-zig@v3
- name: Setup Zig
uses: https://github.com/mlugg/setup-zig@v2.0.5
with: with:
version: 0.15.1 version: 0.13.0
- name: Restore Zig caches - uses: elerch/zig-action-cache@v1.1.6
uses: https://github.com/Hanaasagi/zig-action-cache@3954aae427f8b05914e08dfd79f15e1f2e435929
- name: Build project - name: Build project
run: zig build --summary all run: zig build --summary all
- name: Run tests - name: Run tests
run: zig build test --summary all run: zig build test --summary all
- name: Notify - name: Notify
uses: https://git.lerch.org/lobo/action-notify-ntfy@v2 uses: elerch/action-notify-ntfy@v2.github
if: always() && env.GITEA_ACTIONS == 'true' if: always() && env.GITEA_ACTIONS == 'true'
with: with:
host: ${{ secrets.NTFY_HOST }} host: ${{ secrets.NTFY_HOST }}

View file

@ -1,4 +0,0 @@
[tools]
pre-commit = "latest"
"ubi:DonIsaac/zlint" = "latest"
zig = "0.15.1"

View file

@ -2,6 +2,7 @@ AWS Smithy Model for Zig
======================== ========================
This holds a smithy project for zig, extracted from the AWS SDK for Zig for This holds a smithy project for zig, extracted from the AWS SDK for Zig for
use as a package. Built for zig 0.15.1 use as a package. Built for zig 0.11
TODO: complete the readme TODO: complete the readme
TODO: CI

View file

@ -15,13 +15,13 @@ pub fn build(b: *std.Build) void {
// set a preferred release mode, allowing the user to decide how to optimize. // set a preferred release mode, allowing the user to decide how to optimize.
const optimize = b.standardOptimizeOption(.{}); const optimize = b.standardOptimizeOption(.{});
const lib = b.addLibrary(.{ const lib = b.addStaticLibrary(.{
.name = "smithy", .name = "smithy",
.root_module = b.addModule("smithy", .{ // In this case the main source file is merely a path, however, in more
.root_source_file = b.path("src/smithy.zig"), // complicated build scripts, this could be a generated file.
.target = target, .root_source_file = b.path("src/smithy.zig"),
.optimize = optimize, .target = target,
}), .optimize = optimize,
}); });
// This declares intent for the library to be installed into the standard // This declares intent for the library to be installed into the standard
@ -29,16 +29,17 @@ pub fn build(b: *std.Build) void {
// running `zig build`). // running `zig build`).
b.installArtifact(lib); b.installArtifact(lib);
lib.root_module.addImport("smithy", lib.root_module); const module = b.addModule("smithy", .{
.root_source_file = b.path("src/smithy.zig"),
});
lib.root_module.addImport("smithy", module);
// Creates a step for unit testing. This only builds the test executable // Creates a step for unit testing. This only builds the test executable
// but does not run it. // but does not run it.
const main_tests = b.addTest(.{ const main_tests = b.addTest(.{
.root_module = b.addModule("smithy-test", .{ .root_source_file = b.path("src/smithy.zig"),
.root_source_file = b.path("src/smithy.zig"), .target = target,
.target = target, .optimize = optimize,
.optimize = optimize,
}),
}); });
const run_main_tests = b.addRunArtifact(main_tests); const run_main_tests = b.addRunArtifact(main_tests);
@ -49,7 +50,3 @@ pub fn build(b: *std.Build) void {
const test_step = b.step("test", "Run library tests"); const test_step = b.step("test", "Run library tests");
test_step.dependOn(&run_main_tests.step); test_step.dependOn(&run_main_tests.step);
} }
// define AwsProtocol to support usage within build.zig as an import
const smithy = @import("src/smithy.zig");
pub const AwsProtocol = smithy.AwsProtocol;

View file

@ -1,14 +1,14 @@
.{ .{
.name = .smithy, .name = "smithy",
.version = "1.0.0", .version = "1.0.0",
.fingerprint = 0xccfd3d0281810cb8,
.paths = .{ .paths = .{
"build.zig", "build.zig",
"build.zig.zon", "build.zig.zon",
"src", "src",
"README.md", "README.md",
"LICENSE", "LICENSE",
}, },
.dependencies = .{}, .dependencies = .{
},
} }

View file

@ -235,7 +235,7 @@ const Shape = union(ShapeType) {
traits: []Trait, traits: []Trait,
}, },
service: struct { service: struct {
version: ?[]const u8, // A version is optional in Smithy. https://smithy.io/2.0/spec/service-types.html version: []const u8,
operations: [][]const u8, operations: [][]const u8,
resources: [][]const u8, resources: [][]const u8,
traits: []Trait, traits: []Trait,
@ -285,11 +285,11 @@ pub fn parse(allocator: std.mem.Allocator, json_model: []const u8) !Smithy {
// list must be deinitialized by caller // list must be deinitialized by caller
fn shapes(allocator: std.mem.Allocator, map: anytype) ![]ShapeInfo { fn shapes(allocator: std.mem.Allocator, map: anytype) ![]ShapeInfo {
var list = try std.ArrayList(ShapeInfo).initCapacity(allocator, map.count()); var list = try std.ArrayList(ShapeInfo).initCapacity(allocator, map.count());
defer list.deinit(allocator); defer list.deinit();
var iterator = map.iterator(); var iterator = map.iterator();
while (iterator.next()) |kv| { while (iterator.next()) |kv| {
const id_info = try parseId(kv.key_ptr.*); const id_info = try parseId(kv.key_ptr.*);
list.appendAssumeCapacity(.{ try list.append(.{
.id = id_info.id, .id = id_info.id,
.namespace = id_info.namespace, .namespace = id_info.namespace,
.name = id_info.name, .name = id_info.name,
@ -304,7 +304,7 @@ fn shapes(allocator: std.mem.Allocator, map: anytype) ![]ShapeInfo {
// https://awslabs.github.io/smithy/1.0/spec/core/model.html#simple-types // https://awslabs.github.io/smithy/1.0/spec/core/model.html#simple-types
// But I don't see it in the spec. We might need to preload other similar // But I don't see it in the spec. We might need to preload other similar
// simple types? // simple types?
try list.append(allocator, .{ try list.append(.{
.id = "smithy.api#String", .id = "smithy.api#String",
.namespace = "smithy.api", .namespace = "smithy.api",
.name = "String", .name = "String",
@ -315,7 +315,7 @@ fn shapes(allocator: std.mem.Allocator, map: anytype) ![]ShapeInfo {
}, },
}, },
}); });
try list.append(allocator, .{ try list.append(.{
.id = "smithy.api#Boolean", .id = "smithy.api#Boolean",
.namespace = "smithy.api", .namespace = "smithy.api",
.name = "Boolean", .name = "Boolean",
@ -326,7 +326,7 @@ fn shapes(allocator: std.mem.Allocator, map: anytype) ![]ShapeInfo {
}, },
}, },
}); });
try list.append(allocator, .{ try list.append(.{
.id = "smithy.api#Integer", .id = "smithy.api#Integer",
.namespace = "smithy.api", .namespace = "smithy.api",
.name = "Integer", .name = "Integer",
@ -337,7 +337,7 @@ fn shapes(allocator: std.mem.Allocator, map: anytype) ![]ShapeInfo {
}, },
}, },
}); });
try list.append(allocator, .{ try list.append(.{
.id = "smithy.api#Double", .id = "smithy.api#Double",
.namespace = "smithy.api", .namespace = "smithy.api",
.name = "Double", .name = "Double",
@ -348,7 +348,7 @@ fn shapes(allocator: std.mem.Allocator, map: anytype) ![]ShapeInfo {
}, },
}, },
}); });
try list.append(allocator, .{ try list.append(.{
.id = "smithy.api#Timestamp", .id = "smithy.api#Timestamp",
.namespace = "smithy.api", .namespace = "smithy.api",
.name = "Timestamp", .name = "Timestamp",
@ -359,7 +359,7 @@ fn shapes(allocator: std.mem.Allocator, map: anytype) ![]ShapeInfo {
}, },
}, },
}); });
try list.append(allocator, .{ try list.append(.{
.id = "smithy.api#Blob", .id = "smithy.api#Blob",
.namespace = "smithy.api", .namespace = "smithy.api",
.name = "Blob", .name = "Blob",
@ -370,7 +370,7 @@ fn shapes(allocator: std.mem.Allocator, map: anytype) ![]ShapeInfo {
}, },
}, },
}); });
try list.append(allocator, .{ try list.append(.{
.id = "smithy.api#Unit", .id = "smithy.api#Unit",
.namespace = "smithy.api", .namespace = "smithy.api",
.name = "Unit", .name = "Unit",
@ -381,7 +381,7 @@ fn shapes(allocator: std.mem.Allocator, map: anytype) ![]ShapeInfo {
}, },
}, },
}); });
try list.append(allocator, .{ try list.append(.{
.id = "smithy.api#Long", .id = "smithy.api#Long",
.namespace = "smithy.api", .namespace = "smithy.api",
.name = "Long", .name = "Long",
@ -392,7 +392,7 @@ fn shapes(allocator: std.mem.Allocator, map: anytype) ![]ShapeInfo {
}, },
}, },
}); });
try list.append(allocator, .{ try list.append(.{
.id = "smithy.api#Float", .id = "smithy.api#Float",
.namespace = "smithy.api", .namespace = "smithy.api",
.name = "Float", .name = "Float",
@ -403,7 +403,7 @@ fn shapes(allocator: std.mem.Allocator, map: anytype) ![]ShapeInfo {
}, },
}, },
}); });
try list.append(allocator, .{ try list.append(.{
.id = "smithy.api#Document", .id = "smithy.api#Document",
.namespace = "smithy.api", .namespace = "smithy.api",
.name = "Document", .name = "Document",
@ -419,7 +419,7 @@ fn shapes(allocator: std.mem.Allocator, map: anytype) ![]ShapeInfo {
// byte PrimitiveByte // byte PrimitiveByte
// short PrimitiveShort // short PrimitiveShort
try list.append(allocator, .{ try list.append(.{
.id = "smithy.api#PrimitiveBoolean", .id = "smithy.api#PrimitiveBoolean",
.namespace = "smithy.api", .namespace = "smithy.api",
.name = "PrimitiveBoolean", .name = "PrimitiveBoolean",
@ -430,7 +430,7 @@ fn shapes(allocator: std.mem.Allocator, map: anytype) ![]ShapeInfo {
}, },
}, },
}); });
try list.append(allocator, .{ try list.append(.{
.id = "smithy.api#PrimitiveInteger", .id = "smithy.api#PrimitiveInteger",
.namespace = "smithy.api", .namespace = "smithy.api",
.name = "PrimitiveInteger", .name = "PrimitiveInteger",
@ -441,7 +441,7 @@ fn shapes(allocator: std.mem.Allocator, map: anytype) ![]ShapeInfo {
}, },
}, },
}); });
try list.append(allocator, .{ try list.append(.{
.id = "smithy.api#PrimitiveDouble", .id = "smithy.api#PrimitiveDouble",
.namespace = "smithy.api", .namespace = "smithy.api",
.name = "PrimitiveDouble", .name = "PrimitiveDouble",
@ -452,7 +452,7 @@ fn shapes(allocator: std.mem.Allocator, map: anytype) ![]ShapeInfo {
}, },
}, },
}); });
try list.append(allocator, .{ try list.append(.{
.id = "smithy.api#PrimitiveLong", .id = "smithy.api#PrimitiveLong",
.namespace = "smithy.api", .namespace = "smithy.api",
.name = "PrimitiveLong", .name = "PrimitiveLong",
@ -463,7 +463,7 @@ fn shapes(allocator: std.mem.Allocator, map: anytype) ![]ShapeInfo {
}, },
}, },
}); });
try list.append(allocator, .{ try list.append(.{
.id = "smithy.api#PrimitiveFloat", .id = "smithy.api#PrimitiveFloat",
.namespace = "smithy.api", .namespace = "smithy.api",
.name = "PrimitiveFloat", .name = "PrimitiveFloat",
@ -474,7 +474,7 @@ fn shapes(allocator: std.mem.Allocator, map: anytype) ![]ShapeInfo {
}, },
}, },
}); });
return list.toOwnedSlice(allocator); return list.toOwnedSlice();
} }
fn getShape(allocator: std.mem.Allocator, shape: std.json.Value) SmithyParseError!Shape { fn getShape(allocator: std.mem.Allocator, shape: std.json.Value) SmithyParseError!Shape {
@ -482,7 +482,7 @@ fn getShape(allocator: std.mem.Allocator, shape: std.json.Value) SmithyParseErro
if (std.mem.eql(u8, shape_type, "service")) if (std.mem.eql(u8, shape_type, "service"))
return Shape{ return Shape{
.service = .{ .service = .{
.version = if (shape.object.get("version")) |v| v.string else null, .version = shape.object.get("version").?.string,
.operations = if (shape.object.get("operations")) |ops| .operations = if (shape.object.get("operations")) |ops|
try parseTargetList(allocator, ops.array) try parseTargetList(allocator, ops.array)
else else
@ -591,26 +591,26 @@ fn parseMembers(allocator: std.mem.Allocator, shape: ?std.json.Value) SmithyPars
const map = shape.?.object; const map = shape.?.object;
var list = std.ArrayList(TypeMember).initCapacity(allocator, map.count()) catch return SmithyParseError.OutOfMemory; var list = std.ArrayList(TypeMember).initCapacity(allocator, map.count()) catch return SmithyParseError.OutOfMemory;
defer list.deinit(allocator); defer list.deinit();
var iterator = map.iterator(); var iterator = map.iterator();
while (iterator.next()) |kv| { while (iterator.next()) |kv| {
list.appendAssumeCapacity(TypeMember{ try list.append(TypeMember{
.name = kv.key_ptr.*, .name = kv.key_ptr.*,
.target = kv.value_ptr.*.object.get("target").?.string, .target = kv.value_ptr.*.object.get("target").?.string,
.traits = try parseTraits(allocator, kv.value_ptr.*.object.get("traits")), .traits = try parseTraits(allocator, kv.value_ptr.*.object.get("traits")),
}); });
} }
return list.toOwnedSlice(allocator); return list.toOwnedSlice();
} }
// ArrayList of std.Json.Value // ArrayList of std.Json.Value
fn parseTargetList(allocator: std.mem.Allocator, list: anytype) SmithyParseError![][]const u8 { 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; var array_list = std.ArrayList([]const u8).initCapacity(allocator, list.items.len) catch return SmithyParseError.OutOfMemory;
defer array_list.deinit(allocator); defer array_list.deinit();
for (list.items) |i| { for (list.items) |i| {
array_list.appendAssumeCapacity(i.object.get("target").?.string); try array_list.append(i.object.get("target").?.string);
} }
return array_list.toOwnedSlice(allocator); return array_list.toOwnedSlice();
} }
fn parseTraitsOnly(allocator: std.mem.Allocator, shape: std.json.Value) SmithyParseError!TraitsOnly { fn parseTraitsOnly(allocator: std.mem.Allocator, shape: std.json.Value) SmithyParseError!TraitsOnly {
return TraitsOnly{ return TraitsOnly{
@ -625,13 +625,13 @@ fn parseTraits(allocator: std.mem.Allocator, shape: ?std.json.Value) SmithyParse
const map = shape.?.object; const map = shape.?.object;
var list = std.ArrayList(Trait).initCapacity(allocator, map.count()) catch return SmithyParseError.OutOfMemory; var list = std.ArrayList(Trait).initCapacity(allocator, map.count()) catch return SmithyParseError.OutOfMemory;
defer list.deinit(allocator); defer list.deinit();
var iterator = map.iterator(); var iterator = map.iterator();
while (iterator.next()) |kv| { while (iterator.next()) |kv| {
if (try getTrait(kv.key_ptr.*, kv.value_ptr.*)) |t| if (try getTrait(kv.key_ptr.*, kv.value_ptr.*)) |t|
list.appendAssumeCapacity(t); try list.append(t);
} }
return list.toOwnedSlice(allocator); return list.toOwnedSlice();
} }
fn getTrait(trait_type: []const u8, value: std.json.Value) SmithyParseError!?Trait { fn getTrait(trait_type: []const u8, value: std.json.Value) SmithyParseError!?Trait {
@ -789,7 +789,6 @@ fn getTrait(trait_type: []const u8, value: std.json.Value) SmithyParseError!?Tra
\\smithy.api#timestampFormat \\smithy.api#timestampFormat
\\smithy.api#xmlAttribute \\smithy.api#xmlAttribute
\\smithy.api#xmlFlattened \\smithy.api#xmlFlattened
\\smithy.api#requestCompression
\\smithy.waiters#waitable \\smithy.waiters#waitable
\\smithy.rules#endpointTests \\smithy.rules#endpointTests
\\smithy.api#input \\smithy.api#input
@ -805,24 +804,17 @@ fn getTrait(trait_type: []const u8, value: std.json.Value) SmithyParseError!?Tra
\\smithy.api#recommended \\smithy.api#recommended
\\smithy.api#httpBearerAuth \\smithy.api#httpBearerAuth
\\smithy.api#nestedProperties \\smithy.api#nestedProperties
\\smithy.api#private
\\smithy.rules#endpointRuleSet \\smithy.rules#endpointRuleSet
\\smithy.rules#contextParam \\smithy.rules#contextParam
\\smithy.rules#clientContextParams \\smithy.rules#clientContextParams
\\smithy.rules#staticContextParams \\smithy.rules#staticContextParams
\\smithy.rules#operationContextParams
\\smithy.test#smokeTests
\\aws.cloudformation#cfnResource \\aws.cloudformation#cfnResource
\\aws.cloudformation#cfnMutability \\aws.cloudformation#cfnMutability
\\aws.cloudformation#cfnExcludeProperty \\aws.cloudformation#cfnExcludeProperty
\\aws.cloudformation#cfnAdditionalIdentifier \\aws.cloudformation#cfnAdditionalIdentifier
\\aws.endpoints#standardPartitionalEndpoints
\\aws.endpoints#dualStackOnlyEndpoints
\\aws.endpoints#standardRegionalEndpoints
\\aws.iam#actionPermissionDescription \\aws.iam#actionPermissionDescription
\\aws.iam#requiredActions \\aws.iam#requiredActions
\\aws.iam#conditionKeys \\aws.iam#conditionKeys
\\aws.iam#conditionKeyValue
\\aws.iam#iamResource \\aws.iam#iamResource
\\aws.iam#iamAction \\aws.iam#iamAction
\\aws.iam#supportedPrincipalTypes \\aws.iam#supportedPrincipalTypes
@ -901,37 +893,6 @@ fn read_file_to_string(allocator: std.mem.Allocator, file_name: []const u8, max_
const test_data: []const u8 = @embedFile("test.json"); const test_data: []const u8 = @embedFile("test.json");
const intrinsic_type_count: usize = 15; // 15 intrinsic types are added to every model (see shapes() function) const intrinsic_type_count: usize = 15; // 15 intrinsic types are added to every model (see shapes() function)
test "parse service without version" {
const test_string =
\\ {
\\ "smithy": "1.0",
\\ "shapes": {
\\ "com.amazonaws.sts#AWSSecurityTokenServiceV20110615": {
\\ "type": "service",
\\ "operations": [
\\ {
\\ "target": "op"
\\ }
\\ ]
\\ }
\\ }
\\ }
\\
\\
;
const allocator = std.testing.allocator;
const model = try parse(allocator, test_string);
defer model.deinit();
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.expect(model.shapes[0].shape.service.version == null);
}
test "parse string" { test "parse string" {
const test_string = const test_string =
\\ { \\ {
@ -962,7 +923,7 @@ test "parse string" {
try std.testing.expectEqualStrings("com.amazonaws.sts", model.shapes[0].namespace); try std.testing.expectEqualStrings("com.amazonaws.sts", model.shapes[0].namespace);
try std.testing.expectEqualStrings("AWSSecurityTokenServiceV20110615", model.shapes[0].name); try std.testing.expectEqualStrings("AWSSecurityTokenServiceV20110615", model.shapes[0].name);
try std.testing.expect(model.shapes[0].member == null); try std.testing.expect(model.shapes[0].member == null);
try std.testing.expectEqualStrings("2011-06-15", model.shapes[0].shape.service.version.?); try std.testing.expectEqualStrings("2011-06-15", model.shapes[0].shape.service.version);
} }
test "parse shape with member" { test "parse shape with member" {
const test_string = const test_string =
@ -992,7 +953,7 @@ test "parse shape with member" {
try std.testing.expectEqualStrings("com.amazonaws.sts#AWSSecurityTokenServiceV20110615$member", model.shapes[0].id); 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("com.amazonaws.sts", model.shapes[0].namespace);
try std.testing.expectEqualStrings("AWSSecurityTokenServiceV20110615", model.shapes[0].name); 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("2011-06-15", model.shapes[0].shape.service.version);
try std.testing.expectEqualStrings("member", model.shapes[0].member.?); try std.testing.expectEqualStrings("member", model.shapes[0].member.?);
} }
test "parse file" { test "parse file" {
@ -1017,7 +978,7 @@ test "parse file" {
try std.testing.expectEqualStrings("com.amazonaws.sts#AWSSecurityTokenServiceV20110615", svc.id); try std.testing.expectEqualStrings("com.amazonaws.sts#AWSSecurityTokenServiceV20110615", svc.id);
try std.testing.expectEqualStrings("com.amazonaws.sts", svc.namespace); try std.testing.expectEqualStrings("com.amazonaws.sts", svc.namespace);
try std.testing.expectEqualStrings("AWSSecurityTokenServiceV20110615", svc.name); try std.testing.expectEqualStrings("AWSSecurityTokenServiceV20110615", svc.name);
try std.testing.expectEqualStrings("2011-06-15", svc.shape.service.version.?); try std.testing.expectEqualStrings("2011-06-15", svc.shape.service.version);
// Should be 6, but we don't handle title or xml namespace // Should be 6, but we don't handle title or xml namespace
try std.testing.expectEqual(@as(usize, 4), svc.shape.service.traits.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); try std.testing.expectEqual(@as(usize, 8), svc.shape.service.operations.len);