refactor: use temporal_rs as pkg
This commit is contained in:
parent
518998fcd8
commit
46eb39ae26
21 changed files with 1227 additions and 771 deletions
10
.gitignore
vendored
10
.gitignore
vendored
|
|
@ -1,11 +1,9 @@
|
|||
zig-out
|
||||
.zig-cache
|
||||
target
|
||||
|
||||
# Ignore Rust build target directory
|
||||
vendor/temporal/target
|
||||
|
||||
# But track the specific compiled libraries
|
||||
!vendor/temporal/target/**/libtemporal.a
|
||||
!vendor/temporal/target/**/temporal.lib
|
||||
lib
|
||||
!lib/*/libtemporal_capi.a
|
||||
!lib/*/temporal_capi.lib
|
||||
|
||||
tmp
|
||||
477
Cargo.lock
generated
Normal file
477
Cargo.lock
generated
Normal file
|
|
@ -0,0 +1,477 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3"
|
||||
|
||||
[[package]]
|
||||
name = "calendrical_calculations"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3a0b39595c6ee54a8d0900204ba4c401d0ab4eb45adaf07178e8d017541529e7"
|
||||
dependencies = [
|
||||
"core_maths",
|
||||
"displaydoc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "combine"
|
||||
version = "4.6.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core_maths"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77745e017f5edba1a9c1d854f6f3a52dac8a12dd5af5d2f54aecf61e43d80d30"
|
||||
dependencies = [
|
||||
"libm",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "diplomat"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9adb46b05e2f53dcf6a7dfc242e4ce9eb60c369b6b6eb10826a01e93167f59c6"
|
||||
dependencies = [
|
||||
"diplomat_core",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "diplomat-runtime"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0569bd3caaf13829da7ee4e83dbf9197a0e1ecd72772da6d08f0b4c9285c8d29"
|
||||
|
||||
[[package]]
|
||||
name = "diplomat_core"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51731530ed7f2d4495019abc7df3744f53338e69e2863a6a64ae91821c763df1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"serde",
|
||||
"smallvec",
|
||||
"strck",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "displaydoc"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_calendar"
|
||||
version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d6f0e52e009b6b16ba9c0693578796f2dd4aaa59a7f8f920423706714a89ac4e"
|
||||
dependencies = [
|
||||
"calendrical_calculations",
|
||||
"displaydoc",
|
||||
"icu_calendar_data",
|
||||
"icu_locale",
|
||||
"icu_locale_core",
|
||||
"icu_provider",
|
||||
"tinystr",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_calendar_data"
|
||||
version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "527f04223b17edfe0bd43baf14a0cb1b017830db65f3950dc00224860a9a446d"
|
||||
|
||||
[[package]]
|
||||
name = "icu_collections"
|
||||
version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"potential_utf",
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_locale"
|
||||
version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "532b11722e350ab6bf916ba6eb0efe3ee54b932666afec989465f9243fe6dd60"
|
||||
dependencies = [
|
||||
"icu_collections",
|
||||
"icu_locale_core",
|
||||
"icu_locale_data",
|
||||
"icu_provider",
|
||||
"potential_utf",
|
||||
"tinystr",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_locale_core"
|
||||
version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"litemap",
|
||||
"serde",
|
||||
"tinystr",
|
||||
"writeable",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_locale_data"
|
||||
version = "2.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1c5f1d16b4c3a2642d3a719f18f6b06070ab0aef246a6418130c955ae08aa831"
|
||||
|
||||
[[package]]
|
||||
name = "icu_provider"
|
||||
version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_locale_core",
|
||||
"serde",
|
||||
"stable_deref_trait",
|
||||
"writeable",
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
"zerotrie",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ixdtf"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "84de9d95a6d2547d9b77ee3f25fa0ee32e3c3a6484d47a55adebc0439c077992"
|
||||
|
||||
[[package]]
|
||||
name = "jiff-tzdb"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68971ebff725b9e2ca27a601c5eb38a4c5d64422c4cbab0c535f248087eda5c2"
|
||||
|
||||
[[package]]
|
||||
name = "libm"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981"
|
||||
|
||||
[[package]]
|
||||
name = "litemap"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "potential_utf"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
"writeable",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.106"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_core"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.15.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
||||
|
||||
[[package]]
|
||||
name = "stable_deref_trait"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
|
||||
|
||||
[[package]]
|
||||
name = "strck"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42316e70da376f3d113a68d138a60d8a9883c604fe97942721ec2068dab13a9f"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.114"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "synstructure"
|
||||
version = "0.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "temporal_capi"
|
||||
version = "0.1.2"
|
||||
dependencies = [
|
||||
"temporal_capi 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"temporal_rs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "temporal_capi"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a151e402c2bdb6a3a2a2f3f225eddaead2e7ce7dd5d3fa2090deb11b17aa4ed8"
|
||||
dependencies = [
|
||||
"diplomat",
|
||||
"diplomat-runtime",
|
||||
"icu_calendar",
|
||||
"icu_locale",
|
||||
"num-traits",
|
||||
"temporal_rs",
|
||||
"timezone_provider",
|
||||
"writeable",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "temporal_rs"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88afde3bd75d2fc68d77a914bece426aa08aa7649ffd0cdd4a11c3d4d33474d1"
|
||||
dependencies = [
|
||||
"core_maths",
|
||||
"icu_calendar",
|
||||
"icu_locale",
|
||||
"ixdtf",
|
||||
"num-traits",
|
||||
"timezone_provider",
|
||||
"tinystr",
|
||||
"writeable",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "timezone_provider"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df9ba0000e9e73862f3e7ca1ff159e2ddf915c9d8bb11e38a7874760f445d993"
|
||||
dependencies = [
|
||||
"combine",
|
||||
"jiff-tzdb",
|
||||
"tinystr",
|
||||
"tzif",
|
||||
"zerotrie",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinystr"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"serde_core",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tzif"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a0376dfa52cce372f3b095010fd064fb850a5d8fbfd5be8b0ffa3d64eeab5a5d"
|
||||
dependencies = [
|
||||
"combine",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
|
||||
|
||||
[[package]]
|
||||
name = "writeable"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9"
|
||||
|
||||
[[package]]
|
||||
name = "yoke"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954"
|
||||
dependencies = [
|
||||
"stable_deref_trait",
|
||||
"yoke-derive",
|
||||
"zerofrom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yoke-derive"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerofrom"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5"
|
||||
dependencies = [
|
||||
"zerofrom-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerofrom-derive"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerotrie"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerovec"
|
||||
version = "0.11.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
"zerovec-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerovec-derive"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
21
Cargo.toml
Normal file
21
Cargo.toml
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
[package]
|
||||
name = "temporal_capi"
|
||||
version = "0.1.2"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies.temporal_capi]
|
||||
version = "0.1.2"
|
||||
default-features = false
|
||||
features = ["compiled_data"]
|
||||
|
||||
[dependencies.temporal_rs]
|
||||
version = "*"
|
||||
default-features = false
|
||||
features = ["float64_representable_durations"]
|
||||
|
||||
[lib]
|
||||
crate-type = ["staticlib"]
|
||||
|
||||
[profile.release]
|
||||
strip = true
|
||||
lto = true
|
||||
258
build.zig
258
build.zig
|
|
@ -1,22 +1,85 @@
|
|||
const std = @import("std");
|
||||
const build_crab = @import("build_crab");
|
||||
|
||||
pub fn build(b: *std.Build) void {
|
||||
pub fn build(b: *std.Build) !void {
|
||||
const target = b.standardTargetOptions(.{});
|
||||
const optimize = b.standardOptimizeOption(.{});
|
||||
|
||||
// --- Zig Module: temporalz --- //
|
||||
const mod = b.addModule("temporalz", .{
|
||||
.root_source_file = b.path("src/root.zig"),
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
mod.addObjectFile(b.path(getTemporalRsPath(target)));
|
||||
mod.link_libc = true;
|
||||
if (target.result.os.tag == .linux) mod.linkSystemLibrary("unwind", .{});
|
||||
if (target.result.os.tag == .windows) {
|
||||
mod.linkSystemLibrary("ws2_32", .{});
|
||||
mod.linkSystemLibrary("bcrypt", .{});
|
||||
mod.linkSystemLibrary("advapi32", .{});
|
||||
mod.linkSystemLibrary("userenv", .{});
|
||||
|
||||
// --- Rust C ABI: temporal_capi --- //
|
||||
const temporal_rs = b.dependency("temporal_rs", .{ .target = target, .optimize = optimize });
|
||||
mod.addIncludePath(temporal_rs.path("temporal_capi/bindings/c"));
|
||||
|
||||
// --- Rust Crate: temporal_rs --- //
|
||||
{
|
||||
// Determine target triple string for pre-built library lookup
|
||||
const arch_str = @tagName(target.result.cpu.arch);
|
||||
const os_str = @tagName(target.result.os.tag);
|
||||
const target_triple = b.fmt("{s}-{s}", .{ arch_str, os_str });
|
||||
const lib_name = if (target.result.os.tag == .windows) "temporal_capi.lib" else "libtemporal_capi.a";
|
||||
|
||||
// Check if pre-built library exists in lib/<target>/
|
||||
const prebuilt_lib_path = b.fmt("lib/{s}/{s}", .{ target_triple, lib_name });
|
||||
const prebuilt_lib_file = b.path(prebuilt_lib_path);
|
||||
|
||||
// Try to use pre-built library if it exists by checking the path object
|
||||
const use_prebuilt = blk: {
|
||||
// Use the LazyPath to check if the library exists
|
||||
const lib_full_path = prebuilt_lib_file.getPath(b);
|
||||
const lib_check_file = std.fs.openFileAbsolute(lib_full_path, .{}) catch break :blk false;
|
||||
lib_check_file.close();
|
||||
break :blk true;
|
||||
};
|
||||
|
||||
if (use_prebuilt) {
|
||||
// Use pre-built library (no Rust compiler needed)
|
||||
// std.debug.print("Using pre-built temporal_capi library: {s}\n", .{prebuilt_lib_path});
|
||||
mod.addObjectFile(prebuilt_lib_file);
|
||||
} else {
|
||||
// Build from source using Cargo
|
||||
const build_dir = build_crab.addCargoBuild(
|
||||
b,
|
||||
.{
|
||||
.manifest_path = b.path("Cargo.toml"),
|
||||
.cargo_args = if (optimize == .Debug) &.{} else &.{"--release"},
|
||||
},
|
||||
.{
|
||||
.target = target,
|
||||
.optimize = .ReleaseSafe,
|
||||
},
|
||||
);
|
||||
|
||||
// Install .a/.lib to lib/<target>/libtemporal_capi.a/temporal_capi.lib
|
||||
const install_lib = b.addInstallDirectory(.{
|
||||
.source_dir = build_dir,
|
||||
.install_dir = .{ .custom = "../lib" },
|
||||
.install_subdir = target_triple,
|
||||
});
|
||||
b.getInstallStep().dependOn(&install_lib.step);
|
||||
mod.addObjectFile(build_dir.path(b, lib_name));
|
||||
}
|
||||
|
||||
// --- Rust Misc Deps --- //
|
||||
if (target.result.os.tag == .windows) mod.linkSystemLibrary("userenv", .{});
|
||||
const unwind_stubs = b.addLibrary(.{
|
||||
.linkage = .static,
|
||||
.name = "unwind_stubs",
|
||||
.root_module = b.createModule(.{
|
||||
.root_source_file = b.path("src/unwind_stubs.zig"),
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
}),
|
||||
});
|
||||
mod.linkLibrary(unwind_stubs);
|
||||
}
|
||||
|
||||
// --- Zig Executable: temporalz --- //
|
||||
const exe = b.addExecutable(.{
|
||||
.name = "temporalz",
|
||||
.root_module = b.createModule(.{
|
||||
|
|
@ -30,20 +93,21 @@ pub fn build(b: *std.Build) void {
|
|||
});
|
||||
b.installArtifact(exe);
|
||||
|
||||
// Run command
|
||||
const run_cmd = b.addRunArtifact(exe);
|
||||
run_cmd.step.dependOn(b.getInstallStep());
|
||||
if (b.args) |args| run_cmd.addArgs(args);
|
||||
const run_step = b.step("run", "Run the app");
|
||||
// --- Steps: Run --- //
|
||||
{
|
||||
const run_cmd = b.addRunArtifact(exe);
|
||||
run_cmd.step.dependOn(b.getInstallStep());
|
||||
if (b.args) |args| run_cmd.addArgs(args);
|
||||
const run_step = b.step("run", "Run the app");
|
||||
run_step.dependOn(&run_cmd.step);
|
||||
}
|
||||
|
||||
// Docs
|
||||
// --- Steps: Docs --- //
|
||||
{
|
||||
const docs_step = b.step("docs", "Build the temporalz docs");
|
||||
const docs_obj = b.addObject(.{
|
||||
.name = "temporalz",
|
||||
.root_module = mod,
|
||||
});
|
||||
const docs_obj = b.addObject(.{ .name = "temporalz", .root_module = mod });
|
||||
const docs = docs_obj.getEmittedDocs();
|
||||
|
||||
docs_step.dependOn(&b.addInstallDirectory(.{
|
||||
.source_dir = docs,
|
||||
.install_dir = .prefix,
|
||||
|
|
@ -51,9 +115,8 @@ pub fn build(b: *std.Build) void {
|
|||
}).step);
|
||||
}
|
||||
|
||||
// Tests
|
||||
// --- Steps: Test --- //
|
||||
{
|
||||
run_step.dependOn(&run_cmd.step);
|
||||
const test_step = b.step("test", "Run tests");
|
||||
const mod_tests = b.addTest(.{
|
||||
.root_module = mod,
|
||||
|
|
@ -63,141 +126,46 @@ pub fn build(b: *std.Build) void {
|
|||
},
|
||||
});
|
||||
mod_tests.linkLibC();
|
||||
if (target.result.os.tag == .linux) mod_tests.linkSystemLibrary("unwind");
|
||||
if (target.result.os.tag == .windows) {
|
||||
mod_tests.linkSystemLibrary("ws2_32");
|
||||
mod_tests.linkSystemLibrary("bcrypt");
|
||||
mod_tests.linkSystemLibrary("advapi32");
|
||||
}
|
||||
test_step.dependOn(&b.addRunArtifact(mod_tests).step);
|
||||
const exe_tests = b.addTest(.{ .root_module = exe.root_module });
|
||||
test_step.dependOn(&b.addRunArtifact(exe_tests).step);
|
||||
}
|
||||
|
||||
// Rust cross-compilation prebuild
|
||||
// --- Steps: Build all platforms --- //
|
||||
{
|
||||
const rust_prebuild_step = b.step("temporal-rs", "Build and vendor Rust staticlibs for supported targets");
|
||||
var rustup_args: [3 + rust_targets.len][]const u8 = undefined;
|
||||
rustup_args[0] = "rustup";
|
||||
rustup_args[1] = "target";
|
||||
rustup_args[2] = "add";
|
||||
for (rust_targets, 0..) |t, i| {
|
||||
rustup_args[3 + i] = t.triple;
|
||||
}
|
||||
const rustup_add = b.addSystemCommand(&rustup_args);
|
||||
rust_prebuild_step.dependOn(&rustup_add.step);
|
||||
const build_lib_step = b.step("lib", "Build libraries for all common platforms");
|
||||
|
||||
for (rust_targets) |t| {
|
||||
const cargo_build = b.addSystemCommand(&.{
|
||||
"cargo",
|
||||
"build",
|
||||
"--release",
|
||||
"--manifest-path",
|
||||
"vendor/temporal/Cargo.toml",
|
||||
"--target",
|
||||
t.triple,
|
||||
});
|
||||
cargo_build.step.dependOn(&rustup_add.step);
|
||||
rust_prebuild_step.dependOn(&cargo_build.step);
|
||||
}
|
||||
}
|
||||
inline for (platforms) |p| {
|
||||
const query = try std.Build.parseTargetQuery(.{ .arch_os_abi = p });
|
||||
const platform_target = b.resolveTargetQuery(query);
|
||||
|
||||
// Release builds for all platforms
|
||||
{
|
||||
const release_targets = [_]struct {
|
||||
name: []const u8,
|
||||
target: std.Target.Query,
|
||||
}{
|
||||
.{ .name = "linux-x64", .target = .{ .cpu_arch = .x86_64, .os_tag = .linux } },
|
||||
.{ .name = "linux-aarch64", .target = .{ .cpu_arch = .aarch64, .os_tag = .linux } },
|
||||
.{ .name = "macos-x64", .target = .{ .cpu_arch = .x86_64, .os_tag = .macos } },
|
||||
.{ .name = "macos-aarch64", .target = .{ .cpu_arch = .aarch64, .os_tag = .macos } },
|
||||
.{ .name = "windows-x64", .target = .{ .cpu_arch = .x86_64, .os_tag = .windows, .abi = .msvc } },
|
||||
.{ .name = "windows-aarch64", .target = .{ .cpu_arch = .aarch64, .os_tag = .windows, .abi = .msvc } },
|
||||
};
|
||||
|
||||
const release_step = b.step("release", "Build release binaries for all targets");
|
||||
|
||||
for (release_targets) |release_target| {
|
||||
const resolved_target = b.resolveTargetQuery(release_target.target);
|
||||
|
||||
const release_mod = b.addModule(b.fmt("temporalz-{s}", .{release_target.name}), .{
|
||||
.root_source_file = b.path("src/root.zig"),
|
||||
.target = resolved_target,
|
||||
});
|
||||
release_mod.addObjectFile(b.path(getTemporalRsPath(resolved_target)));
|
||||
release_mod.link_libc = true;
|
||||
if (resolved_target.result.os.tag == .linux) release_mod.linkSystemLibrary("unwind", .{});
|
||||
if (resolved_target.result.os.tag == .windows) {
|
||||
release_mod.linkSystemLibrary("ws2_32", .{});
|
||||
release_mod.linkSystemLibrary("bcrypt", .{});
|
||||
release_mod.linkSystemLibrary("advapi32", .{});
|
||||
release_mod.linkSystemLibrary("userenv", .{});
|
||||
}
|
||||
|
||||
const release_exe = b.addExecutable(.{
|
||||
.name = "temporalz",
|
||||
.root_module = b.createModule(.{
|
||||
.root_source_file = b.path("src/main.zig"),
|
||||
.target = resolved_target,
|
||||
.optimize = .ReleaseSmall,
|
||||
.imports = &.{
|
||||
.{ .name = "temporalz", .module = release_mod },
|
||||
},
|
||||
}),
|
||||
});
|
||||
release_exe.linkLibC();
|
||||
if (resolved_target.result.os.tag == .windows) {
|
||||
release_exe.linkSystemLibrary("ws2_32");
|
||||
release_exe.linkSystemLibrary("bcrypt");
|
||||
release_exe.linkSystemLibrary("advapi32");
|
||||
release_exe.linkSystemLibrary("userenv");
|
||||
}
|
||||
|
||||
const exe_ext = if (resolved_target.result.os.tag == .windows) ".exe" else "";
|
||||
const install_release = b.addInstallArtifact(release_exe, .{
|
||||
.dest_sub_path = b.fmt("release/temporalz-{s}{s}", .{ release_target.name, exe_ext }),
|
||||
});
|
||||
|
||||
const target_step = b.step(
|
||||
b.fmt("release-{s}", .{release_target.name}),
|
||||
b.fmt("Build release binary for {s}", .{release_target.name}),
|
||||
const build_dir = build_crab.addCargoBuild(
|
||||
b,
|
||||
.{
|
||||
.manifest_path = b.path("Cargo.toml"),
|
||||
.cargo_args = &.{"--release"},
|
||||
},
|
||||
.{
|
||||
.target = platform_target,
|
||||
.optimize = .ReleaseSafe,
|
||||
},
|
||||
);
|
||||
target_step.dependOn(&install_release.step);
|
||||
release_step.dependOn(&install_release.step);
|
||||
|
||||
const install_lib = b.addInstallDirectory(.{
|
||||
.source_dir = build_dir,
|
||||
.install_dir = .{ .custom = "../lib" },
|
||||
.install_subdir = p,
|
||||
});
|
||||
build_lib_step.dependOn(&install_lib.step);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Supported cross-compilation targets
|
||||
const rust_targets = [_]struct { triple: []const u8 }{
|
||||
.{ .triple = "aarch64-apple-darwin" },
|
||||
.{ .triple = "x86_64-apple-darwin" },
|
||||
.{ .triple = "x86_64-unknown-linux-gnu" },
|
||||
.{ .triple = "aarch64-unknown-linux-gnu" },
|
||||
.{ .triple = "x86_64-pc-windows-msvc" },
|
||||
.{ .triple = "aarch64-pc-windows-msvc" },
|
||||
const platforms = [_][]const u8{
|
||||
"aarch64-macos",
|
||||
"x86_64-macos",
|
||||
"aarch64-linux-gnu",
|
||||
"x86_64-linux-gnu",
|
||||
"x86_64-windows-gnu",
|
||||
"aarch64-windows-gnu",
|
||||
};
|
||||
|
||||
// Platform-specific Rust library path resolution
|
||||
fn getTemporalRsPath(target: std.Build.ResolvedTarget) []const u8 {
|
||||
const arch_tag = target.result.cpu.arch;
|
||||
return switch (target.result.os.tag) {
|
||||
.macos => switch (arch_tag) {
|
||||
.aarch64 => "vendor/temporal/target/aarch64-apple-darwin/release/libtemporal.a",
|
||||
.x86_64 => "vendor/temporal/target/x86_64-apple-darwin/release/libtemporal.a",
|
||||
else => @panic("unsupported macOS architecture"),
|
||||
},
|
||||
.linux => switch (arch_tag) {
|
||||
.x86_64 => "vendor/temporal/target/x86_64-unknown-linux-gnu/release/libtemporal.a",
|
||||
.aarch64 => "vendor/temporal/target/aarch64-unknown-linux-gnu/release/libtemporal.a",
|
||||
else => @panic("unsupported Linux architecture"),
|
||||
},
|
||||
.windows => switch (arch_tag) {
|
||||
.x86_64 => "vendor/temporal/target/x86_64-pc-windows-msvc/release/temporal.lib",
|
||||
.aarch64 => "vendor/temporal/target/aarch64-pc-windows-msvc/release/temporal.lib",
|
||||
else => @panic("unsupported Windows architecture"),
|
||||
},
|
||||
else => @panic("unsupported OS"),
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,11 +3,23 @@
|
|||
.version = "0.0.0",
|
||||
.fingerprint = 0xd8d79d59acc4faae,
|
||||
.minimum_zig_version = "0.15.2",
|
||||
.dependencies = .{},
|
||||
.dependencies = .{
|
||||
// .temporal_rs = .{
|
||||
// .path = "./pkg/temporal_rs",
|
||||
// },
|
||||
.temporal_rs = .{
|
||||
.url = "git+https://github.com/boa-dev/temporal#v0.1.2",
|
||||
.hash = "N-V-__8AANlkNAB77Z946lqp-lgp2qSsOBDADValZC86PhB-",
|
||||
},
|
||||
.build_crab = .{
|
||||
.url = "git+https://github.com/linusg/build.crab#7c8ace3739023b88f1fb95151df4f5b65bd7a311",
|
||||
.hash = "build_crab-0.2.1-U0id_33BAAD3l81W7CBvgLU3OJSw7WI6FVLBSEHTnjTX",
|
||||
},
|
||||
},
|
||||
.paths = .{
|
||||
"build.zig",
|
||||
"build.zig.zon",
|
||||
"src",
|
||||
"vendor",
|
||||
"lib",
|
||||
},
|
||||
}
|
||||
|
|
|
|||
27
example/build.zig
Normal file
27
example/build.zig
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
const std = @import("std");
|
||||
|
||||
pub fn build(b: *std.Build) void {
|
||||
const target = b.standardTargetOptions(.{});
|
||||
const optimize = b.standardOptimizeOption(.{});
|
||||
|
||||
const temporalz_dep = b.dependency("temporalz", .{ .target = target, .optimize = optimize });
|
||||
const exe = b.addExecutable(.{
|
||||
.name = "example",
|
||||
.root_module = b.createModule(.{
|
||||
.root_source_file = b.path("src/main.zig"),
|
||||
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.imports = &.{
|
||||
.{ .name = "temporalz", .module = temporalz_dep.module("temporalz") },
|
||||
},
|
||||
}),
|
||||
});
|
||||
b.installArtifact(exe);
|
||||
|
||||
const run_step = b.step("run", "Run the app");
|
||||
const run_cmd = b.addRunArtifact(exe);
|
||||
run_step.dependOn(&run_cmd.step);
|
||||
run_cmd.step.dependOn(b.getInstallStep());
|
||||
if (b.args) |args| run_cmd.addArgs(args);
|
||||
}
|
||||
16
example/build.zig.zon
Normal file
16
example/build.zig.zon
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
.{
|
||||
.name = .temporalz_example,
|
||||
.version = "0.1.2",
|
||||
.fingerprint = 0x686c9dfc777f9593,
|
||||
.minimum_zig_version = "0.15.2",
|
||||
.dependencies = .{
|
||||
.temporalz = .{
|
||||
.path = "../",
|
||||
},
|
||||
},
|
||||
.paths = .{
|
||||
"build.zig",
|
||||
"build.zig.zon",
|
||||
"src",
|
||||
},
|
||||
}
|
||||
16
example/src/main.zig
Normal file
16
example/src/main.zig
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
const std = @import("std");
|
||||
const Temporal = @import("temporalz");
|
||||
|
||||
pub fn main() !void {
|
||||
const allocator = std.heap.page_allocator;
|
||||
const result = try Temporal.Instant.init(1_704_067_200_000_000_000); // 2024-01-01 00:00:00 UTC
|
||||
defer result.deinit();
|
||||
|
||||
std.debug.print("Instant.epoch_milliseconds: {}\n", .{result.epoch_milliseconds});
|
||||
std.debug.print("Instant.epoch_nanoseconds: {}\n", .{result.epoch_nanoseconds});
|
||||
const instant_str = try result.toString(allocator, .{});
|
||||
std.debug.print("Instant.toString(): {s}\n", .{instant_str});
|
||||
|
||||
const dur = try Temporal.Duration.from("P1Y2M3DT4H5M6S");
|
||||
defer dur.deinit();
|
||||
}
|
||||
BIN
lib/aarch64-linux-gnu/libtemporal_capi.a
Normal file
BIN
lib/aarch64-linux-gnu/libtemporal_capi.a
Normal file
Binary file not shown.
BIN
lib/aarch64-macos/libtemporal_capi.a
Normal file
BIN
lib/aarch64-macos/libtemporal_capi.a
Normal file
Binary file not shown.
BIN
lib/aarch64-windows-gnu/libtemporal_capi.a
Normal file
BIN
lib/aarch64-windows-gnu/libtemporal_capi.a
Normal file
Binary file not shown.
BIN
lib/x86_64-linux-gnu/libtemporal_capi.a
Normal file
BIN
lib/x86_64-linux-gnu/libtemporal_capi.a
Normal file
Binary file not shown.
BIN
lib/x86_64-macos/libtemporal_capi.a
Normal file
BIN
lib/x86_64-macos/libtemporal_capi.a
Normal file
Binary file not shown.
BIN
lib/x86_64-windows-gnu/libtemporal_capi.a
Normal file
BIN
lib/x86_64-windows-gnu/libtemporal_capi.a
Normal file
Binary file not shown.
418
src/Duration.zig
418
src/Duration.zig
|
|
@ -1,4 +1,6 @@
|
|||
const std = @import("std");
|
||||
const temporal_rs = @import("cabi.zig");
|
||||
const c = temporal_rs.c;
|
||||
|
||||
/// The Zig implementation of `Temporal.Duration`.
|
||||
///
|
||||
|
|
@ -135,7 +137,7 @@ const std = @import("std");
|
|||
/// [mdn-duration]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Duration
|
||||
const Duration = @This();
|
||||
|
||||
_inner: *CDuration,
|
||||
_inner: *c.Duration,
|
||||
|
||||
/// Construct a Duration from years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, and nanoseconds.
|
||||
/// Equivalent to `Temporal.Duration.from()` or the constructor.
|
||||
|
|
@ -151,7 +153,7 @@ pub fn init(
|
|||
microseconds_val: f64,
|
||||
nanoseconds_val: f64,
|
||||
) !Duration {
|
||||
return wrapDuration(temporal_rs_Duration_try_new(
|
||||
return wrapDuration(c.temporal_rs_Duration_try_new(
|
||||
years_val,
|
||||
months_val,
|
||||
weeks_val,
|
||||
|
|
@ -167,156 +169,156 @@ pub fn init(
|
|||
|
||||
/// Parse an ISO 8601 duration string (Temporal.Duration.from).
|
||||
pub fn from(text: []const u8) !Duration {
|
||||
const view = DiplomatStringView{ .data = text.ptr, .len = text.len };
|
||||
return wrapDuration(temporal_rs_Duration_from_utf8(view));
|
||||
const view = temporal_rs.toDiplomatStringView(text);
|
||||
return wrapDuration(c.temporal_rs_Duration_from_utf8(view));
|
||||
}
|
||||
|
||||
/// Parse an ISO 8601 UTF-16 duration string.
|
||||
fn fromUtf16(text: []const u16) !Duration {
|
||||
const view = DiplomatString16View{ .data = text.ptr, .len = text.len };
|
||||
return wrapDuration(temporal_rs_Duration_from_utf16(view));
|
||||
const view = temporal_rs.toDiplomatString16View(text);
|
||||
return wrapDuration(c.temporal_rs_Duration_from_utf16(view));
|
||||
}
|
||||
|
||||
/// Create a Duration from a partial duration (where some fields may be omitted).
|
||||
fn fromPartialDuration(partial: PartialDuration) !Duration {
|
||||
return wrapDuration(temporal_rs_Duration_from_partial_duration(partial));
|
||||
return wrapDuration(c.temporal_rs_Duration_from_partial_duration(partial));
|
||||
}
|
||||
|
||||
/// Check if the time portion of the duration is within valid ranges.
|
||||
fn isTimeWithinRange(self: Duration) bool {
|
||||
return temporal_rs_Duration_is_time_within_range(self._inner);
|
||||
return c.temporal_rs_Duration_is_time_within_range(self._inner);
|
||||
}
|
||||
|
||||
/// Get the years component of the duration.
|
||||
pub fn years(self: Duration) i64 {
|
||||
return temporal_rs_Duration_years(self._inner);
|
||||
return c.temporal_rs_Duration_years(self._inner);
|
||||
}
|
||||
|
||||
/// Get the months component of the duration.
|
||||
pub fn months(self: Duration) i64 {
|
||||
return temporal_rs_Duration_months(self._inner);
|
||||
return c.temporal_rs_Duration_months(self._inner);
|
||||
}
|
||||
|
||||
/// Get the weeks component of the duration.
|
||||
pub fn weeks(self: Duration) i64 {
|
||||
return temporal_rs_Duration_weeks(self._inner);
|
||||
return c.temporal_rs_Duration_weeks(self._inner);
|
||||
}
|
||||
|
||||
/// Get the days component of the duration.
|
||||
pub fn days(self: Duration) i64 {
|
||||
return temporal_rs_Duration_days(self._inner);
|
||||
return c.temporal_rs_Duration_days(self._inner);
|
||||
}
|
||||
|
||||
/// Get the hours component of the duration.
|
||||
pub fn hours(self: Duration) i64 {
|
||||
return temporal_rs_Duration_hours(self._inner);
|
||||
return c.temporal_rs_Duration_hours(self._inner);
|
||||
}
|
||||
|
||||
/// Get the minutes component of the duration.
|
||||
pub fn minutes(self: Duration) i64 {
|
||||
return temporal_rs_Duration_minutes(self._inner);
|
||||
return c.temporal_rs_Duration_minutes(self._inner);
|
||||
}
|
||||
|
||||
/// Get the seconds component of the duration.
|
||||
pub fn seconds(self: Duration) i64 {
|
||||
return temporal_rs_Duration_seconds(self._inner);
|
||||
return c.temporal_rs_Duration_seconds(self._inner);
|
||||
}
|
||||
|
||||
/// Get the milliseconds component of the duration.
|
||||
pub fn milliseconds(self: Duration) i64 {
|
||||
return temporal_rs_Duration_milliseconds(self._inner);
|
||||
return c.temporal_rs_Duration_milliseconds(self._inner);
|
||||
}
|
||||
|
||||
/// Get the microseconds component of the duration.
|
||||
pub fn microseconds(self: Duration) f64 {
|
||||
return temporal_rs_Duration_microseconds(self._inner);
|
||||
return c.temporal_rs_Duration_microseconds(self._inner);
|
||||
}
|
||||
|
||||
/// Get the nanoseconds component of the duration.
|
||||
pub fn nanoseconds(self: Duration) f64 {
|
||||
return temporal_rs_Duration_nanoseconds(self._inner);
|
||||
return c.temporal_rs_Duration_nanoseconds(self._inner);
|
||||
}
|
||||
|
||||
/// Get the sign of the duration: positive (1), zero (0), or negative (-1).
|
||||
pub fn sign(self: Duration) Sign {
|
||||
return temporal_rs_Duration_sign(self._inner);
|
||||
return @as(Sign, @enumFromInt(c.temporal_rs_Duration_sign(self._inner)));
|
||||
}
|
||||
|
||||
/// Check if the duration is zero (all fields are zero).
|
||||
pub fn blank(self: Duration) bool {
|
||||
return temporal_rs_Duration_is_zero(self._inner);
|
||||
return c.temporal_rs_Duration_is_zero(self._inner);
|
||||
}
|
||||
|
||||
/// Returns a new Duration with the absolute value (all components positive).
|
||||
pub fn abs(self: Duration) Duration {
|
||||
const ptr = temporal_rs_Duration_abs(self._inner);
|
||||
const ptr: *c.Duration = c.temporal_rs_Duration_abs(self._inner) orelse unreachable;
|
||||
return .{ ._inner = ptr };
|
||||
}
|
||||
|
||||
/// Returns a new Duration with all components negated.
|
||||
pub fn negated(self: Duration) Duration {
|
||||
const ptr = temporal_rs_Duration_negated(self._inner);
|
||||
const ptr: *c.Duration = c.temporal_rs_Duration_negated(self._inner) orelse unreachable;
|
||||
return .{ ._inner = ptr };
|
||||
}
|
||||
|
||||
/// Add two durations together (Temporal.Duration.prototype.add).
|
||||
pub fn add(self: Duration, other: Duration) !Duration {
|
||||
return wrapDuration(temporal_rs_Duration_add(self._inner, other._inner));
|
||||
return wrapDuration(c.temporal_rs_Duration_add(self._inner, other._inner));
|
||||
}
|
||||
|
||||
/// Subtract another duration from this one (Temporal.Duration.prototype.subtract).
|
||||
pub fn subtract(self: Duration, other: Duration) !Duration {
|
||||
return wrapDuration(temporal_rs_Duration_subtract(self._inner, other._inner));
|
||||
return wrapDuration(c.temporal_rs_Duration_subtract(self._inner, other._inner));
|
||||
}
|
||||
|
||||
/// Round the duration according to the specified options (Temporal.Duration.prototype.round).
|
||||
pub fn round(self: Duration, options: RoundingOptions, relative_to: RelativeTo) !Duration {
|
||||
return wrapDuration(temporal_rs_Duration_round(self._inner, options, relative_to));
|
||||
return wrapDuration(c.temporal_rs_Duration_round(self._inner, options, relative_to));
|
||||
}
|
||||
|
||||
/// Round the duration with an explicit provider.
|
||||
fn roundWithProvider(self: Duration, options: RoundingOptions, relative_to: RelativeTo, provider: *const Provider) !Duration {
|
||||
return wrapDuration(temporal_rs_Duration_round_with_provider(self._inner, options, relative_to, provider));
|
||||
fn roundWithProvider(self: Duration, options: RoundingOptions, relative_to: RelativeTo, provider: *const c.Provider) !Duration {
|
||||
return wrapDuration(c.temporal_rs_Duration_round_with_provider(self._inner, options, relative_to, provider));
|
||||
}
|
||||
|
||||
/// Compare two durations (Temporal.Duration.compare).
|
||||
pub fn compare(self: Duration, other: Duration, relative_to: RelativeTo) !i8 {
|
||||
const res = temporal_rs_Duration_compare(self._inner, other._inner, relative_to);
|
||||
const res = c.temporal_rs_Duration_compare(self._inner, other._inner, relative_to);
|
||||
if (!res.is_ok) return error.TemporalError;
|
||||
return res.result.ok;
|
||||
return res.unnamed_0.ok;
|
||||
}
|
||||
|
||||
/// Compare two durations with an explicit provider.
|
||||
fn compareWithProvider(self: Duration, other: Duration, relative_to: RelativeTo, provider: *const Provider) !i8 {
|
||||
const res = temporal_rs_Duration_compare_with_provider(self._inner, other._inner, relative_to, provider);
|
||||
fn compareWithProvider(self: Duration, other: Duration, relative_to: RelativeTo, provider: *const c.Provider) !i8 {
|
||||
const res = c.temporal_rs_Duration_compare_with_provider(self._inner, other._inner, relative_to, provider);
|
||||
if (!res.is_ok) return error.TemporalError;
|
||||
return res.result.ok;
|
||||
return res.unnamed_0.ok;
|
||||
}
|
||||
|
||||
/// Get the total value of the duration in the specified unit (Temporal.Duration.prototype.total).
|
||||
pub fn total(self: Duration, unit: Unit, relative_to: RelativeTo) !f64 {
|
||||
const res = temporal_rs_Duration_total(self._inner, unit, relative_to);
|
||||
const res = c.temporal_rs_Duration_total(self._inner, @intFromEnum(unit), relative_to);
|
||||
if (!res.is_ok) return error.TemporalError;
|
||||
return res.result.ok;
|
||||
return res.unnamed_0.ok;
|
||||
}
|
||||
|
||||
/// Get the total value of the duration with an explicit provider.
|
||||
fn totalWithProvider(self: Duration, unit: Unit, relative_to: RelativeTo, provider: *const Provider) !f64 {
|
||||
const res = temporal_rs_Duration_total_with_provider(self._inner, unit, relative_to, provider);
|
||||
fn totalWithProvider(self: Duration, unit: Unit, relative_to: RelativeTo, provider: *const c.Provider) !f64 {
|
||||
const res = c.temporal_rs_Duration_total_with_provider(self._inner, @intFromEnum(unit), relative_to, provider);
|
||||
if (!res.is_ok) return error.TemporalError;
|
||||
return res.result.ok;
|
||||
return res.unnamed_0.ok;
|
||||
}
|
||||
|
||||
/// Convert to string (Temporal.Duration.prototype.toString); caller owns returned slice.
|
||||
pub fn toString(self: Duration, allocator: std.mem.Allocator, options: ToStringRoundingOptions) ![]u8 {
|
||||
const writer = diplomat_buffer_write_create(128);
|
||||
defer diplomat_buffer_write_destroy(writer);
|
||||
const writer = c.diplomat_buffer_write_create(128);
|
||||
defer c.diplomat_buffer_write_destroy(writer);
|
||||
|
||||
const res = temporal_rs_Duration_to_string(self._inner, options, writer);
|
||||
const res = c.temporal_rs_Duration_to_string(self._inner, options, writer);
|
||||
try handleVoidResult(res);
|
||||
|
||||
const len = diplomat_buffer_write_len(writer);
|
||||
const source = diplomat_buffer_write_get_bytes(writer)[0..len];
|
||||
const len = c.diplomat_buffer_write_len(writer);
|
||||
const source = c.diplomat_buffer_write_get_bytes(writer)[0..len];
|
||||
|
||||
const out = try allocator.alloc(u8, len);
|
||||
std.mem.copyForwards(u8, out, source);
|
||||
|
|
@ -335,32 +337,30 @@ pub fn toLocaleString(self: Duration, allocator: std.mem.Allocator) ![]u8 {
|
|||
|
||||
/// Clone the underlying duration.
|
||||
fn clone(self: Duration) Duration {
|
||||
const ptr = temporal_rs_Duration_clone(self._inner);
|
||||
const ptr: *c.Duration = c.temporal_rs_Duration_clone(self._inner) orelse unreachable;
|
||||
return .{ ._inner = ptr };
|
||||
}
|
||||
|
||||
pub fn deinit(self: Duration) void {
|
||||
temporal_rs_Duration_destroy(self._inner);
|
||||
c.temporal_rs_Duration_destroy(self._inner);
|
||||
}
|
||||
|
||||
// --- Helpers -----------------------------------------------------------------
|
||||
|
||||
fn wrapDuration(res: DurationResult) !Duration {
|
||||
fn handleVoidResult(res: anytype) !void {
|
||||
if (!res.is_ok) return error.TemporalError;
|
||||
const ptr = res.result.ok orelse return error.TemporalError;
|
||||
}
|
||||
|
||||
fn wrapDuration(res: anytype) !Duration {
|
||||
if (!res.is_ok) return error.TemporalError;
|
||||
const maybe_ptr = res.unnamed_0.ok;
|
||||
if (maybe_ptr == null) return error.TemporalError;
|
||||
const ptr: *c.Duration = maybe_ptr.?;
|
||||
return .{ ._inner = ptr };
|
||||
}
|
||||
|
||||
fn handleVoidResult(res: VoidResult) !void {
|
||||
if (!res.is_ok) return error.TemporalError;
|
||||
}
|
||||
|
||||
fn defaultToStringRoundingOptions() ToStringRoundingOptions {
|
||||
return .{
|
||||
.precision = .{ .is_minute = false, .precision = OptionU8{ .ok = 0, .is_ok = false } },
|
||||
.smallest_unit = Unit_option{ .ok = .auto, .is_ok = false },
|
||||
.rounding_mode = RoundingMode_option{ .ok = .trunc, .is_ok = false },
|
||||
};
|
||||
return temporal_rs.to_string_rounding_options_auto;
|
||||
}
|
||||
|
||||
// --- Public helper types -----------------------------------------------------
|
||||
|
|
@ -379,277 +379,71 @@ pub const ToStringOptions = struct {
|
|||
smallest_unit: ?Unit = null,
|
||||
};
|
||||
|
||||
/// A partial duration where some or all fields may be omitted.
|
||||
pub const PartialDuration = extern struct {
|
||||
years: OptionI64,
|
||||
months: OptionI64,
|
||||
weeks: OptionI64,
|
||||
days: OptionI64,
|
||||
hours: OptionI64,
|
||||
minutes: OptionI64,
|
||||
seconds: OptionI64,
|
||||
milliseconds: OptionI64,
|
||||
microseconds: OptionI128,
|
||||
nanoseconds: OptionI128,
|
||||
|
||||
pub fn empty() PartialDuration {
|
||||
return .{
|
||||
.years = .{ .ok = 0, .is_ok = false },
|
||||
.months = .{ .ok = 0, .is_ok = false },
|
||||
.weeks = .{ .ok = 0, .is_ok = false },
|
||||
.days = .{ .ok = 0, .is_ok = false },
|
||||
.hours = .{ .ok = 0, .is_ok = false },
|
||||
.minutes = .{ .ok = 0, .is_ok = false },
|
||||
.seconds = .{ .ok = 0, .is_ok = false },
|
||||
.milliseconds = .{ .ok = 0, .is_ok = false },
|
||||
.microseconds = .{ .ok = 0, .is_ok = false },
|
||||
.nanoseconds = .{ .ok = 0, .is_ok = false },
|
||||
};
|
||||
}
|
||||
|
||||
pub fn withYears(self: PartialDuration, value: i64) PartialDuration {
|
||||
var result = self;
|
||||
result.years = .{ .ok = value, .is_ok = true };
|
||||
return result;
|
||||
}
|
||||
|
||||
pub fn withMonths(self: PartialDuration, value: i64) PartialDuration {
|
||||
var result = self;
|
||||
result.months = .{ .ok = value, .is_ok = true };
|
||||
return result;
|
||||
}
|
||||
|
||||
pub fn withWeeks(self: PartialDuration, value: i64) PartialDuration {
|
||||
var result = self;
|
||||
result.weeks = .{ .ok = value, .is_ok = true };
|
||||
return result;
|
||||
}
|
||||
|
||||
pub fn withDays(self: PartialDuration, value: i64) PartialDuration {
|
||||
var result = self;
|
||||
result.days = .{ .ok = value, .is_ok = true };
|
||||
return result;
|
||||
}
|
||||
|
||||
pub fn withHours(self: PartialDuration, value: i64) PartialDuration {
|
||||
var result = self;
|
||||
result.hours = .{ .ok = value, .is_ok = true };
|
||||
return result;
|
||||
}
|
||||
|
||||
pub fn withMinutes(self: PartialDuration, value: i64) PartialDuration {
|
||||
var result = self;
|
||||
result.minutes = .{ .ok = value, .is_ok = true };
|
||||
return result;
|
||||
}
|
||||
|
||||
pub fn withSeconds(self: PartialDuration, value: i64) PartialDuration {
|
||||
var result = self;
|
||||
result.seconds = .{ .ok = value, .is_ok = true };
|
||||
return result;
|
||||
}
|
||||
|
||||
pub fn withMilliseconds(self: PartialDuration, value: i64) PartialDuration {
|
||||
var result = self;
|
||||
result.milliseconds = .{ .ok = value, .is_ok = true };
|
||||
return result;
|
||||
}
|
||||
|
||||
pub fn withMicroseconds(self: PartialDuration, value: i128) PartialDuration {
|
||||
var result = self;
|
||||
result.microseconds = .{ .ok = value, .is_ok = true };
|
||||
return result;
|
||||
}
|
||||
|
||||
pub fn withNanoseconds(self: PartialDuration, value: i128) PartialDuration {
|
||||
var result = self;
|
||||
result.nanoseconds = .{ .ok = value, .is_ok = true };
|
||||
return result;
|
||||
}
|
||||
};
|
||||
pub const PartialDuration = c.PartialDuration;
|
||||
|
||||
/// Relative-to context for duration operations.
|
||||
pub const RelativeTo = extern struct {
|
||||
plain_date: ?*PlainDate,
|
||||
zoned_date_time: ?*ZonedDateTime,
|
||||
plain_date: ?*c.PlainDate,
|
||||
zoned_date_time: ?*c.ZonedDateTime,
|
||||
};
|
||||
|
||||
// --- Extern types ------------------------------------------------------------
|
||||
// --- Public type aliases and enums ------------------------------------------
|
||||
|
||||
const CDuration = opaque {};
|
||||
const PlainDate = opaque {};
|
||||
const ZonedDateTime = opaque {};
|
||||
const Provider = opaque {};
|
||||
|
||||
const DiplomatStringView = extern struct { data: [*c]const u8, len: usize };
|
||||
const DiplomatString16View = extern struct { data: [*c]const u16, len: usize };
|
||||
|
||||
const OptionStringView = extern struct { ok: DiplomatStringView, is_ok: bool };
|
||||
const OptionU8 = extern struct { ok: u8, is_ok: bool };
|
||||
const OptionU32 = extern struct { ok: u32, is_ok: bool };
|
||||
const OptionI64 = extern struct { ok: i64, is_ok: bool };
|
||||
const OptionI128 = extern struct { ok: i128, is_ok: bool };
|
||||
|
||||
const DiplomatWrite = extern struct {
|
||||
context: ?*anyopaque,
|
||||
buf: [*c]u8,
|
||||
len: usize,
|
||||
cap: usize,
|
||||
grow_failed: bool,
|
||||
flush: ?*const fn (*DiplomatWrite) void,
|
||||
grow: ?*const fn (*DiplomatWrite, usize) bool,
|
||||
};
|
||||
|
||||
const Precision = extern struct {
|
||||
is_minute: bool,
|
||||
precision: OptionU8,
|
||||
};
|
||||
const OptionU8 = c.OptionU8;
|
||||
const OptionU32 = c.OptionU32;
|
||||
const OptionI64 = c.OptionI64;
|
||||
const OptionF64 = c.OptionF64;
|
||||
const Precision = c.Precision;
|
||||
const Unit_option = c.Unit_option;
|
||||
const RoundingMode_option = c.RoundingMode_option;
|
||||
|
||||
/// Time unit for Temporal operations.
|
||||
/// See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal
|
||||
pub const Unit = enum(c_int) {
|
||||
auto = 0,
|
||||
nanosecond = 1,
|
||||
microsecond = 2,
|
||||
millisecond = 3,
|
||||
second = 4,
|
||||
minute = 5,
|
||||
hour = 6,
|
||||
day = 7,
|
||||
week = 8,
|
||||
month = 9,
|
||||
year = 10,
|
||||
pub const Unit = enum(c_uint) {
|
||||
auto = c.Unit_Auto,
|
||||
nanosecond = c.Unit_Nanosecond,
|
||||
microsecond = c.Unit_Microsecond,
|
||||
millisecond = c.Unit_Millisecond,
|
||||
second = c.Unit_Second,
|
||||
minute = c.Unit_Minute,
|
||||
hour = c.Unit_Hour,
|
||||
day = c.Unit_Day,
|
||||
week = c.Unit_Week,
|
||||
month = c.Unit_Month,
|
||||
year = c.Unit_Year,
|
||||
};
|
||||
|
||||
const Unit_option = extern struct { ok: Unit, is_ok: bool };
|
||||
|
||||
/// Rounding mode for Temporal operations.
|
||||
/// See: https://tc39.es/ecma402/#table-sanctioned-single-unit-identifiers
|
||||
pub const RoundingMode = enum(c_int) {
|
||||
pub const RoundingMode = enum(c_uint) {
|
||||
/// Round toward positive infinity
|
||||
ceil = 0,
|
||||
ceil = c.RoundingMode_Ceil,
|
||||
/// Round toward negative infinity
|
||||
floor = 1,
|
||||
floor = c.RoundingMode_Floor,
|
||||
/// Round away from zero
|
||||
expand = 2,
|
||||
expand = c.RoundingMode_Expand,
|
||||
/// Round toward zero (truncate)
|
||||
trunc = 3,
|
||||
trunc = c.RoundingMode_Trunc,
|
||||
/// Round half toward positive infinity
|
||||
half_ceil = 4,
|
||||
half_ceil = c.RoundingMode_HalfCeil,
|
||||
/// Round half toward negative infinity
|
||||
half_floor = 5,
|
||||
half_floor = c.RoundingMode_HalfFloor,
|
||||
/// Round half away from zero
|
||||
half_expand = 6,
|
||||
half_expand = c.RoundingMode_HalfExpand,
|
||||
/// Round half toward zero
|
||||
half_trunc = 7,
|
||||
half_trunc = c.RoundingMode_HalfTrunc,
|
||||
/// Round half to even (banker's rounding)
|
||||
half_even = 8,
|
||||
};
|
||||
|
||||
const RoundingMode_option = extern struct { ok: RoundingMode, is_ok: bool };
|
||||
|
||||
pub const RoundingOptions = extern struct {
|
||||
largest_unit: Unit_option,
|
||||
smallest_unit: Unit_option,
|
||||
rounding_mode: RoundingMode_option,
|
||||
increment: OptionU32,
|
||||
};
|
||||
|
||||
pub const ToStringRoundingOptions = extern struct {
|
||||
precision: Precision,
|
||||
smallest_unit: Unit_option,
|
||||
rounding_mode: RoundingMode_option,
|
||||
};
|
||||
|
||||
const ErrorKind = enum(c_int) {
|
||||
ErrorKind_Generic = 0,
|
||||
ErrorKind_Type = 1,
|
||||
ErrorKind_Range = 2,
|
||||
ErrorKind_Syntax = 3,
|
||||
ErrorKind_Assert = 4,
|
||||
};
|
||||
|
||||
const TemporalError = extern struct {
|
||||
kind: ErrorKind,
|
||||
msg: OptionStringView,
|
||||
half_even = c.RoundingMode_HalfEven,
|
||||
};
|
||||
|
||||
pub const Sign = enum(c_int) {
|
||||
Sign_Positive = 1,
|
||||
Sign_Zero = 0,
|
||||
Sign_Negative = -1,
|
||||
positive = c.Sign_Positive,
|
||||
zero = c.Sign_Zero,
|
||||
negative = c.Sign_Negative,
|
||||
};
|
||||
|
||||
// --- Result wrappers ---------------------------------------------------------
|
||||
|
||||
const DurationResult = extern struct {
|
||||
result: extern union {
|
||||
ok: ?*CDuration,
|
||||
err: TemporalError,
|
||||
},
|
||||
is_ok: bool,
|
||||
};
|
||||
|
||||
const I8Result = extern struct {
|
||||
result: extern union {
|
||||
ok: i8,
|
||||
err: TemporalError,
|
||||
},
|
||||
is_ok: bool,
|
||||
};
|
||||
|
||||
const F64Result = extern struct {
|
||||
result: extern union {
|
||||
ok: f64,
|
||||
err: TemporalError,
|
||||
},
|
||||
is_ok: bool,
|
||||
};
|
||||
|
||||
const VoidResult = extern struct {
|
||||
result: extern union {
|
||||
err: TemporalError,
|
||||
},
|
||||
is_ok: bool,
|
||||
};
|
||||
|
||||
// --- Extern functions -------------------------------------------------------
|
||||
|
||||
extern "c" fn temporal_rs_Duration_try_new(years: i64, months: i64, weeks: i64, days: i64, hours: i64, minutes: i64, seconds: i64, milliseconds: i64, microseconds: f64, nanoseconds: f64) DurationResult;
|
||||
extern "c" fn temporal_rs_Duration_from_partial_duration(partial: PartialDuration) DurationResult;
|
||||
extern "c" fn temporal_rs_Duration_from_utf8(s: DiplomatStringView) DurationResult;
|
||||
extern "c" fn temporal_rs_Duration_from_utf16(s: DiplomatString16View) DurationResult;
|
||||
extern "c" fn temporal_rs_Duration_is_time_within_range(self: *const CDuration) bool;
|
||||
extern "c" fn temporal_rs_Duration_years(self: *const CDuration) i64;
|
||||
extern "c" fn temporal_rs_Duration_months(self: *const CDuration) i64;
|
||||
extern "c" fn temporal_rs_Duration_weeks(self: *const CDuration) i64;
|
||||
extern "c" fn temporal_rs_Duration_days(self: *const CDuration) i64;
|
||||
extern "c" fn temporal_rs_Duration_hours(self: *const CDuration) i64;
|
||||
extern "c" fn temporal_rs_Duration_minutes(self: *const CDuration) i64;
|
||||
extern "c" fn temporal_rs_Duration_seconds(self: *const CDuration) i64;
|
||||
extern "c" fn temporal_rs_Duration_milliseconds(self: *const CDuration) i64;
|
||||
extern "c" fn temporal_rs_Duration_microseconds(self: *const CDuration) f64;
|
||||
extern "c" fn temporal_rs_Duration_nanoseconds(self: *const CDuration) f64;
|
||||
extern "c" fn temporal_rs_Duration_sign(self: *const CDuration) Sign;
|
||||
extern "c" fn temporal_rs_Duration_is_zero(self: *const CDuration) bool;
|
||||
extern "c" fn temporal_rs_Duration_abs(self: *const CDuration) *CDuration;
|
||||
extern "c" fn temporal_rs_Duration_negated(self: *const CDuration) *CDuration;
|
||||
extern "c" fn temporal_rs_Duration_add(self: *const CDuration, other: *const CDuration) DurationResult;
|
||||
extern "c" fn temporal_rs_Duration_subtract(self: *const CDuration, other: *const CDuration) DurationResult;
|
||||
extern "c" fn temporal_rs_Duration_to_string(self: *const CDuration, options: ToStringRoundingOptions, write: *DiplomatWrite) VoidResult;
|
||||
extern "c" fn temporal_rs_Duration_round(self: *const CDuration, options: RoundingOptions, relative_to: RelativeTo) DurationResult;
|
||||
extern "c" fn temporal_rs_Duration_round_with_provider(self: *const CDuration, options: RoundingOptions, relative_to: RelativeTo, p: *const Provider) DurationResult;
|
||||
extern "c" fn temporal_rs_Duration_compare(self: *const CDuration, other: *const CDuration, relative_to: RelativeTo) I8Result;
|
||||
extern "c" fn temporal_rs_Duration_compare_with_provider(self: *const CDuration, other: *const CDuration, relative_to: RelativeTo, p: *const Provider) I8Result;
|
||||
extern "c" fn temporal_rs_Duration_total(self: *const CDuration, unit: Unit, relative_to: RelativeTo) F64Result;
|
||||
extern "c" fn temporal_rs_Duration_total_with_provider(self: *const CDuration, unit: Unit, relative_to: RelativeTo, p: *const Provider) F64Result;
|
||||
extern "c" fn temporal_rs_Duration_clone(self: *const CDuration) *CDuration;
|
||||
extern "c" fn temporal_rs_Duration_destroy(self: *CDuration) void;
|
||||
|
||||
extern "c" fn diplomat_buffer_write_create(cap: usize) *DiplomatWrite;
|
||||
extern "c" fn diplomat_buffer_write_get_bytes(write: *DiplomatWrite) [*c]u8;
|
||||
extern "c" fn diplomat_buffer_write_len(write: *DiplomatWrite) usize;
|
||||
extern "c" fn diplomat_buffer_write_destroy(write: *DiplomatWrite) void;
|
||||
pub const RoundingOptions = c.RoundingOptions;
|
||||
pub const ToStringRoundingOptions = c.ToStringRoundingOptions;
|
||||
|
||||
// --- Tests -------------------------------------------------------------------
|
||||
|
||||
|
|
@ -689,7 +483,7 @@ test from {
|
|||
defer dur_negative.deinit();
|
||||
|
||||
try std.testing.expectEqual(@as(i64, -1), dur_negative.days());
|
||||
try std.testing.expectEqual(Sign.Sign_Negative, dur_negative.sign());
|
||||
try std.testing.expectEqual(Sign.negative, dur_negative.sign());
|
||||
}
|
||||
|
||||
test blank {
|
||||
|
|
@ -697,7 +491,7 @@ test blank {
|
|||
defer dur.deinit();
|
||||
|
||||
try std.testing.expect(dur.blank());
|
||||
try std.testing.expectEqual(Sign.Sign_Zero, dur.sign());
|
||||
try std.testing.expectEqual(Sign.zero, dur.sign());
|
||||
}
|
||||
|
||||
test add {
|
||||
|
|
@ -735,7 +529,7 @@ test abs {
|
|||
|
||||
try std.testing.expectEqual(@as(i64, 1), abs_dur.hours());
|
||||
try std.testing.expectEqual(@as(i64, 30), abs_dur.minutes());
|
||||
try std.testing.expectEqual(Sign.Sign_Positive, abs_dur.sign());
|
||||
try std.testing.expectEqual(Sign.positive, abs_dur.sign());
|
||||
}
|
||||
|
||||
test negated {
|
||||
|
|
@ -746,7 +540,7 @@ test negated {
|
|||
defer neg.deinit();
|
||||
|
||||
try std.testing.expectEqual(@as(i64, -1), neg.hours());
|
||||
try std.testing.expectEqual(Sign.Sign_Negative, neg.sign());
|
||||
try std.testing.expectEqual(Sign.negative, neg.sign());
|
||||
}
|
||||
|
||||
test toString {
|
||||
|
|
@ -762,9 +556,21 @@ test toString {
|
|||
}
|
||||
|
||||
test fromPartialDuration {
|
||||
var partial = PartialDuration.empty();
|
||||
partial = partial.withHours(3);
|
||||
partial = partial.withMinutes(45);
|
||||
const empty_i64 = temporal_rs.toOption(c.OptionI64, null);
|
||||
const empty_f64 = temporal_rs.toOption(c.OptionF64, null);
|
||||
|
||||
const partial = c.PartialDuration{
|
||||
.years = empty_i64,
|
||||
.months = empty_i64,
|
||||
.weeks = empty_i64,
|
||||
.days = empty_i64,
|
||||
.hours = .{ .is_ok = true, .unnamed_0 = .{ .ok = 3 } },
|
||||
.minutes = .{ .is_ok = true, .unnamed_0 = .{ .ok = 45 } },
|
||||
.seconds = empty_i64,
|
||||
.milliseconds = empty_i64,
|
||||
.microseconds = empty_f64,
|
||||
.nanoseconds = empty_f64,
|
||||
};
|
||||
|
||||
const dur = try Duration.fromPartialDuration(partial);
|
||||
defer dur.deinit();
|
||||
|
|
|
|||
443
src/Instant.zig
443
src/Instant.zig
|
|
@ -1,11 +1,61 @@
|
|||
const std = @import("std");
|
||||
const temporal_rs = @import("cabi.zig");
|
||||
const c = temporal_rs.c;
|
||||
|
||||
const Instant = @This();
|
||||
|
||||
_inner: *CInstant,
|
||||
_inner: *c.Instant,
|
||||
epoch_milliseconds: i64,
|
||||
epoch_nanoseconds: i128,
|
||||
|
||||
pub const Unit = enum(c_uint) {
|
||||
auto = c.Unit_Auto,
|
||||
nanosecond = c.Unit_Nanosecond,
|
||||
microsecond = c.Unit_Microsecond,
|
||||
millisecond = c.Unit_Millisecond,
|
||||
second = c.Unit_Second,
|
||||
minute = c.Unit_Minute,
|
||||
hour = c.Unit_Hour,
|
||||
day = c.Unit_Day,
|
||||
week = c.Unit_Week,
|
||||
month = c.Unit_Month,
|
||||
year = c.Unit_Year,
|
||||
};
|
||||
|
||||
pub const RoundingMode = enum(c_uint) {
|
||||
ceil = c.RoundingMode_Ceil,
|
||||
floor = c.RoundingMode_Floor,
|
||||
expand = c.RoundingMode_Expand,
|
||||
trunc = c.RoundingMode_Trunc,
|
||||
half_ceil = c.RoundingMode_HalfCeil,
|
||||
half_floor = c.RoundingMode_HalfFloor,
|
||||
half_expand = c.RoundingMode_HalfExpand,
|
||||
half_trunc = c.RoundingMode_HalfTrunc,
|
||||
half_even = c.RoundingMode_HalfEven,
|
||||
};
|
||||
pub const Sign = enum(c_int) {
|
||||
positive = c.Sign_Positive,
|
||||
zero = c.Sign_Zero,
|
||||
negative = c.Sign_Negative,
|
||||
};
|
||||
const TimeZone = c.TimeZone;
|
||||
const TimeZone_option = c.TimeZone_option;
|
||||
const Precision = c.Precision;
|
||||
const Unit_option = c.Unit_option;
|
||||
const RoundingMode_option = c.RoundingMode_option;
|
||||
const DifferenceSettings = c.DifferenceSettings;
|
||||
const RoundingOptions = c.RoundingOptions;
|
||||
const ToStringRoundingOptions = c.ToStringRoundingOptions;
|
||||
const OptionU8 = c.OptionU8;
|
||||
const OptionU32 = c.OptionU32;
|
||||
const TemporalError = c.TemporalError;
|
||||
const DiplomatStringView = c.DiplomatStringView;
|
||||
const DiplomatString16View = c.DiplomatString16View;
|
||||
const DiplomatWrite = c.DiplomatWrite;
|
||||
const Duration = c.Duration;
|
||||
const ZonedDateTime = c.ZonedDateTime;
|
||||
const Provider = c.Provider;
|
||||
|
||||
/// Construct from epoch nanoseconds (Temporal.Instant.fromEpochNanoseconds).
|
||||
pub fn init(epoch_ns: i128) !Instant {
|
||||
return fromEpochNanoseconds(epoch_ns);
|
||||
|
|
@ -13,79 +63,75 @@ pub fn init(epoch_ns: i128) !Instant {
|
|||
|
||||
/// Construct from epoch milliseconds.
|
||||
pub fn fromEpochMilliseconds(epoch_ms: i64) !Instant {
|
||||
return wrapInstant(temporal_rs_Instant_from_epoch_milliseconds(epoch_ms));
|
||||
return wrapInstant(c.temporal_rs_Instant_from_epoch_milliseconds(epoch_ms));
|
||||
}
|
||||
|
||||
/// Construct from epoch nanoseconds (Temporal.Instant.fromEpochNanoseconds).
|
||||
pub fn fromEpochNanoseconds(epoch_ns: i128) !Instant {
|
||||
const parts = i128ToParts(epoch_ns);
|
||||
return wrapInstant(temporal_rs_Instant_try_new(parts));
|
||||
const parts = temporal_rs.toI128Nanoseconds(epoch_ns);
|
||||
return wrapInstant(c.temporal_rs_Instant_try_new(parts));
|
||||
}
|
||||
|
||||
/// Parse an ISO 8601 string (Temporal.Instant.from).
|
||||
pub fn from(text: []const u8) !Instant {
|
||||
const view = DiplomatStringView{ .data = text.ptr, .len = text.len };
|
||||
return wrapInstant(temporal_rs_Instant_from_utf8(view));
|
||||
const view = temporal_rs.toDiplomatStringView(text);
|
||||
return wrapInstant(c.temporal_rs_Instant_from_utf8(view));
|
||||
}
|
||||
|
||||
/// Parse an ISO 8601 UTF-16 string (Temporal.Instant.from).
|
||||
fn fromUtf16(text: []const u16) !Instant {
|
||||
const view = DiplomatString16View{ .data = text.ptr, .len = text.len };
|
||||
return wrapInstant(temporal_rs_Instant_from_utf16(view));
|
||||
const view = temporal_rs.toDiplomatString16View(text);
|
||||
return wrapInstant(c.temporal_rs_Instant_from_utf16(view));
|
||||
}
|
||||
|
||||
/// 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(c.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(c.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(c.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(c.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(c.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 c.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 c.temporal_rs_Instant_equals(a._inner, b._inner);
|
||||
}
|
||||
|
||||
/// Convert to string using compiled TZ data; caller owns returned slice.
|
||||
pub fn toString(self: Instant, allocator: std.mem.Allocator, opts: ToStringOptions) ![]u8 {
|
||||
const zone_opt = if (opts.time_zone) |z|
|
||||
TimeZone_option{ .ok = z, .is_ok = true }
|
||||
else
|
||||
TimeZone_option{ .ok = undefined, .is_ok = false };
|
||||
|
||||
const zone_opt = temporal_rs.toTimeZoneOption(opts.time_zone);
|
||||
const rounding = optsToRounding(opts);
|
||||
|
||||
const writer = diplomat_buffer_write_create(128);
|
||||
defer diplomat_buffer_write_destroy(writer);
|
||||
const writer = c.diplomat_buffer_write_create(128);
|
||||
defer c.diplomat_buffer_write_destroy(writer);
|
||||
|
||||
const res = temporal_rs_Instant_to_ixdtf_string_with_compiled_data(self._inner, zone_opt, rounding, writer);
|
||||
const res = c.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);
|
||||
const source = diplomat_buffer_write_get_bytes(writer)[0..len];
|
||||
const len = c.diplomat_buffer_write_len(writer);
|
||||
const source = c.diplomat_buffer_write_get_bytes(writer)[0..len];
|
||||
|
||||
const out = try allocator.alloc(u8, len);
|
||||
std.mem.copyForwards(u8, out, source);
|
||||
|
|
@ -94,21 +140,17 @@ pub fn toString(self: Instant, allocator: std.mem.Allocator, opts: ToStringOptio
|
|||
|
||||
/// Convert to string using an explicit provider.
|
||||
fn toStringWithProvider(self: Instant, allocator: std.mem.Allocator, provider: *const Provider, opts: ToStringOptions) ![]u8 {
|
||||
const zone_opt = if (opts.time_zone) |z|
|
||||
TimeZone_option{ .ok = z, .is_ok = true }
|
||||
else
|
||||
TimeZone_option{ .ok = undefined, .is_ok = false };
|
||||
|
||||
const zone_opt = temporal_rs.toTimeZoneOption(opts.time_zone);
|
||||
const rounding = optsToRounding(opts);
|
||||
|
||||
const writer = diplomat_buffer_write_create(128);
|
||||
defer diplomat_buffer_write_destroy(writer);
|
||||
const writer = c.diplomat_buffer_write_create(128);
|
||||
defer c.diplomat_buffer_write_destroy(writer);
|
||||
|
||||
const res = temporal_rs_Instant_to_ixdtf_string_with_provider(self._inner, zone_opt, rounding, provider, writer);
|
||||
const res = c.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);
|
||||
const source = diplomat_buffer_write_get_bytes(writer)[0..len];
|
||||
const len = c.diplomat_buffer_write_len(writer);
|
||||
const source = c.diplomat_buffer_write_get_bytes(writer)[0..len];
|
||||
|
||||
const out = try allocator.alloc(u8, len);
|
||||
std.mem.copyForwards(u8, out, source);
|
||||
|
|
@ -127,49 +169,49 @@ 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(c.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(c.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, .epoch_milliseconds = temporal_rs_Instant_epoch_milliseconds(ptr), .epoch_nanoseconds = partsToI128(temporal_rs_Instant_epoch_nanoseconds(ptr)) };
|
||||
const ptr = c.temporal_rs_Instant_clone(self._inner) orelse unreachable;
|
||||
return .{ ._inner = ptr, .epoch_milliseconds = c.temporal_rs_Instant_epoch_milliseconds(ptr), .epoch_nanoseconds = temporal_rs.fromI128Nanoseconds(c.temporal_rs_Instant_epoch_nanoseconds(ptr)) };
|
||||
}
|
||||
|
||||
pub fn deinit(self: Instant) void {
|
||||
temporal_rs_Instant_destroy(self._inner);
|
||||
c.temporal_rs_Instant_destroy(self._inner);
|
||||
}
|
||||
|
||||
// --- Helpers -----------------------------------------------------------------
|
||||
|
||||
fn wrapInstant(res: InstantResult) !Instant {
|
||||
fn wrapInstant(res: anytype) !Instant {
|
||||
if (!res.is_ok) return error.TemporalError;
|
||||
const ptr = res.result.ok orelse return error.TemporalError;
|
||||
return .{ ._inner = ptr, .epoch_milliseconds = temporal_rs_Instant_epoch_milliseconds(ptr), .epoch_nanoseconds = partsToI128(temporal_rs_Instant_epoch_nanoseconds(ptr)) };
|
||||
const maybe_ptr = res.unnamed_0.ok;
|
||||
if (maybe_ptr == null) return error.TemporalError;
|
||||
const ptr: *c.Instant = maybe_ptr.?;
|
||||
return .{ ._inner = ptr, .epoch_milliseconds = c.temporal_rs_Instant_epoch_milliseconds(ptr), .epoch_nanoseconds = temporal_rs.fromI128Nanoseconds(c.temporal_rs_Instant_epoch_nanoseconds(ptr)) };
|
||||
}
|
||||
|
||||
fn wrapDuration(res: DurationResult) !DurationHandle {
|
||||
fn wrapDuration(res: anytype) !DurationHandle {
|
||||
if (!res.is_ok) return error.TemporalError;
|
||||
const ptr = res.result.ok orelse return error.TemporalError;
|
||||
return .{ .ptr = ptr };
|
||||
return .{ .ptr = res.unnamed_0.ok orelse return error.TemporalError };
|
||||
}
|
||||
|
||||
fn wrapZonedDateTime(res: ZonedDateTimeResult) !ZonedDateTimeHandle {
|
||||
fn wrapZonedDateTime(res: anytype) !ZonedDateTimeHandle {
|
||||
if (!res.is_ok) return error.TemporalError;
|
||||
const ptr = res.result.ok orelse return error.TemporalError;
|
||||
return .{ .ptr = ptr };
|
||||
return .{ .ptr = res.unnamed_0.ok orelse return error.TemporalError };
|
||||
}
|
||||
|
||||
fn handleVoidResult(res: VoidResult) !void {
|
||||
fn handleVoidResult(res: anytype) !void {
|
||||
if (!res.is_ok) return error.TemporalError;
|
||||
}
|
||||
|
||||
fn i128ToParts(value: i128) I128Nanoseconds {
|
||||
fn i128ToParts(value: i128) temporal_rs.c.I128Nanoseconds {
|
||||
const is_neg = value < 0;
|
||||
const mag: u128 = if (is_neg) @intCast(@as(u128, @intCast(-value))) else @intCast(value);
|
||||
const mask: u64 = 1 << 63;
|
||||
|
|
@ -179,7 +221,7 @@ fn i128ToParts(value: i128) I128Nanoseconds {
|
|||
return .{ .high = high, .low = low };
|
||||
}
|
||||
|
||||
fn partsToI128(value: I128Nanoseconds) i128 {
|
||||
fn partsToI128(value: temporal_rs.c.I128Nanoseconds) i128 {
|
||||
const mask: u64 = 1 << 63;
|
||||
const is_neg = (value.high & mask) != 0;
|
||||
const mag: u128 = ((@as(u128, value.high & ~mask)) << 64) | value.low;
|
||||
|
|
@ -188,48 +230,34 @@ fn partsToI128(value: I128Nanoseconds) i128 {
|
|||
}
|
||||
|
||||
fn defaultPrecision() Precision {
|
||||
return .{ .is_minute = false, .precision = OptionU8{ .ok = 0, .is_ok = false } };
|
||||
return .{ .is_minute = false, .precision = temporal_rs.toOption(c.OptionU8, null) };
|
||||
}
|
||||
|
||||
fn defaultToStringRoundingOptions() ToStringRoundingOptions {
|
||||
return .{
|
||||
.precision = defaultPrecision(),
|
||||
.smallest_unit = Unit_option{ .ok = .auto, .is_ok = false },
|
||||
.rounding_mode = RoundingMode_option{ .ok = .trunc, .is_ok = false },
|
||||
};
|
||||
return temporal_rs.to_string_rounding_options_auto;
|
||||
}
|
||||
|
||||
/// Convert ToStringOptions to ToStringRoundingOptions for the C API
|
||||
fn optsToRounding(opts: ToStringOptions) ToStringRoundingOptions {
|
||||
// If smallest_unit is specified, use it; otherwise use fractional_second_digits for precision
|
||||
const smallest_unit_opt = if (opts.smallest_unit) |unit|
|
||||
Unit_option{ .ok = unit, .is_ok = true }
|
||||
else
|
||||
Unit_option{ .ok = .auto, .is_ok = false };
|
||||
|
||||
const precision = if (opts.fractional_second_digits) |digits|
|
||||
Precision{ .is_minute = false, .precision = OptionU8{ .ok = digits, .is_ok = true } }
|
||||
Precision{ .is_minute = false, .precision = temporal_rs.toOption(c.OptionU8, digits) }
|
||||
else
|
||||
defaultPrecision();
|
||||
|
||||
const rounding_mode_opt = if (opts.rounding_mode) |mode|
|
||||
RoundingMode_option{ .ok = mode, .is_ok = true }
|
||||
else
|
||||
RoundingMode_option{ .ok = .trunc, .is_ok = false };
|
||||
const smallest_unit = if (opts.smallest_unit) |unit| @intFromEnum(unit) else null;
|
||||
const rounding_mode = if (opts.rounding_mode) |mode| @intFromEnum(mode) else null;
|
||||
|
||||
return .{
|
||||
.precision = precision,
|
||||
.smallest_unit = smallest_unit_opt,
|
||||
.rounding_mode = rounding_mode_opt,
|
||||
.smallest_unit = temporal_rs.toUnitOption(smallest_unit),
|
||||
.rounding_mode = temporal_rs.toRoundingModeOption(rounding_mode),
|
||||
};
|
||||
}
|
||||
|
||||
fn parseDuration(text: []const u8) !DurationHandle {
|
||||
const view = DiplomatStringView{ .data = text.ptr, .len = text.len };
|
||||
const res = temporal_rs_Duration_from_utf8(view);
|
||||
if (!res.is_ok) return error.TemporalError;
|
||||
const ptr = res.result.ok orelse return error.TemporalError;
|
||||
return .{ .ptr = ptr };
|
||||
const view = temporal_rs.toDiplomatStringView(text);
|
||||
return wrapDuration(c.temporal_rs_Duration_from_utf8(view));
|
||||
}
|
||||
|
||||
// --- Public helper types -----------------------------------------------------
|
||||
|
|
@ -260,7 +288,7 @@ const DurationHandle = struct {
|
|||
ptr: *Duration,
|
||||
|
||||
pub fn deinit(self: DurationHandle) void {
|
||||
temporal_rs_Duration_destroy(self.ptr);
|
||||
c.temporal_rs_Duration_destroy(self.ptr);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -268,215 +296,10 @@ const ZonedDateTimeHandle = struct {
|
|||
ptr: *ZonedDateTime,
|
||||
|
||||
pub fn deinit(self: ZonedDateTimeHandle) void {
|
||||
temporal_rs_ZonedDateTime_destroy(self.ptr);
|
||||
c.temporal_rs_ZonedDateTime_destroy(self.ptr);
|
||||
}
|
||||
};
|
||||
|
||||
// --- Extern types ------------------------------------------------------------
|
||||
|
||||
const CInstant = opaque {};
|
||||
const Duration = opaque {};
|
||||
const ZonedDateTime = opaque {};
|
||||
const Provider = opaque {};
|
||||
|
||||
const I128Nanoseconds = extern struct { high: u64, low: u64 };
|
||||
const I128Nanoseconds_option = extern struct { ok: I128Nanoseconds, is_ok: bool };
|
||||
|
||||
const DiplomatStringView = extern struct { data: [*c]const u8, len: usize };
|
||||
const DiplomatString16View = extern struct { data: [*c]const u16, len: usize };
|
||||
|
||||
const OptionStringView = extern struct { ok: DiplomatStringView, is_ok: bool };
|
||||
const OptionU8 = extern struct { ok: u8, is_ok: bool };
|
||||
const OptionU32 = extern struct { ok: u32, is_ok: bool };
|
||||
|
||||
const DiplomatWrite = extern struct {
|
||||
context: ?*anyopaque,
|
||||
buf: [*c]u8,
|
||||
len: usize,
|
||||
cap: usize,
|
||||
grow_failed: bool,
|
||||
flush: ?*const fn (*DiplomatWrite) void,
|
||||
grow: ?*const fn (*DiplomatWrite, usize) bool,
|
||||
};
|
||||
|
||||
const TimeZone = extern struct {
|
||||
offset_minutes: i16,
|
||||
resolved_id: usize,
|
||||
normalized_id: usize,
|
||||
is_iana_id: bool,
|
||||
};
|
||||
|
||||
const TimeZone_option = extern struct {
|
||||
ok: TimeZone,
|
||||
is_ok: bool,
|
||||
};
|
||||
|
||||
const Precision = extern struct {
|
||||
is_minute: bool,
|
||||
precision: OptionU8,
|
||||
};
|
||||
|
||||
/// Time unit for Temporal operations.
|
||||
/// See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal
|
||||
pub const Unit = enum(c_int) {
|
||||
auto = 0,
|
||||
nanosecond = 1,
|
||||
microsecond = 2,
|
||||
millisecond = 3,
|
||||
second = 4,
|
||||
minute = 5,
|
||||
hour = 6,
|
||||
day = 7,
|
||||
week = 8,
|
||||
month = 9,
|
||||
year = 10,
|
||||
};
|
||||
|
||||
const Unit_option = extern struct { ok: Unit, is_ok: bool };
|
||||
|
||||
/// Rounding mode for Temporal operations.
|
||||
/// See: https://tc39.es/ecma402/#table-sanctioned-single-unit-identifiers
|
||||
pub const RoundingMode = enum(c_int) {
|
||||
/// Round toward positive infinity
|
||||
ceil = 0,
|
||||
/// Round toward negative infinity
|
||||
floor = 1,
|
||||
/// Round away from zero
|
||||
expand = 2,
|
||||
/// Round toward zero (truncate)
|
||||
trunc = 3,
|
||||
/// Round half toward positive infinity
|
||||
half_ceil = 4,
|
||||
/// Round half toward negative infinity
|
||||
half_floor = 5,
|
||||
/// Round half away from zero
|
||||
half_expand = 6,
|
||||
/// Round half toward zero
|
||||
half_trunc = 7,
|
||||
/// Round half to even (banker's rounding)
|
||||
half_even = 8,
|
||||
};
|
||||
|
||||
const RoundingMode_option = extern struct { ok: RoundingMode, is_ok: bool };
|
||||
|
||||
const DifferenceSettings = extern struct {
|
||||
largest_unit: Unit_option,
|
||||
smallest_unit: Unit_option,
|
||||
rounding_mode: RoundingMode_option,
|
||||
increment: OptionU32,
|
||||
};
|
||||
|
||||
const RoundingOptions = extern struct {
|
||||
largest_unit: Unit_option,
|
||||
smallest_unit: Unit_option,
|
||||
rounding_mode: RoundingMode_option,
|
||||
increment: OptionU32,
|
||||
};
|
||||
|
||||
const ToStringRoundingOptions = extern struct {
|
||||
precision: Precision,
|
||||
smallest_unit: Unit_option,
|
||||
rounding_mode: RoundingMode_option,
|
||||
};
|
||||
|
||||
const ErrorKind = enum(c_int) {
|
||||
ErrorKind_Generic = 0,
|
||||
ErrorKind_Type = 1,
|
||||
ErrorKind_Range = 2,
|
||||
ErrorKind_Syntax = 3,
|
||||
ErrorKind_Assert = 4,
|
||||
};
|
||||
|
||||
const TemporalError = extern struct {
|
||||
kind: ErrorKind,
|
||||
msg: OptionStringView,
|
||||
};
|
||||
|
||||
const Sign = enum(c_int) {
|
||||
Sign_Positive = 1,
|
||||
Sign_Zero = 0,
|
||||
Sign_Negative = -1,
|
||||
};
|
||||
|
||||
// --- Result wrappers ---------------------------------------------------------
|
||||
|
||||
const InstantResult = extern struct {
|
||||
result: extern union {
|
||||
ok: ?*CInstant,
|
||||
err: TemporalError,
|
||||
},
|
||||
is_ok: bool,
|
||||
};
|
||||
|
||||
const DurationResult = extern struct {
|
||||
result: extern union {
|
||||
ok: ?*Duration,
|
||||
err: TemporalError,
|
||||
},
|
||||
is_ok: bool,
|
||||
};
|
||||
|
||||
const DurationParseResult = extern struct {
|
||||
result: extern union {
|
||||
ok: ?*Duration,
|
||||
err: TemporalError,
|
||||
},
|
||||
is_ok: bool,
|
||||
};
|
||||
|
||||
const ZonedDateTimeResult = extern struct {
|
||||
result: extern union {
|
||||
ok: ?*ZonedDateTime,
|
||||
err: TemporalError,
|
||||
},
|
||||
is_ok: bool,
|
||||
};
|
||||
|
||||
const VoidResult = extern struct {
|
||||
result: extern union {
|
||||
err: TemporalError,
|
||||
},
|
||||
is_ok: bool,
|
||||
};
|
||||
|
||||
// --- Extern functions -------------------------------------------------------
|
||||
|
||||
extern "c" fn temporal_rs_Instant_try_new(ns: I128Nanoseconds) InstantResult;
|
||||
extern "c" fn temporal_rs_Instant_from_epoch_milliseconds(epoch_milliseconds: i64) InstantResult;
|
||||
extern "c" fn temporal_rs_Instant_from_utf8(s: DiplomatStringView) InstantResult;
|
||||
extern "c" fn temporal_rs_Instant_from_utf16(s: DiplomatString16View) InstantResult;
|
||||
extern "c" fn temporal_rs_Instant_add(self: *const CInstant, duration: *const Duration) InstantResult;
|
||||
extern "c" fn temporal_rs_Instant_subtract(self: *const CInstant, duration: *const Duration) InstantResult;
|
||||
extern "c" fn temporal_rs_Instant_since(self: *const CInstant, other: *const CInstant, settings: DifferenceSettings) DurationResult;
|
||||
extern "c" fn temporal_rs_Instant_until(self: *const CInstant, other: *const CInstant, settings: DifferenceSettings) DurationResult;
|
||||
extern "c" fn temporal_rs_Instant_round(self: *const CInstant, options: RoundingOptions) InstantResult;
|
||||
extern "c" fn temporal_rs_Instant_compare(self: *const CInstant, other: *const CInstant) i8;
|
||||
extern "c" fn temporal_rs_Instant_equals(self: *const CInstant, other: *const CInstant) bool;
|
||||
extern "c" fn temporal_rs_Instant_epoch_milliseconds(self: *const CInstant) i64;
|
||||
extern "c" fn temporal_rs_Instant_epoch_nanoseconds(self: *const CInstant) I128Nanoseconds;
|
||||
extern "c" fn temporal_rs_Instant_to_ixdtf_string_with_compiled_data(self: *const CInstant, zone: TimeZone_option, options: ToStringRoundingOptions, write: *DiplomatWrite) VoidResult;
|
||||
extern "c" fn temporal_rs_Instant_to_ixdtf_string_with_provider(self: *const CInstant, zone: TimeZone_option, options: ToStringRoundingOptions, p: *const Provider, write: *DiplomatWrite) VoidResult;
|
||||
extern "c" fn temporal_rs_Instant_to_zoned_date_time_iso(self: *const CInstant, zone: TimeZone) ZonedDateTimeResult;
|
||||
extern "c" fn temporal_rs_Instant_to_zoned_date_time_iso_with_provider(self: *const CInstant, zone: TimeZone, p: *const Provider) ZonedDateTimeResult;
|
||||
extern "c" fn temporal_rs_Instant_clone(self: *const CInstant) *CInstant;
|
||||
extern "c" fn temporal_rs_Instant_destroy(self: *CInstant) void;
|
||||
|
||||
extern "c" fn temporal_rs_Duration_destroy(self: *Duration) void;
|
||||
extern "c" fn temporal_rs_Duration_from_utf8(s: DiplomatStringView) DurationParseResult;
|
||||
extern "c" fn temporal_rs_Duration_hours(self: *const Duration) i64;
|
||||
extern "c" fn temporal_rs_Duration_minutes(self: *const Duration) i64;
|
||||
extern "c" fn temporal_rs_Duration_seconds(self: *const Duration) i64;
|
||||
extern "c" fn temporal_rs_Duration_milliseconds(self: *const Duration) i64;
|
||||
extern "c" fn temporal_rs_Duration_microseconds(self: *const Duration) f64;
|
||||
extern "c" fn temporal_rs_Duration_nanoseconds(self: *const Duration) f64;
|
||||
extern "c" fn temporal_rs_Duration_sign(self: *const Duration) Sign;
|
||||
extern "c" fn temporal_rs_ZonedDateTime_destroy(self: *ZonedDateTime) void;
|
||||
|
||||
extern "c" fn diplomat_buffer_write_create(cap: usize) *DiplomatWrite;
|
||||
extern "c" fn diplomat_buffer_write_get_bytes(write: *DiplomatWrite) [*c]u8;
|
||||
extern "c" fn diplomat_buffer_write_len(write: *DiplomatWrite) usize;
|
||||
extern "c" fn diplomat_buffer_write_destroy(write: *DiplomatWrite) void;
|
||||
|
||||
// --- Tests -------------------------------------------------------------------
|
||||
|
||||
test init {
|
||||
|
|
@ -544,13 +367,13 @@ test compare {
|
|||
defer a.deinit();
|
||||
const b = try Instant.fromEpochMilliseconds(0);
|
||||
defer b.deinit();
|
||||
const c = try Instant.fromEpochMilliseconds(1_000);
|
||||
defer c.deinit();
|
||||
const cc = try Instant.fromEpochMilliseconds(1_000);
|
||||
defer cc.deinit();
|
||||
|
||||
try std.testing.expectEqual(@as(i8, 0), Instant.compare(a, b));
|
||||
try std.testing.expect(Instant.equals(a, b));
|
||||
try std.testing.expectEqual(@as(i8, -1), Instant.compare(a, c));
|
||||
try std.testing.expectEqual(@as(i8, 1), Instant.compare(c, a));
|
||||
try std.testing.expectEqual(@as(i8, -1), Instant.compare(a, cc));
|
||||
try std.testing.expectEqual(@as(i8, 1), Instant.compare(cc, a));
|
||||
}
|
||||
|
||||
test equals {
|
||||
|
|
@ -558,13 +381,13 @@ test equals {
|
|||
defer a.deinit();
|
||||
const b = try Instant.fromEpochMilliseconds(0);
|
||||
defer b.deinit();
|
||||
const c = try Instant.fromEpochMilliseconds(1_000);
|
||||
defer c.deinit();
|
||||
const cc = try Instant.fromEpochMilliseconds(1_000);
|
||||
defer cc.deinit();
|
||||
|
||||
try std.testing.expectEqual(@as(i8, 0), Instant.compare(a, b));
|
||||
try std.testing.expect(Instant.equals(a, b));
|
||||
try std.testing.expectEqual(@as(i8, -1), Instant.compare(a, c));
|
||||
try std.testing.expectEqual(@as(i8, 1), Instant.compare(c, a));
|
||||
try std.testing.expectEqual(@as(i8, -1), Instant.compare(a, cc));
|
||||
try std.testing.expectEqual(@as(i8, 1), Instant.compare(cc, a));
|
||||
}
|
||||
|
||||
test until {
|
||||
|
|
@ -574,21 +397,21 @@ test until {
|
|||
defer later.deinit();
|
||||
|
||||
const settings = DifferenceSettings{
|
||||
.largest_unit = Unit_option{ .ok = .hour, .is_ok = true },
|
||||
.smallest_unit = Unit_option{ .ok = .second, .is_ok = true },
|
||||
.rounding_mode = RoundingMode_option{ .ok = .trunc, .is_ok = true },
|
||||
.increment = OptionU32{ .ok = 0, .is_ok = false },
|
||||
.largest_unit = temporal_rs.toUnitOption(@intFromEnum(Unit.hour)),
|
||||
.smallest_unit = temporal_rs.toUnitOption(@intFromEnum(Unit.second)),
|
||||
.rounding_mode = temporal_rs.toRoundingModeOption(@intFromEnum(RoundingMode.trunc)),
|
||||
.increment = temporal_rs.toOption(c.OptionU32, null),
|
||||
};
|
||||
|
||||
var until_handle = try earlier.until(later, settings);
|
||||
defer until_handle.deinit();
|
||||
try std.testing.expectEqual(Sign.Sign_Positive, temporal_rs_Duration_sign(until_handle.ptr));
|
||||
try std.testing.expectEqual(@as(i64, 1), temporal_rs_Duration_hours(until_handle.ptr));
|
||||
try std.testing.expectEqual(Sign.positive, @as(Sign, @enumFromInt(temporal_rs.c.temporal_rs_Duration_sign(until_handle.ptr))));
|
||||
try std.testing.expectEqual(@as(i64, 1), temporal_rs.c.temporal_rs_Duration_hours(until_handle.ptr));
|
||||
|
||||
var since_handle = try later.since(earlier, settings);
|
||||
defer since_handle.deinit();
|
||||
try std.testing.expectEqual(Sign.Sign_Positive, temporal_rs_Duration_sign(since_handle.ptr));
|
||||
try std.testing.expectEqual(@as(i64, 1), temporal_rs_Duration_hours(since_handle.ptr));
|
||||
try std.testing.expectEqual(Sign.positive, @as(Sign, @enumFromInt(temporal_rs.c.temporal_rs_Duration_sign(since_handle.ptr))));
|
||||
try std.testing.expectEqual(@as(i64, 1), temporal_rs.c.temporal_rs_Duration_hours(since_handle.ptr));
|
||||
}
|
||||
|
||||
test since {
|
||||
|
|
@ -598,21 +421,21 @@ test since {
|
|||
defer later.deinit();
|
||||
|
||||
const settings = DifferenceSettings{
|
||||
.largest_unit = Unit_option{ .ok = .hour, .is_ok = true },
|
||||
.smallest_unit = Unit_option{ .ok = .second, .is_ok = true },
|
||||
.rounding_mode = RoundingMode_option{ .ok = .trunc, .is_ok = true },
|
||||
.increment = OptionU32{ .ok = 0, .is_ok = false },
|
||||
.largest_unit = temporal_rs.toUnitOption(@intFromEnum(Unit.hour)),
|
||||
.smallest_unit = temporal_rs.toUnitOption(@intFromEnum(Unit.second)),
|
||||
.rounding_mode = temporal_rs.toRoundingModeOption(@intFromEnum(RoundingMode.trunc)),
|
||||
.increment = temporal_rs.toOption(c.OptionU32, null),
|
||||
};
|
||||
|
||||
var until_handle = try earlier.until(later, settings);
|
||||
defer until_handle.deinit();
|
||||
try std.testing.expectEqual(Sign.Sign_Positive, temporal_rs_Duration_sign(until_handle.ptr));
|
||||
try std.testing.expectEqual(@as(i64, 1), temporal_rs_Duration_hours(until_handle.ptr));
|
||||
try std.testing.expectEqual(Sign.positive, @as(Sign, @enumFromInt(temporal_rs.c.temporal_rs_Duration_sign(until_handle.ptr))));
|
||||
try std.testing.expectEqual(@as(i64, 1), temporal_rs.c.temporal_rs_Duration_hours(until_handle.ptr));
|
||||
|
||||
var since_handle = try later.since(earlier, settings);
|
||||
defer since_handle.deinit();
|
||||
try std.testing.expectEqual(Sign.Sign_Positive, temporal_rs_Duration_sign(since_handle.ptr));
|
||||
try std.testing.expectEqual(@as(i64, 1), temporal_rs_Duration_hours(since_handle.ptr));
|
||||
try std.testing.expectEqual(Sign.positive, @as(Sign, @enumFromInt(temporal_rs.c.temporal_rs_Duration_sign(since_handle.ptr))));
|
||||
try std.testing.expectEqual(@as(i64, 1), temporal_rs.c.temporal_rs_Duration_hours(since_handle.ptr));
|
||||
}
|
||||
|
||||
test round {
|
||||
|
|
@ -620,10 +443,10 @@ test round {
|
|||
defer inst.deinit();
|
||||
|
||||
const opts = RoundingOptions{
|
||||
.largest_unit = Unit_option{ .ok = .auto, .is_ok = false },
|
||||
.smallest_unit = Unit_option{ .ok = .second, .is_ok = true },
|
||||
.rounding_mode = RoundingMode_option{ .ok = .half_expand, .is_ok = true },
|
||||
.increment = OptionU32{ .ok = 0, .is_ok = false },
|
||||
.largest_unit = temporal_rs.toUnitOption(null),
|
||||
.smallest_unit = temporal_rs.toUnitOption(@intFromEnum(Unit.second)),
|
||||
.rounding_mode = temporal_rs.toRoundingModeOption(@intFromEnum(RoundingMode.half_expand)),
|
||||
.increment = temporal_rs.toOption(c.OptionU32, null),
|
||||
};
|
||||
|
||||
const rounded = try inst.round(opts);
|
||||
|
|
|
|||
263
src/cabi.zig
Normal file
263
src/cabi.zig
Normal file
|
|
@ -0,0 +1,263 @@
|
|||
const std = @import("std");
|
||||
|
||||
pub const c = @cImport({
|
||||
@cInclude("AnyCalendarKind.h");
|
||||
@cInclude("Calendar.h");
|
||||
@cInclude("Duration.h");
|
||||
@cInclude("ErrorKind.h");
|
||||
@cInclude("I128Nanoseconds.h");
|
||||
@cInclude("Instant.h");
|
||||
@cInclude("OwnedRelativeTo.h");
|
||||
@cInclude("ParsedDate.h");
|
||||
@cInclude("ParsedDateTime.h");
|
||||
@cInclude("ParsedZonedDateTime.h");
|
||||
@cInclude("PlainDate.h");
|
||||
@cInclude("PlainDateTime.h");
|
||||
@cInclude("PlainMonthDay.h");
|
||||
@cInclude("PlainTime.h");
|
||||
@cInclude("PlainYearMonth.h");
|
||||
@cInclude("RelativeTo.h");
|
||||
@cInclude("TimeZone.h");
|
||||
@cInclude("ZonedDateTime.h");
|
||||
});
|
||||
|
||||
pub const to_string_rounding_options_auto: c.ToStringRoundingOptions = .{
|
||||
.precision = .{ .is_minute = false, .precision = toOption(c.OptionU8, null) },
|
||||
.smallest_unit = toUnitOption(null),
|
||||
.rounding_mode = toRoundingModeOption(null),
|
||||
};
|
||||
|
||||
const u64_high_bit_mask: u64 = 1 << 63;
|
||||
|
||||
/// Covert a Rust `I128Nanoseconds` struct to a Zig `i128`.
|
||||
///
|
||||
/// Ported from temporal_rs's [`From<ffi::I128Nanoseconds>`](https://github.com/boa-dev/temporal/blob/89bfca1f5b918d00a19354664e1da11da51305ee/temporal_capi/src/instant.rs#L172-L186) trait for `i128`.
|
||||
pub fn fromI128Nanoseconds(ns: c.I128Nanoseconds) i128 {
|
||||
const is_neg = (ns.high & u64_high_bit_mask) != 0;
|
||||
const ns_high: u128 = @intCast((ns.high & ~u64_high_bit_mask));
|
||||
const total: i128 = @intCast((ns_high << 64) + ns.low);
|
||||
return if (is_neg) -total else total;
|
||||
}
|
||||
|
||||
/// Covert a Zig `i128` to a Rust `I128Nanoseconds` struct.
|
||||
///
|
||||
/// Ported from temporal_rs's [`From<i128>`](https://github.com/boa-dev/temporal/blob/89bfca1f5b918d00a19354664e1da11da51305ee/temporal_capi/src/instant.rs#L188-L207) trait for `ffi::I128Nanoseconds`.
|
||||
pub fn toI128Nanoseconds(ns: i128) c.I128Nanoseconds {
|
||||
std.debug.assert(ns != std.math.minInt(i128));
|
||||
const is_neg = ns < 0;
|
||||
const ns_abs = @abs(ns);
|
||||
const high: u64 = @intCast(ns_abs >> 64);
|
||||
const low: u64 = @truncate(ns_abs);
|
||||
return .{ .high = if (is_neg) high | u64_high_bit_mask else high, .low = low };
|
||||
}
|
||||
|
||||
/// Convert a Rust `DiplomatStringView` to a Zig slice.
|
||||
pub fn fromDiplomatStringView(sv: c.DiplomatStringView) []const u8 {
|
||||
return sv.data[0..sv.len];
|
||||
}
|
||||
|
||||
/// Convert a Zig slice to a Rust `DiplomatStringView`.
|
||||
pub fn toDiplomatStringView(s: []const u8) c.DiplomatStringView {
|
||||
return .{ .data = s.ptr, .len = s.len };
|
||||
}
|
||||
|
||||
/// Convert a Zig slice to a Rust `DiplomatString16View`.
|
||||
pub fn toDiplomatString16View(s: []const u16) c.DiplomatString16View {
|
||||
return .{ .data = s.ptr, .len = s.len };
|
||||
}
|
||||
|
||||
/// Convert a Rust `Option<T>` to a Zig `?T`.
|
||||
pub fn fromOption(value: anytype) ?Success(@TypeOf(value)) {
|
||||
return success(value);
|
||||
}
|
||||
|
||||
/// Convert a Zig `?T` to a Rust `Option<T>`.
|
||||
pub fn toOption(comptime T: type, maybe_value: ?Success(T)) T {
|
||||
return if (maybe_value) |value|
|
||||
.{ .is_ok = true, .unnamed_0 = .{ .ok = value } }
|
||||
else
|
||||
.{ .is_ok = false };
|
||||
}
|
||||
|
||||
/// Convert a Zig `?ArithmeticOverflow` to a Rust `Option<ArithmeticOverflow>`.
|
||||
pub fn toArithmeticOverflowOption(maybe_value: ?c.RoundingMode) c.ArithmeticOverflow_option {
|
||||
return toOption(c.ArithmeticOverflow_option, maybe_value);
|
||||
}
|
||||
|
||||
/// Convert a Zig `?Disambiguation` to a Rust `Option<Disambiguation>`.
|
||||
pub fn toDisambiguationOption(maybe_value: ?c.Disambiguation) c.Disambiguation_option {
|
||||
return toOption(c.Disambiguation_option, maybe_value);
|
||||
}
|
||||
|
||||
/// Convert a Zig `?OffsetDisambiguation` to a Rust `Option<OffsetDisambiguation>`.
|
||||
pub fn toOffsetDisambiguationOption(maybe_value: ?c.OffsetDisambiguation) c.OffsetDisambiguation_option {
|
||||
return toOption(c.OffsetDisambiguation_option, maybe_value);
|
||||
}
|
||||
|
||||
/// Convert a Zig `?PartialDate` to a Rust `Option<PartialDate>`.
|
||||
pub fn toPartialDateOption(maybe_value: ?c.PartialDate) c.PartialDate_option {
|
||||
return toOption(c.PartialDate_option, maybe_value);
|
||||
}
|
||||
|
||||
/// Convert a Zig `?RoundingMode` to a Rust `Option<RoundingMode>`.
|
||||
pub fn toRoundingModeOption(maybe_value: ?c.RoundingMode) c.RoundingMode_option {
|
||||
return toOption(c.RoundingMode_option, maybe_value);
|
||||
}
|
||||
|
||||
/// Convert a Zig `?TimeZone` to a Rust `Option<TimeZone>`.
|
||||
pub fn toTimeZoneOption(maybe_value: ?c.TimeZone) c.TimeZone_option {
|
||||
return toOption(c.TimeZone_option, maybe_value);
|
||||
}
|
||||
|
||||
/// Convert a Zig `?Unit` to a Rust `Option<Unit>`.
|
||||
pub fn toUnitOption(maybe_value: ?c.Unit) c.Unit_option {
|
||||
return toOption(c.Unit_option, maybe_value);
|
||||
}
|
||||
|
||||
// Wraps values from a `c.RelativeTo` or `c.OwnedRelativeTo`.
|
||||
pub const RelativeTo = union(enum) {
|
||||
none,
|
||||
owned_plain_date: *c.PlainDate,
|
||||
owned_zoned_date_time: *c.ZonedDateTime,
|
||||
borrowed_plain_date: *const c.PlainDate,
|
||||
borrowed_zoned_date_time: *const c.ZonedDateTime,
|
||||
|
||||
pub fn fromOwned(owned: c.OwnedRelativeTo) RelativeTo {
|
||||
if (owned.date) |plain_date| {
|
||||
return .{ .owned_plain_date = plain_date };
|
||||
} else if (owned.zoned) |zoned_date_time| {
|
||||
return .{ .owned_zoned_date_time = zoned_date_time };
|
||||
} else {
|
||||
return .none;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn toRust(self: RelativeTo) c.RelativeTo {
|
||||
return switch (self) {
|
||||
.none => .{ .date = null, .zoned = null },
|
||||
.owned_plain_date => |plain_date| .{ .date = plain_date, .zoned = null },
|
||||
.owned_zoned_date_time => |zoned_date_time| .{ .date = null, .zoned = zoned_date_time },
|
||||
.borrowed_plain_date => |plain_date| .{ .date = plain_date, .zoned = null },
|
||||
.borrowed_zoned_date_time => |zoned_date_time| .{ .date = null, .zoned = zoned_date_time },
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: RelativeTo) void {
|
||||
switch (self) {
|
||||
.owned_plain_date => |plain_date| c.temporal_rs_PlainDate_destroy(plain_date),
|
||||
.owned_zoned_date_time => |zoned_date_time| c.temporal_rs_ZonedDateTime_destroy(zoned_date_time),
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pub const DiplomatWrite = struct {
|
||||
gpa: std.mem.Allocator,
|
||||
array_list: std.ArrayList(u8),
|
||||
inner: c.DiplomatWrite,
|
||||
|
||||
pub fn init(gpa: std.mem.Allocator) DiplomatWrite {
|
||||
return .{
|
||||
.gpa = gpa,
|
||||
.array_list = .empty,
|
||||
.inner = .{
|
||||
// NOTE: We use `@fieldParentPtr()` on the `inner` struct field to get to the other
|
||||
// fields instead of creating a context externally and storing a pointer.
|
||||
.context = null,
|
||||
.buf = undefined,
|
||||
.len = 0,
|
||||
.cap = 0,
|
||||
.grow_failed = false,
|
||||
.flush = flush,
|
||||
.grow = grow,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *DiplomatWrite) void {
|
||||
self.array_list.deinit(self.gpa);
|
||||
}
|
||||
|
||||
pub fn toOwnedSlice(self: *DiplomatWrite) std.mem.Allocator.Error![]u8 {
|
||||
if (self.inner.grow_failed) return error.OutOfMemory;
|
||||
self.inner = undefined; // Invalidate the inner struct to prevent further writes
|
||||
return self.array_list.toOwnedSlice(self.gpa);
|
||||
}
|
||||
|
||||
fn flush(inner: ?*c.DiplomatWrite) callconv(.c) void {
|
||||
const self: *DiplomatWrite = @fieldParentPtr("inner", inner.?);
|
||||
self.array_list.items.len = inner.?.len;
|
||||
}
|
||||
|
||||
fn grow(inner: ?*c.DiplomatWrite, size: usize) callconv(.c) bool {
|
||||
const self: *DiplomatWrite = @fieldParentPtr("inner", inner.?);
|
||||
self.array_list.ensureTotalCapacity(self.gpa, size) catch return false;
|
||||
inner.?.buf = self.array_list.items.ptr;
|
||||
inner.?.cap = self.array_list.capacity;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
test DiplomatWrite {
|
||||
const gpa = std.testing.allocator;
|
||||
var write = DiplomatWrite.init(gpa);
|
||||
defer write.deinit();
|
||||
|
||||
const WriteImpl = struct {
|
||||
inner: *c.DiplomatWrite,
|
||||
|
||||
// https://github.com/rust-diplomat/diplomat/blob/2b903255187976779798fc89df3fee7298641c80/runtime/src/write.rs#L70-L73
|
||||
pub fn flush(self: @This()) void {
|
||||
self.inner.flush.?(self.inner);
|
||||
}
|
||||
|
||||
// https://github.com/rust-diplomat/diplomat/blob/2b903255187976779798fc89df3fee7298641c80/runtime/src/write.rs#L76-L94
|
||||
pub fn writeStr(self: @This(), s: []const u8) void {
|
||||
if (self.inner.grow_failed) {
|
||||
return;
|
||||
}
|
||||
const needed_len = self.inner.len + s.len;
|
||||
if (needed_len > self.inner.cap) {
|
||||
const success_ = self.inner.grow.?(self.inner, needed_len);
|
||||
if (!success_) {
|
||||
self.inner.grow_failed = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
std.debug.assert(needed_len <= self.inner.cap);
|
||||
@memcpy(self.inner.buf[self.inner.len..][0..s.len], s);
|
||||
self.inner.len = needed_len;
|
||||
}
|
||||
};
|
||||
|
||||
var write_impl: WriteImpl = .{ .inner = &write.inner };
|
||||
write_impl.writeStr("Hello World");
|
||||
write_impl.flush();
|
||||
|
||||
try std.testing.expectEqual(write.array_list.items.ptr, write.inner.buf);
|
||||
try std.testing.expectEqual(write.array_list.items.len, write.inner.len);
|
||||
try std.testing.expectEqual(write.array_list.capacity, write.inner.cap);
|
||||
try std.testing.expectEqual(false, write.inner.grow_failed);
|
||||
try std.testing.expectEqualSlices(u8, "Hello World", write.array_list.items);
|
||||
|
||||
const slice = try write.toOwnedSlice();
|
||||
defer gpa.free(slice);
|
||||
try std.testing.expectEqualSlices(u8, "Hello World", slice);
|
||||
try std.testing.expectEqualSlices(u8, &.{}, write.array_list.items);
|
||||
}
|
||||
|
||||
/// Converts a "result" value to its "success" type, or returns `null` if the value is an error.
|
||||
/// This is `inline` to prevent binary bloat, because each instantiation is expected to be called
|
||||
/// only once.
|
||||
pub inline fn success(result: anytype) ?Success(@TypeOf(result)) {
|
||||
if (!result.is_ok) return null;
|
||||
if (Success(@TypeOf(result)) == void) return;
|
||||
return result.unnamed_0.ok;
|
||||
}
|
||||
|
||||
/// Given the C API representation of a `Result<T, E>`, returns the type 'T'.
|
||||
pub fn Success(comptime Result: type) type {
|
||||
const Union = @FieldType(Result, "unnamed_0");
|
||||
if (!@hasField(Union, "ok")) return void;
|
||||
return @FieldType(Union, "ok");
|
||||
}
|
||||
1
src/lib.rs
Normal file
1
src/lib.rs
Normal file
|
|
@ -0,0 +1 @@
|
|||
extern crate temporal_capi;
|
||||
|
|
@ -13,6 +13,4 @@ pub fn main() !void {
|
|||
|
||||
const dur = try Temporal.Duration.from("P1Y2M3DT4H5M6S");
|
||||
defer dur.deinit();
|
||||
// const dur_str = try dur.toString(allocator, .{});
|
||||
// std.debug.print("Duration String: {s}\n", .{dur_str});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -113,6 +113,7 @@ test "Temporal.Instant" {
|
|||
"ToStringOptions",
|
||||
"Unit",
|
||||
"RoundingMode",
|
||||
"Sign",
|
||||
};
|
||||
|
||||
try assertDecls(Instant, checks);
|
||||
|
|
|
|||
29
src/unwind_stubs.zig
Normal file
29
src/unwind_stubs.zig
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
//! Provides unwind symbols because apparently Rust still might include these when using panic=abort.
|
||||
//!
|
||||
//! See:
|
||||
//! - https://github.com/rust-lang/rust/issues/47493
|
||||
//! - https://code.videolan.org/videolan/vlc/-/merge_requests/2878
|
||||
|
||||
export fn _Unwind_Backtrace() void {}
|
||||
export fn _Unwind_DeleteException() void {}
|
||||
export fn _Unwind_GetDataRelBase() void {}
|
||||
export fn _Unwind_GetIP() void {}
|
||||
export fn _Unwind_GetIPInfo() void {}
|
||||
export fn _Unwind_GetLanguageSpecificData() void {}
|
||||
export fn _Unwind_GetRegionStart() void {}
|
||||
export fn _Unwind_GetTextRelBase() void {}
|
||||
export fn _Unwind_RaiseException() void {}
|
||||
export fn _Unwind_Resume() void {}
|
||||
export fn _Unwind_SetGR() void {}
|
||||
export fn _Unwind_SetIP() void {}
|
||||
// These appear when building for riscv64
|
||||
export fn _Unwind_FindEnclosingFunction() void {}
|
||||
export fn _Unwind_GetCFA() void {}
|
||||
// These appear when building for arm
|
||||
export fn _Unwind_VRS_Get() void {}
|
||||
export fn _Unwind_VRS_Set() void {}
|
||||
export fn __gnu_unwind_frame() void {}
|
||||
// This appears when building for s390x
|
||||
export fn _Unwind_GetGR() void {}
|
||||
// This appears when building for x86_64-windows
|
||||
export fn _GCC_specific_handler() void {}
|
||||
Loading…
Add table
Reference in a new issue