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,744 @@
|
|
|
1
|
+
//! Core type system for strongly typed physical quantities.
|
|
2
|
+
//!
|
|
3
|
+
//! `qtty-core` provides a minimal, zero-cost units model:
|
|
4
|
+
//!
|
|
5
|
+
//! - A *unit* is a zero-sized marker type implementing [`Unit`].
|
|
6
|
+
//! - A value tagged with a unit is a [`Quantity<U>`], backed by an `f64`.
|
|
7
|
+
//! - Conversion is an explicit, type-checked scaling via [`Quantity::to`].
|
|
8
|
+
//! - Derived units like velocity are expressed as [`Per<N, D>`] (e.g. `Meter/Second`).
|
|
9
|
+
//!
|
|
10
|
+
//! Most users should depend on `qtty` (the facade crate) unless they need direct access to these primitives.
|
|
11
|
+
//!
|
|
12
|
+
//! # What this crate solves
|
|
13
|
+
//!
|
|
14
|
+
//! - Compile-time separation of dimensions (length vs time vs angle, …).
|
|
15
|
+
//! - Zero runtime overhead for unit tags (phantom types only).
|
|
16
|
+
//! - Full dimensional arithmetic: `m * m → m²`, `m / s → velocity`, `m² / m → m`.
|
|
17
|
+
//! - Automatic compile-time verification that multiplied/divided quantities produce the correct dimension.
|
|
18
|
+
//!
|
|
19
|
+
//! # What this crate does not try to solve
|
|
20
|
+
//!
|
|
21
|
+
//! - Exact arithmetic (`Quantity` is `f64`).
|
|
22
|
+
//! - General-purpose symbolic simplification of arbitrary unit expressions.
|
|
23
|
+
//!
|
|
24
|
+
//! # Quick start
|
|
25
|
+
//!
|
|
26
|
+
//! Convert between predefined units:
|
|
27
|
+
//!
|
|
28
|
+
//! ```rust
|
|
29
|
+
//! use qtty_core::length::{Kilometers, Meter};
|
|
30
|
+
//!
|
|
31
|
+
//! let km = Kilometers::new(1.25);
|
|
32
|
+
//! let m = km.to::<Meter>();
|
|
33
|
+
//! assert!((m.value() - 1250.0).abs() < 1e-12);
|
|
34
|
+
//! ```
|
|
35
|
+
//!
|
|
36
|
+
//! Compose derived units using `/`:
|
|
37
|
+
//!
|
|
38
|
+
//! ```rust
|
|
39
|
+
//! use qtty_core::length::{Meter, Meters};
|
|
40
|
+
//! use qtty_core::time::{Second, Seconds};
|
|
41
|
+
//! use qtty_core::velocity::Velocity;
|
|
42
|
+
//!
|
|
43
|
+
//! let d = Meters::new(100.0);
|
|
44
|
+
//! let t = Seconds::new(20.0);
|
|
45
|
+
//! let v: Velocity<Meter, Second> = d / t;
|
|
46
|
+
//! assert!((v.value() - 5.0).abs() < 1e-12);
|
|
47
|
+
//! ```
|
|
48
|
+
//!
|
|
49
|
+
//! # `no_std`
|
|
50
|
+
//!
|
|
51
|
+
//! Disable default features to build `qtty-core` without `std`:
|
|
52
|
+
//!
|
|
53
|
+
//! ```toml
|
|
54
|
+
//! [dependencies]
|
|
55
|
+
//! qtty-core = { version = "0.1.0", default-features = false }
|
|
56
|
+
//! ```
|
|
57
|
+
//!
|
|
58
|
+
//! When `std` is disabled, floating-point math that isn't available in `core` is provided via `libm`.
|
|
59
|
+
//!
|
|
60
|
+
//! # Feature flags
|
|
61
|
+
//!
|
|
62
|
+
//! - `std` (default): enables `std` support.
|
|
63
|
+
//! - `cross-unit-ops` (default): enables direct cross-unit comparison operators (`==`, `<`, etc.) for built-in unit catalogs.
|
|
64
|
+
//! - `serde`: enables `serde` support for `Quantity<U>`; serialization is the raw `f64` value only.
|
|
65
|
+
//! - `pyo3`: enables PyO3 bindings for Python interop via `#[pyclass]` and `#[pymethods]`.
|
|
66
|
+
//!
|
|
67
|
+
//! # Panics and errors
|
|
68
|
+
//!
|
|
69
|
+
//! This crate does not define an error type and does not return `Result` from its core operations. Conversions and
|
|
70
|
+
//! arithmetic are pure `f64` computations; they do not panic on their own, but they follow IEEE-754 behavior (NaN and
|
|
71
|
+
//! infinities propagate according to the underlying operation).
|
|
72
|
+
//!
|
|
73
|
+
//! # SemVer and stability
|
|
74
|
+
//!
|
|
75
|
+
//! This crate is currently `0.x`. Expect breaking changes between minor versions until `1.0`.
|
|
76
|
+
|
|
77
|
+
#![deny(missing_docs)]
|
|
78
|
+
#![cfg_attr(not(feature = "std"), no_std)]
|
|
79
|
+
#![forbid(unsafe_code)]
|
|
80
|
+
|
|
81
|
+
#[cfg(not(feature = "std"))]
|
|
82
|
+
extern crate libm;
|
|
83
|
+
|
|
84
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
85
|
+
// Core modules
|
|
86
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
87
|
+
|
|
88
|
+
mod dimension;
|
|
89
|
+
#[cfg(feature = "diesel")]
|
|
90
|
+
mod feature_diesel;
|
|
91
|
+
#[cfg(feature = "pyo3")]
|
|
92
|
+
mod feature_pyo3;
|
|
93
|
+
#[cfg(feature = "serde")]
|
|
94
|
+
mod feature_serde;
|
|
95
|
+
#[cfg(feature = "tiberius")]
|
|
96
|
+
mod feature_tiberius;
|
|
97
|
+
mod macros;
|
|
98
|
+
mod quantity;
|
|
99
|
+
pub mod scalar;
|
|
100
|
+
mod unit;
|
|
101
|
+
|
|
102
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
103
|
+
// Public re-exports of core types
|
|
104
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
105
|
+
|
|
106
|
+
pub use dimension::{
|
|
107
|
+
// Derived dimensions
|
|
108
|
+
Acceleration,
|
|
109
|
+
// Additional base dimensions (less commonly used)
|
|
110
|
+
AmountOfSubstance,
|
|
111
|
+
// Base dimensions
|
|
112
|
+
Angular,
|
|
113
|
+
Area,
|
|
114
|
+
Current,
|
|
115
|
+
Dim,
|
|
116
|
+
DimDiv,
|
|
117
|
+
DimMul,
|
|
118
|
+
Dimension,
|
|
119
|
+
Dimensionless,
|
|
120
|
+
DivDim,
|
|
121
|
+
Energy,
|
|
122
|
+
Force,
|
|
123
|
+
FrequencyDim,
|
|
124
|
+
Length,
|
|
125
|
+
LuminousIntensity,
|
|
126
|
+
Mass,
|
|
127
|
+
MulDim,
|
|
128
|
+
Power,
|
|
129
|
+
Temperature,
|
|
130
|
+
Time,
|
|
131
|
+
VelocityDim,
|
|
132
|
+
Volume,
|
|
133
|
+
};
|
|
134
|
+
pub use quantity::{
|
|
135
|
+
Quantity, Quantity32, Quantity64, QuantityI128, QuantityI16, QuantityI32, QuantityI64,
|
|
136
|
+
QuantityI8,
|
|
137
|
+
};
|
|
138
|
+
pub use scalar::{Exact, IntegerScalar, Real, Scalar, Transcendental};
|
|
139
|
+
pub use unit::{Per, Prod, Simplify, Unit, Unitless};
|
|
140
|
+
|
|
141
|
+
#[cfg(feature = "scalar-decimal")]
|
|
142
|
+
pub use quantity::QuantityDecimal;
|
|
143
|
+
|
|
144
|
+
#[cfg(feature = "scalar-rational")]
|
|
145
|
+
pub use quantity::QuantityRational;
|
|
146
|
+
|
|
147
|
+
#[cfg(feature = "serde")]
|
|
148
|
+
pub use feature_serde::serde_with_unit;
|
|
149
|
+
|
|
150
|
+
#[cfg(feature = "serde")]
|
|
151
|
+
pub use feature_serde::serde_scalar;
|
|
152
|
+
|
|
153
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
154
|
+
// Predefined unit modules (grouped by dimension)
|
|
155
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
156
|
+
|
|
157
|
+
/// Predefined unit modules (grouped by dimension).
|
|
158
|
+
///
|
|
159
|
+
/// These are defined in `qtty-core` so they can implement formatting and helper traits without running into Rust's
|
|
160
|
+
/// orphan rules.
|
|
161
|
+
pub mod units;
|
|
162
|
+
|
|
163
|
+
pub use units::angular;
|
|
164
|
+
pub use units::area;
|
|
165
|
+
pub use units::frequency;
|
|
166
|
+
pub use units::length;
|
|
167
|
+
pub use units::mass;
|
|
168
|
+
pub use units::power;
|
|
169
|
+
pub use units::time;
|
|
170
|
+
pub use units::unitless;
|
|
171
|
+
pub use units::velocity;
|
|
172
|
+
pub use units::volume;
|
|
173
|
+
|
|
174
|
+
#[cfg(test)]
|
|
175
|
+
mod tests {
|
|
176
|
+
use super::*;
|
|
177
|
+
|
|
178
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
179
|
+
// Test dimension and unit for lib.rs tests
|
|
180
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
181
|
+
|
|
182
|
+
// Use Length as the test dimension (it's a type alias for Dim<P1, Z0, …>).
|
|
183
|
+
type TestDim = Length;
|
|
184
|
+
|
|
185
|
+
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
|
|
186
|
+
pub enum TestUnit {}
|
|
187
|
+
impl Unit for TestUnit {
|
|
188
|
+
const RATIO: f64 = 1.0;
|
|
189
|
+
type Dim = TestDim;
|
|
190
|
+
const SYMBOL: &'static str = "tu";
|
|
191
|
+
}
|
|
192
|
+
impl core::fmt::Display for Quantity<TestUnit> {
|
|
193
|
+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
194
|
+
write!(f, "{} tu", self.value())
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
|
|
199
|
+
pub enum DoubleTestUnit {}
|
|
200
|
+
impl Unit for DoubleTestUnit {
|
|
201
|
+
const RATIO: f64 = 2.0;
|
|
202
|
+
type Dim = TestDim;
|
|
203
|
+
const SYMBOL: &'static str = "dtu";
|
|
204
|
+
}
|
|
205
|
+
impl core::fmt::Display for Quantity<DoubleTestUnit> {
|
|
206
|
+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
207
|
+
write!(f, "{} dtu", self.value())
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
|
|
212
|
+
pub enum HalfTestUnit {}
|
|
213
|
+
impl Unit for HalfTestUnit {
|
|
214
|
+
const RATIO: f64 = 0.5;
|
|
215
|
+
type Dim = TestDim;
|
|
216
|
+
const SYMBOL: &'static str = "htu";
|
|
217
|
+
}
|
|
218
|
+
impl core::fmt::Display for Quantity<HalfTestUnit> {
|
|
219
|
+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
220
|
+
write!(f, "{} htu", self.value())
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
type TU = Quantity<TestUnit>;
|
|
225
|
+
type Dtu = Quantity<DoubleTestUnit>;
|
|
226
|
+
|
|
227
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
228
|
+
// Quantity core behavior
|
|
229
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
230
|
+
|
|
231
|
+
#[test]
|
|
232
|
+
fn quantity_new_and_value() {
|
|
233
|
+
let q = TU::new(42.0);
|
|
234
|
+
assert_eq!(q.value(), 42.0);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
#[test]
|
|
238
|
+
fn quantity_nan_constant() {
|
|
239
|
+
assert!(TU::NAN.value().is_nan());
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
#[test]
|
|
243
|
+
fn quantity_abs() {
|
|
244
|
+
assert_eq!(TU::new(-5.0).abs().value(), 5.0);
|
|
245
|
+
assert_eq!(TU::new(5.0).abs().value(), 5.0);
|
|
246
|
+
assert_eq!(TU::new(0.0).abs().value(), 0.0);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
#[test]
|
|
250
|
+
fn quantity_from_f64() {
|
|
251
|
+
let q: TU = 123.456.into();
|
|
252
|
+
assert_eq!(q.value(), 123.456);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
256
|
+
// Conversion via `to`
|
|
257
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
258
|
+
|
|
259
|
+
#[test]
|
|
260
|
+
fn quantity_conversion_to_same_unit() {
|
|
261
|
+
let q = TU::new(10.0);
|
|
262
|
+
let converted = q.to::<TestUnit>();
|
|
263
|
+
assert_eq!(converted.value(), 10.0);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
#[test]
|
|
267
|
+
fn quantity_conversion_to_different_unit() {
|
|
268
|
+
// 1 DoubleTestUnit = 2 TestUnit (in canonical terms)
|
|
269
|
+
// So 10 TU -> 10 * (1.0 / 2.0) = 5 DTU
|
|
270
|
+
let q = TU::new(10.0);
|
|
271
|
+
let converted = q.to::<DoubleTestUnit>();
|
|
272
|
+
assert!((converted.value() - 5.0).abs() < 1e-12);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
#[test]
|
|
276
|
+
fn quantity_conversion_roundtrip() {
|
|
277
|
+
let original = TU::new(100.0);
|
|
278
|
+
let converted = original.to::<DoubleTestUnit>();
|
|
279
|
+
let back = converted.to::<TestUnit>();
|
|
280
|
+
assert!((back.value() - original.value()).abs() < 1e-12);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
284
|
+
// Const helper methods: add/sub/mul/div/min
|
|
285
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
286
|
+
|
|
287
|
+
#[test]
|
|
288
|
+
fn const_add() {
|
|
289
|
+
let a = TU::new(3.0);
|
|
290
|
+
let b = TU::new(7.0);
|
|
291
|
+
assert_eq!(a.const_add(b).value(), 10.0);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
#[test]
|
|
295
|
+
fn const_sub() {
|
|
296
|
+
let a = TU::new(10.0);
|
|
297
|
+
let b = TU::new(3.0);
|
|
298
|
+
assert_eq!(a.const_sub(b).value(), 7.0);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
#[test]
|
|
302
|
+
fn const_mul() {
|
|
303
|
+
let a = TU::new(4.0);
|
|
304
|
+
let b = 5.0;
|
|
305
|
+
assert_eq!(a.const_mul(b).value(), 20.0);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
#[test]
|
|
309
|
+
fn const_div() {
|
|
310
|
+
let a = TU::new(20.0);
|
|
311
|
+
let b = 4.0;
|
|
312
|
+
assert_eq!(a.const_div(b).value(), 5.0);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
#[test]
|
|
316
|
+
fn const_min() {
|
|
317
|
+
let a = TU::new(5.0);
|
|
318
|
+
let b = TU::new(3.0);
|
|
319
|
+
assert_eq!(a.min_const(b).value(), 3.0);
|
|
320
|
+
assert_eq!(b.min_const(a).value(), 3.0);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
#[test]
|
|
324
|
+
fn const_max() {
|
|
325
|
+
let a = TU::new(3.0);
|
|
326
|
+
let b = TU::new(5.0);
|
|
327
|
+
// When self < other, max_const returns other (the else branch)
|
|
328
|
+
assert_eq!(a.max_const(b).value(), 5.0);
|
|
329
|
+
// When self > other, max_const returns self (the if branch)
|
|
330
|
+
assert_eq!(b.max_const(a).value(), 5.0);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
#[test]
|
|
334
|
+
fn sum_quantities_owned() {
|
|
335
|
+
let qs = vec![TU::new(1.0), TU::new(2.0), TU::new(3.0)];
|
|
336
|
+
let total: TU = qs.into_iter().sum();
|
|
337
|
+
assert_eq!(total.value(), 6.0);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
#[test]
|
|
341
|
+
fn sum_quantities_by_ref() {
|
|
342
|
+
let qs = [TU::new(1.0), TU::new(2.0), TU::new(3.0)];
|
|
343
|
+
let total: TU = qs.iter().sum();
|
|
344
|
+
assert_eq!(total.value(), 6.0);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
#[test]
|
|
348
|
+
fn sum_quantities_into_f64() {
|
|
349
|
+
let qs = vec![TU::new(1.0), TU::new(2.0), TU::new(3.0)];
|
|
350
|
+
let total: f64 = qs.into_iter().sum();
|
|
351
|
+
assert_eq!(total, 6.0);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
355
|
+
// Operator traits: Add, Sub, Mul, Div, Neg, Rem
|
|
356
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
357
|
+
|
|
358
|
+
#[test]
|
|
359
|
+
fn operator_add() {
|
|
360
|
+
let a = TU::new(3.0);
|
|
361
|
+
let b = TU::new(7.0);
|
|
362
|
+
assert_eq!((a + b).value(), 10.0);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
#[test]
|
|
366
|
+
fn operator_sub() {
|
|
367
|
+
let a = TU::new(10.0);
|
|
368
|
+
let b = TU::new(3.0);
|
|
369
|
+
assert_eq!((a - b).value(), 7.0);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
#[test]
|
|
373
|
+
fn operator_mul_by_f64() {
|
|
374
|
+
let q = TU::new(5.0);
|
|
375
|
+
assert_eq!((q * 3.0).value(), 15.0);
|
|
376
|
+
assert_eq!((3.0 * q).value(), 15.0);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
#[test]
|
|
380
|
+
fn operator_div_by_f64() {
|
|
381
|
+
let q = TU::new(15.0);
|
|
382
|
+
assert_eq!((q / 3.0).value(), 5.0);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
#[test]
|
|
386
|
+
fn operator_neg() {
|
|
387
|
+
let q = TU::new(5.0);
|
|
388
|
+
assert_eq!((-q).value(), -5.0);
|
|
389
|
+
assert_eq!((-(-q)).value(), 5.0);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
#[test]
|
|
393
|
+
fn operator_rem() {
|
|
394
|
+
let q = TU::new(10.0);
|
|
395
|
+
assert_eq!((q % 3.0).value(), 1.0);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
399
|
+
// Assignment operators: AddAssign, SubAssign, DivAssign
|
|
400
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
401
|
+
|
|
402
|
+
#[test]
|
|
403
|
+
fn operator_add_assign() {
|
|
404
|
+
let mut q = TU::new(5.0);
|
|
405
|
+
q += TU::new(3.0);
|
|
406
|
+
assert_eq!(q.value(), 8.0);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
#[test]
|
|
410
|
+
fn operator_sub_assign() {
|
|
411
|
+
let mut q = TU::new(10.0);
|
|
412
|
+
q -= TU::new(3.0);
|
|
413
|
+
assert_eq!(q.value(), 7.0);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
#[test]
|
|
417
|
+
fn operator_div_assign() {
|
|
418
|
+
let mut q = TU::new(20.0);
|
|
419
|
+
q /= 4.0;
|
|
420
|
+
assert_eq!(q.value(), 5.0);
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
424
|
+
// PartialEq<f64>
|
|
425
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
426
|
+
|
|
427
|
+
#[test]
|
|
428
|
+
fn partial_eq_f64() {
|
|
429
|
+
let q = TU::new(5.0);
|
|
430
|
+
assert!(q == 5.0);
|
|
431
|
+
assert!(!(q == 4.0));
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
435
|
+
// Division yielding Per<N, D>
|
|
436
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
437
|
+
|
|
438
|
+
#[test]
|
|
439
|
+
fn division_creates_per_type() {
|
|
440
|
+
let num = TU::new(100.0);
|
|
441
|
+
let den = Dtu::new(20.0);
|
|
442
|
+
let ratio: Quantity<Per<TestUnit, DoubleTestUnit>> = num / den;
|
|
443
|
+
assert!((ratio.value() - 5.0).abs() < 1e-12);
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
#[test]
|
|
447
|
+
fn per_ratio_conversion() {
|
|
448
|
+
let v1: Quantity<Per<DoubleTestUnit, TestUnit>> = Quantity::new(10.0);
|
|
449
|
+
let v2: Quantity<Per<TestUnit, TestUnit>> = v1.to();
|
|
450
|
+
assert!((v2.value() - 20.0).abs() < 1e-12);
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
#[test]
|
|
454
|
+
fn per_multiplication_recovers_numerator() {
|
|
455
|
+
let rate: Quantity<Per<TestUnit, DoubleTestUnit>> = Quantity::new(5.0);
|
|
456
|
+
let time = Dtu::new(4.0);
|
|
457
|
+
// Blanket Mul gives Quantity<Prod<Per<TU,DTU>, DTU>>; .to() converts back to TU
|
|
458
|
+
// because Prod<Per<TU,DTU>, DTU> has dimension Length (same as TU).
|
|
459
|
+
let result: TU = (rate * time).to();
|
|
460
|
+
assert!((result.value() - 20.0).abs() < 1e-12);
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
#[test]
|
|
464
|
+
fn per_multiplication_commutative() {
|
|
465
|
+
let rate: Quantity<Per<TestUnit, DoubleTestUnit>> = Quantity::new(5.0);
|
|
466
|
+
let time = Dtu::new(4.0);
|
|
467
|
+
let result1: TU = (rate * time).to();
|
|
468
|
+
let result2: TU = (time * rate).to();
|
|
469
|
+
assert!((result1.value() - result2.value()).abs() < 1e-12);
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
473
|
+
// Simplify trait
|
|
474
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
475
|
+
|
|
476
|
+
#[test]
|
|
477
|
+
fn simplify_per_u_u_to_unitless() {
|
|
478
|
+
let ratio: Quantity<Per<TestUnit, TestUnit>> = Quantity::new(1.23456);
|
|
479
|
+
let unitless: Quantity<Unitless> = ratio.simplify();
|
|
480
|
+
assert!((unitless.value() - 1.23456).abs() < 1e-12);
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
#[test]
|
|
484
|
+
fn simplify_per_n_per_n_d_to_d() {
|
|
485
|
+
let q: Quantity<Per<TestUnit, Per<TestUnit, DoubleTestUnit>>> = Quantity::new(7.5);
|
|
486
|
+
let simplified: Dtu = q.simplify();
|
|
487
|
+
assert!((simplified.value() - 7.5).abs() < 1e-12);
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
491
|
+
// Quantity<Per<U,U>>::asin()
|
|
492
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
493
|
+
|
|
494
|
+
#[test]
|
|
495
|
+
fn per_u_u_asin() {
|
|
496
|
+
let ratio: Quantity<Per<TestUnit, TestUnit>> = Quantity::new(0.5);
|
|
497
|
+
let result = ratio.asin();
|
|
498
|
+
assert!((result - 0.5_f64.asin()).abs() < 1e-12);
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
#[test]
|
|
502
|
+
fn per_u_u_asin_boundary_values() {
|
|
503
|
+
let one: Quantity<Per<TestUnit, TestUnit>> = Quantity::new(1.0);
|
|
504
|
+
assert!((one.asin() - core::f64::consts::FRAC_PI_2).abs() < 1e-12);
|
|
505
|
+
|
|
506
|
+
let neg_one: Quantity<Per<TestUnit, TestUnit>> = Quantity::new(-1.0);
|
|
507
|
+
assert!((neg_one.asin() - (-core::f64::consts::FRAC_PI_2)).abs() < 1e-12);
|
|
508
|
+
|
|
509
|
+
let zero: Quantity<Per<TestUnit, TestUnit>> = Quantity::new(0.0);
|
|
510
|
+
assert!((zero.asin() - 0.0).abs() < 1e-12);
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
514
|
+
// Display formatting
|
|
515
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
516
|
+
|
|
517
|
+
#[test]
|
|
518
|
+
fn display_simple_quantity() {
|
|
519
|
+
let q = TU::new(42.5);
|
|
520
|
+
let s = format!("{}", q);
|
|
521
|
+
assert_eq!(s, "42.5 tu");
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
#[test]
|
|
525
|
+
fn display_per_quantity() {
|
|
526
|
+
let q: Quantity<Per<TestUnit, DoubleTestUnit>> = Quantity::new(2.5);
|
|
527
|
+
let s = format!("{}", q);
|
|
528
|
+
assert_eq!(s, "2.5 tu/dtu");
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
#[test]
|
|
532
|
+
fn display_negative_value() {
|
|
533
|
+
let q = TU::new(-99.9);
|
|
534
|
+
let s = format!("{}", q);
|
|
535
|
+
assert_eq!(s, "-99.9 tu");
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
#[test]
|
|
539
|
+
fn display_double_test_unit() {
|
|
540
|
+
let q = Dtu::new(2.5);
|
|
541
|
+
let s = format!("{}", q);
|
|
542
|
+
assert_eq!(s, "2.5 dtu");
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
#[test]
|
|
546
|
+
fn display_half_test_unit() {
|
|
547
|
+
type Htu = Quantity<HalfTestUnit>;
|
|
548
|
+
let q = Htu::new(3.0);
|
|
549
|
+
let s = format!("{}", q);
|
|
550
|
+
assert_eq!(s, "3 htu");
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
554
|
+
// Edge cases
|
|
555
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
556
|
+
|
|
557
|
+
#[test]
|
|
558
|
+
fn edge_case_zero() {
|
|
559
|
+
let zero = TU::new(0.0);
|
|
560
|
+
assert_eq!(zero.value(), 0.0);
|
|
561
|
+
assert_eq!((-zero).value(), 0.0);
|
|
562
|
+
assert_eq!(zero.abs().value(), 0.0);
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
#[test]
|
|
566
|
+
fn edge_case_negative_values() {
|
|
567
|
+
let neg = TU::new(-10.0);
|
|
568
|
+
let pos = TU::new(5.0);
|
|
569
|
+
|
|
570
|
+
assert_eq!((neg + pos).value(), -5.0);
|
|
571
|
+
assert_eq!((neg - pos).value(), -15.0);
|
|
572
|
+
assert_eq!((neg * 2.0).value(), -20.0);
|
|
573
|
+
assert_eq!(neg.abs().value(), 10.0);
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
#[test]
|
|
577
|
+
fn edge_case_large_values() {
|
|
578
|
+
let large = TU::new(1e100);
|
|
579
|
+
let small = TU::new(1e-100);
|
|
580
|
+
assert_eq!(large.value(), 1e100);
|
|
581
|
+
assert_eq!(small.value(), 1e-100);
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
#[test]
|
|
585
|
+
fn edge_case_infinity() {
|
|
586
|
+
let inf = TU::new(f64::INFINITY);
|
|
587
|
+
let neg_inf = TU::new(f64::NEG_INFINITY);
|
|
588
|
+
|
|
589
|
+
assert!(inf.value().is_infinite());
|
|
590
|
+
assert!(neg_inf.value().is_infinite());
|
|
591
|
+
assert_eq!(inf.value().signum(), 1.0);
|
|
592
|
+
assert_eq!(neg_inf.value().signum(), -1.0);
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
596
|
+
// Serde tests
|
|
597
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
598
|
+
|
|
599
|
+
#[cfg(feature = "serde")]
|
|
600
|
+
mod serde_tests {
|
|
601
|
+
use super::*;
|
|
602
|
+
use serde::{Deserialize, Serialize};
|
|
603
|
+
|
|
604
|
+
#[test]
|
|
605
|
+
fn serialize_quantity() {
|
|
606
|
+
let q = TU::new(42.5);
|
|
607
|
+
let json = serde_json::to_string(&q).unwrap();
|
|
608
|
+
assert_eq!(json, "42.5");
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
#[test]
|
|
612
|
+
fn deserialize_quantity() {
|
|
613
|
+
let json = "42.5";
|
|
614
|
+
let q: TU = serde_json::from_str(json).unwrap();
|
|
615
|
+
assert_eq!(q.value(), 42.5);
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
#[test]
|
|
619
|
+
fn serde_roundtrip() {
|
|
620
|
+
let original = TU::new(123.456);
|
|
621
|
+
let json = serde_json::to_string(&original).unwrap();
|
|
622
|
+
let restored: TU = serde_json::from_str(&json).unwrap();
|
|
623
|
+
assert!((restored.value() - original.value()).abs() < 1e-12);
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
627
|
+
// serde_with_unit module tests
|
|
628
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
629
|
+
|
|
630
|
+
#[derive(Serialize, Deserialize, Debug)]
|
|
631
|
+
struct TestStruct {
|
|
632
|
+
#[serde(with = "crate::serde_with_unit")]
|
|
633
|
+
distance: TU,
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
#[test]
|
|
637
|
+
fn serde_with_unit_serialize() {
|
|
638
|
+
let data = TestStruct {
|
|
639
|
+
distance: TU::new(42.5),
|
|
640
|
+
};
|
|
641
|
+
let json = serde_json::to_string(&data).unwrap();
|
|
642
|
+
assert!(json.contains("\"value\""));
|
|
643
|
+
assert!(json.contains("\"unit\""));
|
|
644
|
+
assert!(json.contains("42.5"));
|
|
645
|
+
assert!(json.contains("\"tu\""));
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
#[test]
|
|
649
|
+
fn serde_with_unit_deserialize() {
|
|
650
|
+
let json = r#"{"distance":{"value":42.5,"unit":"tu"}}"#;
|
|
651
|
+
let data: TestStruct = serde_json::from_str(json).unwrap();
|
|
652
|
+
assert_eq!(data.distance.value(), 42.5);
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
#[test]
|
|
656
|
+
fn serde_with_unit_deserialize_no_unit_field() {
|
|
657
|
+
// Should work without unit field for backwards compatibility
|
|
658
|
+
let json = r#"{"distance":{"value":42.5}}"#;
|
|
659
|
+
let data: TestStruct = serde_json::from_str(json).unwrap();
|
|
660
|
+
assert_eq!(data.distance.value(), 42.5);
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
#[test]
|
|
664
|
+
fn serde_with_unit_deserialize_wrong_unit() {
|
|
665
|
+
let json = r#"{"distance":{"value":42.5,"unit":"wrong"}}"#;
|
|
666
|
+
let result: Result<TestStruct, _> = serde_json::from_str(json);
|
|
667
|
+
assert!(result.is_err());
|
|
668
|
+
let err_msg = result.unwrap_err().to_string();
|
|
669
|
+
assert!(err_msg.contains("unit mismatch") || err_msg.contains("expected"));
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
#[test]
|
|
673
|
+
fn serde_with_unit_deserialize_missing_value() {
|
|
674
|
+
let json = r#"{"distance":{"unit":"tu"}}"#;
|
|
675
|
+
let result: Result<TestStruct, _> = serde_json::from_str(json);
|
|
676
|
+
assert!(result.is_err());
|
|
677
|
+
let err_msg = result.unwrap_err().to_string();
|
|
678
|
+
assert!(err_msg.contains("missing field") || err_msg.contains("value"));
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
#[test]
|
|
682
|
+
fn serde_with_unit_deserialize_duplicate_value() {
|
|
683
|
+
let json = r#"{"distance":{"value":42.5,"value":100.0,"unit":"tu"}}"#;
|
|
684
|
+
let result: Result<TestStruct, _> = serde_json::from_str(json);
|
|
685
|
+
// This should either error or use one of the values (implementation-dependent)
|
|
686
|
+
// but we're testing that it doesn't panic
|
|
687
|
+
let _ = result;
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
#[test]
|
|
691
|
+
fn serde_with_unit_deserialize_duplicate_unit() {
|
|
692
|
+
let json = r#"{"distance":{"value":42.5,"unit":"tu","unit":"tu"}}"#;
|
|
693
|
+
let result: Result<TestStruct, _> = serde_json::from_str(json);
|
|
694
|
+
// Similar to above - just ensure no panic
|
|
695
|
+
let _ = result;
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
#[test]
|
|
699
|
+
fn serde_with_unit_deserialize_invalid_format() {
|
|
700
|
+
// Test the expecting() method by providing wrong format
|
|
701
|
+
let json = r#"{"distance":"not_an_object"}"#;
|
|
702
|
+
let result: Result<TestStruct, _> = serde_json::from_str(json);
|
|
703
|
+
assert!(result.is_err());
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
#[test]
|
|
707
|
+
fn serde_with_unit_deserialize_array() {
|
|
708
|
+
// Test the expecting() method with array format
|
|
709
|
+
let json = r#"{"distance":[42.5, "tu"]}"#;
|
|
710
|
+
let result: Result<TestStruct, _> = serde_json::from_str(json);
|
|
711
|
+
assert!(result.is_err());
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
#[test]
|
|
715
|
+
fn serde_with_unit_roundtrip() {
|
|
716
|
+
let original = TestStruct {
|
|
717
|
+
distance: TU::new(123.456),
|
|
718
|
+
};
|
|
719
|
+
let json = serde_json::to_string(&original).unwrap();
|
|
720
|
+
let restored: TestStruct = serde_json::from_str(&json).unwrap();
|
|
721
|
+
assert!((restored.distance.value() - original.distance.value()).abs() < 1e-12);
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
#[test]
|
|
725
|
+
fn serde_with_unit_special_values() {
|
|
726
|
+
// Note: JSON doesn't support Infinity and NaN natively.
|
|
727
|
+
// serde_json serializes them as null, which can't be deserialized
|
|
728
|
+
// back to f64. So we'll test with very large numbers instead.
|
|
729
|
+
let test_large = TestStruct {
|
|
730
|
+
distance: TU::new(1e100),
|
|
731
|
+
};
|
|
732
|
+
let json = serde_json::to_string(&test_large).unwrap();
|
|
733
|
+
let restored: TestStruct = serde_json::from_str(&json).unwrap();
|
|
734
|
+
assert!((restored.distance.value() - 1e100).abs() < 1e88);
|
|
735
|
+
|
|
736
|
+
let test_small = TestStruct {
|
|
737
|
+
distance: TU::new(-1e-100),
|
|
738
|
+
};
|
|
739
|
+
let json = serde_json::to_string(&test_small).unwrap();
|
|
740
|
+
let restored: TestStruct = serde_json::from_str(&json).unwrap();
|
|
741
|
+
assert!((restored.distance.value() + 1e-100).abs() < 1e-112);
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
}
|