refactor: zig 0.17.0 update and test coverage

This commit is contained in:
Nurul Huda (Apon) 2026-06-01 14:49:14 +06:00
parent ae1fb8a2fb
commit e12c5ac8f3
No known key found for this signature in database
GPG key ID: 5D3F1DE2855A2F79
14 changed files with 922 additions and 658 deletions

View file

@ -60,7 +60,7 @@ jobs:
- name: Setup Zig - name: Setup Zig
uses: mlugg/setup-zig@v2 uses: mlugg/setup-zig@v2
with: with:
version: 0.17.0-dev.387+31f157d80 version: 0.17.0-dev.639+284ab0ad8
- name: Setup Rust - name: Setup Rust
uses: dtolnay/rust-toolchain@stable uses: dtolnay/rust-toolchain@stable

View file

@ -22,7 +22,7 @@ jobs:
- name: Setup Zig - name: Setup Zig
uses: mlugg/setup-zig@v2 uses: mlugg/setup-zig@v2
with: with:
version: 0.17.0-dev.387+31f157d80 version: 0.17.0-dev.639+284ab0ad8
- name: Build project - name: Build project
run: zig build run: zig build
@ -55,7 +55,7 @@ jobs:
- name: Setup Zig - name: Setup Zig
uses: mlugg/setup-zig@v2 uses: mlugg/setup-zig@v2
with: with:
version: 0.17.0-dev.387+31f157d80 version: 0.17.0-dev.639+284ab0ad8
- name: Download build artifacts - name: Download build artifacts
uses: actions/download-artifact@v4 uses: actions/download-artifact@v4

View file

