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,229 @@
|
|
|
1
|
+
//! C-compatible FFI bindings for `qtty` physical quantities and unit conversions.
|
|
2
|
+
//!
|
|
3
|
+
//! `qtty-ffi` provides a stable C ABI for `qtty`, enabling interoperability with C/C++ code
|
|
4
|
+
//! and other languages with C FFI support. It also provides helper types and macros for
|
|
5
|
+
//! downstream Rust crates that need to expose their own FFI APIs using `qtty` quantities.
|
|
6
|
+
//!
|
|
7
|
+
//! # Features
|
|
8
|
+
//!
|
|
9
|
+
//! - **ABI-stable types**: `#[repr(C)]` and `#[repr(u32)]` types safe for FFI
|
|
10
|
+
//! - **Unit registry**: Mapping between FFI unit IDs and conversion factors
|
|
11
|
+
//! - **C API**: `extern "C"` functions for quantity construction and conversion
|
|
12
|
+
//! - **Rust helpers**: Macros and trait implementations for downstream integration
|
|
13
|
+
//!
|
|
14
|
+
//! # Quick Start (C/C++)
|
|
15
|
+
//!
|
|
16
|
+
//! Include the generated header and link against the library:
|
|
17
|
+
//!
|
|
18
|
+
//! ```c
|
|
19
|
+
//! #include "qtty_ffi.h"
|
|
20
|
+
//!
|
|
21
|
+
//! // Create a quantity
|
|
22
|
+
//! QttyQuantity meters;
|
|
23
|
+
//! qtty_quantity_make(1000.0, UnitId_Meter, &meters);
|
|
24
|
+
//!
|
|
25
|
+
//! // Convert to kilometers
|
|
26
|
+
//! QttyQuantity kilometers;
|
|
27
|
+
//! int32_t status = qtty_quantity_convert(meters, UnitId_Kilometer, &kilometers);
|
|
28
|
+
//! if (status == QTTY_OK) {
|
|
29
|
+
//! // kilometers.value == 1.0
|
|
30
|
+
//! }
|
|
31
|
+
//! ```
|
|
32
|
+
//!
|
|
33
|
+
//! # Quick Start (Rust)
|
|
34
|
+
//!
|
|
35
|
+
//! Use the helper traits and macros for seamless conversion:
|
|
36
|
+
//!
|
|
37
|
+
//! ```rust
|
|
38
|
+
//! use qtty::length::{Meters, Kilometers};
|
|
39
|
+
//! use qtty_ffi::{QttyQuantity, UnitId};
|
|
40
|
+
//!
|
|
41
|
+
//! // Convert Rust type to FFI
|
|
42
|
+
//! let meters = Meters::new(1000.0);
|
|
43
|
+
//! let ffi_qty: QttyQuantity = meters.into();
|
|
44
|
+
//!
|
|
45
|
+
//! // Convert FFI back to Rust type (with automatic unit conversion)
|
|
46
|
+
//! let km: Kilometers = ffi_qty.try_into().unwrap();
|
|
47
|
+
//! assert!((km.value() - 1.0).abs() < 1e-12);
|
|
48
|
+
//! ```
|
|
49
|
+
//!
|
|
50
|
+
//! # ABI Stability
|
|
51
|
+
//!
|
|
52
|
+
//! The following are part of the ABI contract and will never change:
|
|
53
|
+
//!
|
|
54
|
+
//! - [`UnitId`] discriminant values (existing variants)
|
|
55
|
+
//! - [`DimensionId`] discriminant values (existing variants)
|
|
56
|
+
//! - [`QttyQuantity`] memory layout
|
|
57
|
+
//! - Status code values ([`QTTY_OK`], [`QTTY_ERR_UNKNOWN_UNIT`], etc.)
|
|
58
|
+
//! - Function signatures of exported `extern "C"` functions
|
|
59
|
+
//!
|
|
60
|
+
//! New variants may be added to enums (with new discriminant values), and new functions
|
|
61
|
+
//! may be added, but existing items will remain stable.
|
|
62
|
+
//!
|
|
63
|
+
//! # Supported Units (v1)
|
|
64
|
+
//!
|
|
65
|
+
//! ## Length
|
|
66
|
+
//! - [`UnitId::Meter`] - SI base unit
|
|
67
|
+
//! - [`UnitId::Kilometer`] - 1000 meters
|
|
68
|
+
//!
|
|
69
|
+
//! ## Time
|
|
70
|
+
//! - [`UnitId::Second`] - SI base unit
|
|
71
|
+
//! - [`UnitId::Minute`] - 60 seconds
|
|
72
|
+
//! - [`UnitId::Hour`] - 3600 seconds
|
|
73
|
+
//! - [`UnitId::Day`] - 86400 seconds
|
|
74
|
+
//!
|
|
75
|
+
//! ## Angle
|
|
76
|
+
//! - [`UnitId::Radian`] - SI unit
|
|
77
|
+
//! - [`UnitId::Degree`] - π/180 radians
|
|
78
|
+
//!
|
|
79
|
+
//! # Error Handling
|
|
80
|
+
//!
|
|
81
|
+
//! All FFI functions return status codes:
|
|
82
|
+
//!
|
|
83
|
+
//! - [`QTTY_OK`] (0): Success
|
|
84
|
+
//! - [`QTTY_ERR_UNKNOWN_UNIT`] (-1): Invalid unit ID
|
|
85
|
+
//! - [`QTTY_ERR_INCOMPATIBLE_DIM`] (-2): Dimension mismatch
|
|
86
|
+
//! - [`QTTY_ERR_NULL_OUT`] (-3): Null output pointer
|
|
87
|
+
//! - [`QTTY_ERR_INVALID_VALUE`] (-4): Invalid value (reserved)
|
|
88
|
+
//! - [`QTTY_ERR_BUFFER_TOO_SMALL`] (-5): Output buffer too small
|
|
89
|
+
//!
|
|
90
|
+
//! Format flags for [`qtty_quantity_format`]:
|
|
91
|
+
//!
|
|
92
|
+
//! - [`QTTY_FMT_DEFAULT`] (0): decimal notation
|
|
93
|
+
//! - [`QTTY_FMT_LOWER_EXP`] (1): scientific notation (lowercase `e`)
|
|
94
|
+
//! - [`QTTY_FMT_UPPER_EXP`] (2): scientific notation (uppercase `E`)
|
|
95
|
+
//!
|
|
96
|
+
//! # Thread Safety
|
|
97
|
+
//!
|
|
98
|
+
//! All functions are thread-safe. The library contains no global mutable state.
|
|
99
|
+
|
|
100
|
+
#![deny(missing_docs)]
|
|
101
|
+
// PyO3 generated code contains unsafe operations, so we can't enforce this when pyo3 feature is enabled
|
|
102
|
+
#![cfg_attr(not(feature = "pyo3"), deny(unsafe_op_in_unsafe_fn))]
|
|
103
|
+
|
|
104
|
+
// Core modules
|
|
105
|
+
mod ffi;
|
|
106
|
+
pub mod helpers;
|
|
107
|
+
#[macro_use]
|
|
108
|
+
pub mod macros;
|
|
109
|
+
pub mod registry;
|
|
110
|
+
mod types;
|
|
111
|
+
|
|
112
|
+
// Re-export FFI functions
|
|
113
|
+
pub use ffi::{
|
|
114
|
+
qtty_derived_convert, qtty_derived_from_json, qtty_derived_make, qtty_derived_to_json,
|
|
115
|
+
qtty_ffi_version, qtty_quantity_convert, qtty_quantity_convert_value, qtty_quantity_format,
|
|
116
|
+
qtty_quantity_from_json, qtty_quantity_from_json_value, qtty_quantity_make,
|
|
117
|
+
qtty_quantity_to_json, qtty_quantity_to_json_value, qtty_string_free, qtty_unit_dimension,
|
|
118
|
+
qtty_unit_is_valid, qtty_unit_name, qtty_units_compatible,
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
// Re-export types
|
|
122
|
+
pub use types::{
|
|
123
|
+
DimensionId, QttyDerivedQuantity, QttyQuantity, UnitId, QTTY_ERR_BUFFER_TOO_SMALL,
|
|
124
|
+
QTTY_ERR_INCOMPATIBLE_DIM, QTTY_ERR_INVALID_VALUE, QTTY_ERR_NULL_OUT, QTTY_ERR_UNKNOWN_UNIT,
|
|
125
|
+
QTTY_FMT_DEFAULT, QTTY_FMT_LOWER_EXP, QTTY_FMT_UPPER_EXP, QTTY_OK,
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
// The impl_unit_ffi! macro is automatically exported at crate root by #[macro_export]
|
|
129
|
+
|
|
130
|
+
// Re-export helper functions
|
|
131
|
+
pub use helpers::{
|
|
132
|
+
days_into_ffi, degrees_into_ffi, hours_into_ffi, kilometers_into_ffi, meters_into_ffi,
|
|
133
|
+
minutes_into_ffi, radians_into_ffi, seconds_into_ffi, try_into_days, try_into_degrees,
|
|
134
|
+
try_into_hours, try_into_kilometers, try_into_meters, try_into_minutes, try_into_radians,
|
|
135
|
+
try_into_seconds,
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
#[cfg(test)]
|
|
139
|
+
mod tests {
|
|
140
|
+
use super::*;
|
|
141
|
+
use core::mem::{align_of, size_of};
|
|
142
|
+
|
|
143
|
+
/// Test that QttyQuantity has the expected size and alignment for FFI.
|
|
144
|
+
#[test]
|
|
145
|
+
fn test_qtty_quantity_layout() {
|
|
146
|
+
// QttyQuantity should be:
|
|
147
|
+
// - f64 (8 bytes) + UnitId (4 bytes) + padding (4 bytes) = 16 bytes
|
|
148
|
+
// - Aligned to 8 bytes (alignment of f64)
|
|
149
|
+
assert_eq!(size_of::<QttyQuantity>(), 16);
|
|
150
|
+
assert_eq!(align_of::<QttyQuantity>(), 8);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/// Test that UnitId has the expected size.
|
|
154
|
+
#[test]
|
|
155
|
+
fn test_unit_id_layout() {
|
|
156
|
+
assert_eq!(size_of::<UnitId>(), 4);
|
|
157
|
+
assert_eq!(align_of::<UnitId>(), 4);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/// Test that DimensionId has the expected size.
|
|
161
|
+
#[test]
|
|
162
|
+
fn test_dimension_id_layout() {
|
|
163
|
+
assert_eq!(size_of::<DimensionId>(), 4);
|
|
164
|
+
assert_eq!(align_of::<DimensionId>(), 4);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/// Test known conversion: 1000 meters → 1 kilometer
|
|
168
|
+
#[test]
|
|
169
|
+
fn test_known_conversion_meters_to_kilometers() {
|
|
170
|
+
let mut out = QttyQuantity::default();
|
|
171
|
+
let src = QttyQuantity::new(1000.0, UnitId::Meter);
|
|
172
|
+
|
|
173
|
+
let status = unsafe { qtty_quantity_convert(src, UnitId::Kilometer, &mut out) };
|
|
174
|
+
|
|
175
|
+
assert_eq!(status, QTTY_OK);
|
|
176
|
+
assert!((out.value - 1.0).abs() < 1e-12);
|
|
177
|
+
assert_eq!(out.unit, UnitId::Kilometer);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/// Test known conversion: 3600 seconds → 1 hour
|
|
181
|
+
#[test]
|
|
182
|
+
fn test_known_conversion_seconds_to_hours() {
|
|
183
|
+
let mut out = QttyQuantity::default();
|
|
184
|
+
let src = QttyQuantity::new(3600.0, UnitId::Second);
|
|
185
|
+
|
|
186
|
+
let status = unsafe { qtty_quantity_convert(src, UnitId::Hour, &mut out) };
|
|
187
|
+
|
|
188
|
+
assert_eq!(status, QTTY_OK);
|
|
189
|
+
assert!((out.value - 1.0).abs() < 1e-12);
|
|
190
|
+
assert_eq!(out.unit, UnitId::Hour);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/// Test known conversion: 180 degrees → π radians
|
|
194
|
+
#[test]
|
|
195
|
+
fn test_known_conversion_degrees_to_radians() {
|
|
196
|
+
use core::f64::consts::PI;
|
|
197
|
+
|
|
198
|
+
let mut out = QttyQuantity::default();
|
|
199
|
+
let src = QttyQuantity::new(180.0, UnitId::Degree);
|
|
200
|
+
|
|
201
|
+
let status = unsafe { qtty_quantity_convert(src, UnitId::Radian, &mut out) };
|
|
202
|
+
|
|
203
|
+
assert_eq!(status, QTTY_OK);
|
|
204
|
+
assert!((out.value - PI).abs() < 1e-12);
|
|
205
|
+
assert_eq!(out.unit, UnitId::Radian);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/// Test incompatible conversion: meters → seconds fails
|
|
209
|
+
#[test]
|
|
210
|
+
fn test_incompatible_conversion_fails() {
|
|
211
|
+
let mut out = QttyQuantity::default();
|
|
212
|
+
let src = QttyQuantity::new(100.0, UnitId::Meter);
|
|
213
|
+
|
|
214
|
+
let status = unsafe { qtty_quantity_convert(src, UnitId::Second, &mut out) };
|
|
215
|
+
|
|
216
|
+
assert_eq!(status, QTTY_ERR_INCOMPATIBLE_DIM);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/// Test null output pointer handling
|
|
220
|
+
#[test]
|
|
221
|
+
fn test_null_out_pointer() {
|
|
222
|
+
let src = QttyQuantity::new(100.0, UnitId::Meter);
|
|
223
|
+
|
|
224
|
+
let status =
|
|
225
|
+
unsafe { qtty_quantity_convert(src, UnitId::Kilometer, core::ptr::null_mut()) };
|
|
226
|
+
|
|
227
|
+
assert_eq!(status, QTTY_ERR_NULL_OUT);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
//! Macros for implementing FFI conversions for qtty unit types.
|
|
2
|
+
//!
|
|
3
|
+
//! This module provides macros that make it easy to implement `From` and `TryFrom`
|
|
4
|
+
//! conversions between `qtty` unit types and [`QttyQuantity`], as well as code generation
|
|
5
|
+
//! macros for defining all FFI units in one place.
|
|
6
|
+
//!
|
|
7
|
+
//! # Example
|
|
8
|
+
//!
|
|
9
|
+
//! The macro is intended to be used in crates that own the types being converted.
|
|
10
|
+
//! Since `qtty-ffi` already implements these for the standard `qtty` types, you
|
|
11
|
+
//! typically only need this for custom wrapper types.
|
|
12
|
+
//!
|
|
13
|
+
//! ```rust,ignore
|
|
14
|
+
//! use qtty_ffi::{impl_unit_ffi, QttyQuantity, UnitId};
|
|
15
|
+
//! use qtty::length::Meters;
|
|
16
|
+
//!
|
|
17
|
+
//! // This would work in your own crate that defines MyMeters:
|
|
18
|
+
//! impl_unit_ffi!(MyMeters, UnitId::Meter);
|
|
19
|
+
//!
|
|
20
|
+
//! // Then you can convert between MyMeters and QttyQuantity
|
|
21
|
+
//! let meters = MyMeters::new(100.0);
|
|
22
|
+
//! let quantity: QttyQuantity = meters.into();
|
|
23
|
+
//! ```
|
|
24
|
+
|
|
25
|
+
/// Internal helper macro for generating unit match arms in `UnitId` methods.
|
|
26
|
+
#[doc(hidden)]
|
|
27
|
+
#[macro_export]
|
|
28
|
+
macro_rules! unit_match_arms {
|
|
29
|
+
($(($variant:ident, $name:expr)),* $(,)?) => {
|
|
30
|
+
$(
|
|
31
|
+
UnitId::$variant => $name
|
|
32
|
+
),*
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/// Internal helper macro for generating unit `from_u32` match arms.
|
|
37
|
+
#[doc(hidden)]
|
|
38
|
+
#[macro_export]
|
|
39
|
+
macro_rules! unit_from_u32_arms {
|
|
40
|
+
($(($discriminant:expr, $variant:ident)),* $(,)?) => {
|
|
41
|
+
$(
|
|
42
|
+
$discriminant => Some(UnitId::$variant)
|
|
43
|
+
),*
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/// Internal helper macro for generating registry metadata match arms.
|
|
48
|
+
#[doc(hidden)]
|
|
49
|
+
#[macro_export]
|
|
50
|
+
macro_rules! registry_match_arms {
|
|
51
|
+
($(($variant:ident, $dim:ident, $scale:expr, $name:expr)),* $(,)?) => {
|
|
52
|
+
$(
|
|
53
|
+
UnitId::$variant => Some(UnitMeta {
|
|
54
|
+
dim: DimensionId::$dim,
|
|
55
|
+
scale_to_canonical: $scale,
|
|
56
|
+
name: $name,
|
|
57
|
+
})
|
|
58
|
+
),*
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/// Implements `From<$qty_type>` for `QttyQuantity` and `TryFrom<QttyQuantity>` for `$qty_type`.
|
|
63
|
+
///
|
|
64
|
+
/// This macro generates bidirectional conversion implementations between a specific
|
|
65
|
+
/// `qtty` quantity type (like `Meters`, `Seconds`, etc.) and the FFI-safe [`QttyQuantity`] type.
|
|
66
|
+
///
|
|
67
|
+
/// # Arguments
|
|
68
|
+
///
|
|
69
|
+
/// * `$qty_type` - The `qtty` quantity type (e.g., `qtty::length::Meters`)
|
|
70
|
+
/// * `$unit_id` - The corresponding [`UnitId`] variant (e.g., `UnitId::Meter`)
|
|
71
|
+
///
|
|
72
|
+
/// # Generated Implementations
|
|
73
|
+
///
|
|
74
|
+
/// * `impl From<$qty_type> for QttyQuantity` - Converts the typed quantity to FFI format
|
|
75
|
+
/// * `impl TryFrom<QttyQuantity> for $qty_type` - Converts FFI format back to typed quantity,
|
|
76
|
+
/// performing unit conversion if the FFI quantity is in a compatible unit
|
|
77
|
+
///
|
|
78
|
+
/// # Example
|
|
79
|
+
///
|
|
80
|
+
/// The macro is used internally by `qtty-ffi` to implement conversions for standard types.
|
|
81
|
+
/// For your own types, usage looks like:
|
|
82
|
+
///
|
|
83
|
+
/// ```rust,ignore
|
|
84
|
+
/// use qtty_ffi::{impl_unit_ffi, QttyQuantity, UnitId};
|
|
85
|
+
///
|
|
86
|
+
/// // Your custom type (defined in your crate)
|
|
87
|
+
/// struct MySeconds(f64);
|
|
88
|
+
/// impl MySeconds {
|
|
89
|
+
/// fn new(v: f64) -> Self { Self(v) }
|
|
90
|
+
/// fn value(&self) -> f64 { self.0 }
|
|
91
|
+
/// }
|
|
92
|
+
///
|
|
93
|
+
/// impl_unit_ffi!(MySeconds, UnitId::Second);
|
|
94
|
+
/// ```
|
|
95
|
+
#[macro_export]
|
|
96
|
+
macro_rules! impl_unit_ffi {
|
|
97
|
+
($qty_type:ty, $unit_id:expr) => {
|
|
98
|
+
impl From<$qty_type> for $crate::QttyQuantity {
|
|
99
|
+
#[inline]
|
|
100
|
+
fn from(qty: $qty_type) -> Self {
|
|
101
|
+
$crate::QttyQuantity::new(qty.value(), $unit_id)
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
impl core::convert::TryFrom<$crate::QttyQuantity> for $qty_type {
|
|
106
|
+
type Error = i32;
|
|
107
|
+
|
|
108
|
+
#[inline]
|
|
109
|
+
fn try_from(qty: $crate::QttyQuantity) -> Result<Self, Self::Error> {
|
|
110
|
+
// If already the right unit, just wrap
|
|
111
|
+
if qty.unit == $unit_id {
|
|
112
|
+
return Ok(<$qty_type>::new(qty.value));
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Otherwise, try to convert
|
|
116
|
+
let converted = $crate::registry::convert_value(qty.value, qty.unit, $unit_id)?;
|
|
117
|
+
Ok(<$qty_type>::new(converted))
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
}
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
//! Unit registry and conversion logic for FFI.
|
|
2
|
+
//!
|
|
3
|
+
//! This module provides a Rust-only unit registry that maps [`UnitId`] values to their
|
|
4
|
+
//! metadata (dimension, scaling factor, name) and implements conversion between compatible units.
|
|
5
|
+
//!
|
|
6
|
+
//! # Conversion Formula
|
|
7
|
+
//!
|
|
8
|
+
//! Conversions use a canonical unit per dimension:
|
|
9
|
+
//! - Length: Meter
|
|
10
|
+
//! - Time: Second
|
|
11
|
+
//! - Angle: Radian
|
|
12
|
+
//!
|
|
13
|
+
//! The conversion formula is:
|
|
14
|
+
//! ```text
|
|
15
|
+
//! v_canonical = v_src * src.scale_to_canonical
|
|
16
|
+
//! v_dst = v_canonical / dst.scale_to_canonical
|
|
17
|
+
//! ```
|
|
18
|
+
//!
|
|
19
|
+
//! Which simplifies to:
|
|
20
|
+
//! ```text
|
|
21
|
+
//! v_dst = v_src * (src.scale_to_canonical / dst.scale_to_canonical)
|
|
22
|
+
//! ```
|
|
23
|
+
|
|
24
|
+
use crate::types::{
|
|
25
|
+
DimensionId, UnitId, QTTY_ERR_INCOMPATIBLE_DIM, QTTY_ERR_UNKNOWN_UNIT, QTTY_OK,
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
// =============================================================================
|
|
29
|
+
// Unit Metadata
|
|
30
|
+
// =============================================================================
|
|
31
|
+
|
|
32
|
+
/// Metadata about a unit for internal registry use.
|
|
33
|
+
///
|
|
34
|
+
/// This struct is Rust-only and not exposed via FFI.
|
|
35
|
+
#[derive(Debug, Clone, Copy)]
|
|
36
|
+
pub struct UnitMeta {
|
|
37
|
+
/// The dimension this unit belongs to.
|
|
38
|
+
pub dim: DimensionId,
|
|
39
|
+
/// Scaling factor to convert to the canonical unit for this dimension.
|
|
40
|
+
///
|
|
41
|
+
/// For example, for Kilometer: `scale_to_canonical = 1000.0` (1 km = 1000 m)
|
|
42
|
+
pub scale_to_canonical: f64,
|
|
43
|
+
/// Human-readable name of the unit.
|
|
44
|
+
pub name: &'static str,
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// =============================================================================
|
|
48
|
+
// Registry Functions
|
|
49
|
+
// =============================================================================
|
|
50
|
+
|
|
51
|
+
/// Returns metadata for the given unit ID.
|
|
52
|
+
///
|
|
53
|
+
/// Returns `None` if the unit ID is not recognized.
|
|
54
|
+
#[inline]
|
|
55
|
+
pub fn meta(id: UnitId) -> Option<UnitMeta> {
|
|
56
|
+
include!(concat!(env!("OUT_DIR"), "/unit_registry.rs"))
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/// Returns the dimension for the given unit ID.
|
|
60
|
+
///
|
|
61
|
+
/// Returns `None` if the unit ID is not recognized.
|
|
62
|
+
#[inline]
|
|
63
|
+
pub fn dimension(id: UnitId) -> Option<DimensionId> {
|
|
64
|
+
meta(id).map(|m| m.dim)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/// Checks if two units are compatible (same dimension).
|
|
68
|
+
///
|
|
69
|
+
/// Returns `true` if both units have the same dimension, `false` otherwise.
|
|
70
|
+
/// Also returns `false` if either unit is not recognized.
|
|
71
|
+
#[inline]
|
|
72
|
+
pub fn compatible(a: UnitId, b: UnitId) -> bool {
|
|
73
|
+
match (dimension(a), dimension(b)) {
|
|
74
|
+
(Some(da), Some(db)) => da == db,
|
|
75
|
+
_ => false,
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/// Converts a value from one unit to another.
|
|
80
|
+
///
|
|
81
|
+
/// # Arguments
|
|
82
|
+
///
|
|
83
|
+
/// * `v` - The value to convert
|
|
84
|
+
/// * `src` - The source unit
|
|
85
|
+
/// * `dst` - The destination unit
|
|
86
|
+
///
|
|
87
|
+
/// # Returns
|
|
88
|
+
///
|
|
89
|
+
/// * `Ok(converted_value)` on success
|
|
90
|
+
/// * `Err(QTTY_ERR_UNKNOWN_UNIT)` if either unit is not recognized
|
|
91
|
+
/// * `Err(QTTY_ERR_INCOMPATIBLE_DIM)` if units have different dimensions
|
|
92
|
+
///
|
|
93
|
+
/// # Example
|
|
94
|
+
///
|
|
95
|
+
/// ```rust
|
|
96
|
+
/// use qtty_ffi::{registry, UnitId};
|
|
97
|
+
///
|
|
98
|
+
/// let meters = registry::convert_value(1000.0, UnitId::Meter, UnitId::Kilometer);
|
|
99
|
+
/// assert!((meters.unwrap() - 1.0).abs() < 1e-12);
|
|
100
|
+
/// ```
|
|
101
|
+
#[inline]
|
|
102
|
+
pub fn convert_value(v: f64, src: UnitId, dst: UnitId) -> Result<f64, i32> {
|
|
103
|
+
let src_meta = meta(src).ok_or(QTTY_ERR_UNKNOWN_UNIT)?;
|
|
104
|
+
let dst_meta = meta(dst).ok_or(QTTY_ERR_UNKNOWN_UNIT)?;
|
|
105
|
+
|
|
106
|
+
if src_meta.dim != dst_meta.dim {
|
|
107
|
+
return Err(QTTY_ERR_INCOMPATIBLE_DIM);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// If same unit, no conversion needed
|
|
111
|
+
if src == dst {
|
|
112
|
+
return Ok(v);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Convert: v_canonical = v * src_scale, then v_dst = v_canonical / dst_scale
|
|
116
|
+
let v_canonical = v * src_meta.scale_to_canonical;
|
|
117
|
+
let v_dst = v_canonical / dst_meta.scale_to_canonical;
|
|
118
|
+
|
|
119
|
+
Ok(v_dst)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/// Converts a value from one unit to another, returning a status code.
|
|
123
|
+
///
|
|
124
|
+
/// This is a convenience function that returns `QTTY_OK` on success and
|
|
125
|
+
/// the appropriate error code on failure. The converted value is stored
|
|
126
|
+
/// in `result` (which must be initialized).
|
|
127
|
+
///
|
|
128
|
+
/// # Arguments
|
|
129
|
+
///
|
|
130
|
+
/// * `v` - The value to convert
|
|
131
|
+
/// * `src` - The source unit
|
|
132
|
+
/// * `dst` - The destination unit
|
|
133
|
+
/// * `result` - Mutable reference to store the converted value
|
|
134
|
+
///
|
|
135
|
+
/// # Returns
|
|
136
|
+
///
|
|
137
|
+
/// * `QTTY_OK` on success
|
|
138
|
+
/// * `QTTY_ERR_UNKNOWN_UNIT` if either unit is not recognized
|
|
139
|
+
/// * `QTTY_ERR_INCOMPATIBLE_DIM` if units have different dimensions
|
|
140
|
+
#[inline]
|
|
141
|
+
pub fn convert_value_status(v: f64, src: UnitId, dst: UnitId, result: &mut f64) -> i32 {
|
|
142
|
+
match convert_value(v, src, dst) {
|
|
143
|
+
Ok(converted) => {
|
|
144
|
+
*result = converted;
|
|
145
|
+
QTTY_OK
|
|
146
|
+
}
|
|
147
|
+
Err(code) => code,
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
#[cfg(test)]
|
|
152
|
+
mod tests {
|
|
153
|
+
use super::*;
|
|
154
|
+
use approx::assert_relative_eq;
|
|
155
|
+
use core::f64::consts::PI;
|
|
156
|
+
|
|
157
|
+
#[test]
|
|
158
|
+
fn test_meta_returns_correct_dimensions() {
|
|
159
|
+
assert_eq!(meta(UnitId::Meter).unwrap().dim, DimensionId::Length);
|
|
160
|
+
assert_eq!(meta(UnitId::Kilometer).unwrap().dim, DimensionId::Length);
|
|
161
|
+
assert_eq!(meta(UnitId::Second).unwrap().dim, DimensionId::Time);
|
|
162
|
+
assert_eq!(meta(UnitId::Minute).unwrap().dim, DimensionId::Time);
|
|
163
|
+
assert_eq!(meta(UnitId::Hour).unwrap().dim, DimensionId::Time);
|
|
164
|
+
assert_eq!(meta(UnitId::Day).unwrap().dim, DimensionId::Time);
|
|
165
|
+
assert_eq!(meta(UnitId::Radian).unwrap().dim, DimensionId::Angle);
|
|
166
|
+
assert_eq!(meta(UnitId::Degree).unwrap().dim, DimensionId::Angle);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
#[test]
|
|
170
|
+
fn test_compatible_same_dimension() {
|
|
171
|
+
assert!(compatible(UnitId::Meter, UnitId::Kilometer));
|
|
172
|
+
assert!(compatible(UnitId::Second, UnitId::Hour));
|
|
173
|
+
assert!(compatible(UnitId::Radian, UnitId::Degree));
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
#[test]
|
|
177
|
+
fn test_compatible_different_dimension() {
|
|
178
|
+
assert!(!compatible(UnitId::Meter, UnitId::Second));
|
|
179
|
+
assert!(!compatible(UnitId::Hour, UnitId::Radian));
|
|
180
|
+
assert!(!compatible(UnitId::Degree, UnitId::Kilometer));
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
#[test]
|
|
184
|
+
fn test_convert_meters_to_kilometers() {
|
|
185
|
+
let result = convert_value(1000.0, UnitId::Meter, UnitId::Kilometer).unwrap();
|
|
186
|
+
assert_relative_eq!(result, 1.0, epsilon = 1e-12);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
#[test]
|
|
190
|
+
fn test_convert_kilometers_to_meters() {
|
|
191
|
+
let result = convert_value(1.0, UnitId::Kilometer, UnitId::Meter).unwrap();
|
|
192
|
+
assert_relative_eq!(result, 1000.0, epsilon = 1e-12);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
#[test]
|
|
196
|
+
fn test_convert_seconds_to_minutes() {
|
|
197
|
+
let result = convert_value(60.0, UnitId::Second, UnitId::Minute).unwrap();
|
|
198
|
+
assert_relative_eq!(result, 1.0, epsilon = 1e-12);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
#[test]
|
|
202
|
+
fn test_convert_seconds_to_hours() {
|
|
203
|
+
let result = convert_value(3600.0, UnitId::Second, UnitId::Hour).unwrap();
|
|
204
|
+
assert_relative_eq!(result, 1.0, epsilon = 1e-12);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
#[test]
|
|
208
|
+
fn test_convert_hours_to_seconds() {
|
|
209
|
+
let result = convert_value(1.0, UnitId::Hour, UnitId::Second).unwrap();
|
|
210
|
+
assert_relative_eq!(result, 3600.0, epsilon = 1e-12);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
#[test]
|
|
214
|
+
fn test_convert_days_to_hours() {
|
|
215
|
+
let result = convert_value(1.0, UnitId::Day, UnitId::Hour).unwrap();
|
|
216
|
+
assert_relative_eq!(result, 24.0, epsilon = 1e-12);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
#[test]
|
|
220
|
+
fn test_convert_degrees_to_radians() {
|
|
221
|
+
let result = convert_value(180.0, UnitId::Degree, UnitId::Radian).unwrap();
|
|
222
|
+
assert_relative_eq!(result, PI, epsilon = 1e-12);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
#[test]
|
|
226
|
+
fn test_convert_radians_to_degrees() {
|
|
227
|
+
let result = convert_value(PI, UnitId::Radian, UnitId::Degree).unwrap();
|
|
228
|
+
assert_relative_eq!(result, 180.0, epsilon = 1e-12);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
#[test]
|
|
232
|
+
fn test_convert_same_unit() {
|
|
233
|
+
let result = convert_value(42.0, UnitId::Meter, UnitId::Meter).unwrap();
|
|
234
|
+
assert_relative_eq!(result, 42.0, epsilon = 1e-12);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
#[test]
|
|
238
|
+
fn test_convert_incompatible_dimensions() {
|
|
239
|
+
let result = convert_value(1.0, UnitId::Meter, UnitId::Second);
|
|
240
|
+
assert_eq!(result, Err(QTTY_ERR_INCOMPATIBLE_DIM));
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
#[test]
|
|
244
|
+
fn test_convert_preserves_special_values() {
|
|
245
|
+
// NaN
|
|
246
|
+
let nan_result = convert_value(f64::NAN, UnitId::Meter, UnitId::Kilometer).unwrap();
|
|
247
|
+
assert!(nan_result.is_nan());
|
|
248
|
+
|
|
249
|
+
// Infinity
|
|
250
|
+
let inf_result = convert_value(f64::INFINITY, UnitId::Second, UnitId::Minute).unwrap();
|
|
251
|
+
assert!(inf_result.is_infinite() && inf_result.is_sign_positive());
|
|
252
|
+
|
|
253
|
+
// Negative infinity
|
|
254
|
+
let neg_inf_result =
|
|
255
|
+
convert_value(f64::NEG_INFINITY, UnitId::Second, UnitId::Minute).unwrap();
|
|
256
|
+
assert!(neg_inf_result.is_infinite() && neg_inf_result.is_sign_negative());
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
#[test]
|
|
260
|
+
fn test_convert_value_status_success() {
|
|
261
|
+
let mut out = 0.0;
|
|
262
|
+
let status = convert_value_status(2.0, UnitId::Hour, UnitId::Minute, &mut out);
|
|
263
|
+
assert_eq!(status, QTTY_OK);
|
|
264
|
+
assert_relative_eq!(out, 120.0, epsilon = 1e-12);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
#[test]
|
|
268
|
+
fn test_convert_value_status_incompatible_dimension() {
|
|
269
|
+
let mut out = -1.0;
|
|
270
|
+
let status = convert_value_status(1.0, UnitId::Meter, UnitId::Second, &mut out);
|
|
271
|
+
assert_eq!(status, QTTY_ERR_INCOMPATIBLE_DIM);
|
|
272
|
+
assert_relative_eq!(out, -1.0, epsilon = 1e-12);
|
|
273
|
+
}
|
|
274
|
+
}
|