fix: create zig errror wrapper

This commit is contained in:
Nurul Huda (Apon) 2026-01-27 00:31:53 +06:00
parent d2f98710f2
commit ae74ee2aa7
3 changed files with 68 additions and 20 deletions

View file

@ -216,27 +216,27 @@ fn roundWithProvider(self: Duration, options: RoundingOptions, relative_to: Rela
/// Compare two durations (Temporal.Duration.compare). /// Compare two durations (Temporal.Duration.compare).
pub fn compare(self: Duration, other: Duration, relative_to: RelativeTo) !i8 { 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()); 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. /// Compare two durations with an explicit provider.
fn compareWithProvider(self: Duration, other: Duration, relative_to: RelativeTo, provider: *const abi.c.Provider) !i8 { 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); 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). /// Get the total value of the duration in the specified unit (Temporal.Duration.prototype.total).
pub fn total(self: Duration, options: TotalOptions) !f64 { 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 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); 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. /// Get the total value of the duration with an explicit provider.
fn totalWithProvider(self: Duration, options: TotalOptions, provider: *const abi.c.Provider) !f64 { 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 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); 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. /// Convert to string (Temporal.Duration.prototype.toString); caller owns returned slice.
@ -272,12 +272,12 @@ pub fn deinit(self: Duration) void {
// --- Helpers ----------------------------------------------------------------- // --- Helpers -----------------------------------------------------------------
fn handleVoidResult(res: anytype) !void { inline fn handleVoidResult(res: anytype) !void {
_ = abi.success(res) orelse return error.TemporalError; _ = try abi.extractResult(res);
} }
fn wrapDuration(res: anytype) !Duration { 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 }; return .{ ._inner = ptr };
} }
@ -431,3 +431,28 @@ test clone {
try std.testing.expectEqual(dur.hours(), cloned.hours()); 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 }));
}
}

View file

@ -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 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; 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 }; return .{ ._inner = ptr };
} }
@ -285,12 +278,6 @@ pub fn deinit(self: PlainDate) void {
fn wrapPlainDate(res: anytype) !PlainDate { fn wrapPlainDate(res: anytype) !PlainDate {
const ptr = (abi.success(res) orelse return error.TemporalError) orelse return error.TemporalError; 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 }; return .{ ._inner = ptr };
} }

View file

@ -261,3 +261,39 @@ pub fn Success(comptime Result: type) type {
if (!@hasField(Union, "ok")) return void; if (!@hasField(Union, "ok")) return void;
return @FieldType(Union, "ok"); 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,
};
}