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,620 @@
|
|
|
1
|
+
//! ABI-stable FFI types for `qtty` quantities.
|
|
2
|
+
//!
|
|
3
|
+
//! This module defines the `#[repr(C)]` and `#[repr(u32)]` types that form the stable C ABI
|
|
4
|
+
//! for cross-language interoperability. These types are designed to be safe to pass across
|
|
5
|
+
//! FFI boundaries.
|
|
6
|
+
//!
|
|
7
|
+
//! # ABI Stability Contract
|
|
8
|
+
//!
|
|
9
|
+
//! The discriminant values for [`UnitId`] and [`DimensionId`] are part of the ABI contract
|
|
10
|
+
//! and **MUST NEVER CHANGE** once assigned. New variants may be added with new discriminant
|
|
11
|
+
//! values, but existing values must remain stable across all versions.
|
|
12
|
+
|
|
13
|
+
use core::ffi::c_char;
|
|
14
|
+
use serde::{Deserialize, Serialize};
|
|
15
|
+
|
|
16
|
+
// =============================================================================
|
|
17
|
+
// Status Codes
|
|
18
|
+
// =============================================================================
|
|
19
|
+
|
|
20
|
+
/// Success status code.
|
|
21
|
+
pub const QTTY_OK: i32 = 0;
|
|
22
|
+
|
|
23
|
+
/// Error: the provided unit ID is not recognized/valid.
|
|
24
|
+
pub const QTTY_ERR_UNKNOWN_UNIT: i32 = -1;
|
|
25
|
+
|
|
26
|
+
/// Error: conversion requested between incompatible dimensions.
|
|
27
|
+
pub const QTTY_ERR_INCOMPATIBLE_DIM: i32 = -2;
|
|
28
|
+
|
|
29
|
+
/// Error: a required output pointer was null.
|
|
30
|
+
pub const QTTY_ERR_NULL_OUT: i32 = -3;
|
|
31
|
+
|
|
32
|
+
/// Error: the provided value is invalid (reserved for future use).
|
|
33
|
+
pub const QTTY_ERR_INVALID_VALUE: i32 = -4;
|
|
34
|
+
|
|
35
|
+
/// Error: the provided output buffer is too small.
|
|
36
|
+
pub const QTTY_ERR_BUFFER_TOO_SMALL: i32 = -5;
|
|
37
|
+
|
|
38
|
+
// =============================================================================
|
|
39
|
+
// Format Flags (for qtty_quantity_format)
|
|
40
|
+
// =============================================================================
|
|
41
|
+
|
|
42
|
+
/// Format flag: default decimal notation (e.g. `"1234.57 m"`).
|
|
43
|
+
pub const QTTY_FMT_DEFAULT: u32 = 0;
|
|
44
|
+
|
|
45
|
+
/// Format flag: scientific notation with lowercase `e` (e.g. `"1.23e3 m"`).
|
|
46
|
+
pub const QTTY_FMT_LOWER_EXP: u32 = 1;
|
|
47
|
+
|
|
48
|
+
/// Format flag: scientific notation with uppercase `E` (e.g. `"1.23E3 m"`).
|
|
49
|
+
pub const QTTY_FMT_UPPER_EXP: u32 = 2;
|
|
50
|
+
|
|
51
|
+
// =============================================================================
|
|
52
|
+
// Dimension Identifiers
|
|
53
|
+
// =============================================================================
|
|
54
|
+
|
|
55
|
+
/// Dimension identifier for FFI.
|
|
56
|
+
///
|
|
57
|
+
/// Represents the physical dimension of a quantity. All discriminant values are
|
|
58
|
+
/// explicitly assigned and are part of the ABI contract.
|
|
59
|
+
///
|
|
60
|
+
/// # ABI Contract
|
|
61
|
+
///
|
|
62
|
+
/// **Discriminant values must never change.** New dimensions may be added with
|
|
63
|
+
/// new explicit discriminant values.
|
|
64
|
+
#[repr(u32)]
|
|
65
|
+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
|
66
|
+
pub enum DimensionId {
|
|
67
|
+
/// Length dimension (e.g., meters, kilometers).
|
|
68
|
+
Length = 1,
|
|
69
|
+
/// Time dimension (e.g., seconds, hours).
|
|
70
|
+
Time = 2,
|
|
71
|
+
/// Angle dimension (e.g., radians, degrees).
|
|
72
|
+
Angle = 3,
|
|
73
|
+
/// Mass dimension (e.g., grams, kilograms).
|
|
74
|
+
Mass = 4,
|
|
75
|
+
/// Power dimension (e.g., watts, kilowatts).
|
|
76
|
+
Power = 5,
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// =============================================================================
|
|
80
|
+
// Unit Identifiers
|
|
81
|
+
// =============================================================================
|
|
82
|
+
|
|
83
|
+
// =============================================================================
|
|
84
|
+
// Unit Identifiers
|
|
85
|
+
// =============================================================================
|
|
86
|
+
|
|
87
|
+
// The UnitId enum is generated by build.rs from qtty-core unit definitions.
|
|
88
|
+
// All discriminant values are explicitly assigned and are part of the ABI contract.
|
|
89
|
+
// Discriminant encoding: DSSCC where D=dimension (1 digit), SS=system (2 digits), CC=counter (2 digits)
|
|
90
|
+
// Units are grouped by dimension with ranges:
|
|
91
|
+
// - Length units: 1xxxx (10000-19999), Time units: 2xxxx (20000-29999), Angle units: 3xxxx (30000-39999)
|
|
92
|
+
// - Mass units: 4xxxx (40000-49999), Power units: 5xxxx (50000-59999)
|
|
93
|
+
include!(concat!(env!("OUT_DIR"), "/unit_id_enum.rs"));
|
|
94
|
+
|
|
95
|
+
impl UnitId {
|
|
96
|
+
/// Returns the unit name as a static NUL-terminated C string.
|
|
97
|
+
///
|
|
98
|
+
/// This is safe to call from C code and returns a pointer to static memory.
|
|
99
|
+
#[inline]
|
|
100
|
+
pub const fn name_cstr(&self) -> *const c_char {
|
|
101
|
+
include!(concat!(env!("OUT_DIR"), "/unit_names_cstr.rs"))
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/// Returns the unit name as a Rust string slice.
|
|
105
|
+
#[inline]
|
|
106
|
+
pub const fn name(&self) -> &'static str {
|
|
107
|
+
include!(concat!(env!("OUT_DIR"), "/unit_names.rs"))
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/// Returns the unit symbol as a Rust string slice (e.g., "m", "km", "s").
|
|
111
|
+
#[inline]
|
|
112
|
+
pub const fn symbol(&self) -> &'static str {
|
|
113
|
+
include!(concat!(env!("OUT_DIR"), "/unit_symbols.rs"))
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/// Attempts to create a `UnitId` from a raw `u32` discriminant value.
|
|
117
|
+
///
|
|
118
|
+
/// Returns `None` if the value does not correspond to a valid unit.
|
|
119
|
+
#[inline]
|
|
120
|
+
pub const fn from_u32(value: u32) -> Option<Self> {
|
|
121
|
+
include!(concat!(env!("OUT_DIR"), "/unit_from_u32.rs"))
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// =============================================================================
|
|
126
|
+
// Quantity Carrier Type
|
|
127
|
+
// =============================================================================
|
|
128
|
+
|
|
129
|
+
/// A POD quantity carrier type suitable for FFI.
|
|
130
|
+
///
|
|
131
|
+
/// This struct represents a physical quantity as a value paired with its unit.
|
|
132
|
+
/// It is `#[repr(C)]` to ensure a stable, predictable memory layout across
|
|
133
|
+
/// language boundaries.
|
|
134
|
+
///
|
|
135
|
+
/// # Memory Layout
|
|
136
|
+
///
|
|
137
|
+
/// - `value`: 8 bytes (f64)
|
|
138
|
+
/// - `unit`: 4 bytes (u32 via UnitId)
|
|
139
|
+
/// - Padding: 4 bytes (for alignment)
|
|
140
|
+
/// - Total: 16 bytes on most platforms
|
|
141
|
+
///
|
|
142
|
+
/// # Example
|
|
143
|
+
///
|
|
144
|
+
/// ```rust
|
|
145
|
+
/// use qtty_ffi::{QttyQuantity, UnitId};
|
|
146
|
+
///
|
|
147
|
+
/// let q = QttyQuantity {
|
|
148
|
+
/// value: 1000.0,
|
|
149
|
+
/// unit: UnitId::Meter,
|
|
150
|
+
/// };
|
|
151
|
+
/// ```
|
|
152
|
+
#[repr(C)]
|
|
153
|
+
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
|
|
154
|
+
pub struct QttyQuantity {
|
|
155
|
+
/// The numeric value of the quantity.
|
|
156
|
+
pub value: f64,
|
|
157
|
+
/// The unit identifier for this quantity.
|
|
158
|
+
pub unit: UnitId,
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
impl QttyQuantity {
|
|
162
|
+
/// Creates a new quantity with the given value and unit.
|
|
163
|
+
#[inline]
|
|
164
|
+
pub const fn new(value: f64, unit: UnitId) -> Self {
|
|
165
|
+
Self { value, unit }
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/// Checks if this quantity is compatible with another (same dimension).
|
|
169
|
+
///
|
|
170
|
+
/// # Example
|
|
171
|
+
///
|
|
172
|
+
/// ```rust
|
|
173
|
+
/// use qtty_ffi::{QttyQuantity, UnitId};
|
|
174
|
+
///
|
|
175
|
+
/// let meters = QttyQuantity::new(100.0, UnitId::Meter);
|
|
176
|
+
/// let km = QttyQuantity::new(1.0, UnitId::Kilometer);
|
|
177
|
+
/// let seconds = QttyQuantity::new(60.0, UnitId::Second);
|
|
178
|
+
///
|
|
179
|
+
/// assert!(meters.compatible(&km));
|
|
180
|
+
/// assert!(!meters.compatible(&seconds));
|
|
181
|
+
/// ```
|
|
182
|
+
#[inline]
|
|
183
|
+
pub fn compatible(&self, other: &Self) -> bool {
|
|
184
|
+
crate::registry::compatible(self.unit, other.unit)
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/// Returns the dimension of this quantity.
|
|
188
|
+
#[inline]
|
|
189
|
+
pub fn dimension(&self) -> Option<DimensionId> {
|
|
190
|
+
crate::registry::dimension(self.unit)
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/// Converts this quantity to a different unit.
|
|
194
|
+
///
|
|
195
|
+
/// Returns `None` if the units are incompatible (different dimensions).
|
|
196
|
+
///
|
|
197
|
+
/// # Example
|
|
198
|
+
///
|
|
199
|
+
/// ```rust
|
|
200
|
+
/// use qtty_ffi::{QttyQuantity, UnitId};
|
|
201
|
+
///
|
|
202
|
+
/// let meters = QttyQuantity::new(1000.0, UnitId::Meter);
|
|
203
|
+
/// let km = meters.convert_to(UnitId::Kilometer).unwrap();
|
|
204
|
+
/// assert!((km.value - 1.0).abs() < 1e-12);
|
|
205
|
+
/// assert_eq!(km.unit, UnitId::Kilometer);
|
|
206
|
+
/// ```
|
|
207
|
+
#[inline]
|
|
208
|
+
pub fn convert_to(&self, target: UnitId) -> Option<Self> {
|
|
209
|
+
crate::registry::convert_value(self.value, self.unit, target)
|
|
210
|
+
.ok()
|
|
211
|
+
.map(|v| Self::new(v, target))
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/// Adds two quantities, returning result in the left operand's unit.
|
|
215
|
+
///
|
|
216
|
+
/// Returns `None` if the quantities have different dimensions.
|
|
217
|
+
///
|
|
218
|
+
/// # Example
|
|
219
|
+
///
|
|
220
|
+
/// ```rust
|
|
221
|
+
/// use qtty_ffi::{QttyQuantity, UnitId};
|
|
222
|
+
///
|
|
223
|
+
/// let a = QttyQuantity::new(1.0, UnitId::Kilometer);
|
|
224
|
+
/// let b = QttyQuantity::new(500.0, UnitId::Meter);
|
|
225
|
+
/// let sum = a.add(&b).unwrap();
|
|
226
|
+
/// assert!((sum.value - 1.5).abs() < 1e-12);
|
|
227
|
+
/// assert_eq!(sum.unit, UnitId::Kilometer);
|
|
228
|
+
/// ```
|
|
229
|
+
#[inline]
|
|
230
|
+
pub fn add(&self, other: &Self) -> Option<Self> {
|
|
231
|
+
let other_converted = other.convert_to(self.unit)?;
|
|
232
|
+
Some(Self::new(self.value + other_converted.value, self.unit))
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/// Subtracts another quantity from this one, returning result in this quantity's unit.
|
|
236
|
+
///
|
|
237
|
+
/// Returns `None` if the quantities have different dimensions.
|
|
238
|
+
///
|
|
239
|
+
/// # Example
|
|
240
|
+
///
|
|
241
|
+
/// ```rust
|
|
242
|
+
/// use qtty_ffi::{QttyQuantity, UnitId};
|
|
243
|
+
///
|
|
244
|
+
/// let a = QttyQuantity::new(2.0, UnitId::Kilometer);
|
|
245
|
+
/// let b = QttyQuantity::new(500.0, UnitId::Meter);
|
|
246
|
+
/// let diff = a.sub(&b).unwrap();
|
|
247
|
+
/// assert!((diff.value - 1.5).abs() < 1e-12);
|
|
248
|
+
/// assert_eq!(diff.unit, UnitId::Kilometer);
|
|
249
|
+
/// ```
|
|
250
|
+
#[inline]
|
|
251
|
+
pub fn sub(&self, other: &Self) -> Option<Self> {
|
|
252
|
+
let other_converted = other.convert_to(self.unit)?;
|
|
253
|
+
Some(Self::new(self.value - other_converted.value, self.unit))
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/// Multiplies the quantity by a scalar value.
|
|
257
|
+
///
|
|
258
|
+
/// # Example
|
|
259
|
+
///
|
|
260
|
+
/// ```rust
|
|
261
|
+
/// use qtty_ffi::{QttyQuantity, UnitId};
|
|
262
|
+
///
|
|
263
|
+
/// let q = QttyQuantity::new(5.0, UnitId::Meter);
|
|
264
|
+
/// let result = q.mul_scalar(3.0);
|
|
265
|
+
/// assert!((result.value - 15.0).abs() < 1e-12);
|
|
266
|
+
/// ```
|
|
267
|
+
#[inline]
|
|
268
|
+
pub const fn mul_scalar(&self, scalar: f64) -> Self {
|
|
269
|
+
Self::new(self.value * scalar, self.unit)
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/// Divides the quantity by a scalar value.
|
|
273
|
+
///
|
|
274
|
+
/// # Example
|
|
275
|
+
///
|
|
276
|
+
/// ```rust
|
|
277
|
+
/// use qtty_ffi::{QttyQuantity, UnitId};
|
|
278
|
+
///
|
|
279
|
+
/// let q = QttyQuantity::new(15.0, UnitId::Meter);
|
|
280
|
+
/// let result = q.div_scalar(3.0);
|
|
281
|
+
/// assert!((result.value - 5.0).abs() < 1e-12);
|
|
282
|
+
/// ```
|
|
283
|
+
#[inline]
|
|
284
|
+
pub const fn div_scalar(&self, scalar: f64) -> Self {
|
|
285
|
+
Self::new(self.value / scalar, self.unit)
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/// Negates the quantity value.
|
|
289
|
+
#[inline]
|
|
290
|
+
pub const fn neg(&self) -> Self {
|
|
291
|
+
Self::new(-self.value, self.unit)
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
impl Default for QttyQuantity {
|
|
296
|
+
fn default() -> Self {
|
|
297
|
+
Self {
|
|
298
|
+
value: 0.0,
|
|
299
|
+
unit: UnitId::Meter,
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// =============================================================================
|
|
305
|
+
// QttyDerivedQuantity - Compound quantities like velocity (m/s)
|
|
306
|
+
// =============================================================================
|
|
307
|
+
|
|
308
|
+
/// A derived quantity representing a compound unit (numerator/denominator).
|
|
309
|
+
///
|
|
310
|
+
/// This is useful for quantities like velocity (m/s), frequency (rad/s), etc.
|
|
311
|
+
///
|
|
312
|
+
/// # ABI Stability
|
|
313
|
+
///
|
|
314
|
+
/// This struct has `#[repr(C)]` layout:
|
|
315
|
+
/// - `value` at offset 0 (8 bytes)
|
|
316
|
+
/// - `numerator` at offset 8 (4 bytes)
|
|
317
|
+
/// - `denominator` at offset 12 (4 bytes)
|
|
318
|
+
/// - Total size: 16 bytes
|
|
319
|
+
///
|
|
320
|
+
/// # Example
|
|
321
|
+
///
|
|
322
|
+
/// ```rust
|
|
323
|
+
/// use qtty_ffi::{QttyDerivedQuantity, UnitId};
|
|
324
|
+
///
|
|
325
|
+
/// // Create a velocity: 100 m/s
|
|
326
|
+
/// let velocity = QttyDerivedQuantity::new(100.0, UnitId::Meter, UnitId::Second);
|
|
327
|
+
/// assert_eq!(velocity.value, 100.0);
|
|
328
|
+
/// assert_eq!(velocity.numerator, UnitId::Meter);
|
|
329
|
+
/// assert_eq!(velocity.denominator, UnitId::Second);
|
|
330
|
+
/// ```
|
|
331
|
+
#[repr(C)]
|
|
332
|
+
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
|
|
333
|
+
pub struct QttyDerivedQuantity {
|
|
334
|
+
/// The numeric value of the derived quantity.
|
|
335
|
+
pub value: f64,
|
|
336
|
+
/// The numerator unit identifier.
|
|
337
|
+
pub numerator: UnitId,
|
|
338
|
+
/// The denominator unit identifier.
|
|
339
|
+
pub denominator: UnitId,
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
impl QttyDerivedQuantity {
|
|
343
|
+
/// Creates a new derived quantity with the given value and units.
|
|
344
|
+
#[inline]
|
|
345
|
+
pub const fn new(value: f64, numerator: UnitId, denominator: UnitId) -> Self {
|
|
346
|
+
Self {
|
|
347
|
+
value,
|
|
348
|
+
numerator,
|
|
349
|
+
denominator,
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/// Returns the symbol string for this derived quantity (e.g., "m/s").
|
|
354
|
+
#[inline]
|
|
355
|
+
pub fn symbol(&self) -> String {
|
|
356
|
+
format!("{}/{}", self.numerator.symbol(), self.denominator.symbol())
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/// Converts this derived quantity to different units.
|
|
360
|
+
///
|
|
361
|
+
/// Returns `None` if the numerator or denominator dimensions are incompatible.
|
|
362
|
+
///
|
|
363
|
+
/// # Example
|
|
364
|
+
///
|
|
365
|
+
/// ```rust
|
|
366
|
+
/// use qtty_ffi::{QttyDerivedQuantity, UnitId};
|
|
367
|
+
///
|
|
368
|
+
/// // Convert 100 m/s to km/h
|
|
369
|
+
/// let velocity = QttyDerivedQuantity::new(100.0, UnitId::Meter, UnitId::Second);
|
|
370
|
+
/// let converted = velocity.convert_to(UnitId::Kilometer, UnitId::Hour).unwrap();
|
|
371
|
+
/// // 100 m/s = 360 km/h
|
|
372
|
+
/// assert!((converted.value - 360.0).abs() < 1e-9);
|
|
373
|
+
/// ```
|
|
374
|
+
pub fn convert_to(&self, target_num: UnitId, target_den: UnitId) -> Option<Self> {
|
|
375
|
+
// Check dimensional compatibility
|
|
376
|
+
let num_dim = crate::registry::dimension(self.numerator)?;
|
|
377
|
+
let den_dim = crate::registry::dimension(self.denominator)?;
|
|
378
|
+
let target_num_dim = crate::registry::dimension(target_num)?;
|
|
379
|
+
let target_den_dim = crate::registry::dimension(target_den)?;
|
|
380
|
+
|
|
381
|
+
if num_dim != target_num_dim || den_dim != target_den_dim {
|
|
382
|
+
return None;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
// Convert numerator: e.g., 100 m -> ? km
|
|
386
|
+
let num_converted =
|
|
387
|
+
crate::registry::convert_value(self.value, self.numerator, target_num).ok()?;
|
|
388
|
+
|
|
389
|
+
// Convert denominator scale: e.g., 1 s -> ? h (0.000278 h)
|
|
390
|
+
// If 1 s = 0.000278 h, then dividing by that gives us the factor
|
|
391
|
+
let den_converted =
|
|
392
|
+
crate::registry::convert_value(1.0, self.denominator, target_den).ok()?;
|
|
393
|
+
|
|
394
|
+
// Result = (num in new units) / (den scale factor)
|
|
395
|
+
// 100 m = 0.1 km, 1 s = 1/3600 h
|
|
396
|
+
// 100 m/s = 0.1 km / (1/3600 h) = 0.1 * 3600 km/h = 360 km/h
|
|
397
|
+
let result_value = num_converted / den_converted;
|
|
398
|
+
|
|
399
|
+
Some(Self::new(result_value, target_num, target_den))
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/// Multiplies the derived quantity by a scalar.
|
|
403
|
+
#[inline]
|
|
404
|
+
pub const fn mul_scalar(&self, scalar: f64) -> Self {
|
|
405
|
+
Self::new(self.value * scalar, self.numerator, self.denominator)
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
/// Divides the derived quantity by a scalar.
|
|
409
|
+
#[inline]
|
|
410
|
+
pub const fn div_scalar(&self, scalar: f64) -> Self {
|
|
411
|
+
Self::new(self.value / scalar, self.numerator, self.denominator)
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
/// Negates the derived quantity.
|
|
415
|
+
#[inline]
|
|
416
|
+
pub const fn neg(&self) -> Self {
|
|
417
|
+
Self::new(-self.value, self.numerator, self.denominator)
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
impl Default for QttyDerivedQuantity {
|
|
422
|
+
fn default() -> Self {
|
|
423
|
+
Self {
|
|
424
|
+
value: 0.0,
|
|
425
|
+
numerator: UnitId::Meter,
|
|
426
|
+
denominator: UnitId::Second,
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
#[cfg(test)]
|
|
432
|
+
mod tests {
|
|
433
|
+
use super::*;
|
|
434
|
+
|
|
435
|
+
#[test]
|
|
436
|
+
fn unit_id_discriminants_are_stable() {
|
|
437
|
+
// These values are part of the ABI contract and must never change
|
|
438
|
+
assert_eq!(UnitId::Meter as u32, 10011); // Length, SI, #11
|
|
439
|
+
assert_eq!(UnitId::Kilometer as u32, 10014); // Length, SI, #14
|
|
440
|
+
assert_eq!(UnitId::Second as u32, 20008); // Time, SI, #8
|
|
441
|
+
assert_eq!(UnitId::Minute as u32, 21000); // Time, Common, #0
|
|
442
|
+
assert_eq!(UnitId::Hour as u32, 21001); // Time, Common, #1
|
|
443
|
+
assert_eq!(UnitId::Day as u32, 21002); // Time, Common, #2
|
|
444
|
+
assert_eq!(UnitId::Radian as u32, 30001); // Angle, Radian, #1
|
|
445
|
+
assert_eq!(UnitId::Degree as u32, 31004); // Angle, Degree, #4
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
#[test]
|
|
449
|
+
fn dimension_id_discriminants_are_stable() {
|
|
450
|
+
// These values are part of the ABI contract and must never change
|
|
451
|
+
assert_eq!(DimensionId::Length as u32, 1);
|
|
452
|
+
assert_eq!(DimensionId::Time as u32, 2);
|
|
453
|
+
assert_eq!(DimensionId::Angle as u32, 3);
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
#[test]
|
|
457
|
+
fn unit_id_from_u32_roundtrips() {
|
|
458
|
+
for unit in [
|
|
459
|
+
UnitId::Meter,
|
|
460
|
+
UnitId::Kilometer,
|
|
461
|
+
UnitId::Second,
|
|
462
|
+
UnitId::Minute,
|
|
463
|
+
UnitId::Hour,
|
|
464
|
+
UnitId::Day,
|
|
465
|
+
UnitId::Radian,
|
|
466
|
+
UnitId::Degree,
|
|
467
|
+
] {
|
|
468
|
+
let value = unit as u32;
|
|
469
|
+
assert_eq!(UnitId::from_u32(value), Some(unit));
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
#[test]
|
|
474
|
+
fn unit_id_from_u32_rejects_invalid() {
|
|
475
|
+
assert_eq!(UnitId::from_u32(0), None);
|
|
476
|
+
assert_eq!(UnitId::from_u32(9999), None);
|
|
477
|
+
assert_eq!(UnitId::from_u32(60000), None);
|
|
478
|
+
assert_eq!(UnitId::from_u32(99999), None);
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
#[test]
|
|
482
|
+
fn unit_names_are_not_empty() {
|
|
483
|
+
for unit in [
|
|
484
|
+
UnitId::Meter,
|
|
485
|
+
UnitId::Kilometer,
|
|
486
|
+
UnitId::Second,
|
|
487
|
+
UnitId::Minute,
|
|
488
|
+
UnitId::Hour,
|
|
489
|
+
UnitId::Day,
|
|
490
|
+
UnitId::Radian,
|
|
491
|
+
UnitId::Degree,
|
|
492
|
+
] {
|
|
493
|
+
assert!(!unit.name().is_empty());
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
// ─── QttyQuantity method coverage ────────────────────────────────────────
|
|
498
|
+
|
|
499
|
+
#[test]
|
|
500
|
+
fn qtty_quantity_default() {
|
|
501
|
+
let q = QttyQuantity::default();
|
|
502
|
+
assert_eq!(q.value, 0.0);
|
|
503
|
+
assert_eq!(q.unit, UnitId::Meter);
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
#[test]
|
|
507
|
+
fn qtty_quantity_neg() {
|
|
508
|
+
let q = QttyQuantity::new(5.0, UnitId::Meter);
|
|
509
|
+
let n = q.neg();
|
|
510
|
+
assert_eq!(n.value, -5.0);
|
|
511
|
+
assert_eq!(n.unit, UnitId::Meter);
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
#[test]
|
|
515
|
+
fn qtty_quantity_mul_scalar() {
|
|
516
|
+
let q = QttyQuantity::new(4.0, UnitId::Kilometer);
|
|
517
|
+
let r = q.mul_scalar(2.5);
|
|
518
|
+
assert_eq!(r.value, 10.0);
|
|
519
|
+
assert_eq!(r.unit, UnitId::Kilometer);
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
#[test]
|
|
523
|
+
fn qtty_quantity_div_scalar() {
|
|
524
|
+
let q = QttyQuantity::new(15.0, UnitId::Second);
|
|
525
|
+
let r = q.div_scalar(3.0);
|
|
526
|
+
assert_eq!(r.value, 5.0);
|
|
527
|
+
assert_eq!(r.unit, UnitId::Second);
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
#[test]
|
|
531
|
+
fn qtty_quantity_dimension() {
|
|
532
|
+
let q = QttyQuantity::new(1.0, UnitId::Meter);
|
|
533
|
+
assert_eq!(q.dimension(), Some(DimensionId::Length));
|
|
534
|
+
|
|
535
|
+
let t = QttyQuantity::new(1.0, UnitId::Second);
|
|
536
|
+
assert_eq!(t.dimension(), Some(DimensionId::Time));
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
#[test]
|
|
540
|
+
fn qtty_quantity_compatible() {
|
|
541
|
+
let a = QttyQuantity::new(1.0, UnitId::Meter);
|
|
542
|
+
let b = QttyQuantity::new(1.0, UnitId::Kilometer);
|
|
543
|
+
let c = QttyQuantity::new(1.0, UnitId::Second);
|
|
544
|
+
|
|
545
|
+
assert!(a.compatible(&b));
|
|
546
|
+
assert!(!a.compatible(&c));
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
#[test]
|
|
550
|
+
fn qtty_quantity_add_incompatible_returns_none() {
|
|
551
|
+
let a = QttyQuantity::new(1.0, UnitId::Meter);
|
|
552
|
+
let b = QttyQuantity::new(1.0, UnitId::Second);
|
|
553
|
+
assert!(a.add(&b).is_none());
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
#[test]
|
|
557
|
+
fn qtty_quantity_sub_incompatible_returns_none() {
|
|
558
|
+
let a = QttyQuantity::new(1.0, UnitId::Meter);
|
|
559
|
+
let b = QttyQuantity::new(1.0, UnitId::Second);
|
|
560
|
+
assert!(a.sub(&b).is_none());
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
// ─── QttyDerivedQuantity method coverage ─────────────────────────────────
|
|
564
|
+
|
|
565
|
+
#[test]
|
|
566
|
+
fn qtty_derived_quantity_default() {
|
|
567
|
+
let d = QttyDerivedQuantity::default();
|
|
568
|
+
assert_eq!(d.value, 0.0);
|
|
569
|
+
assert_eq!(d.numerator, UnitId::Meter);
|
|
570
|
+
assert_eq!(d.denominator, UnitId::Second);
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
#[test]
|
|
574
|
+
fn qtty_derived_quantity_symbol() {
|
|
575
|
+
let d = QttyDerivedQuantity::new(1.0, UnitId::Meter, UnitId::Second);
|
|
576
|
+
let sym = d.symbol();
|
|
577
|
+
// format is "numerator_symbol/denominator_symbol"
|
|
578
|
+
assert!(sym.contains('/'), "symbol should contain '/'");
|
|
579
|
+
assert!(!sym.is_empty());
|
|
580
|
+
// Should look like "m/s"
|
|
581
|
+
assert_eq!(
|
|
582
|
+
sym,
|
|
583
|
+
format!("{}/{}", UnitId::Meter.symbol(), UnitId::Second.symbol())
|
|
584
|
+
);
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
#[test]
|
|
588
|
+
fn qtty_derived_quantity_mul_scalar() {
|
|
589
|
+
let d = QttyDerivedQuantity::new(10.0, UnitId::Meter, UnitId::Second);
|
|
590
|
+
let r = d.mul_scalar(3.0);
|
|
591
|
+
assert_eq!(r.value, 30.0);
|
|
592
|
+
assert_eq!(r.numerator, UnitId::Meter);
|
|
593
|
+
assert_eq!(r.denominator, UnitId::Second);
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
#[test]
|
|
597
|
+
fn qtty_derived_quantity_div_scalar() {
|
|
598
|
+
let d = QttyDerivedQuantity::new(30.0, UnitId::Kilometer, UnitId::Hour);
|
|
599
|
+
let r = d.div_scalar(2.0);
|
|
600
|
+
assert_eq!(r.value, 15.0);
|
|
601
|
+
assert_eq!(r.numerator, UnitId::Kilometer);
|
|
602
|
+
assert_eq!(r.denominator, UnitId::Hour);
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
#[test]
|
|
606
|
+
fn qtty_derived_quantity_neg() {
|
|
607
|
+
let d = QttyDerivedQuantity::new(5.0, UnitId::Meter, UnitId::Second);
|
|
608
|
+
let n = d.neg();
|
|
609
|
+
assert_eq!(n.value, -5.0);
|
|
610
|
+
assert_eq!(n.numerator, UnitId::Meter);
|
|
611
|
+
assert_eq!(n.denominator, UnitId::Second);
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
#[test]
|
|
615
|
+
fn qtty_derived_quantity_convert_to_incompatible_returns_none() {
|
|
616
|
+
// m/s → kg/h: numerator dimension mismatch
|
|
617
|
+
let d = QttyDerivedQuantity::new(1.0, UnitId::Meter, UnitId::Second);
|
|
618
|
+
assert!(d.convert_to(UnitId::Kilogram, UnitId::Hour).is_none());
|
|
619
|
+
}
|
|
620
|
+
}
|