feat: more native zig types

This commit is contained in:
Nurul Huda (Apon) 2026-01-26 14:23:31 +06:00
parent ca29ace7fa
commit acbadeb89a
No known key found for this signature in database
GPG key ID: 5D3F1DE2855A2F79
5 changed files with 108 additions and 31 deletions

View file

@ -16,6 +16,8 @@
.paths = .{ .paths = .{
"build.zig", "build.zig",
"build.zig.zon", "build.zig.zon",
"Cargo.toml",
"Cargo.lock",
"src", "src",
"lib", "lib",
}, },

View file

@ -140,12 +140,12 @@ 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, relative_to: RelativeTo) !Duration {
return wrapDuration(abi.c.temporal_rs_Duration_round(self._inner, options, relative_to)); return wrapDuration(abi.c.temporal_rs_Duration_round(self._inner, options.toCApi(), relative_to));
} }
/// Round the duration with an explicit provider. /// Round the duration with an explicit provider.
fn roundWithProvider(self: Duration, options: RoundingOptions, relative_to: RelativeTo, provider: *const abi.c.Provider) !Duration { fn roundWithProvider(self: Duration, options: RoundingOptions, relative_to: RelativeTo, provider: *const abi.c.Provider) !Duration {
return wrapDuration(abi.c.temporal_rs_Duration_round_with_provider(self._inner, options, relative_to, provider)); return wrapDuration(abi.c.temporal_rs_Duration_round_with_provider(self._inner, options.toCApi(), relative_to, provider));
} }
/// Compare two durations (Temporal.Duration.compare). /// Compare two durations (Temporal.Duration.compare).
@ -252,9 +252,8 @@ const Precision = abi.c.Precision;
const Unit_option = abi.c.Unit_option; const Unit_option = abi.c.Unit_option;
const RoundingMode_option = abi.c.RoundingMode_option; const RoundingMode_option = abi.c.RoundingMode_option;
pub const RoundingOptions = abi.c.RoundingOptions; pub const RoundingOptions = temporal.RoundingOptions;
pub const ToStringRoundingOptions = abi.c.ToStringRoundingOptions; pub const ToStringRoundingOptions = abi.c.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;

View file

