diff --git a/src/Instant.zig b/src/Instant.zig index 10faca2..e07e36a 100644 --- a/src/Instant.zig +++ b/src/Instant.zig @@ -2,7 +2,7 @@ const std = @import("std"); const Instant = @This(); -inner: *CInstant, +_inner: *CInstant, epochMilliseconds: i64, epochNanoseconds: i128, @@ -36,37 +36,37 @@ fn fromUtf16(text: []const u16) !Instant { /// Add a Duration to this instant (Temporal.Instant.prototype.add). pub fn add(self: Instant, duration: *const Duration) !Instant { - return wrapInstant(temporal_rs_Instant_add(self.inner, duration)); + return wrapInstant(temporal_rs_Instant_add(self._inner, duration)); } /// Subtract a Duration from this instant (Temporal.Instant.prototype.subtract). pub fn subtract(self: Instant, duration: *const Duration) !Instant { - return wrapInstant(temporal_rs_Instant_subtract(self.inner, duration)); + return wrapInstant(temporal_rs_Instant_subtract(self._inner, duration)); } /// Difference until another instant (Temporal.Instant.prototype.until). pub fn until(self: Instant, other: Instant, settings: DifferenceSettings) !DurationHandle { - return wrapDuration(temporal_rs_Instant_until(self.inner, other.inner, settings)); + return wrapDuration(temporal_rs_Instant_until(self._inner, other._inner, settings)); } /// Difference since another instant (Temporal.Instant.prototype.since). pub fn since(self: Instant, other: Instant, settings: DifferenceSettings) !DurationHandle { - return wrapDuration(temporal_rs_Instant_since(self.inner, other.inner, settings)); + return wrapDuration(temporal_rs_Instant_since(self._inner, other._inner, settings)); } /// Round this instant (Temporal.Instant.prototype.round). pub fn round(self: Instant, options: RoundingOptions) !Instant { - return wrapInstant(temporal_rs_Instant_round(self.inner, options)); + return wrapInstant(temporal_rs_Instant_round(self._inner, options)); } /// Compare two instants (Temporal.Instant.compare). pub fn compare(a: Instant, b: Instant) i8 { - return temporal_rs_Instant_compare(a.inner, b.inner); + return temporal_rs_Instant_compare(a._inner, b._inner); } /// Equality check (Temporal.Instant.prototype.equals). pub fn equals(a: Instant, b: Instant) bool { - return temporal_rs_Instant_equals(a.inner, b.inner); + return temporal_rs_Instant_equals(a._inner, b._inner); } /// Epoch milliseconds accessor (Temporal.Instant.prototype.epochMilliseconds). @@ -92,7 +92,7 @@ pub fn toString(self: Instant, allocator: std.mem.Allocator, opts: ToStringOptio const writer = diplomat_buffer_write_create(128); defer diplomat_buffer_write_destroy(writer); - const res = temporal_rs_Instant_to_ixdtf_string_with_compiled_data(self.inner, zone_opt, rounding, writer); + const res = temporal_rs_Instant_to_ixdtf_string_with_compiled_data(self._inner, zone_opt, rounding, writer); try handleVoidResult(res); const len = diplomat_buffer_write_len(writer); @@ -115,7 +115,7 @@ fn toStringWithProvider(self: Instant, allocator: std.mem.Allocator, provider: * const writer = diplomat_buffer_write_create(128); defer diplomat_buffer_write_destroy(writer); - const res = temporal_rs_Instant_to_ixdtf_string_with_provider(self.inner, zone_opt, rounding, provider, writer); + const res = temporal_rs_Instant_to_ixdtf_string_with_provider(self._inner, zone_opt, rounding, provider, writer); try handleVoidResult(res); const len = diplomat_buffer_write_len(writer); @@ -140,22 +140,22 @@ pub fn toLocaleString(self: Instant, allocator: std.mem.Allocator) ![]u8 { /// Convert to ZonedDateTime using built-in provider (Temporal.Instant.prototype.toZonedDateTimeISO). pub fn toZonedDateTimeISO(self: Instant, zone: TimeZone) !ZonedDateTimeHandle { - return wrapZonedDateTime(temporal_rs_Instant_to_zoned_date_time_iso(self.inner, zone)); + return wrapZonedDateTime(temporal_rs_Instant_to_zoned_date_time_iso(self._inner, zone)); } /// Convert to ZonedDateTime using an explicit provider. fn toZonedDateTimeIsoWithProvider(self: Instant, zone: TimeZone, provider: *const Provider) !ZonedDateTimeHandle { - return wrapZonedDateTime(temporal_rs_Instant_to_zoned_date_time_iso_with_provider(self.inner, zone, provider)); + return wrapZonedDateTime(temporal_rs_Instant_to_zoned_date_time_iso_with_provider(self._inner, zone, provider)); } /// Clone the underlying instant. fn clone(self: Instant) Instant { - const ptr = temporal_rs_Instant_clone(self.inner); - return .{ .inner = ptr, .epochMilliseconds = temporal_rs_Instant_epoch_milliseconds(ptr), .epochNanoseconds = partsToI128(temporal_rs_Instant_epoch_nanoseconds(ptr)) }; + const ptr = temporal_rs_Instant_clone(self._inner); + return .{ ._inner = ptr, .epochMilliseconds = temporal_rs_Instant_epoch_milliseconds(ptr), .epochNanoseconds = partsToI128(temporal_rs_Instant_epoch_nanoseconds(ptr)) }; } pub fn deinit(self: Instant) void { - temporal_rs_Instant_destroy(self.inner); + temporal_rs_Instant_destroy(self._inner); } // --- Helpers ----------------------------------------------------------------- @@ -163,7 +163,7 @@ pub fn deinit(self: Instant) void { fn wrapInstant(res: InstantResult) !Instant { if (!res.is_ok) return error.TemporalError; const ptr = res.result.ok orelse return error.TemporalError; - return .{ .inner = ptr, .epochMilliseconds = temporal_rs_Instant_epoch_milliseconds(ptr), .epochNanoseconds = partsToI128(temporal_rs_Instant_epoch_nanoseconds(ptr)) }; + return .{ ._inner = ptr, .epochMilliseconds = temporal_rs_Instant_epoch_milliseconds(ptr), .epochNanoseconds = partsToI128(temporal_rs_Instant_epoch_nanoseconds(ptr)) }; } fn wrapDuration(res: DurationResult) !DurationHandle { diff --git a/src/root.zig b/src/root.zig index fcfd387..55bb862 100644 --- a/src/root.zig +++ b/src/root.zig @@ -93,13 +93,13 @@ test "Temporal.Instant" { "toJSON", "toLocaleString", "toString", - "toZonedDateTimeISO", // Temporal.Instant.toZonedDateTimeISO + "toZonedDateTimeISO", "until", "valueOf", // Properties - // "epochMilliseconds", - // "epochNanoseconds", + "epochMilliseconds", + "epochNanoseconds", }; try assertDecls(Instant, checks); @@ -403,22 +403,38 @@ fn assertDecls(comptime T: type, checks: anytype) !void { const std = @import("std"); const typeInfo = @typeInfo(T); - // Check: all items in checks exist + // Check: all items in checks exist (either as decls or as fields) inline for (checks) |check| { const should_ignore = std.mem.eql(u8, check, "deinit") or std.mem.eql(u8, check, "valueOf"); - const has = @hasDecl(T, check); if (!should_ignore) { - if (!has) std.log.err("Missing {s} decl: {s}", .{ @typeName(T), check }); + const hasDecl = @hasDecl(T, check); + + // Also check if it's a field (property) + var hasField = false; + if (typeInfo == .@"struct") { + const struct_info = typeInfo.@"struct"; + inline for (struct_info.fields) |field| { + if (std.mem.eql(u8, field.name, check)) { + hasField = true; + break; + } + } + } + + const has = hasDecl or hasField; + if (!has) std.log.err("Missing {s} decl or field: {s}", .{ @typeName(T), check }); try std.testing.expect(has); } } - // Check: no extraneous declarations beyond checks + // Check: no extraneous declarations or fields beyond checks if (typeInfo == .@"struct") { const struct_info = typeInfo.@"struct"; + + // Check declarations inline for (struct_info.decls) |decl| { // Allow deinit as extraneous if (comptime std.mem.eql(u8, decl.name, "deinit")) continue; @@ -436,5 +452,24 @@ fn assertDecls(comptime T: type, checks: anytype) !void { try std.testing.expect(false); } } + + // Check fields (properties) + inline for (struct_info.fields) |field| { + // Allow internal fields (starting with underscore) + if (comptime std.mem.startsWith(u8, field.name, "_")) continue; + + var found = false; + inline for (checks) |check| { + if (std.mem.eql(u8, field.name, check)) { + found = true; + break; + } + } + + if (!found) { + std.log.err("Extraneous {s} field: {s}", .{ @typeName(T), field.name }); + try std.testing.expect(false); + } + } } }