refactor: extract date methods into shared date lib

This commit is contained in:
Simon Hartcher 2025-04-23 11:51:53 +10:00
parent 0192c1e888
commit 29e9dd481b
10 changed files with 175 additions and 44 deletions

View file

@ -19,14 +19,7 @@ const test_targets = [_]std.Target.Query{
}; };
pub fn build(b: *Builder) !void { pub fn build(b: *Builder) !void {
// Standard target options allows the person running `zig build` to choose
// what target to build for. Here we do not override the defaults, which
// means any target is allowed, and the default is native. Other options
// for restricting supported target set are available.
const target = b.standardTargetOptions(.{}); const target = b.standardTargetOptions(.{});
// Standard release options allow the person running `zig build` to select
// between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall.
const optimize = b.standardOptimizeOption(.{}); const optimize = b.standardOptimizeOption(.{});
const no_llvm = b.option( const no_llvm = b.option(
@ -46,6 +39,7 @@ pub fn build(b: *Builder) !void {
"test-filter", "test-filter",
"Skip tests that do not match any of the specified filters", "Skip tests that do not match any of the specified filters",
) orelse &.{}; ) orelse &.{};
// TODO: Embed the current git version in the code. We can do this // TODO: Embed the current git version in the code. We can do this
// by looking for .git/HEAD (if it exists, follow the ref to /ref/heads/whatevs, // by looking for .git/HEAD (if it exists, follow the ref to /ref/heads/whatevs,
// grab that commit, and use b.addOptions/exe.addOptions to generate the // grab that commit, and use b.addOptions/exe.addOptions to generate the
@ -58,13 +52,17 @@ pub fn build(b: *Builder) !void {
// executable // executable
// TODO: This executable should not be built when importing as a package. // TODO: This executable should not be built when importing as a package.
// It relies on code gen and is all fouled up when getting imported // It relies on code gen and is all fouled up when getting imported
const exe = b.addExecutable(.{ const mod_exe = b.createModule(.{
.name = "demo",
.root_source_file = b.path("src/main.zig"), .root_source_file = b.path("src/main.zig"),
.target = target, .target = target,
.optimize = optimize, .optimize = optimize,
}); });
exe.use_llvm = !no_llvm;
const exe = b.addExecutable(.{
.name = "demo",
.root_module = mod_exe,
.use_llvm = !no_llvm,
});
// External dependencies // External dependencies
const dep_smithy = b.dependency("smithy", .{ const dep_smithy = b.dependency("smithy", .{
@ -72,16 +70,31 @@ pub fn build(b: *Builder) !void {
.optimize = optimize, .optimize = optimize,
}); });
const mod_smithy = dep_smithy.module("smithy"); const mod_smithy = dep_smithy.module("smithy");
exe.root_module.addImport("smithy", mod_smithy); // not sure this should be here... mod_exe.addImport("smithy", mod_smithy); // not sure this should be here...
const dep_zeit = b.dependency("zeit", .{ const dep_zeit = b.dependency("zeit", .{
.target = target, .target = target,
.optimize = optimize, .optimize = optimize,
}); });
const mod_zeit = dep_zeit.module("zeit"); const mod_zeit = dep_zeit.module("zeit");
exe.root_module.addImport("zeit", mod_zeit); mod_exe.addImport("zeit", mod_zeit);
// End External dependencies // End External dependencies
// Private modules/dependencies
const mod_json = b.createModule(.{
.root_source_file = b.path("codegen/src/json.zig"),
.target = target,
.optimize = optimize,
});
const dep_date = b.dependency("date", .{
.target = target,
.optimize = optimize,
});
const mod_date = dep_date.module("date");
mod_exe.addImport("date", mod_date);
// End private modules/dependencies
const run_cmd = b.addRunArtifact(exe); const run_cmd = b.addRunArtifact(exe);
run_cmd.step.dependOn(b.getInstallStep()); run_cmd.step.dependOn(b.getInstallStep());
if (b.args) |args| { if (b.args) |args| {
@ -93,14 +106,19 @@ pub fn build(b: *Builder) !void {
const cg = b.step("gen", "Generate zig service code from smithy models"); const cg = b.step("gen", "Generate zig service code from smithy models");
const cg_exe = b.addExecutable(.{ const cg_mod = b.createModule(.{
.name = "codegen",
.root_source_file = b.path("codegen/src/main.zig"), .root_source_file = b.path("codegen/src/main.zig"),
// We need this generated for the host, not the real target // We need this generated for the host, not the real target
.target = b.graph.host, .target = b.graph.host,
.optimize = if (b.verbose) .Debug else .ReleaseSafe, .optimize = if (b.verbose) .Debug else .ReleaseSafe,
}); });
cg_exe.root_module.addImport("smithy", mod_smithy); cg_mod.addImport("smithy", mod_smithy);
cg_mod.addImport("date", mod_date);
const cg_exe = b.addExecutable(.{
.name = "codegen",
.root_module = cg_mod,
});
var cg_cmd = b.addRunArtifact(cg_exe); var cg_cmd = b.addRunArtifact(cg_exe);
cg_cmd.addArg("--models"); cg_cmd.addArg("--models");
cg_cmd.addArg(try std.fs.path.join( cg_cmd.addArg(try std.fs.path.join(
@ -133,21 +151,6 @@ pub fn build(b: *Builder) !void {
exe.step.dependOn(cg); exe.step.dependOn(cg);
// Codegen private modules
const mod_json = b.createModule(.{
.root_source_file = b.path("codegen/src/json.zig"),
.target = target,
.optimize = optimize,
});
const mod_date = b.createModule(.{
.root_source_file = b.path("codegen/src/date.zig"),
.target = target,
.optimize = optimize,
});
mod_date.addImport("zeit", mod_zeit);
// End codegen private modules
// This allows us to have each module depend on the // This allows us to have each module depend on the
// generated service manifest. // generated service manifest.
const service_manifest_module = b.createModule(.{ const service_manifest_module = b.createModule(.{
@ -165,16 +168,20 @@ pub fn build(b: *Builder) !void {
// Expose module to others // Expose module to others
const mod_aws = b.addModule("aws", .{ const mod_aws = b.addModule("aws", .{
.root_source_file = b.path("src/aws.zig"), .root_source_file = b.path("src/aws.zig"),
.target = target,
.optimize = optimize,
}); });
mod_aws.addImport("smithy", mod_smithy); mod_aws.addImport("smithy", mod_smithy);
mod_aws.addImport("service_manifest", service_manifest_module); mod_aws.addImport("service_manifest", service_manifest_module);
mod_aws.addImport("date", mod_date); mod_aws.addImport("date", mod_date);
mod_aws.addImport("zeit", mod_zeit);
// Expose module to others // Expose module to others
_ = b.addModule("aws-signing", .{ const mod_aws_signing = b.addModule("aws-signing", .{
.root_source_file = b.path("src/aws_signing.zig"), .root_source_file = b.path("src/aws_signing.zig"),
.imports = &.{.{ .name = "smithy", .module = mod_smithy }},
}); });
mod_aws_signing.addImport("date", mod_date);
mod_aws_signing.addImport("smithy", mod_smithy);
// Similar to creating the run step earlier, this exposes a `test` step to // Similar to creating the run step earlier, this exposes a `test` step to
// the `zig build --help` menu, providing a way for the user to request // the `zig build --help` menu, providing a way for the user to request
@ -234,14 +241,10 @@ pub fn build(b: *Builder) !void {
// 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 smoke_test = b.addTest(.{ const smoke_test = b.addTest(.{
.root_source_file = b.path("src/aws.zig"), .root_module = mod_aws,
.target = target,
.optimize = optimize,
.filters = test_filters, .filters = test_filters,
}); });
smoke_test.use_llvm = !no_llvm; smoke_test.use_llvm = !no_llvm;
smoke_test.root_module.addImport("smithy", mod_smithy);
smoke_test.root_module.addImport("service_manifest", service_manifest_module);
smoke_test.step.dependOn(cg); smoke_test.step.dependOn(cg);
const run_smoke_test = b.addRunArtifact(smoke_test); const run_smoke_test = b.addRunArtifact(smoke_test);

View file

@ -7,6 +7,7 @@
"build.zig.zon", "build.zig.zon",
"src", "src",
"codegen", "codegen",
"lib",
"README.md", "README.md",
"LICENSE", "LICENSE",
}, },
@ -24,5 +25,8 @@
.url = "git+https://github.com/rockorager/zeit#fb6557ad4bd0cd0f0f728ae978061d7fe992c528", .url = "git+https://github.com/rockorager/zeit#fb6557ad4bd0cd0f0f728ae978061d7fe992c528",
.hash = "zeit-0.6.0-5I6bk29nAgDhK6AVMtXMWhkKTYgUncrWjnlI_8X9DPSd", .hash = "zeit-0.6.0-5I6bk29nAgDhK6AVMtXMWhkKTYgUncrWjnlI_8X9DPSd",
}, },
.date = .{
.path = "lib/date",
},
}, },
} }

35
lib/date/build.zig Normal file
View file

@ -0,0 +1,35 @@
const std = @import("std");
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const lib_mod = b.addModule("date", .{
.root_source_file = b.path("src/root.zig"),
.target = target,
.optimize = optimize,
});
const lib = b.addLibrary(.{
.linkage = .static,
.name = "date",
.root_module = lib_mod,
});
b.installArtifact(lib);
const lib_unit_tests = b.addTest(.{
.root_module = lib_mod,
});
const run_lib_unit_tests = b.addRunArtifact(lib_unit_tests);
const test_step = b.step("test", "Run unit tests");
test_step.dependOn(&run_lib_unit_tests.step);
const dep_zeit = b.dependency("zeit", .{
.target = target,
.optimize = optimize,
});
lib_mod.addImport("zeit", dep_zeit.module("zeit"));
}

17
lib/date/build.zig.zon Normal file
View file

@ -0,0 +1,17 @@
.{
.name = .date,
.version = "0.0.0",
.fingerprint = 0xaa9e377a226d739e, // Changing this has security and trust implications.
.minimum_zig_version = "0.14.0",
.dependencies = .{
.zeit = .{
.url = "git+https://github.com/rockorager/zeit#fb6557ad4bd0cd0f0f728ae978061d7fe992c528",
.hash = "zeit-0.6.0-5I6bk29nAgDhK6AVMtXMWhkKTYgUncrWjnlI_8X9DPSd",
},
},
.paths = .{
"build.zig",
"build.zig.zon",
"src",
},
}

View file

@ -3,12 +3,8 @@
// really requires the TZ DB. // really requires the TZ DB.
const std = @import("std"); const std = @import("std");
const codegen_date = @import("date");
const log = std.log.scoped(.date); const log = std.log.scoped(.date);
pub const Timestamp = codegen_date.Timestamp;
pub const DateTime = struct { day: u8, month: u8, year: u16, hour: u8, minute: u8, second: u8 }; pub const DateTime = struct { day: u8, month: u8, year: u16, hour: u8, minute: u8, second: u8 };
const SECONDS_PER_DAY = 86400; //* 24* 60 * 60 */ const SECONDS_PER_DAY = 86400; //* 24* 60 * 60 */

9
lib/date/src/root.zig Normal file
View file

@ -0,0 +1,9 @@
const std = @import("std");
const testing = std.testing;
pub usingnamespace @import("parsing.zig");
pub usingnamespace @import("timestamp.zig");
test {
testing.refAllDeclsRecursive(@This());
}

View file

@ -0,0 +1,67 @@
const std = @import("std");
const zeit = @import("zeit");
pub const DateFormat = enum {
rfc1123,
iso8601,
};
pub const Timestamp = enum(zeit.Nanoseconds) {
_,
pub fn jsonStringify(value: Timestamp, options: anytype, out_stream: anytype) !void {
_ = options;
const instant = try zeit.instant(.{
.source = .{
.unix_nano = @intFromEnum(value),
},
});
try out_stream.writeAll("\"");
try instant.time().gofmt(out_stream, "Mon, 02 Jan 2006 15:04:05 GMT");
try out_stream.writeAll("\"");
}
pub fn parse(val: []const u8) !Timestamp {
const date_format = blk: {
if (std.ascii.isDigit(val[0])) {
break :blk DateFormat.iso8601;
} else {
break :blk DateFormat.rfc1123;
}
};
const ins = try zeit.instant(.{
.source = switch (date_format) {
DateFormat.iso8601 => .{
.iso8601 = val,
},
DateFormat.rfc1123 => .{
.rfc1123 = val,
},
},
});
return @enumFromInt(ins.timestamp);
}
};
test Timestamp {
const in_date = "Wed, 23 Apr 2025 11:23:45 GMT";
const expected_ts: Timestamp = @enumFromInt(1745407425000000000);
const actual_ts = try Timestamp.parse(in_date);
try std.testing.expectEqual(expected_ts, actual_ts);
var buf: [100]u8 = undefined;
var fbs = std.io.fixedBufferStream(&buf);
var counting_writer = std.io.countingWriter(fbs.writer());
try Timestamp.jsonStringify(expected_ts, .{}, counting_writer.writer());
const expected_json = "\"" ++ in_date ++ "\"";
const actual_json = buf[0..counting_writer.bytes_written];
try std.testing.expectEqualStrings(expected_json, actual_json);
}

View file

@ -6,7 +6,7 @@ const awshttp = @import("aws_http.zig");
const json = @import("json.zig"); const json = @import("json.zig");
const url = @import("url.zig"); const url = @import("url.zig");
const case = @import("case.zig"); const case = @import("case.zig");
const date = @import("date.zig"); const date = @import("date");
const servicemodel = @import("servicemodel.zig"); const servicemodel = @import("servicemodel.zig");
const xml_shaper = @import("xml_shaper.zig"); const xml_shaper = @import("xml_shaper.zig");
const xml_serializer = @import("xml_serializer.zig"); const xml_serializer = @import("xml_serializer.zig");

View file

@ -1,7 +1,7 @@
const std = @import("std"); const std = @import("std");
const base = @import("aws_http_base.zig"); const base = @import("aws_http_base.zig");
const auth = @import("aws_authentication.zig"); const auth = @import("aws_authentication.zig");
const date = @import("date.zig"); const date = @import("date");
const scoped_log = std.log.scoped(.aws_signing); const scoped_log = std.log.scoped(.aws_signing);

View file

@ -1,6 +1,6 @@
const std = @import("std"); const std = @import("std");
const xml = @import("xml.zig"); const xml = @import("xml.zig");
const date = @import("date.zig"); const date = @import("date");
const log = std.log.scoped(.xml_shaper); const log = std.log.scoped(.xml_shaper);