Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 23 additions & 8 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion tempoch-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ path = "src/lib.rs"

[dependencies]
chrono = "0.4.43"
qtty = "0.3.0"
qtty = "0.3.1"
serde = { version = "1.0", default-features = false, features = ["derive"], optional = true }

[dev-dependencies]
Expand Down
5 changes: 3 additions & 2 deletions tempoch-ffi/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@ serde = ["tempoch/serde", "dep:serde_json"]

[dependencies]
tempoch = { path = "../tempoch", version = "0.3.0" }
qtty = "0.3.0"
chrono = "0.4.43"
qtty = "0.3.1"
qtty-ffi = "0.3.1"
chrono = "0.4.44"
serde_json = { version = "1.0", optional = true }

[build-dependencies]
Expand Down
7 changes: 7 additions & 0 deletions tempoch-ffi/cbindgen.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,23 @@ autogen_warning = "/* Warning: this file is auto-generated by cbindgen. Do not m
documentation_style = "c99"
style = "both"
cpp_compat = true
after_includes = """
/* QttyQuantity and UnitId definitions come from qtty_ffi.h */
#include "qtty_ffi.h"
"""

[defines]
"feature = serde" = "TEMPOCH_FFI_HAS_SERDE"

[export]
# Exclude qtty-ffi types — they come from qtty_ffi.h (included via after_includes)
exclude = ["QttyQuantity", "UnitId", "DimensionId", "QttyDerivedQuantity"]

[export.rename]
"TempochStatus" = "tempoch_status_t"
"TempochUtc" = "tempoch_utc_t"
"TempochPeriodMjd" = "tempoch_period_mjd_t"
"QttyQuantity" = "qtty_quantity_t"

[enum]
rename_variants = "ScreamingSnakeCase"
Expand Down
29 changes: 29 additions & 0 deletions tempoch-ffi/include/tempoch_ffi.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
/* QttyQuantity and UnitId definitions come from qtty_ffi.h */
#include "qtty_ffi.h"


// Status codes returned by tempoch-ffi functions.
//
Expand Down Expand Up @@ -131,6 +134,32 @@ tempoch_status_t tempoch_period_mjd_intersection(struct tempoch_period_mjd_t a,
// Compute Julian centuries since J2000 for a given Julian Date.
double tempoch_jd_julian_centuries(double jd);

// Compute the difference between two Julian Dates as a `QttyQuantity` in days.
qtty_quantity_t tempoch_jd_difference_qty(double jd1, double jd2);

// Add a `QttyQuantity` duration (must be time-compatible) to a Julian Date.
// The quantity is converted to days internally.
//
// # Safety
// `out` must be a valid, writable pointer to `f64`.
tempoch_status_t tempoch_jd_add_qty(double jd, qtty_quantity_t duration, double *out);

// Compute the difference between two Modified Julian Dates as a `QttyQuantity` in days.
qtty_quantity_t tempoch_mjd_difference_qty(double mjd1, double mjd2);

// Add a `QttyQuantity` duration (must be time-compatible) to a Modified Julian Date.
// The quantity is converted to days internally.
//
// # Safety
// `out` must be a valid, writable pointer to `f64`.
tempoch_status_t tempoch_mjd_add_qty(double mjd, qtty_quantity_t duration, double *out);

// Compute Julian centuries since J2000 as a `QttyQuantity`.
qtty_quantity_t tempoch_jd_julian_centuries_qty(double jd);

// Compute the duration of a period as a `QttyQuantity` in days.
qtty_quantity_t tempoch_period_mjd_duration_qty(struct tempoch_period_mjd_t period);

#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
Expand Down
17 changes: 17 additions & 0 deletions tempoch-ffi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
//!
//! This crate exposes a flat C-compatible API for creating and manipulating
//! Julian Dates, Modified Julian Dates, time periods, and UTC conversions.
//!
//! Duration-related functions return [`QttyQuantity`] from qtty-ffi, providing
//! type-safe unit information alongside numeric values.

mod error;
mod period;
Expand All @@ -14,6 +17,20 @@ pub use error::*;
pub use period::*;
pub use time::*;

// Re-export qtty-ffi types used in our public FFI surface
pub use qtty_ffi::{QttyQuantity, UnitId};

/// Catches any panic and returns an error value instead of unwinding across FFI.
macro_rules! catch_panic {
($default:expr, $body:expr) => {{
match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| $body)) {
Ok(result) => result,
Err(_) => $default,
}
}};
}
pub(crate) use catch_panic;

/// Returns the tempoch-ffi ABI version (semver-encoded: major*10000 + minor*100 + patch).
#[allow(clippy::erasing_op, clippy::identity_op)]
#[no_mangle]
Expand Down
49 changes: 26 additions & 23 deletions tempoch-ffi/src/period.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

//! FFI bindings for tempoch period/interval types.

use crate::catch_panic;
use crate::error::TempochStatus;
use tempoch::{Interval, ModifiedJulianDate, Period, MJD};

Expand Down Expand Up @@ -50,17 +51,18 @@ pub unsafe extern "C" fn tempoch_period_mjd_new(
end_mjd: f64,
out: *mut TempochPeriodMjd,
) -> TempochStatus {
if out.is_null() {
return TempochStatus::NullPointer;
}
if start_mjd > end_mjd {
return TempochStatus::InvalidPeriod;
}
// SAFETY: `out` was checked for null and the caller guarantees it points to writable memory.
unsafe {
*out = TempochPeriodMjd { start_mjd, end_mjd };
}
TempochStatus::Ok
catch_panic!(TempochStatus::InvalidPeriod, {
if out.is_null() {
return TempochStatus::NullPointer;
}
if start_mjd > end_mjd {
return TempochStatus::InvalidPeriod;
}
unsafe {
*out = TempochPeriodMjd { start_mjd, end_mjd };
}
TempochStatus::Ok
})
}

/// Compute the duration of a period in days.
Expand All @@ -80,17 +82,18 @@ pub unsafe extern "C" fn tempoch_period_mjd_intersection(
b: TempochPeriodMjd,
out: *mut TempochPeriodMjd,
) -> TempochStatus {
if out.is_null() {
return TempochStatus::NullPointer;
}
let pa = a.to_period();
let pb = b.to_period();
match pa.intersection(&pb) {
Some(result) => {
// SAFETY: `out` was checked for null and the caller guarantees it points to writable memory.
unsafe { *out = TempochPeriodMjd::from_period(&result) };
TempochStatus::Ok
catch_panic!(TempochStatus::NoIntersection, {
if out.is_null() {
return TempochStatus::NullPointer;
}
None => TempochStatus::NoIntersection,
}
let pa = a.to_period();
let pb = b.to_period();
match pa.intersection(&pb) {
Some(result) => {
unsafe { *out = TempochPeriodMjd::from_period(&result) };
TempochStatus::Ok
}
None => TempochStatus::NoIntersection,
}
})
}
Loading