From ae74ee2aa7270a9195d5d8bbfa25674d2bf1d76e Mon Sep 17 00:00:00 2001 From: "Nurul Huda (Apon)" Date: Tue, 27 Jan 2026 00:31:53 +0600 Subject: [PATCH] fix: create zig errror wrapper --- src/Duration.zig | 39 ++++++++++++++++++++++++++++++++------- src/PlainDate.zig | 13 ------------- src/abi.zig | 36 ++++++++++++++++++++++++++++++++++++ 3 files changed, 68 insertions(+), 20 deletions(-) diff --git a/src/Duration.zig b/src/Duration.zig index 39283ac..75bee6d 100644 --- a/src/Duration.zig +++ b/src/Duration.zig @@ -216,27 +216,27 @@ fn roundWithProvider(self: Duration, options: RoundingOptions, relative_to: Rela /// Compare two durations (Temporal.Duration.compare). pub fn compare(self: Duration, other: Duration, relative_to: RelativeTo) !i8 { const res = abi.c.temporal_rs_Duration_compare(self._inner, other._inner, relative_to.toCApi()); - return abi.success(res) orelse return error.TemporalError; + return try abi.extractResult(res); } /// Compare two durations with an explicit provider. fn compareWithProvider(self: Duration, other: Duration, relative_to: RelativeTo, provider: *const abi.c.Provider) !i8 { const res = abi.c.temporal_rs_Duration_compare_with_provider(self._inner, other._inner, relative_to.toCApi(), provider); - return abi.success(res) orelse return error.TemporalError; + return try abi.extractResult(res); } /// Get the total value of the duration in the specified unit (Temporal.Duration.prototype.total). pub fn total(self: Duration, options: TotalOptions) !f64 { const rel = if (options.relative_to) |r| r.toCApi() else abi.c.RelativeTo{ .date = null, .zoned = null }; const res = abi.c.temporal_rs_Duration_total(self._inner, options.unit.toCApi(), rel); - return abi.success(res) orelse return error.TemporalError; + return try abi.extractResult(res); } /// Get the total value of the duration with an explicit provider. fn totalWithProvider(self: Duration, options: TotalOptions, provider: *const abi.c.Provider) !f64 { const rel = if (options.relative_to) |r| r.toCApi() else abi.c.RelativeTo{ .date = null, .zoned = null }; const res = abi.c.temporal_rs_Duration_total_with_provider(self._inner, options.unit.toCApi(), rel, provider); - return abi.success(res) orelse return error.TemporalError; + return try abi.extractResult(res); } /// Convert to string (Temporal.Duration.prototype.toString); caller owns returned slice. @@ -272,12 +272,12 @@ pub fn deinit(self: Duration) void { // --- Helpers ----------------------------------------------------------------- -fn handleVoidResult(res: anytype) !void { - _ = abi.success(res) orelse return error.TemporalError; +inline fn handleVoidResult(res: anytype) !void { + _ = try abi.extractResult(res); } fn wrapDuration(res: anytype) !Duration { - const ptr = (abi.success(res) orelse return error.TemporalError) orelse return error.TemporalError; + const ptr = (try abi.extractResult(res)) orelse return abi.TemporalError.Generic; return .{ ._inner = ptr }; } @@ -431,3 +431,28 @@ test clone { try std.testing.expectEqual(dur.hours(), cloned.hours()); } + +test total { + { + const dur = try Duration.init(0, 0, 0, 0, 1, 30, 0, 0, 0, 0); + defer dur.deinit(); + + const t = try dur.total(.{ .unit = .hour }); + try std.testing.expectEqual(1.5, t); + } + { + const dur = try Duration.from("PT4H5M6S"); + defer dur.deinit(); + + const t = try dur.total(.{ .unit = .hour }); + try std.testing.expectEqual(4.085, t); + } + + { + // Calendar units require relative_to; the C API will return RangeError. + const dur = try Duration.from("P1Y"); + defer dur.deinit(); + + try std.testing.expectError(error.RangeError, dur.total(.{ .unit = .day })); + } +} diff --git a/src/PlainDate.zig b/src/PlainDate.zig index f1030ca..ec297a7 100644 --- a/src/PlainDate.zig +++ b/src/PlainDate.zig @@ -152,13 +152,6 @@ pub fn toZonedDateTime(self: PlainDate, options: ToZonedDateTimeOptions) !ZonedD const time_ptr = if (options.plain_time) |t| t._inner else null; const ptr = (abi.success(abi.c.temporal_rs_PlainDate_to_zoned_date_time(self._inner, time_zone, time_ptr)) orelse return error.TemporalError) orelse return error.TemporalError; - // Get time zone identifier for ZonedDateTime - // const allocator = std.heap.page_allocator; - // var write = abi.DiplomatWrite.init(allocator); - // defer write.deinit(); - // abi.c.temporal_rs_TimeZone_identifier(time_zone, &write.inner); - // const tz_id = try write.toOwnedSlice(); - return .{ ._inner = ptr }; } @@ -285,12 +278,6 @@ pub fn deinit(self: PlainDate) void { fn wrapPlainDate(res: anytype) !PlainDate { const ptr = (abi.success(res) orelse return error.TemporalError) orelse return error.TemporalError; - // const calendar_ptr = abi.c.temporal_rs_PlainDate_calendar(ptr) orelse return error.TemporalError; - // const cal_id_view = abi.c.temporal_rs_Calendar_identifier(calendar_ptr); - - // const allocator = std.heap.page_allocator; - // const cal_id = allocator.dupe(u8, cal_id_view.data[0..cal_id_view.len]) catch "iso8601"; - return .{ ._inner = ptr }; } diff --git a/src/abi.zig b/src/abi.zig index 2b01193..ec44649 100644 --- a/src/abi.zig +++ b/src/abi.zig @@ -261,3 +261,39 @@ pub fn Success(comptime Result: type) type { if (!@hasField(Union, "ok")) return void; return @FieldType(Union, "ok"); } + +/// Temporal error set mapping to C API error kinds +pub const TemporalError = error{ + /// Generic temporal error + Generic, + /// Type error (invalid type or conversion) + TypeError, + /// Range error (value out of valid range) + RangeError, + /// Syntax error (invalid format or parsing) + SyntaxError, + /// Assertion failed (should not happen) + AssertionFailed, + /// Other unspecified error + Unknown, +}; + +/// Extract result from C API, mapping errors to specific Zig error types. +pub inline fn extractResult(result: anytype) TemporalError!Success(@TypeOf(result)) { + if (success(result)) |value| return value; + + const err = result.unnamed_0.err; + // const message = if (fromOption(err.msg)) |sv| + // fromDiplomatStringView(sv) + // else + // "(no error message)"; + + return switch (err.kind) { + c.ErrorKind_Generic => TemporalError.Generic, + c.ErrorKind_Type => TemporalError.TypeError, + c.ErrorKind_Range => TemporalError.RangeError, + c.ErrorKind_Syntax => TemporalError.SyntaxError, + c.ErrorKind_Assert => @panic("temporal_rs assertion failed"), + else => TemporalError.Unknown, + }; +}