siderust-js 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/ci.yml +166 -0
- package/.gitmodules +9 -0
- package/CHANGELOG.md +26 -0
- package/LICENSE +661 -0
- package/README.md +138 -0
- package/package.json +12 -0
- package/qtty-js/.github/workflows/ci.yml +151 -0
- package/qtty-js/.gitmodules +3 -0
- package/qtty-js/CHANGELOG.md +31 -0
- package/qtty-js/LICENSE +661 -0
- package/qtty-js/README.md +132 -0
- package/qtty-js/package.json +20 -0
- package/qtty-js/qtty/.github/workflows/ci.yml +155 -0
- package/qtty-js/qtty/CHANGELOG.md +120 -0
- package/qtty-js/qtty/Cargo.lock +1462 -0
- package/qtty-js/qtty/Cargo.toml +12 -0
- package/qtty-js/qtty/LICENSE +661 -0
- package/qtty-js/qtty/README.md +9 -0
- package/qtty-js/qtty/qtty/Cargo.toml +41 -0
- package/qtty-js/qtty/qtty/README.md +8 -0
- package/qtty-js/qtty/qtty/examples/angles.rs +14 -0
- package/qtty-js/qtty/qtty/examples/astronomy.rs +17 -0
- package/qtty-js/qtty/qtty/examples/dimensional_arithmetic.rs +83 -0
- package/qtty-js/qtty/qtty/examples/python_integration.rs +61 -0
- package/qtty-js/qtty/qtty/examples/quickstart.rs +15 -0
- package/qtty-js/qtty/qtty/examples/ratios.rs +12 -0
- package/qtty-js/qtty/qtty/examples/serde_with_unit.rs +234 -0
- package/qtty-js/qtty/qtty/examples/serialization.rs +141 -0
- package/qtty-js/qtty/qtty/examples/serialization_advanced.rs +155 -0
- package/qtty-js/qtty/qtty/src/f32.rs +108 -0
- package/qtty-js/qtty/qtty/src/f64.rs +30 -0
- package/qtty-js/qtty/qtty/src/i128.rs +111 -0
- package/qtty-js/qtty/qtty/src/i16.rs +111 -0
- package/qtty-js/qtty/qtty/src/i32.rs +111 -0
- package/qtty-js/qtty/qtty/src/i64.rs +111 -0
- package/qtty-js/qtty/qtty/src/i8.rs +111 -0
- package/qtty-js/qtty/qtty/src/lib.rs +238 -0
- package/qtty-js/qtty/qtty/tests/fixtures/qtty-vec-no-std/Cargo.lock +83 -0
- package/qtty-js/qtty/qtty/tests/fixtures/qtty-vec-no-std/Cargo.toml +10 -0
- package/qtty-js/qtty/qtty/tests/fixtures/qtty-vec-no-std/src/lib.rs +7 -0
- package/qtty-js/qtty/qtty/tests/fixtures/qtty-vec-no-std-alloc/Cargo.lock +83 -0
- package/qtty-js/qtty/qtty/tests/fixtures/qtty-vec-no-std-alloc/Cargo.toml +10 -0
- package/qtty-js/qtty/qtty/tests/fixtures/qtty-vec-no-std-alloc/src/lib.rs +7 -0
- package/qtty-js/qtty/qtty/tests/fixtures/qtty-vec-std/Cargo.lock +83 -0
- package/qtty-js/qtty/qtty/tests/fixtures/qtty-vec-std/Cargo.toml +10 -0
- package/qtty-js/qtty/qtty/tests/fixtures/qtty-vec-std/src/lib.rs +5 -0
- package/qtty-js/qtty/qtty/tests/integration_tests.rs +529 -0
- package/qtty-js/qtty/qtty/tests/qtty_vec_feature_matrix.rs +58 -0
- package/qtty-js/qtty/qtty-core/Cargo.toml +41 -0
- package/qtty-js/qtty/qtty-core/README.md +8 -0
- package/qtty-js/qtty/qtty-core/examples/diesel_integration.rs +145 -0
- package/qtty-js/qtty/qtty-core/examples/quantity_db_serde.rs +215 -0
- package/qtty-js/qtty/qtty-core/src/dimension.rs +249 -0
- package/qtty-js/qtty/qtty-core/src/feature_diesel.rs +318 -0
- package/qtty-js/qtty/qtty-core/src/feature_pyo3.rs +27 -0
- package/qtty-js/qtty/qtty-core/src/feature_serde.rs +203 -0
- package/qtty-js/qtty/qtty-core/src/feature_tiberius.rs +28 -0
- package/qtty-js/qtty/qtty-core/src/lib.rs +744 -0
- package/qtty-js/qtty/qtty-core/src/macros.rs +93 -0
- package/qtty-js/qtty/qtty-core/src/quantity.rs +810 -0
- package/qtty-js/qtty/qtty-core/src/scalar.rs +1742 -0
- package/qtty-js/qtty/qtty-core/src/unit.rs +332 -0
- package/qtty-js/qtty/qtty-core/src/units/angular.rs +1228 -0
- package/qtty-js/qtty/qtty-core/src/units/area.rs +243 -0
- package/qtty-js/qtty/qtty-core/src/units/frequency.rs +179 -0
- package/qtty-js/qtty/qtty-core/src/units/length.rs +1270 -0
- package/qtty-js/qtty/qtty-core/src/units/mass.rs +488 -0
- package/qtty-js/qtty/qtty-core/src/units/mod.rs +26 -0
- package/qtty-js/qtty/qtty-core/src/units/power.rs +324 -0
- package/qtty-js/qtty/qtty-core/src/units/time.rs +667 -0
- package/qtty-js/qtty/qtty-core/src/units/unitless.rs +212 -0
- package/qtty-js/qtty/qtty-core/src/units/velocity.rs +210 -0
- package/qtty-js/qtty/qtty-core/src/units/volume.rs +269 -0
- package/qtty-js/qtty/qtty-core/tests/core.rs +628 -0
- package/qtty-js/qtty/qtty-core/tests/diesel.rs +461 -0
- package/qtty-js/qtty/qtty-core/tests/integers.rs +632 -0
- package/qtty-js/qtty/qtty-core/tests/no_cross_unit_ops.rs +35 -0
- package/qtty-js/qtty/qtty-core/tests/pyo3.rs +334 -0
- package/qtty-js/qtty/qtty-core/tests/quantity_f32.rs +276 -0
- package/qtty-js/qtty/qtty-core/tests/scalar_decimal.rs +258 -0
- package/qtty-js/qtty/qtty-core/tests/scalar_f32.rs +286 -0
- package/qtty-js/qtty/qtty-core/tests/scalar_f64_real.rs +287 -0
- package/qtty-js/qtty/qtty-core/tests/scalar_rational.rs +260 -0
- package/qtty-js/qtty/qtty-core/tests/serde.rs +256 -0
- package/qtty-js/qtty/qtty-core/tests/tiberius.rs +208 -0
- package/qtty-js/qtty/qtty-derive/Cargo.toml +23 -0
- package/qtty-js/qtty/qtty-derive/README.md +8 -0
- package/qtty-js/qtty/qtty-derive/src/lib.rs +340 -0
- package/qtty-js/qtty/qtty-ffi/ARCHITECTURE.md +3 -0
- package/qtty-js/qtty/qtty-ffi/Cargo.toml +31 -0
- package/qtty-js/qtty/qtty-ffi/README.md +9 -0
- package/qtty-js/qtty/qtty-ffi/build.rs +326 -0
- package/qtty-js/qtty/qtty-ffi/cbindgen.toml +105 -0
- package/qtty-js/qtty/qtty-ffi/include/qtty_ffi.h +1126 -0
- package/qtty-js/qtty/qtty-ffi/src/ffi.rs +1251 -0
- package/qtty-js/qtty/qtty-ffi/src/ffi_serde.rs +294 -0
- package/qtty-js/qtty/qtty-ffi/src/helpers.rs +310 -0
- package/qtty-js/qtty/qtty-ffi/src/lib.rs +229 -0
- package/qtty-js/qtty/qtty-ffi/src/macros.rs +121 -0
- package/qtty-js/qtty/qtty-ffi/src/registry.rs +274 -0
- package/qtty-js/qtty/qtty-ffi/src/types.rs +620 -0
- package/qtty-js/qtty/qtty-ffi/tests/integration_tests.rs +842 -0
- package/qtty-js/qtty/qtty-ffi/units.csv +156 -0
- package/qtty-js/qtty/qtty-ffi/units.csv.md +3 -0
- package/qtty-js/qtty-node/.prettierignore +6 -0
- package/qtty-js/qtty-node/.prettierrc.json +6 -0
- package/qtty-js/qtty-node/README.md +250 -0
- package/qtty-js/qtty-node/c8.config.json +11 -0
- package/qtty-js/qtty-node/eslint.config.js +31 -0
- package/qtty-js/qtty-node/examples/arithmetic.mjs +64 -0
- package/qtty-js/qtty-node/examples/astronomy.mjs +90 -0
- package/qtty-js/qtty-node/examples/quickstart.mjs +36 -0
- package/qtty-js/qtty-node/examples/serialization.mjs +125 -0
- package/qtty-js/qtty-node/examples/unit_factories.mjs +74 -0
- package/qtty-js/qtty-node/index.d.ts +219 -0
- package/qtty-js/qtty-node/index.js +323 -0
- package/qtty-js/qtty-node/lib/DerivedQuantity.js +122 -0
- package/qtty-js/qtty-node/lib/Quantity.js +151 -0
- package/qtty-js/qtty-node/lib/backend.js +25 -0
- package/qtty-js/qtty-node/native.cjs +306 -0
- package/qtty-js/qtty-node/package-lock.json +3223 -0
- package/qtty-js/qtty-node/package.json +70 -0
- package/qtty-js/qtty-node/units.d.ts +299 -0
- package/qtty-js/qtty-node/units.js +210 -0
- package/qtty-js/qtty-web/Cargo.lock +767 -0
- package/qtty-js/qtty-web/Cargo.toml +21 -0
- package/qtty-js/qtty-web/index.d.ts +140 -0
- package/qtty-js/qtty-web/index.js +20 -0
- package/qtty-js/qtty-web/lib/DerivedQuantity.js +58 -0
- package/qtty-js/qtty-web/lib/Quantity.js +75 -0
- package/qtty-js/qtty-web/lib/backend.js +80 -0
- package/qtty-js/qtty-web/package.json +45 -0
- package/qtty-js/qtty-web/src/lib.rs +111 -0
- package/qtty-js/scripts/ci.sh +73 -0
- package/scripts/ci.sh +123 -0
- package/siderust-core/Cargo.lock +787 -0
- package/siderust-core/Cargo.toml +18 -0
- package/siderust-core/DEDUPLICATION.md +124 -0
- package/siderust-core/src/body.rs +120 -0
- package/siderust-core/src/events.rs +184 -0
- package/siderust-core/src/lib.rs +20 -0
- package/siderust-core/src/observer.rs +55 -0
- package/siderust-core/src/position.rs +213 -0
- package/siderust-node/.prettierignore +7 -0
- package/siderust-node/.prettierrc.json +6 -0
- package/siderust-node/Cargo.lock +906 -0
- package/siderust-node/Cargo.toml +29 -0
- package/siderust-node/README.md +109 -0
- package/siderust-node/__test__/index.test.mjs +248 -0
- package/siderust-node/build.rs +5 -0
- package/siderust-node/c8.config.json +3 -0
- package/siderust-node/eslint.config.js +31 -0
- package/siderust-node/examples/01_basic_coordinates.mjs +24 -0
- package/siderust-node/examples/02_coordinate_transformations.mjs +25 -0
- package/siderust-node/examples/03_all_frames_conversions.mjs +26 -0
- package/siderust-node/examples/04_all_center_conversions.mjs +24 -0
- package/siderust-node/examples/05_target_tracking.mjs +22 -0
- package/siderust-node/examples/06_night_events.mjs +18 -0
- package/siderust-node/examples/07_moon_properties.mjs +21 -0
- package/siderust-node/examples/08_solar_system.mjs +19 -0
- package/siderust-node/examples/09_star_observability.mjs +22 -0
- package/siderust-node/examples/10_time_periods.mjs +9 -0
- package/siderust-node/examples/11_serialization.mjs +31 -0
- package/siderust-node/examples/12_runtime_ephemeris.mjs +27 -0
- package/siderust-node/examples/13_coordinate_operations.mjs +20 -0
- package/siderust-node/index.d.ts +623 -0
- package/siderust-node/index.js +79 -0
- package/siderust-node/lib/Observer.js +112 -0
- package/siderust-node/lib/Star.js +118 -0
- package/siderust-node/lib/backend.js +63 -0
- package/siderust-node/lib/wrappers.js +566 -0
- package/siderust-node/main.js +20 -0
- package/siderust-node/native.cjs +360 -0
- package/siderust-node/package-lock.json +3261 -0
- package/siderust-node/package.json +71 -0
- package/siderust-node/src/body.rs +74 -0
- package/siderust-node/src/coordinates.rs +372 -0
- package/siderust-node/src/ephemeris.rs +462 -0
- package/siderust-node/src/events.rs +577 -0
- package/siderust-node/src/lib.rs +43 -0
- package/siderust-node/src/observer.rs +132 -0
- package/siderust-node/src/phase.rs +218 -0
- package/siderust-node/src/position.rs +292 -0
- package/siderust-node/src/star.rs +200 -0
- package/siderust-web/Cargo.lock +855 -0
- package/siderust-web/Cargo.toml +34 -0
- package/siderust-web/README.md +100 -0
- package/siderust-web/__test__/index.test.mjs +118 -0
- package/siderust-web/examples/github-pages/README.md +31 -0
- package/siderust-web/examples/github-pages/index.html +135 -0
- package/siderust-web/index.d.ts +311 -0
- package/siderust-web/index.js +66 -0
- package/siderust-web/lib/Observer.js +103 -0
- package/siderust-web/lib/Star.js +116 -0
- package/siderust-web/lib/backend.js +400 -0
- package/siderust-web/lib/wrappers.js +512 -0
- package/siderust-web/package.json +55 -0
- package/siderust-web/src/body.rs +69 -0
- package/siderust-web/src/coordinates.rs +302 -0
- package/siderust-web/src/ephemeris.rs +456 -0
- package/siderust-web/src/events.rs +520 -0
- package/siderust-web/src/lib.rs +51 -0
- package/siderust-web/src/observer.rs +117 -0
- package/siderust-web/src/phase.rs +190 -0
- package/siderust-web/src/position.rs +291 -0
- package/siderust-web/src/star.rs +178 -0
- package/tempoch-js/.github/workflows/ci.yml +142 -0
- package/tempoch-js/.gitmodules +3 -0
- package/tempoch-js/CHANGELOG.md +25 -0
- package/tempoch-js/LICENSE +661 -0
- package/tempoch-js/README.md +126 -0
- package/tempoch-js/package.json +20 -0
- package/tempoch-js/scripts/ci.sh +73 -0
- package/tempoch-js/tempoch/.github/workflows/ci.yml +113 -0
- package/tempoch-js/tempoch/CHANGELOG.md +82 -0
- package/tempoch-js/tempoch/Cargo.lock +947 -0
- package/tempoch-js/tempoch/Cargo.toml +3 -0
- package/tempoch-js/tempoch/LICENSE +661 -0
- package/tempoch-js/tempoch/README.md +76 -0
- package/tempoch-js/tempoch/tempoch/Cargo.toml +27 -0
- package/tempoch-js/tempoch/tempoch/examples/periods.rs +45 -0
- package/tempoch-js/tempoch/tempoch/examples/quickstart.rs +13 -0
- package/tempoch-js/tempoch/tempoch/src/lib.rs +49 -0
- package/tempoch-js/tempoch/tempoch/tests/integration.rs +57 -0
- package/tempoch-js/tempoch/tempoch-core/Cargo.toml +24 -0
- package/tempoch-js/tempoch/tempoch-core/src/delta_t.rs +345 -0
- package/tempoch-js/tempoch/tempoch-core/src/instant.rs +811 -0
- package/tempoch-js/tempoch/tempoch-core/src/julian_date_ext.rs +142 -0
- package/tempoch-js/tempoch/tempoch-core/src/lib.rs +81 -0
- package/tempoch-js/tempoch/tempoch-core/src/period.rs +1168 -0
- package/tempoch-js/tempoch/tempoch-core/src/scales.rs +779 -0
- package/tempoch-js/tempoch/tempoch-ffi/Cargo.lock +889 -0
- package/tempoch-js/tempoch/tempoch-ffi/Cargo.toml +26 -0
- package/tempoch-js/tempoch/tempoch-ffi/build.rs +24 -0
- package/tempoch-js/tempoch/tempoch-ffi/cbindgen.toml +30 -0
- package/tempoch-js/tempoch/tempoch-ffi/src/error.rs +19 -0
- package/tempoch-js/tempoch/tempoch-ffi/src/lib.rs +82 -0
- package/tempoch-js/tempoch/tempoch-ffi/src/period.rs +101 -0
- package/tempoch-js/tempoch/tempoch-ffi/src/time.rs +711 -0
- package/tempoch-js/tempoch/tempoch-ffi/tests/ffi.rs +265 -0
- package/tempoch-js/tempoch-node/.prettierignore +6 -0
- package/tempoch-js/tempoch-node/.prettierrc.json +6 -0
- package/tempoch-js/tempoch-node/Cargo.lock +496 -0
- package/tempoch-js/tempoch-node/Cargo.toml +29 -0
- package/tempoch-js/tempoch-node/README.md +265 -0
- package/tempoch-js/tempoch-node/__test__/index.test.mjs +598 -0
- package/tempoch-js/tempoch-node/build.rs +5 -0
- package/tempoch-js/tempoch-node/c8.config.json +3 -0
- package/tempoch-js/tempoch-node/eslint.config.js +31 -0
- package/tempoch-js/tempoch-node/examples/periods.mjs +79 -0
- package/tempoch-js/tempoch-node/examples/quickstart.mjs +71 -0
- package/tempoch-js/tempoch-node/examples/timescales.mjs +92 -0
- package/tempoch-js/tempoch-node/index.d.ts +280 -0
- package/tempoch-js/tempoch-node/index.js +32 -0
- package/tempoch-js/tempoch-node/lib/JulianDate.js +176 -0
- package/tempoch-js/tempoch-node/lib/ModifiedJulianDate.js +156 -0
- package/tempoch-js/tempoch-node/lib/Period.js +133 -0
- package/tempoch-js/tempoch-node/lib/backend.js +38 -0
- package/tempoch-js/tempoch-node/lib/qttyCompat.js +92 -0
- package/tempoch-js/tempoch-node/native.cjs +317 -0
- package/tempoch-js/tempoch-node/package-lock.json +3223 -0
- package/tempoch-js/tempoch-node/package.json +56 -0
- package/tempoch-js/tempoch-node/src/lib.rs +573 -0
- package/tempoch-js/tempoch-web/Cargo.toml +23 -0
- package/tempoch-js/tempoch-web/index.d.ts +95 -0
- package/tempoch-js/tempoch-web/index.js +27 -0
- package/tempoch-js/tempoch-web/lib/JulianDate.js +170 -0
- package/tempoch-js/tempoch-web/lib/ModifiedJulianDate.js +145 -0
- package/tempoch-js/tempoch-web/lib/Period.js +121 -0
- package/tempoch-js/tempoch-web/lib/backend.js +118 -0
- package/tempoch-js/tempoch-web/package.json +46 -0
- package/tempoch-js/tempoch-web/src/lib.rs +184 -0
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
//! JSON serialization/deserialization FFI functions.
|
|
2
|
+
//!
|
|
3
|
+
//! Provides `extern "C"` functions for serializing and deserializing
|
|
4
|
+
//! [`QttyQuantity`] values to/from JSON, as well as a companion
|
|
5
|
+
//! `qtty_string_free` for releasing heap-allocated C strings returned by
|
|
6
|
+
//! these functions.
|
|
7
|
+
//!
|
|
8
|
+
//! # JSON formats
|
|
9
|
+
//!
|
|
10
|
+
//! * **value-only** (`qtty_quantity_to/from_json_value`): a bare JSON number,
|
|
11
|
+
//! e.g. `123.456`.
|
|
12
|
+
//! * **full object** (`qtty_quantity_to/from_json`): a JSON object with
|
|
13
|
+
//! `"value"` (f64) and `"unit_id"` (u32) fields,
|
|
14
|
+
//! e.g. `{"value":123.456,"unit_id":10001}`.
|
|
15
|
+
|
|
16
|
+
use crate::types::{
|
|
17
|
+
QttyQuantity, UnitId, QTTY_ERR_INVALID_VALUE, QTTY_ERR_NULL_OUT, QTTY_ERR_UNKNOWN_UNIT, QTTY_OK,
|
|
18
|
+
};
|
|
19
|
+
use core::ffi::c_char;
|
|
20
|
+
use std::ffi::{CStr, CString};
|
|
21
|
+
|
|
22
|
+
// =============================================================================
|
|
23
|
+
// Helper macro (mirrors the one in ffi.rs)
|
|
24
|
+
// =============================================================================
|
|
25
|
+
|
|
26
|
+
macro_rules! catch_panic {
|
|
27
|
+
($default:expr, $body:expr) => {{
|
|
28
|
+
match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| $body)) {
|
|
29
|
+
Ok(result) => result,
|
|
30
|
+
Err(_) => $default,
|
|
31
|
+
}
|
|
32
|
+
}};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// =============================================================================
|
|
36
|
+
// String management
|
|
37
|
+
// =============================================================================
|
|
38
|
+
|
|
39
|
+
/// Frees a C string previously returned by a `qtty_quantity_to_json*` function.
|
|
40
|
+
///
|
|
41
|
+
/// Passing a null pointer is safe and is a no-op.
|
|
42
|
+
///
|
|
43
|
+
/// # Safety
|
|
44
|
+
///
|
|
45
|
+
/// The pointer **must** have been returned by `qtty_quantity_to_json` or
|
|
46
|
+
/// `qtty_quantity_to_json_value` and must not be freed more than once.
|
|
47
|
+
#[no_mangle]
|
|
48
|
+
pub unsafe extern "C" fn qtty_string_free(s: *mut c_char) {
|
|
49
|
+
if !s.is_null() {
|
|
50
|
+
// SAFETY: Pointer was produced by `CString::into_raw` in this crate.
|
|
51
|
+
unsafe { drop(CString::from_raw(s)) }
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// =============================================================================
|
|
56
|
+
// Value-only JSON (bare number)
|
|
57
|
+
// =============================================================================
|
|
58
|
+
|
|
59
|
+
/// Serializes only the numeric value of a quantity as a JSON number string.
|
|
60
|
+
///
|
|
61
|
+
/// On success, `*out_json` is set to a heap-allocated NUL-terminated C string
|
|
62
|
+
/// (e.g. `"123.456789"`) that the caller **must** release with
|
|
63
|
+
/// [`qtty_string_free`].
|
|
64
|
+
///
|
|
65
|
+
/// # Returns
|
|
66
|
+
///
|
|
67
|
+
/// * `QTTY_OK` on success.
|
|
68
|
+
/// * `QTTY_ERR_NULL_OUT` if `out_json` is null.
|
|
69
|
+
/// * `QTTY_ERR_INVALID_VALUE` if serialization fails.
|
|
70
|
+
///
|
|
71
|
+
/// # Safety
|
|
72
|
+
///
|
|
73
|
+
/// `out_json` must point to a writable `*mut c_char` location.
|
|
74
|
+
#[no_mangle]
|
|
75
|
+
pub unsafe extern "C" fn qtty_quantity_to_json_value(
|
|
76
|
+
src: QttyQuantity,
|
|
77
|
+
out_json: *mut *mut c_char,
|
|
78
|
+
) -> i32 {
|
|
79
|
+
catch_panic!(QTTY_ERR_UNKNOWN_UNIT, {
|
|
80
|
+
if out_json.is_null() {
|
|
81
|
+
return QTTY_ERR_NULL_OUT;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
let s = match serde_json::to_string(&src.value) {
|
|
85
|
+
Ok(s) => s,
|
|
86
|
+
Err(_) => return QTTY_ERR_INVALID_VALUE,
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
match CString::new(s) {
|
|
90
|
+
Ok(c) => {
|
|
91
|
+
// SAFETY: `out_json` is not null (checked above).
|
|
92
|
+
unsafe { *out_json = c.into_raw() };
|
|
93
|
+
QTTY_OK
|
|
94
|
+
}
|
|
95
|
+
Err(_) => QTTY_ERR_INVALID_VALUE,
|
|
96
|
+
}
|
|
97
|
+
})
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/// Deserializes a JSON number string into a quantity with the given unit.
|
|
101
|
+
///
|
|
102
|
+
/// # Returns
|
|
103
|
+
///
|
|
104
|
+
/// * `QTTY_OK` on success.
|
|
105
|
+
/// * `QTTY_ERR_NULL_OUT` if either `json` or `out` is null.
|
|
106
|
+
/// * `QTTY_ERR_INVALID_VALUE` if `json` is not a valid JSON number.
|
|
107
|
+
///
|
|
108
|
+
/// # Safety
|
|
109
|
+
///
|
|
110
|
+
/// `json` must be a valid NUL-terminated C string.
|
|
111
|
+
/// `out` must point to a writable [`QttyQuantity`] location.
|
|
112
|
+
#[no_mangle]
|
|
113
|
+
pub unsafe extern "C" fn qtty_quantity_from_json_value(
|
|
114
|
+
unit: UnitId,
|
|
115
|
+
json: *const c_char,
|
|
116
|
+
out: *mut QttyQuantity,
|
|
117
|
+
) -> i32 {
|
|
118
|
+
catch_panic!(QTTY_ERR_UNKNOWN_UNIT, {
|
|
119
|
+
if json.is_null() || out.is_null() {
|
|
120
|
+
return QTTY_ERR_NULL_OUT;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// SAFETY: Caller guarantees `json` is a valid NUL-terminated string.
|
|
124
|
+
let json_str = match unsafe { CStr::from_ptr(json) }.to_str() {
|
|
125
|
+
Ok(s) => s,
|
|
126
|
+
Err(_) => return QTTY_ERR_INVALID_VALUE,
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
match serde_json::from_str::<f64>(json_str) {
|
|
130
|
+
Ok(value) => {
|
|
131
|
+
// SAFETY: `out` is not null (checked above).
|
|
132
|
+
unsafe { *out = QttyQuantity::new(value, unit) };
|
|
133
|
+
QTTY_OK
|
|
134
|
+
}
|
|
135
|
+
Err(_) => QTTY_ERR_INVALID_VALUE,
|
|
136
|
+
}
|
|
137
|
+
})
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// =============================================================================
|
|
141
|
+
// Full-object JSON {"value":<f64>, "unit_id":<u32>}
|
|
142
|
+
// =============================================================================
|
|
143
|
+
|
|
144
|
+
/// Serializes a quantity as a JSON object `{"value":<f64>,"unit_id":<u32>}`.
|
|
145
|
+
///
|
|
146
|
+
/// On success, `*out_json` is set to a heap-allocated NUL-terminated C string
|
|
147
|
+
/// that the caller **must** release with [`qtty_string_free`].
|
|
148
|
+
///
|
|
149
|
+
/// # Returns
|
|
150
|
+
///
|
|
151
|
+
/// * `QTTY_OK` on success.
|
|
152
|
+
/// * `QTTY_ERR_NULL_OUT` if `out_json` is null.
|
|
153
|
+
/// * `QTTY_ERR_INVALID_VALUE` if serialization fails.
|
|
154
|
+
///
|
|
155
|
+
/// # Safety
|
|
156
|
+
///
|
|
157
|
+
/// `out_json` must point to a writable `*mut c_char` location.
|
|
158
|
+
#[no_mangle]
|
|
159
|
+
pub unsafe extern "C" fn qtty_quantity_to_json(
|
|
160
|
+
src: QttyQuantity,
|
|
161
|
+
out_json: *mut *mut c_char,
|
|
162
|
+
) -> i32 {
|
|
163
|
+
catch_panic!(QTTY_ERR_UNKNOWN_UNIT, {
|
|
164
|
+
if out_json.is_null() {
|
|
165
|
+
return QTTY_ERR_NULL_OUT;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
let obj = serde_json::json!({
|
|
169
|
+
"value": src.value,
|
|
170
|
+
"unit_id": src.unit as u32,
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
let s = match serde_json::to_string(&obj) {
|
|
174
|
+
Ok(s) => s,
|
|
175
|
+
Err(_) => return QTTY_ERR_INVALID_VALUE,
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
match CString::new(s) {
|
|
179
|
+
Ok(c) => {
|
|
180
|
+
// SAFETY: `out_json` is not null (checked above).
|
|
181
|
+
unsafe { *out_json = c.into_raw() };
|
|
182
|
+
QTTY_OK
|
|
183
|
+
}
|
|
184
|
+
Err(_) => QTTY_ERR_INVALID_VALUE,
|
|
185
|
+
}
|
|
186
|
+
})
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/// Deserializes a JSON object `{"value":<f64>,"unit_id":<u32>}` into a quantity.
|
|
190
|
+
///
|
|
191
|
+
/// # Returns
|
|
192
|
+
///
|
|
193
|
+
/// * `QTTY_OK` on success.
|
|
194
|
+
/// * `QTTY_ERR_NULL_OUT` if either `json` or `out` is null.
|
|
195
|
+
/// * `QTTY_ERR_INVALID_VALUE` if `json` is not a valid JSON object with the
|
|
196
|
+
/// expected fields.
|
|
197
|
+
/// * `QTTY_ERR_UNKNOWN_UNIT` if the `unit_id` field does not map to a known unit.
|
|
198
|
+
///
|
|
199
|
+
/// # Safety
|
|
200
|
+
///
|
|
201
|
+
/// `json` must be a valid NUL-terminated C string.
|
|
202
|
+
/// `out` must point to a writable [`QttyQuantity`] location.
|
|
203
|
+
#[no_mangle]
|
|
204
|
+
pub unsafe extern "C" fn qtty_quantity_from_json(
|
|
205
|
+
json: *const c_char,
|
|
206
|
+
out: *mut QttyQuantity,
|
|
207
|
+
) -> i32 {
|
|
208
|
+
catch_panic!(QTTY_ERR_UNKNOWN_UNIT, {
|
|
209
|
+
if json.is_null() || out.is_null() {
|
|
210
|
+
return QTTY_ERR_NULL_OUT;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// SAFETY: Caller guarantees `json` is a valid NUL-terminated string.
|
|
214
|
+
let json_str = match unsafe { CStr::from_ptr(json) }.to_str() {
|
|
215
|
+
Ok(s) => s,
|
|
216
|
+
Err(_) => return QTTY_ERR_INVALID_VALUE,
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
#[derive(serde::Deserialize)]
|
|
220
|
+
struct QtyJson {
|
|
221
|
+
value: f64,
|
|
222
|
+
unit_id: u32,
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
match serde_json::from_str::<QtyJson>(json_str) {
|
|
226
|
+
Ok(q) => match UnitId::from_u32(q.unit_id) {
|
|
227
|
+
Some(unit) => {
|
|
228
|
+
// SAFETY: `out` is not null (checked above).
|
|
229
|
+
unsafe { *out = QttyQuantity::new(q.value, unit) };
|
|
230
|
+
QTTY_OK
|
|
231
|
+
}
|
|
232
|
+
None => QTTY_ERR_UNKNOWN_UNIT,
|
|
233
|
+
},
|
|
234
|
+
Err(_) => QTTY_ERR_INVALID_VALUE,
|
|
235
|
+
}
|
|
236
|
+
})
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
#[cfg(test)]
|
|
240
|
+
mod tests {
|
|
241
|
+
use super::*;
|
|
242
|
+
|
|
243
|
+
#[test]
|
|
244
|
+
fn test_to_json_value_roundtrip() {
|
|
245
|
+
let src = QttyQuantity::new(123.456, UnitId::Meter);
|
|
246
|
+
let mut ptr: *mut c_char = std::ptr::null_mut();
|
|
247
|
+
|
|
248
|
+
let status = unsafe { qtty_quantity_to_json_value(src, &mut ptr) };
|
|
249
|
+
assert_eq!(status, QTTY_OK);
|
|
250
|
+
assert!(!ptr.is_null());
|
|
251
|
+
|
|
252
|
+
let mut out = QttyQuantity::default();
|
|
253
|
+
let status2 = unsafe { qtty_quantity_from_json_value(UnitId::Meter, ptr, &mut out) };
|
|
254
|
+
unsafe { qtty_string_free(ptr) };
|
|
255
|
+
|
|
256
|
+
assert_eq!(status2, QTTY_OK);
|
|
257
|
+
assert!((out.value - 123.456).abs() < 1e-12);
|
|
258
|
+
assert_eq!(out.unit, UnitId::Meter);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
#[test]
|
|
262
|
+
fn test_to_json_roundtrip() {
|
|
263
|
+
let src = QttyQuantity::new(1.5, UnitId::Kilometer);
|
|
264
|
+
let mut ptr: *mut c_char = std::ptr::null_mut();
|
|
265
|
+
|
|
266
|
+
let status = unsafe { qtty_quantity_to_json(src, &mut ptr) };
|
|
267
|
+
assert_eq!(status, QTTY_OK);
|
|
268
|
+
assert!(!ptr.is_null());
|
|
269
|
+
|
|
270
|
+
let mut out = QttyQuantity::default();
|
|
271
|
+
let status2 = unsafe { qtty_quantity_from_json(ptr, &mut out) };
|
|
272
|
+
unsafe { qtty_string_free(ptr) };
|
|
273
|
+
|
|
274
|
+
assert_eq!(status2, QTTY_OK);
|
|
275
|
+
assert!((out.value - 1.5).abs() < 1e-12);
|
|
276
|
+
assert_eq!(out.unit, UnitId::Kilometer);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
#[test]
|
|
280
|
+
fn test_from_json_value_invalid_input() {
|
|
281
|
+
let bad_json = std::ffi::CString::new("not a number").unwrap();
|
|
282
|
+
let mut out = QttyQuantity::default();
|
|
283
|
+
|
|
284
|
+
let status =
|
|
285
|
+
unsafe { qtty_quantity_from_json_value(UnitId::Meter, bad_json.as_ptr(), &mut out) };
|
|
286
|
+
assert_eq!(status, QTTY_ERR_INVALID_VALUE);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
#[test]
|
|
290
|
+
fn test_string_free_null_is_noop() {
|
|
291
|
+
// Must not crash
|
|
292
|
+
unsafe { qtty_string_free(std::ptr::null_mut()) };
|
|
293
|
+
}
|
|
294
|
+
}
|
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
//! Helper functions and trait implementations for downstream crate integration.
|
|
2
|
+
//!
|
|
3
|
+
//! This module provides pre-implemented conversions between `qtty` types and [`QttyQuantity`],
|
|
4
|
+
//! making it easy for downstream Rust crates to integrate with the FFI layer.
|
|
5
|
+
//!
|
|
6
|
+
//! # Auto-generated conversions
|
|
7
|
+
//!
|
|
8
|
+
//! `From`/`TryFrom` implementations are auto-generated at build time from `units.csv` for
|
|
9
|
+
//! all units that have a corresponding Rust type mapping. This covers the vast majority of
|
|
10
|
+
//! units in the registry.
|
|
11
|
+
//!
|
|
12
|
+
//! Additional unit conversions can be implemented in downstream crates using the
|
|
13
|
+
//! [`impl_unit_ffi!`] macro.
|
|
14
|
+
//!
|
|
15
|
+
//! # Usage
|
|
16
|
+
//!
|
|
17
|
+
//! ## Converting to FFI format
|
|
18
|
+
//!
|
|
19
|
+
//! ```rust
|
|
20
|
+
//! use qtty::length::Meters;
|
|
21
|
+
//! use qtty_ffi::QttyQuantity;
|
|
22
|
+
//!
|
|
23
|
+
//! let meters = Meters::new(1000.0);
|
|
24
|
+
//! let ffi_qty: QttyQuantity = meters.into();
|
|
25
|
+
//! ```
|
|
26
|
+
//!
|
|
27
|
+
//! ## Converting from FFI format
|
|
28
|
+
//!
|
|
29
|
+
//! ```rust
|
|
30
|
+
//! use qtty::length::Kilometers;
|
|
31
|
+
//! use qtty_ffi::{QttyQuantity, UnitId};
|
|
32
|
+
//!
|
|
33
|
+
//! // A quantity received from FFI (could be in any compatible unit)
|
|
34
|
+
//! let ffi_qty = QttyQuantity::new(1000.0, UnitId::Meter);
|
|
35
|
+
//!
|
|
36
|
+
//! // Convert to Kilometers (automatic unit conversion happens)
|
|
37
|
+
//! let km: Kilometers = ffi_qty.try_into().unwrap();
|
|
38
|
+
//! assert!((km.value() - 1.0).abs() < 1e-12);
|
|
39
|
+
//! ```
|
|
40
|
+
//!
|
|
41
|
+
//! ## Error handling
|
|
42
|
+
//!
|
|
43
|
+
//! Conversion from [`QttyQuantity`] can fail if the dimensions are incompatible:
|
|
44
|
+
//!
|
|
45
|
+
//! ```rust
|
|
46
|
+
//! use qtty::length::Meters;
|
|
47
|
+
//! use qtty_ffi::{QttyQuantity, UnitId, QTTY_ERR_INCOMPATIBLE_DIM};
|
|
48
|
+
//!
|
|
49
|
+
//! let time_qty = QttyQuantity::new(60.0, UnitId::Second);
|
|
50
|
+
//! let result: Result<Meters, i32> = time_qty.try_into();
|
|
51
|
+
//! assert_eq!(result, Err(QTTY_ERR_INCOMPATIBLE_DIM));
|
|
52
|
+
//! ```
|
|
53
|
+
|
|
54
|
+
// Auto-generated From/TryFrom impls for all units with Rust type mappings.
|
|
55
|
+
// Generated by build.rs from units.csv.
|
|
56
|
+
include!(concat!(env!("OUT_DIR"), "/unit_conversions.rs"));
|
|
57
|
+
|
|
58
|
+
// =============================================================================
|
|
59
|
+
// Explicit Helper Functions (convenience API for common units)
|
|
60
|
+
// =============================================================================
|
|
61
|
+
|
|
62
|
+
use crate::QttyQuantity;
|
|
63
|
+
|
|
64
|
+
/// Converts `Meters` to an FFI-safe `QttyQuantity`.
|
|
65
|
+
#[inline]
|
|
66
|
+
pub fn meters_into_ffi(m: qtty::length::Meters) -> QttyQuantity {
|
|
67
|
+
m.into()
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/// Attempts to convert a `QttyQuantity` to `Meters`.
|
|
71
|
+
///
|
|
72
|
+
/// Returns an error if the quantity's unit is not length-compatible.
|
|
73
|
+
#[inline]
|
|
74
|
+
pub fn try_into_meters(q: QttyQuantity) -> Result<qtty::length::Meters, i32> {
|
|
75
|
+
q.try_into()
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/// Converts `Kilometers` to an FFI-safe `QttyQuantity`.
|
|
79
|
+
#[inline]
|
|
80
|
+
pub fn kilometers_into_ffi(km: qtty::length::Kilometers) -> QttyQuantity {
|
|
81
|
+
km.into()
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/// Attempts to convert a `QttyQuantity` to `Kilometers`.
|
|
85
|
+
///
|
|
86
|
+
/// Returns an error if the quantity's unit is not length-compatible.
|
|
87
|
+
#[inline]
|
|
88
|
+
pub fn try_into_kilometers(q: QttyQuantity) -> Result<qtty::length::Kilometers, i32> {
|
|
89
|
+
q.try_into()
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/// Converts `Seconds` to an FFI-safe `QttyQuantity`.
|
|
93
|
+
#[inline]
|
|
94
|
+
pub fn seconds_into_ffi(s: qtty::time::Seconds) -> QttyQuantity {
|
|
95
|
+
s.into()
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/// Attempts to convert a `QttyQuantity` to `Seconds`.
|
|
99
|
+
///
|
|
100
|
+
/// Returns an error if the quantity's unit is not time-compatible.
|
|
101
|
+
#[inline]
|
|
102
|
+
pub fn try_into_seconds(q: QttyQuantity) -> Result<qtty::time::Seconds, i32> {
|
|
103
|
+
q.try_into()
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/// Converts `Minutes` to an FFI-safe `QttyQuantity`.
|
|
107
|
+
#[inline]
|
|
108
|
+
pub fn minutes_into_ffi(m: qtty::time::Minutes) -> QttyQuantity {
|
|
109
|
+
m.into()
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/// Attempts to convert a `QttyQuantity` to `Minutes`.
|
|
113
|
+
///
|
|
114
|
+
/// Returns an error if the quantity's unit is not time-compatible.
|
|
115
|
+
#[inline]
|
|
116
|
+
pub fn try_into_minutes(q: QttyQuantity) -> Result<qtty::time::Minutes, i32> {
|
|
117
|
+
q.try_into()
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/// Converts `Hours` to an FFI-safe `QttyQuantity`.
|
|
121
|
+
#[inline]
|
|
122
|
+
pub fn hours_into_ffi(h: qtty::time::Hours) -> QttyQuantity {
|
|
123
|
+
h.into()
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/// Attempts to convert a `QttyQuantity` to `Hours`.
|
|
127
|
+
///
|
|
128
|
+
/// Returns an error if the quantity's unit is not time-compatible.
|
|
129
|
+
#[inline]
|
|
130
|
+
pub fn try_into_hours(q: QttyQuantity) -> Result<qtty::time::Hours, i32> {
|
|
131
|
+
q.try_into()
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/// Converts `Days` to an FFI-safe `QttyQuantity`.
|
|
135
|
+
#[inline]
|
|
136
|
+
pub fn days_into_ffi(d: qtty::time::Days) -> QttyQuantity {
|
|
137
|
+
d.into()
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/// Attempts to convert a `QttyQuantity` to `Days`.
|
|
141
|
+
///
|
|
142
|
+
/// Returns an error if the quantity's unit is not time-compatible.
|
|
143
|
+
#[inline]
|
|
144
|
+
pub fn try_into_days(q: QttyQuantity) -> Result<qtty::time::Days, i32> {
|
|
145
|
+
q.try_into()
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/// Converts `Radians` to an FFI-safe `QttyQuantity`.
|
|
149
|
+
#[inline]
|
|
150
|
+
pub fn radians_into_ffi(r: qtty::angular::Radians) -> QttyQuantity {
|
|
151
|
+
r.into()
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/// Attempts to convert a `QttyQuantity` to `Radians`.
|
|
155
|
+
///
|
|
156
|
+
/// Returns an error if the quantity's unit is not angle-compatible.
|
|
157
|
+
#[inline]
|
|
158
|
+
pub fn try_into_radians(q: QttyQuantity) -> Result<qtty::angular::Radians, i32> {
|
|
159
|
+
q.try_into()
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/// Converts `Degrees` to an FFI-safe `QttyQuantity`.
|
|
163
|
+
#[inline]
|
|
164
|
+
pub fn degrees_into_ffi(d: qtty::angular::Degrees) -> QttyQuantity {
|
|
165
|
+
d.into()
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/// Attempts to convert a `QttyQuantity` to `Degrees`.
|
|
169
|
+
///
|
|
170
|
+
/// Returns an error if the quantity's unit is not angle-compatible.
|
|
171
|
+
#[inline]
|
|
172
|
+
pub fn try_into_degrees(q: QttyQuantity) -> Result<qtty::angular::Degrees, i32> {
|
|
173
|
+
q.try_into()
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
#[cfg(test)]
|
|
177
|
+
mod tests {
|
|
178
|
+
use super::*;
|
|
179
|
+
use crate::UnitId;
|
|
180
|
+
use crate::QTTY_ERR_INCOMPATIBLE_DIM;
|
|
181
|
+
use approx::assert_relative_eq;
|
|
182
|
+
use core::f64::consts::PI;
|
|
183
|
+
|
|
184
|
+
#[test]
|
|
185
|
+
fn test_meters_roundtrip() {
|
|
186
|
+
let original = qtty::length::Meters::new(42.5);
|
|
187
|
+
let ffi: QttyQuantity = original.into();
|
|
188
|
+
let back: qtty::length::Meters = ffi.try_into().unwrap();
|
|
189
|
+
assert_relative_eq!(back.value(), 42.5, epsilon = 1e-12);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
#[test]
|
|
193
|
+
fn test_kilometers_roundtrip() {
|
|
194
|
+
let original = qtty::length::Kilometers::new(1.5);
|
|
195
|
+
let ffi: QttyQuantity = original.into();
|
|
196
|
+
let back: qtty::length::Kilometers = ffi.try_into().unwrap();
|
|
197
|
+
assert_relative_eq!(back.value(), 1.5, epsilon = 1e-12);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
#[test]
|
|
201
|
+
fn test_meters_to_kilometers_via_ffi() {
|
|
202
|
+
let meters = qtty::length::Meters::new(1000.0);
|
|
203
|
+
let ffi: QttyQuantity = meters.into();
|
|
204
|
+
let km: qtty::length::Kilometers = ffi.try_into().unwrap();
|
|
205
|
+
assert_relative_eq!(km.value(), 1.0, epsilon = 1e-12);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
#[test]
|
|
209
|
+
fn test_seconds_roundtrip() {
|
|
210
|
+
let original = qtty::time::Seconds::new(3600.0);
|
|
211
|
+
let ffi: QttyQuantity = original.into();
|
|
212
|
+
let back: qtty::time::Seconds = ffi.try_into().unwrap();
|
|
213
|
+
assert_relative_eq!(back.value(), 3600.0, epsilon = 1e-12);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
#[test]
|
|
217
|
+
fn test_hours_to_seconds_via_ffi() {
|
|
218
|
+
let hours = qtty::time::Hours::new(1.0);
|
|
219
|
+
let ffi: QttyQuantity = hours.into();
|
|
220
|
+
let secs: qtty::time::Seconds = ffi.try_into().unwrap();
|
|
221
|
+
assert_relative_eq!(secs.value(), 3600.0, epsilon = 1e-12);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
#[test]
|
|
225
|
+
fn test_degrees_to_radians_via_ffi() {
|
|
226
|
+
let degrees = qtty::angular::Degrees::new(180.0);
|
|
227
|
+
let ffi: QttyQuantity = degrees.into();
|
|
228
|
+
let radians: qtty::angular::Radians = ffi.try_into().unwrap();
|
|
229
|
+
assert_relative_eq!(radians.value(), PI, epsilon = 1e-12);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
#[test]
|
|
233
|
+
fn test_incompatible_conversion_fails() {
|
|
234
|
+
let meters = qtty::length::Meters::new(100.0);
|
|
235
|
+
let ffi: QttyQuantity = meters.into();
|
|
236
|
+
let result: Result<qtty::time::Seconds, i32> = ffi.try_into();
|
|
237
|
+
assert_eq!(result, Err(QTTY_ERR_INCOMPATIBLE_DIM));
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
#[test]
|
|
241
|
+
fn test_explicit_helper_functions() {
|
|
242
|
+
let m = qtty::length::Meters::new(1000.0);
|
|
243
|
+
let ffi = meters_into_ffi(m);
|
|
244
|
+
let km = try_into_kilometers(ffi).unwrap();
|
|
245
|
+
assert_relative_eq!(km.value(), 1.0, epsilon = 1e-12);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
#[test]
|
|
249
|
+
fn test_all_helper_functions_cover_unit_variants() {
|
|
250
|
+
use qtty::angular::{Degrees, Radians};
|
|
251
|
+
use qtty::length::{Kilometers, Meters};
|
|
252
|
+
use qtty::time::{Days, Hours, Minutes, Seconds};
|
|
253
|
+
|
|
254
|
+
let meters_qty = meters_into_ffi(Meters::new(12.0));
|
|
255
|
+
assert_eq!(meters_qty.unit, UnitId::Meter);
|
|
256
|
+
assert_relative_eq!(meters_qty.value, 12.0, epsilon = 1e-12);
|
|
257
|
+
|
|
258
|
+
let kilometers_qty = kilometers_into_ffi(Kilometers::new(3.0));
|
|
259
|
+
assert_eq!(kilometers_qty.unit, UnitId::Kilometer);
|
|
260
|
+
assert_relative_eq!(kilometers_qty.value, 3.0, epsilon = 1e-12);
|
|
261
|
+
|
|
262
|
+
let meters_from_kilometers = try_into_meters(kilometers_qty).unwrap();
|
|
263
|
+
assert_relative_eq!(meters_from_kilometers.value(), 3000.0, epsilon = 1e-12);
|
|
264
|
+
|
|
265
|
+
let kilometers_from_meters = try_into_kilometers(meters_qty).unwrap();
|
|
266
|
+
assert_relative_eq!(kilometers_from_meters.value(), 0.012, epsilon = 1e-12);
|
|
267
|
+
|
|
268
|
+
let seconds_qty = seconds_into_ffi(Seconds::new(90.0));
|
|
269
|
+
assert_eq!(seconds_qty.unit, UnitId::Second);
|
|
270
|
+
assert_relative_eq!(seconds_qty.value, 90.0, epsilon = 1e-12);
|
|
271
|
+
|
|
272
|
+
let minutes_qty = minutes_into_ffi(Minutes::new(2.5));
|
|
273
|
+
assert_eq!(minutes_qty.unit, UnitId::Minute);
|
|
274
|
+
assert_relative_eq!(minutes_qty.value, 2.5, epsilon = 1e-12);
|
|
275
|
+
|
|
276
|
+
let seconds_from_minutes = try_into_seconds(minutes_qty).unwrap();
|
|
277
|
+
assert_relative_eq!(seconds_from_minutes.value(), 150.0, epsilon = 1e-12);
|
|
278
|
+
|
|
279
|
+
let minutes_from_seconds = try_into_minutes(seconds_qty).unwrap();
|
|
280
|
+
assert_relative_eq!(minutes_from_seconds.value(), 1.5, epsilon = 1e-12);
|
|
281
|
+
|
|
282
|
+
let hours_qty = hours_into_ffi(Hours::new(4.0));
|
|
283
|
+
assert_eq!(hours_qty.unit, UnitId::Hour);
|
|
284
|
+
assert_relative_eq!(hours_qty.value, 4.0, epsilon = 1e-12);
|
|
285
|
+
|
|
286
|
+
let hours_from_minutes = try_into_hours(minutes_into_ffi(Minutes::new(180.0))).unwrap();
|
|
287
|
+
assert_relative_eq!(hours_from_minutes.value(), 3.0, epsilon = 1e-12);
|
|
288
|
+
|
|
289
|
+
let days_qty = days_into_ffi(Days::new(1.25));
|
|
290
|
+
assert_eq!(days_qty.unit, UnitId::Day);
|
|
291
|
+
assert_relative_eq!(days_qty.value, 1.25, epsilon = 1e-12);
|
|
292
|
+
|
|
293
|
+
let days_from_hours = try_into_days(hours_into_ffi(Hours::new(48.0))).unwrap();
|
|
294
|
+
assert_relative_eq!(days_from_hours.value(), 2.0, epsilon = 1e-12);
|
|
295
|
+
|
|
296
|
+
let radians_qty = radians_into_ffi(Radians::new(PI));
|
|
297
|
+
assert_eq!(radians_qty.unit, UnitId::Radian);
|
|
298
|
+
assert_relative_eq!(radians_qty.value, PI, epsilon = 1e-12);
|
|
299
|
+
|
|
300
|
+
let degrees_qty = degrees_into_ffi(Degrees::new(270.0));
|
|
301
|
+
assert_eq!(degrees_qty.unit, UnitId::Degree);
|
|
302
|
+
assert_relative_eq!(degrees_qty.value, 270.0, epsilon = 1e-12);
|
|
303
|
+
|
|
304
|
+
let radians_from_degrees = try_into_radians(degrees_qty).unwrap();
|
|
305
|
+
assert_relative_eq!(radians_from_degrees.value(), 1.5 * PI, epsilon = 1e-12);
|
|
306
|
+
|
|
307
|
+
let degrees_from_radians = try_into_degrees(radians_qty).unwrap();
|
|
308
|
+
assert_relative_eq!(degrees_from_radians.value(), 180.0, epsilon = 1e-12);
|
|
309
|
+
}
|
|
310
|
+
}
|