fix: instant/duration complete tc39 alignment

This commit is contained in:
Nurul Huda (Apon) 2026-01-28 02:50:48 +06:00
parent 7c4cb5a3ca
commit 6ff4ef1298
8 changed files with 245 additions and 70 deletions

View file

@ -2,15 +2,35 @@ const std = @import("std");
const abi = @import("abi.zig"); const abi = @import("abi.zig");
const temporal = @import("temporal.zig"); const temporal = @import("temporal.zig");
const PlainDate = @import("PlainDate.zig");
const PlainDateTime = @import("PlainDateTime.zig");
const ZonedDateTime = @import("ZonedDateTime.zig");
const Duration = @This(); const Duration = @This();
pub const RoundingOptions = temporal.RoundingOptions;
pub const ToStringOptions = temporal.ToStringRoundingOptions; pub const ToStringOptions = temporal.ToStringRoundingOptions;
pub const ToStringRoundingOptions = temporal.ToStringRoundingOptions; pub const ToStringRoundingOptions = temporal.ToStringRoundingOptions;
pub const Unit = temporal.Unit; pub const Unit = temporal.Unit;
pub const RoundingMode = temporal.RoundingMode; pub const RoundingMode = temporal.RoundingMode;
pub const Sign = temporal.Sign; pub const Sign = temporal.Sign;
pub const RoundingOptions = struct {
largest_unit: ?Unit = null,
smallest_unit: ?Unit = null,
rounding_mode: ?RoundingMode = null,
rounding_increment: ?u32 = null,
relative_to: ?RelativeTo = null,
pub fn toCApi(self: RoundingOptions) abi.c.RoundingOptions {
return .{
.largest_unit = abi.toUnitOption(temporal.toCUnit(self.largest_unit)),
.smallest_unit = abi.toUnitOption(temporal.toCUnit(self.smallest_unit)),
.rounding_mode = abi.toRoundingModeOption(temporal.toCRoundingMode(self.rounding_mode)),
.increment = abi.toOption(abi.c.OptionU32, self.rounding_increment),
};
}
};
/// Partial duration specification for creating Duration objects. /// Partial duration specification for creating Duration objects.
/// This is a wrapper around the C API type to avoid exposing C types directly. /// This is a wrapper around the C API type to avoid exposing C types directly.
pub const PartialDuration = struct { pub const PartialDuration = struct {
@ -52,23 +72,30 @@ const ZonedDateTimeRef = struct {
}; };
/// Relative-to context for duration operations. /// Relative-to context for duration operations.
pub const RelativeTo = struct { pub const RelativeTo = union(enum) {
plain_date: ?PlainDateRef = null, plain_date: PlainDate,
zoned_date_time: ?ZonedDateTimeRef = null, plain_date_time: PlainDateTime,
zoned_date_time: ZonedDateTime,
fn toCApi(self: RelativeTo) abi.c.RelativeTo { fn toCApi(self: RelativeTo) abi.c.RelativeTo {
return .{ switch (self) {
.date = if (self.plain_date) |pd| pd._inner else null, .plain_date => |pd| return .{ .date = pd._inner },
.zoned = if (self.zoned_date_time) |zdt| zdt._inner else null, .zoned_date_time => |zdt| return .{ .zoned = zdt._inner },
}; .plain_date_time => |pdt| return .{ .date = (pdt.toPlainDate() catch unreachable)._inner },
}
} }
}; };
/// Options for Duration.total() providing unit and relative-to context. /// Options for Duration.total() providing unit and relative-to context.
pub const TotalOptions = struct { pub const TotalOptions = struct {
unit: Unit, unit: Unit,
relative_to: ?RelativeTo = null, relative_to: ?RelativeTo = null,
}; };
pub const CompareOptions = struct {
relative_to: ?RelativeTo = null,
};
_inner: *abi.c.Duration, _inner: *abi.c.Duration,
/// Construct a Duration from years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, and nanoseconds. /// Construct a Duration from years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, and nanoseconds.
@ -99,18 +126,54 @@ pub fn init(
)); ));
} }
/// Parse an ISO 8601 duration string (Temporal.Duration.from). /// The Temporal.Duration.from() static method creates a new Temporal.Duration object from one of the following:
pub fn from(text: []const u8) !Duration { /// - A Temporal.Duration instance, which creates a copy of the instance.
const view = abi.toDiplomatStringView(text); /// - An ISO 8601 string representing a duration.
return wrapDuration(abi.c.temporal_rs_Duration_from_utf8(view)); /// - A @Temporal.Duration.PartialDuration struct containing at least one of the following properties:
/// - days
/// - hours
/// - microseconds
/// - milliseconds
/// - minutes
/// - months
/// - nanoseconds
/// - seconds
/// - weeks
/// - years
/// The resulting duration must not have mixed signs, so all of these properties must have the same sign (or zero). Missing properties are treated as zero.
pub fn from(info: anytype) !Duration {
const T = @TypeOf(info);
if (T == Duration) return info.clone();
if (T == PartialDuration) return fromPartialDuration(info);
// Handle string types (both literals and slices)
const type_info = @typeInfo(T);
switch (type_info) {
.pointer => {
const ptr = type_info.pointer;
const ChildType = switch (@typeInfo(ptr.child)) {
.array => |arr| arr.child,
else => ptr.child,
};
if (ChildType == u8) return fromUtf8(info);
if (ChildType == u16) return fromUtf16(info);
},
else => @compileError("from() expects a Duration, []const u8, or []const u16, or Temporal.Duration.PartialDuration"),
}
} }
/// Parse an ISO 8601 UTF-16 duration string. inline fn fromUtf16(text: []const u16) !Duration {
fn fromUtf16(text: []const u16) !Duration {
const view = abi.toDiplomatString16View(text); const view = abi.toDiplomatString16View(text);
return wrapDuration(abi.c.temporal_rs_Duration_from_utf16(view)); return wrapDuration(abi.c.temporal_rs_Duration_from_utf16(view));
} }
inline fn fromUtf8(text: []const u8) !Duration {
const view = abi.toDiplomatStringView(text);
return wrapDuration(abi.c.temporal_rs_Duration_from_utf8(view));
}
/// Create a Duration from a partial duration (where some fields may be omitted). /// Create a Duration from a partial duration (where some fields may be omitted).
fn fromPartialDuration(partial: PartialDuration) !Duration { fn fromPartialDuration(partial: PartialDuration) !Duration {
return wrapDuration(abi.c.temporal_rs_Duration_from_partial_duration(partial.toCApi())); return wrapDuration(abi.c.temporal_rs_Duration_from_partial_duration(partial.toCApi()));
@ -204,8 +267,9 @@ pub fn subtract(self: Duration, other: Duration) !Duration {
} }
/// Round the duration according to the specified options (Temporal.Duration.prototype.round). /// Round the duration according to the specified options (Temporal.Duration.prototype.round).
pub fn round(self: Duration, options: RoundingOptions, relative_to: RelativeTo) !Duration { pub fn round(self: Duration, options: RoundingOptions) !Duration {
return wrapDuration(abi.c.temporal_rs_Duration_round(self._inner, options.toCApi(), relative_to.toCApi())); const rel = if (options.relative_to) |r| r.toCApi() else abi.c.RelativeTo{ .date = null, .zoned = null };
return wrapDuration(abi.c.temporal_rs_Duration_round(self._inner, options.toCApi(), rel));
} }
/// Round the duration with an explicit provider. /// Round the duration with an explicit provider.
@ -214,8 +278,9 @@ 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, options: CompareOptions) !i8 {
const res = abi.c.temporal_rs_Duration_compare(self._inner, other._inner, relative_to.toCApi()); const rel = if (options.relative_to) |r| r.toCApi() else abi.c.RelativeTo{ .date = null, .zoned = null };
const res = abi.c.temporal_rs_Duration_compare(self._inner, other._inner, rel);
return try abi.extractResult(res); return try abi.extractResult(res);
} }
@ -304,7 +369,7 @@ test init {
} }
test from { test from {
// P1Y2M3DT4H5M6.789S = 1 year, 2 months, 3 days, 4 hours, 5 minutes, 6.789 seconds { // P1Y2M3DT4H5M6.789S = 1 year, 2 months, 3 days, 4 hours, 5 minutes, 6.789 seconds
const dur = try Duration.from("P1Y2M3DT4H5M6.789S"); const dur = try Duration.from("P1Y2M3DT4H5M6.789S");
defer dur.deinit(); defer dur.deinit();
@ -330,6 +395,28 @@ test from {
try std.testing.expectEqual(@as(i64, -1), dur_negative.days()); try std.testing.expectEqual(@as(i64, -1), dur_negative.days());
try std.testing.expectEqual(Sign.negative, dur_negative.sign()); try std.testing.expectEqual(Sign.negative, dur_negative.sign());
}
{
const partial = PartialDuration{
.hours = 3,
.minutes = 45,
};
const dur = try Duration.from(partial);
defer dur.deinit();
try std.testing.expectEqual(@as(i64, 3), dur.hours());
try std.testing.expectEqual(@as(i64, 45), dur.minutes());
}
{
const dur1 = try Duration.from("PT1H");
defer dur1.deinit();
const dur2 = try Duration.from(dur1);
defer dur2.deinit();
try std.testing.expectEqual(@as(i64, 1), dur2.hours());
try std.testing.expectEqual(@as(i64, 0), dur2.minutes());
}
} }
test blank { test blank {
@ -353,6 +440,17 @@ test add {
try std.testing.expectEqual(@as(i64, 30), result.minutes()); try std.testing.expectEqual(@as(i64, 30), result.minutes());
} }
test compare {
const dur1 = try Duration.from("PT1H");
defer dur1.deinit();
const dur2 = try Duration.from("PT30M");
defer dur2.deinit();
const result = try dur1.compare(dur2, .{});
try std.testing.expectEqual(@as(i8, 1), result);
}
test subtract { test subtract {
const dur1 = try Duration.from("PT2H"); const dur1 = try Duration.from("PT2H");
defer dur1.deinit(); defer dur1.deinit();
@ -432,6 +530,51 @@ test clone {
try std.testing.expectEqual(dur.hours(), cloned.hours()); try std.testing.expectEqual(dur.hours(), cloned.hours());
} }
test round {
{
const dur = try Duration.from("PT1H30M");
defer dur.deinit();
const rounded = try dur.round(.{
.smallest_unit = .hour,
.relative_to = .{
.plain_date_time = try PlainDateTime.init(2024, 1, 1, 12, 0, 0, 0, 0, 0),
},
});
defer rounded.deinit();
try std.testing.expectEqual(@as(i64, 2), rounded.hours());
try std.testing.expectEqual(@as(i64, 0), rounded.minutes());
}
{
const dur = try Duration.from("PT1H30M");
defer dur.deinit();
const rounded = try dur.round(.{
.smallest_unit = .hour,
.relative_to = .{
.plain_date = try PlainDate.init(2024, 1, 1),
},
});
defer rounded.deinit();
try std.testing.expectEqual(@as(i64, 2), rounded.hours());
try std.testing.expectEqual(@as(i64, 0), rounded.minutes());
}
{
const dur = try Duration.from("PT1H30M");
defer dur.deinit();
const rounded = try dur.round(.{ .smallest_unit = .hour });
defer rounded.deinit();
try std.testing.expectEqual(@as(i64, 2), rounded.hours());
try std.testing.expectEqual(@as(i64, 0), rounded.minutes());
}
}
test total { test total {
{ {
const dur = try Duration.init(0, 0, 0, 0, 1, 30, 0, 0, 0, 0); const dur = try Duration.init(0, 0, 0, 0, 1, 30, 0, 0, 0, 0);

View file

@ -48,8 +48,6 @@ pub const ToStringOptions = struct {
}; };
_inner: *abi.c.Instant, _inner: *abi.c.Instant,
epoch_milliseconds: i64,
epoch_nanoseconds: i128,
/// Construct from epoch nanoseconds (Temporal.Instant.fromEpochNanoseconds). /// Construct from epoch nanoseconds (Temporal.Instant.fromEpochNanoseconds).
pub fn init(epoch_ns: i128) !Instant { pub fn init(epoch_ns: i128) !Instant {
@ -193,7 +191,15 @@ fn toZonedDateTimeIsoWithProvider(self: Instant, zone: TimeZone, provider: *cons
/// Clone the underlying instant. /// Clone the underlying instant.
fn clone(self: Instant) Instant { fn clone(self: Instant) Instant {
const ptr = abi.c.temporal_rs_Instant_clone(self._inner) orelse unreachable; const ptr = abi.c.temporal_rs_Instant_clone(self._inner) orelse unreachable;
return .{ ._inner = ptr, .epoch_milliseconds = abi.c.temporal_rs_Instant_epoch_milliseconds(ptr), .epoch_nanoseconds = abi.fromI128Nanoseconds(abi.c.temporal_rs_Instant_epoch_nanoseconds(ptr)) }; return .{ ._inner = ptr };
}
pub fn epochMilliseconds(self: Instant) i64 {
return abi.c.temporal_rs_Instant_epoch_milliseconds(self._inner);
}
pub fn epochNanoseconds(self: Instant) i128 {
return abi.fromI128Nanoseconds(abi.c.temporal_rs_Instant_epoch_nanoseconds(self._inner));
} }
pub fn deinit(self: Instant) void { pub fn deinit(self: Instant) void {
@ -206,8 +212,6 @@ fn wrapInstant(res: anytype) !Instant {
const ptr = (try abi.extractResult(res)) orelse return abi.TemporalError.Generic; const ptr = (try abi.extractResult(res)) orelse return abi.TemporalError.Generic;
return .{ return .{
._inner = ptr, ._inner = ptr,
.epoch_milliseconds = abi.c.temporal_rs_Instant_epoch_milliseconds(ptr),
.epoch_nanoseconds = abi.fromI128Nanoseconds(abi.c.temporal_rs_Instant_epoch_nanoseconds(ptr)),
}; };
} }
@ -258,7 +262,7 @@ test init {
const inst = try Instant.init(epoch_ns); const inst = try Instant.init(epoch_ns);
defer inst.deinit(); defer inst.deinit();
try std.testing.expectEqual(epoch_ns, inst.epoch_nanoseconds); try std.testing.expectEqual(epoch_ns, inst.epochNanoseconds());
} }
test fromEpochNanoseconds { test fromEpochNanoseconds {
@ -271,8 +275,8 @@ test fromEpochNanoseconds {
const min_inst = try Instant.fromEpochNanoseconds(min_ns); const min_inst = try Instant.fromEpochNanoseconds(min_ns);
defer min_inst.deinit(); defer min_inst.deinit();
try std.testing.expectEqual(max_ns, max_inst.epoch_nanoseconds); try std.testing.expectEqual(max_ns, max_inst.epochNanoseconds());
try std.testing.expectEqual(min_ns, min_inst.epoch_nanoseconds); try std.testing.expectEqual(min_ns, min_inst.epochNanoseconds());
try std.testing.expectError(error.RangeError, Instant.fromEpochNanoseconds(max_ns + 1)); try std.testing.expectError(error.RangeError, Instant.fromEpochNanoseconds(max_ns + 1));
try std.testing.expectError(error.RangeError, Instant.fromEpochNanoseconds(min_ns - 1)); try std.testing.expectError(error.RangeError, Instant.fromEpochNanoseconds(min_ns - 1));
@ -282,13 +286,13 @@ test from {
// Test parsing from UTF-8 string // Test parsing from UTF-8 string
const inst = try Instant.from("2024-03-15T14:30:45.123Z"); const inst = try Instant.from("2024-03-15T14:30:45.123Z");
defer inst.deinit(); defer inst.deinit();
try std.testing.expectEqual(@as(i64, 1_710_513_045_123), inst.epoch_milliseconds); try std.testing.expectEqual(@as(i64, 1_710_513_045_123), inst.epochMilliseconds());
// Test creating from another Instant // Test creating from another Instant
const inst2 = try Instant.from(inst); const inst2 = try Instant.from(inst);
defer inst2.deinit(); defer inst2.deinit();
try std.testing.expectEqual(inst.epoch_milliseconds, inst2.epoch_milliseconds); try std.testing.expectEqual(inst.epochMilliseconds(), inst2.epochMilliseconds());
try std.testing.expectEqual(inst.epoch_nanoseconds, inst2.epoch_nanoseconds); try std.testing.expectEqual(inst.epochNanoseconds(), inst2.epochNanoseconds());
try std.testing.expect(Instant.equals(inst, inst2)); try std.testing.expect(Instant.equals(inst, inst2));
} }
@ -301,7 +305,7 @@ test fromUtf16 {
const inst = try Instant.fromUtf16(utf16); const inst = try Instant.fromUtf16(utf16);
defer inst.deinit(); defer inst.deinit();
try std.testing.expectEqual(@as(i64, 1_710_513_045_123), inst.epoch_milliseconds); try std.testing.expectEqual(@as(i64, 1_710_513_045_123), inst.epochMilliseconds());
} }
test subtract { test subtract {
@ -313,11 +317,11 @@ test subtract {
const added = try base.add(&dur); const added = try base.add(&dur);
defer added.deinit(); defer added.deinit();
try std.testing.expectEqual(@as(i64, 5_400_000), added.epoch_milliseconds); try std.testing.expectEqual(@as(i64, 5_400_000), added.epochMilliseconds());
const subbed = try added.subtract(&dur); const subbed = try added.subtract(&dur);
defer subbed.deinit(); defer subbed.deinit();
try std.testing.expectEqual(@as(i64, 0), subbed.epoch_milliseconds); try std.testing.expectEqual(@as(i64, 0), subbed.epochMilliseconds());
} }
test compare { test compare {
@ -410,7 +414,7 @@ test round {
const rounded = try inst.round(opts); const rounded = try inst.round(opts);
defer rounded.deinit(); defer rounded.deinit();
const ns = rounded.epoch_nanoseconds; const ns = rounded.epochNanoseconds();
try std.testing.expectEqual(@as(i128, 1_609_459_245_000_000_000), ns); try std.testing.expectEqual(@as(i128, 1_609_459_245_000_000_000), ns);
} }
@ -421,7 +425,7 @@ test clone {
const cloned = inst.clone(); const cloned = inst.clone();
defer cloned.deinit(); defer cloned.deinit();
try std.testing.expectEqual(inst.epoch_milliseconds, cloned.epoch_milliseconds); try std.testing.expectEqual(inst.epochMilliseconds(), cloned.epochMilliseconds());
} }
test toString { test toString {

View file

@ -98,7 +98,7 @@ fn currentParts() CurrentParts {
test instant { test instant {
const inst = try instant(); const inst = try instant();
defer inst.deinit(); defer inst.deinit();
try std.testing.expect(inst.epoch_nanoseconds > 0); try std.testing.expect(inst.epochNanoseconds() > 0);
} }
test plainDateISO { test plainDateISO {

View file

@ -44,10 +44,10 @@ pub const ToZonedDateTimeOptions = struct {
_inner: *abi.c.PlainDate, _inner: *abi.c.PlainDate,
pub fn init(year_val: i32, month_val: u8, day_val: u8) !PlainDate { pub fn init(year_val: i32, month_val: u8, day_val: u8) !PlainDate {
return initWithCalendar(year_val, month_val, day_val, "iso8601"); return calInit(year_val, month_val, day_val, "iso8601");
} }
pub fn initWithCalendar(year_val: i32, month_val: u8, day_val: u8, calendar: []const u8) !PlainDate { pub fn calInit(year_val: i32, month_val: u8, day_val: u8, calendar: []const u8) !PlainDate {
const cal_view = abi.toDiplomatStringView(calendar); const cal_view = abi.toDiplomatStringView(calendar);
const cal_result = abi.c.temporal_rs_AnyCalendarKind_parse_temporal_calendar_string(cal_view); const cal_result = abi.c.temporal_rs_AnyCalendarKind_parse_temporal_calendar_string(cal_view);
const cal_kind = try abi.extractResult(cal_result); const cal_kind = try abi.extractResult(cal_result);

View file

@ -48,6 +48,7 @@ pub const WithOptions = struct {
microsecond: ?u16 = null, microsecond: ?u16 = null,
nanosecond: ?u16 = null, nanosecond: ?u16 = null,
}; };
const PartialDateTime = struct {};
// Constructor - creates a PlainDateTime with all parameters // Constructor - creates a PlainDateTime with all parameters
pub fn init( pub fn init(
@ -75,7 +76,7 @@ pub fn init(
)); ));
} }
pub fn initWithCalendar( pub fn calInit(
year_val: i32, year_val: i32,
month_val: u8, month_val: u8,
day_val: u8, day_val: u8,
@ -105,8 +106,37 @@ pub fn initWithCalendar(
} }
// Parse from string // Parse from string
pub fn from(s: []const u8) !PlainDateTime { pub fn from(info: anytype) !PlainDateTime {
return fromUtf8(s); const T = @TypeOf(info);
if (T == PlainDateTime) return info.clone();
if (T == PartialDateTime) return fromPartial(info);
// Handle string types (both literals and slices)
const type_info = @typeInfo(T);
switch (type_info) {
.pointer => {
const ptr = type_info.pointer;
const ChildType = switch (@typeInfo(ptr.child)) {
.array => |arr| arr.child,
else => ptr.child,
};
if (ChildType == u8) return fromUtf8(info);
if (ChildType == u16) return fromUtf16(info);
},
else => @compileError("from() expects a Duration, []const u8, or []const u16, or Temporal.Duration.PartialDuration"),
}
}
fn fromPartial(info: PartialDateTime) !PlainDateTime {
_ = info;
return error.NotImplemented;
// return abi.c.temporal_rs_PlainDateTime_from_partial(.{
// .date = .{
// .
// }
// }, overflow: struct_ArithmeticOverflow_option)
} }
fn fromUtf8(utf8: []const u8) !PlainDateTime { fn fromUtf8(utf8: []const u8) !PlainDateTime {

View file

@ -202,10 +202,7 @@ pub fn subtract(self: ZonedDateTime, duration: Duration) !ZonedDateTime {
/// Convert to Instant /// Convert to Instant
pub fn toInstant(self: ZonedDateTime) !Instant { pub fn toInstant(self: ZonedDateTime) !Instant {
const instant_ptr = abi.c.temporal_rs_ZonedDateTime_to_instant(self._inner) orelse return error.TemporalError; const instant_ptr = abi.c.temporal_rs_ZonedDateTime_to_instant(self._inner) orelse return error.TemporalError;
const epoch_ms = abi.c.temporal_rs_Instant_epoch_milliseconds(instant_ptr); return .{ ._inner = instant_ptr };
const epoch_ns_parts = abi.c.temporal_rs_Instant_epoch_nanoseconds(instant_ptr);
const epoch_ns = abi.fromI128Nanoseconds(epoch_ns_parts);
return .{ ._inner = instant_ptr, .epoch_milliseconds = epoch_ms, .epoch_nanoseconds = epoch_ns };
} }
/// Convert to JSON string (ISO 8601 format) /// Convert to JSON string (ISO 8601 format)
@ -502,7 +499,7 @@ test toInstant {
const instant = try zdt.toInstant(); const instant = try zdt.toInstant();
defer instant.deinit(); defer instant.deinit();
try std.testing.expectEqual(@as(i64, 1609459200000), instant.epoch_milliseconds); try std.testing.expectEqual(@as(i64, 1609459200000), instant.epochMilliseconds());
} }
test toPlainDate { test toPlainDate {

View file

@ -78,6 +78,7 @@ test Duration {
"ToStringRoundingOptions", "ToStringRoundingOptions",
"Sign", "Sign",
"TotalOptions", "TotalOptions",
"CompareOptions",
}; };
try assertDecls(Duration, checks); try assertDecls(Duration, checks);
@ -142,7 +143,7 @@ test PlainDate {
const checks = .{ const checks = .{
// Constructor // Constructor
"init", "init",
"initWithCalendar", "calInit",
// Static methods // Static methods
"compare", "compare",
@ -200,7 +201,7 @@ test PlainDateTime {
const checks = .{ const checks = .{
// Constructor // Constructor
"init", // Temporal.PlainDateTime() "init", // Temporal.PlainDateTime()
"initWithCalendar", "calInit",
// Static methods // Static methods
"compare", "compare",

View file

@ -159,10 +159,10 @@ pub const ToStringRoundingOptions = struct {
} }
}; };
fn toCUnit(opt: ?Unit) ?c.Unit { pub fn toCUnit(opt: ?Unit) ?c.Unit {
return if (opt) |u| @as(c.Unit, @intCast(u.toCApi())) else null; return if (opt) |u| @as(c.Unit, @intCast(u.toCApi())) else null;
} }
fn toCRoundingMode(opt: ?RoundingMode) ?c.RoundingMode { pub fn toCRoundingMode(opt: ?RoundingMode) ?c.RoundingMode {
return if (opt) |m| @as(c.RoundingMode, @intCast(m.toCApi())) else null; return if (opt) |m| @as(c.RoundingMode, @intCast(m.toCApi())) else null;
} }