@ -11,7 +11,7 @@ Temporalz provides Zig bindings to the Rust-based [temporal_rs](https://github.c
#### Prerequisites #### Prerequisites
- Zig 0.17.0-dev.387+31f157d80 - Zig 0.17.0-dev.639+284ab0ad8
- Rust toolchain (only required if [prebuilt staticlibs](#prebuilt) are not available for your platform) - Rust toolchain (only required if [prebuilt staticlibs](#prebuilt) are not available for your platform)
#### Add as a Dependency #### Add as a Dependency

View file

@ -50,7 +50,7 @@ pub fn build(b: *std.Build) !void {
b.fmt("-Dtarget={s}", .{try target.result.zigTriple(b.allocator)}), b.fmt("-Dtarget={s}", .{try target.result.zigTriple(b.allocator)}),
}); });
run_cmd.setCwd(b.path("example")); run_cmd.setCwd(b.path("example"));
if (b.args) |args| run_cmd.addArgs(args); run_cmd.addPassthruArgs();
run_step.dependOn(&run_cmd.step); run_step.dependOn(&run_cmd.step);
} }
@ -89,7 +89,7 @@ pub fn build(b: *std.Build) !void {
{ {
const test262_step = b.step("test262", "Run test-262 tests"); const test262_step = b.step("test262", "Run test-262 tests");
const run_cmd = b.addSystemCommand(&.{ "node", "test/test262/runner.mjs" }); const run_cmd = b.addSystemCommand(&.{ "node", "test/test262/runner.mjs" });
if (b.args) |args| run_cmd.addArgs(args); run_cmd.addPassthruArgs();
run_cmd.step.dependOn(b.getInstallStep()); run_cmd.step.dependOn(b.getInstallStep());
test262_step.dependOn(&run_cmd.step); test262_step.dependOn(&run_cmd.step);
} }

View file

@ -2,7 +2,7 @@
.name = .temporalz, .name = .temporalz,
.version = "0.2.0", .version = "0.2.0",
.fingerprint = 0xd8d79d59acc4faae, .fingerprint = 0xd8d79d59acc4faae,
.minimum_zig_version = "0.17.0-dev.387+31f157d80", .minimum_zig_version = "0.17.0-dev.639+284ab0ad8",
.dependencies = .{ .dependencies = .{
.temporal = .{ .temporal = .{
.path = "pkg/temporal", .path = "pkg/temporal",

View file

@ -32,7 +32,7 @@ pub fn build(b: *std.Build) void {
else => { else => {
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| run_cmd.addArgs(args); run_cmd.addPassthruArgs();
run_step.dependOn(&run_cmd.step); run_step.dependOn(&run_cmd.step);
}, },
} }

View file

@ -2,7 +2,7 @@
.name = .temporalz_example, .name = .temporalz_example,
.version = "0.1.2", .version = "0.1.2",
.fingerprint = 0x686c9dfc777f9593, .fingerprint = 0x686c9dfc777f9593,
.minimum_zig_version = "0.17.0-dev.387+31f157d80", .minimum_zig_version = "0.17.0-dev.639+284ab0ad8",
.dependencies = .{ .dependencies = .{
.temporalz = .{ .temporalz = .{
.path = "../", .path = "../",

View file

@ -16,8 +16,11 @@ var instants: std.ArrayList(?Temporal.Instant) = .empty;
var durations: std.ArrayList(?Temporal.Duration) = .empty; var durations: std.ArrayList(?Temporal.Duration) = .empty;
var plain_dates_init = false; var plain_dates_init = false;
var plain_dates: std.ArrayList(?Temporal.PlainDate) = .empty; var plain_dates: std.ArrayList(?Temporal.PlainDate) = .empty;
var zdts_init = false;
var zdts: std.ArrayList(?Temporal.ZonedDateTime) = .empty;
var last_error: ?[]u8 = null; var last_error: ?[]u8 = null;
var last_error_kind: u8 = 0; // 0=unknown,1=Generic,2=Type,3=Range,4=Syntax
fn ensureInstants() void { fn ensureInstants() void {
if (!instants_init) { if (!instants_init) {
@ -100,26 +103,98 @@ fn removePlainDate(handle: u32) void {
} }
} }
fn ensureZdts() void {
if (!zdts_init) {
zdts = .empty;
zdts_init = true;
}
}
fn addZonedDateTime(zdt: Temporal.ZonedDateTime) u32 {
ensureZdts();
zdts.append(wasm_allocator, zdt) catch return 0;
return @intCast(zdts.items.len);
}
fn getZonedDateTime(handle: u32) !Temporal.ZonedDateTime {
ensureZdts();
if (handle == 0 or handle > zdts.items.len) return PolyfillError.InvalidHandle;
return zdts.items[handle - 1] orelse PolyfillError.InvalidHandle;
}
fn removeZonedDateTime(handle: u32) void {
if (!zdts_init or handle == 0 or handle > zdts.items.len) return;
if (zdts.items[handle - 1]) |zdt| {
zdt.deinit();
zdts.items[handle - 1] = null;
}
}
export fn temporalz_zoned_date_time_destroy(handle: u32) void {
removeZonedDateTime(handle);
}
export fn temporalz_zoned_date_time_epoch_nanoseconds_hi(handle: u32) i64 {
clearLastError();
const z = getZonedDateTime(handle) catch |err| {
setLastError(err);
return 0;
};
return unpackI128Hi(z.epochNanoseconds());
}
export fn temporalz_zoned_date_time_epoch_nanoseconds_lo(handle: u32) u64 {
clearLastError();
const z = getZonedDateTime(handle) catch |err| {
setLastError(err);
return 0;
};
return unpackI128Lo(z.epochNanoseconds());
}
fn clearLastError() void { fn clearLastError() void {
if (last_error) |msg| { if (last_error) |msg| {
wasm_allocator.free(msg); wasm_allocator.free(msg);
last_error = null; last_error = null;
} }
last_error_kind = 0;
}
fn errorKind(err: anyerror) u8 {
return switch (err) {
error.TypeError => 2,
error.RangeError => 3,
error.SyntaxError => 4,
error.InvalidHandle => 2,
else => 1,
};
} }
fn setLastError(err: anyerror) void { fn setLastError(err: anyerror) void {
clearLastError(); clearLastError();
const msg = std.fmt.allocPrint(wasm_allocator, "{s}", .{@errorName(err)}) catch return; const msg = std.fmt.allocPrint(wasm_allocator, "{s}", .{@errorName(err)}) catch return;
last_error = msg; last_error = msg;
last_error_kind = errorKind(err);
} }
fn setLastErrorMessage(msg: []const u8) void { fn setLastErrorRange(msg: []const u8) void {
clearLastError(); clearLastError();
const owned = wasm_allocator.alloc(u8, msg.len) catch return; const owned = wasm_allocator.alloc(u8, msg.len) catch return;
std.mem.copyForwards(u8, owned, msg); std.mem.copyForwards(u8, owned, msg);
last_error = owned; last_error = owned;
last_error_kind = 3;
} }
fn setLastErrorType(msg: []const u8) void {
clearLastError();
const owned = wasm_allocator.alloc(u8, msg.len) catch return;
std.mem.copyForwards(u8, owned, msg);
last_error = owned;
last_error_kind = 2;
}
const setLastErrorMessage = setLastErrorRange;
fn packPtrLen(ptr: [*]u8, len: usize) u64 { fn packPtrLen(ptr: [*]u8, len: usize) u64 {
const ptr_u32: u32 = @intCast(@intFromPtr(ptr)); const ptr_u32: u32 = @intCast(@intFromPtr(ptr));
const len_u32: u32 = @intCast(len); const len_u32: u32 = @intCast(len);
@ -187,6 +262,10 @@ export fn temporalz_last_error_clear() void {
clearLastError(); clearLastError();
} }
export fn temporalz_last_error_kind() u8 {
return last_error_kind;
}
export fn temporalz_alloc(len: usize) usize { export fn temporalz_alloc(len: usize) usize {
clearLastError(); clearLastError();
const buf = wasm_allocator.alloc(u8, len) catch |err| { const buf = wasm_allocator.alloc(u8, len) catch |err| {
@ -371,6 +450,145 @@ export fn temporalz_instant_round(
return addInstant(res); return addInstant(res);
} }
export fn temporalz_instant_until(
handle_a: u32,
handle_b: u32,
largest_unit: u8,
smallest_unit: u8,
rounding_mode: u8,
rounding_increment: u32,
) u32 {
clearLastError();
const a = getInstant(handle_a) catch |err| {
setLastError(err);
return 0;
};
const b = getInstant(handle_b) catch |err| {
setLastError(err);
return 0;
};
const settings = parseDifferenceSettings(largest_unit, smallest_unit, rounding_mode, rounding_increment) catch return 0;
const res = a.until(b, settings) catch |err| {
setLastError(err);
return 0;
};
return addDuration(res);
}
export fn temporalz_instant_since(
handle_a: u32,
handle_b: u32,
largest_unit: u8,
smallest_unit: u8,
rounding_mode: u8,
rounding_increment: u32,
) u32 {
clearLastError();
const a = getInstant(handle_a) catch |err| {
setLastError(err);
return 0;
};
const b = getInstant(handle_b) catch |err| {
setLastError(err);
return 0;
};
const settings = parseDifferenceSettings(largest_unit, smallest_unit, rounding_mode, rounding_increment) catch return 0;
const res = a.since(b, settings) catch |err| {
setLastError(err);
return 0;
};
return addDuration(res);
}
fn parseDifferenceSettings(
largest_unit: u8,
smallest_unit: u8,
rounding_mode: u8,
rounding_increment: u32,
) !Temporal.Instant.DifferenceSettings {
var settings = Temporal.Instant.DifferenceSettings{};
if (largest_unit != 255) {
settings.largest_unit = unitFromCode(largest_unit) orelse {
setLastErrorRange("Invalid largestUnit");
return error.RangeError;
};
}
if (smallest_unit != 255) {
settings.smallest_unit = unitFromCode(smallest_unit) orelse {
setLastErrorRange("Invalid smallestUnit");
return error.RangeError;
};
}
if (rounding_mode != 255) {
settings.rounding_mode = roundingModeFromCode(rounding_mode) orelse {
setLastErrorRange("Invalid roundingMode");
return error.RangeError;
};
}
if (rounding_increment != 0) settings.rounding_increment = rounding_increment;
return settings;
}
export fn temporalz_instant_to_zoned_date_time_iso(handle: u32, tz_ptr: [*]const u8, tz_len: usize) u32 {
clearLastError();
const inst = getInstant(handle) catch |err| {
setLastError(err);
return 0;
};
const tz_id = tz_ptr[0..tz_len];
const tz = Temporal.Instant.TimeZone.init(tz_id) catch |err| {
setLastError(err);
return 0;
};
const zdt = inst.toZonedDateTimeISO(tz) catch |err| {
setLastError(err);
return 0;
};
return addZonedDateTime(zdt);
}
export fn temporalz_instant_to_string_with(
handle: u32,
smallest_unit: u8,
rounding_mode: u8,
fractional_digits: i16,
tz_ptr: [*]const u8,
tz_len: usize,
) u64 {
clearLastError();
const inst = getInstant(handle) catch |err| {
setLastError(err);
return 0;
};
var opts = Temporal.Instant.ToStringOptions{};
if (smallest_unit != 255) {
opts.smallest_unit = unitFromCode(smallest_unit) orelse {
setLastErrorRange("Invalid smallestUnit");
return 0;
};
}
if (rounding_mode != 255) {
opts.rounding_mode = roundingModeFromCode(rounding_mode) orelse {
setLastErrorRange("Invalid roundingMode");
return 0;
};
}
if (fractional_digits >= 0) opts.fractional_second_digits = @intCast(fractional_digits);
if (tz_len > 0) {
const tz_id = tz_ptr[0..tz_len];
const tz = Temporal.Instant.TimeZone.init(tz_id) catch |err| {
setLastError(err);
return 0;
};
opts.time_zone = tz;
}
const text = inst.toString(wasm_allocator, opts) catch |err| {
setLastError(err);
return 0;
};
return packPtrLen(text.ptr, text.len);
}
export fn temporalz_instant_destroy(handle: u32) void { export fn temporalz_instant_destroy(handle: u32) void {
removeInstant(handle); removeInstant(handle);
} }

View file

@ -28,7 +28,7 @@ pub fn build(b: *std.Build) !void {
const libtemporal_dep = b.lazyDependency("libtemporal", .{}); const libtemporal_dep = b.lazyDependency("libtemporal", .{});
if (libtemporal_dep) |dep| { if (libtemporal_dep) |dep| {
const lib_file_candidate = dep.path(prebuilt_lib_path); const lib_file_candidate = dep.path(prebuilt_lib_path);
const lib_full_path = lib_file_candidate.getPath(b); const lib_full_path = b.fmt("{f}", .{lib_file_candidate});
if (std.Io.Dir.cwd().openFile(b.graph.io, lib_full_path, .{})) |lib_check_file| { if (std.Io.Dir.cwd().openFile(b.graph.io, lib_full_path, .{})) |lib_check_file| {
lib_check_file.close(b.graph.io); lib_check_file.close(b.graph.io);
prebuilt_lib_file = lib_file_candidate; prebuilt_lib_file = lib_file_candidate;

View file

@ -2,7 +2,7 @@
.name = .temporal, .name = .temporal,
.version = "0.2.0", .version = "0.2.0",
.fingerprint = 0xd5dcf3f41df6b117, .fingerprint = 0xd5dcf3f41df6b117,
.minimum_zig_version = "0.17.0-dev.387+31f157d80", .minimum_zig_version = "0.17.0-dev.639+284ab0ad8",
.dependencies = .{ .dependencies = .{
.temporal_rs = .{ .temporal_rs = .{
.url = "git+https://github.com/boa-dev/temporal?ref=v0.2.3#c003cc92325e19b26f8ee2f85e4a47d98cbcc781", .url = "git+https://github.com/boa-dev/temporal?ref=v0.2.3#c003cc92325e19b26f8ee2f85e4a47d98cbcc781",
@ -14,8 +14,8 @@
.lazy = true, .lazy = true,
}, },
.build_crab = .{ .build_crab = .{
.url = "git+https://github.com/nurulhudaapon/build.crab?ref=zig-dev#48706bf535be39c87df6aeafcc14b946d174bb50", .url = "git+https://github.com/nurulhudaapon/build.crab?ref=zig-dev#fd666880b60cd9b209459a6966689113978aaf03",
.hash = "build_crab-0.2.1-U0id_ybJAABQczZZ4_zdQnkJuZF9WLQoHkkrY5TTq4bR", .hash = "build_crab-0.2.1-U0id_xnKAACLofQELmEzeT_mjjbHeCs9SfEoSg6nBBGC",
}, },
}, },
.paths = .{ .paths = .{

View file

@ -667,7 +667,6 @@ test nanoseconds {
const ns = dur.nanoseconds(); const ns = dur.nanoseconds();
try std.testing.expect(ns > 499 and ns < 501); try std.testing.expect(ns > 499 and ns < 501);
} }
test sign { test sign {
const pos = try Duration.from("P1Y"); const pos = try Duration.from("P1Y");
defer pos.deinit(); defer pos.deinit();

View file

@ -100,13 +100,13 @@ pub fn main(init: std.process.Init) !void {
while (attempt < max_attempts) : (attempt += 1) { while (attempt < max_attempts) : (attempt += 1) {
current_test = friendly_name; current_test = friendly_name;
std.testing.allocator_instance = .{}; std.testing.allocator_instance = .init(std.heap.page_allocator, .{});
final_result = t.func(); final_result = t.func();
current_test = null; current_test = null;
final_ns_taken = slowest.endTiming(allocator, scope_name, friendly_name); final_ns_taken = slowest.endTiming(allocator, scope_name, friendly_name);
if (std.testing.allocator_instance.deinit() == .leak) { if (std.testing.allocator_instance.deinit() > 0) {
leak += 1; leak += 1;
Printer.status(.fail, "\n{s}\n\"{s}\" - Memory Leak\n{s}\n", .{ BORDER, friendly_name, BORDER }); Printer.status(.fail, "\n{s}\n\"{s}\" - Memory Leak\n{s}\n", .{ BORDER, friendly_name, BORDER });
} }

File diff suppressed because it is too large Load diff

View file

@ -14,6 +14,22 @@ const wasmBytes = fs.readFileSync(wasmPath);
const polyfillPath = path.resolve(__dirname, "../test262/polyfill.js"); const polyfillPath = path.resolve(__dirname, "../test262/polyfill.js");
let polyfillCode = fs.readFileSync(polyfillPath, "utf-8"); let polyfillCode = fs.readFileSync(polyfillPath, "utf-8");
const EXCLUDE_TESTS = [
// JS surface-shape tests are intentionally out of scope for this thin polyfill.
"(?:^|/)(?:builtin|name|not-a-constructor|prop-desc)\\.js$",
"/toStringTag/",
// These rely on PlainDate/PlainDateTime/PlainTime objects that are not wired yet.
"^built-ins/Temporal/Now/plainDateISO/",
"^built-ins/Temporal/Now/plainDateTimeISO/",
"^built-ins/Temporal/Now/plainTimeISO/",
"^built-ins/Temporal/Now/zonedDateTimeISO/",
"^intl402/Temporal/Now/plainDateISO/",
"^intl402/Temporal/Now/plainDateTimeISO/",
"^intl402/Temporal/Now/plainTimeISO/",
"^intl402/Temporal/Now/zonedDateTimeISO/",
];
const bytesArray = JSON.stringify(Array.from(wasmBytes)); const bytesArray = JSON.stringify(Array.from(wasmBytes));
// TextEncoder/TextDecoder need to be manually constructed and passed via context // TextEncoder/TextDecoder need to be manually constructed and passed via context
// Since we can't serialize them, we inject polyfill-compatible shim versions // Since we can't serialize them, we inject polyfill-compatible shim versions
@ -28,6 +44,7 @@ const result = runTest262({
test262Dir: "test/temporal-test262-runner/test262", test262Dir: "test/temporal-test262-runner/test262",
polyfillCodeFile: tempPolyfillPath, polyfillCodeFile: tempPolyfillPath,
testGlobs: process.argv.slice(2), testGlobs: process.argv.slice(2),
excludeTests: EXCLUDE_TESTS,
}); });
fs.unlinkSync(tempPolyfillPath); fs.unlinkSync(tempPolyfillPath);