@ -12,6 +12,8 @@ epoch_nanoseconds: i128,
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 = temporal.RoundingOptions;
pub const DifferenceSettings = temporal.DifferenceSettings;
/// 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 {
@ -29,18 +31,41 @@ pub fn fromEpochNanoseconds(epoch_ns: i128) !Instant {
return wrapInstant(abi.c.temporal_rs_Instant_try_new(parts)); return wrapInstant(abi.c.temporal_rs_Instant_try_new(parts));
} }
/// Parse an ISO 8601 string (Temporal.Instant.from). /// Parse an ISO 8601 string (Temporal.Instant.from) or from another Temporal.Instant.
pub fn from(text: []const u8) !Instant { pub fn from(info: anytype) !Instant {
const view = abi.toDiplomatStringView(text); const T = @TypeOf(info);
return wrapInstant(abi.c.temporal_rs_Instant_from_utf8(view));
if (T == Instant) return info.clone();
// 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 an Instant, []const u8, or []const u16"),
}
} }
/// Parse an ISO 8601 UTF-16 string (Temporal.Instant.from). /// Parse an ISO 8601 UTF-16 string (Temporal.Instant.from).
fn fromUtf16(text: []const u16) !Instant { inline fn fromUtf16(text: []const u16) !Instant {
const view = abi.toDiplomatString16View(text); const view = abi.toDiplomatString16View(text);
return wrapInstant(abi.c.temporal_rs_Instant_from_utf16(view)); return wrapInstant(abi.c.temporal_rs_Instant_from_utf16(view));
} }
/// Parse an ISO 8601 UTF-16 string (Temporal.Instant.from).
inline fn fromUtf8(text: []const u8) !Instant {
const view = abi.toDiplomatStringView(text);
return wrapInstant(abi.c.temporal_rs_Instant_from_utf8(view));
}
/// Add a Duration to this instant (Temporal.Instant.prototype.add). /// Add a Duration to this instant (Temporal.Instant.prototype.add).
pub fn add(self: Instant, duration: *Duration) !Instant { pub fn add(self: Instant, duration: *Duration) !Instant {
return wrapInstant(abi.c.temporal_rs_Instant_add(self._inner, duration._inner)); return wrapInstant(abi.c.temporal_rs_Instant_add(self._inner, duration._inner));
@ -52,18 +77,18 @@ pub fn subtract(self: Instant, duration: *Duration) !Instant {
} }
/// Difference until another instant (Temporal.Instant.prototype.until). /// Difference until another instant (Temporal.Instant.prototype.until).
pub fn until(self: Instant, other: Instant, settings: abi.c.DifferenceSettings) !DurationHandle { pub fn until(self: Instant, other: Instant, settings: DifferenceSettings) !DurationHandle {
return wrapDuration(abi.c.temporal_rs_Instant_until(self._inner, other._inner, settings)); return wrapDuration(abi.c.temporal_rs_Instant_until(self._inner, other._inner, settings.toCApi()));
} }
/// Difference since another instant (Temporal.Instant.prototype.since). /// Difference since another instant (Temporal.Instant.prototype.since).
pub fn since(self: Instant, other: Instant, settings: abi.c.DifferenceSettings) !DurationHandle { pub fn since(self: Instant, other: Instant, settings: DifferenceSettings) !DurationHandle {
return wrapDuration(abi.c.temporal_rs_Instant_since(self._inner, other._inner, settings)); return wrapDuration(abi.c.temporal_rs_Instant_since(self._inner, other._inner, settings.toCApi()));
} }
/// Round this instant (Temporal.Instant.prototype.round). /// Round this instant (Temporal.Instant.prototype.round).
pub fn round(self: Instant, options: abi.c.RoundingOptions) !Instant { pub fn round(self: Instant, options: RoundingOptions) !Instant {
return wrapInstant(abi.c.temporal_rs_Instant_round(self._inner, options)); return wrapInstant(abi.c.temporal_rs_Instant_round(self._inner, options.toCApi()));
} }
/// Compare two instants (Temporal.Instant.compare). /// Compare two instants (Temporal.Instant.compare).
@ -258,10 +283,17 @@ test fromEpochNanoseconds {
} }
test from { test from {
// 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.epoch_milliseconds);
// Test creating from another Instant
const inst2 = try Instant.from(inst);
defer inst2.deinit();
try std.testing.expectEqual(inst.epoch_milliseconds, inst2.epoch_milliseconds);
try std.testing.expectEqual(inst.epoch_nanoseconds, inst2.epoch_nanoseconds);
try std.testing.expect(Instant.equals(inst, inst2));
} }
test fromUtf16 { test fromUtf16 {
@ -326,11 +358,11 @@ test until {
const later = try Instant.fromEpochMilliseconds(3_600_000); const later = try Instant.fromEpochMilliseconds(3_600_000);
defer later.deinit(); defer later.deinit();
const settings = abi.c.DifferenceSettings{ const settings = DifferenceSettings{
.largest_unit = abi.toUnitOption(Unit.hour.toCApi()), .largest_unit = .hour,
.smallest_unit = abi.toUnitOption(Unit.second.toCApi()), .smallest_unit = .second,
.rounding_mode = abi.toRoundingModeOption(RoundingMode.trunc.toCApi()), .rounding_mode = .trunc,
.increment = abi.toOption(abi.c.OptionU32, null), .increment = null,
}; };
var until_handle = try earlier.until(later, settings); var until_handle = try earlier.until(later, settings);
@ -350,11 +382,11 @@ test since {
const later = try Instant.fromEpochMilliseconds(3_600_000); const later = try Instant.fromEpochMilliseconds(3_600_000);
defer later.deinit(); defer later.deinit();
const settings = abi.c.DifferenceSettings{ const settings = DifferenceSettings{
.largest_unit = abi.toUnitOption(Unit.hour.toCApi()), .largest_unit = .hour,
.smallest_unit = abi.toUnitOption(Unit.second.toCApi()), .smallest_unit = .second,
.rounding_mode = abi.toRoundingModeOption(RoundingMode.trunc.toCApi()), .rounding_mode = .trunc,
.increment = abi.toOption(abi.c.OptionU32, null), .increment = null,
}; };
var until_handle = try earlier.until(later, settings); var until_handle = try earlier.until(later, settings);
@ -372,11 +404,11 @@ test round {
const inst = try Instant.fromEpochNanoseconds(1_609_459_245_123_456_789); const inst = try Instant.fromEpochNanoseconds(1_609_459_245_123_456_789);
defer inst.deinit(); defer inst.deinit();
const opts = abi.c.RoundingOptions{ const opts = RoundingOptions{
.largest_unit = abi.toUnitOption(null), .largest_unit = null,
.smallest_unit = abi.toUnitOption(Unit.second.toCApi()), .smallest_unit = .second,
.rounding_mode = abi.toRoundingModeOption(RoundingMode.half_expand.toCApi()), .rounding_mode = .half_expand,
.increment = abi.toOption(abi.c.OptionU32, null), .increment = null,
}; };
const rounded = try inst.round(opts); const rounded = try inst.round(opts);

View file

@ -114,6 +114,8 @@ test Instant {
"Unit", "Unit",
"RoundingMode", "RoundingMode",
"Sign", "Sign",
"RoundingOptions",
"DifferenceSettings",
}; };
try assertDecls(Instant, checks); try assertDecls(Instant, checks);

View file

@ -93,3 +93,45 @@ pub const Sign = enum {
}; };
} }
}; };
/// Options for rounding operations (Instant.round, Duration.round, etc.).
pub const RoundingOptions = struct {
largest_unit: ?Unit = null,
smallest_unit: ?Unit = null,
rounding_mode: ?RoundingMode = null,
increment: ?u32 = null,
pub fn toCApi(self: RoundingOptions) c.RoundingOptions {
return .{
.largest_unit = abi.toUnitOption(toCUnit(self.largest_unit)),
.smallest_unit = abi.toUnitOption(toCUnit(self.smallest_unit)),
.rounding_mode = abi.toRoundingModeOption(toCRoundingMode(self.rounding_mode)),
.increment = abi.toOption(c.OptionU32, self.increment),
};
}
};
/// Options for computing differences between instants.
pub const DifferenceSettings = struct {
largest_unit: ?Unit = null,
smallest_unit: ?Unit = null,
rounding_mode: ?RoundingMode = null,
increment: ?u32 = null,
pub fn toCApi(self: DifferenceSettings) c.DifferenceSettings {
return .{
.largest_unit = abi.toUnitOption(toCUnit(self.largest_unit)),
.smallest_unit = abi.toUnitOption(toCUnit(self.smallest_unit)),
.rounding_mode = abi.toRoundingModeOption(toCRoundingMode(self.rounding_mode)),
.increment = abi.toOption(c.OptionU32, self.increment),
};
}
};
fn toCUnit(opt: ?Unit) ?c.Unit {
return if (opt) |u| @as(c.Unit, @intCast(u.toCApi())) else null;
}
fn toCRoundingMode(opt: ?RoundingMode) ?c.RoundingMode {
return if (opt) |m| @as(c.RoundingMode, @intCast(m.toCApi())) else null;
}