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.
Files changed (272) hide show
  1. package/.github/workflows/ci.yml +166 -0
  2. package/.gitmodules +9 -0
  3. package/CHANGELOG.md +26 -0
  4. package/LICENSE +661 -0
  5. package/README.md +138 -0
  6. package/package.json +12 -0
  7. package/qtty-js/.github/workflows/ci.yml +151 -0
  8. package/qtty-js/.gitmodules +3 -0
  9. package/qtty-js/CHANGELOG.md +31 -0
  10. package/qtty-js/LICENSE +661 -0
  11. package/qtty-js/README.md +132 -0
  12. package/qtty-js/package.json +20 -0
  13. package/qtty-js/qtty/.github/workflows/ci.yml +155 -0
  14. package/qtty-js/qtty/CHANGELOG.md +120 -0
  15. package/qtty-js/qtty/Cargo.lock +1462 -0
  16. package/qtty-js/qtty/Cargo.toml +12 -0
  17. package/qtty-js/qtty/LICENSE +661 -0
  18. package/qtty-js/qtty/README.md +9 -0
  19. package/qtty-js/qtty/qtty/Cargo.toml +41 -0
  20. package/qtty-js/qtty/qtty/README.md +8 -0
  21. package/qtty-js/qtty/qtty/examples/angles.rs +14 -0
  22. package/qtty-js/qtty/qtty/examples/astronomy.rs +17 -0
  23. package/qtty-js/qtty/qtty/examples/dimensional_arithmetic.rs +83 -0
  24. package/qtty-js/qtty/qtty/examples/python_integration.rs +61 -0
  25. package/qtty-js/qtty/qtty/examples/quickstart.rs +15 -0
  26. package/qtty-js/qtty/qtty/examples/ratios.rs +12 -0
  27. package/qtty-js/qtty/qtty/examples/serde_with_unit.rs +234 -0
  28. package/qtty-js/qtty/qtty/examples/serialization.rs +141 -0
  29. package/qtty-js/qtty/qtty/examples/serialization_advanced.rs +155 -0
  30. package/qtty-js/qtty/qtty/src/f32.rs +108 -0
  31. package/qtty-js/qtty/qtty/src/f64.rs +30 -0
  32. package/qtty-js/qtty/qtty/src/i128.rs +111 -0
  33. package/qtty-js/qtty/qtty/src/i16.rs +111 -0
  34. package/qtty-js/qtty/qtty/src/i32.rs +111 -0
  35. package/qtty-js/qtty/qtty/src/i64.rs +111 -0
  36. package/qtty-js/qtty/qtty/src/i8.rs +111 -0
  37. package/qtty-js/qtty/qtty/src/lib.rs +238 -0
  38. package/qtty-js/qtty/qtty/tests/fixtures/qtty-vec-no-std/Cargo.lock +83 -0
  39. package/qtty-js/qtty/qtty/tests/fixtures/qtty-vec-no-std/Cargo.toml +10 -0
  40. package/qtty-js/qtty/qtty/tests/fixtures/qtty-vec-no-std/src/lib.rs +7 -0
  41. package/qtty-js/qtty/qtty/tests/fixtures/qtty-vec-no-std-alloc/Cargo.lock +83 -0
  42. package/qtty-js/qtty/qtty/tests/fixtures/qtty-vec-no-std-alloc/Cargo.toml +10 -0
  43. package/qtty-js/qtty/qtty/tests/fixtures/qtty-vec-no-std-alloc/src/lib.rs +7 -0
  44. package/qtty-js/qtty/qtty/tests/fixtures/qtty-vec-std/Cargo.lock +83 -0
  45. package/qtty-js/qtty/qtty/tests/fixtures/qtty-vec-std/Cargo.toml +10 -0
  46. package/qtty-js/qtty/qtty/tests/fixtures/qtty-vec-std/src/lib.rs +5 -0
  47. package/qtty-js/qtty/qtty/tests/integration_tests.rs +529 -0
  48. package/qtty-js/qtty/qtty/tests/qtty_vec_feature_matrix.rs +58 -0
  49. package/qtty-js/qtty/qtty-core/Cargo.toml +41 -0
  50. package/qtty-js/qtty/qtty-core/README.md +8 -0
  51. package/qtty-js/qtty/qtty-core/examples/diesel_integration.rs +145 -0
  52. package/qtty-js/qtty/qtty-core/examples/quantity_db_serde.rs +215 -0
  53. package/qtty-js/qtty/qtty-core/src/dimension.rs +249 -0
  54. package/qtty-js/qtty/qtty-core/src/feature_diesel.rs +318 -0
  55. package/qtty-js/qtty/qtty-core/src/feature_pyo3.rs +27 -0
  56. package/qtty-js/qtty/qtty-core/src/feature_serde.rs +203 -0
  57. package/qtty-js/qtty/qtty-core/src/feature_tiberius.rs +28 -0
  58. package/qtty-js/qtty/qtty-core/src/lib.rs +744 -0
  59. package/qtty-js/qtty/qtty-core/src/macros.rs +93 -0
  60. package/qtty-js/qtty/qtty-core/src/quantity.rs +810 -0
  61. package/qtty-js/qtty/qtty-core/src/scalar.rs +1742 -0
  62. package/qtty-js/qtty/qtty-core/src/unit.rs +332 -0
  63. package/qtty-js/qtty/qtty-core/src/units/angular.rs +1228 -0
  64. package/qtty-js/qtty/qtty-core/src/units/area.rs +243 -0
  65. package/qtty-js/qtty/qtty-core/src/units/frequency.rs +179 -0
  66. package/qtty-js/qtty/qtty-core/src/units/length.rs +1270 -0
  67. package/qtty-js/qtty/qtty-core/src/units/mass.rs +488 -0
  68. package/qtty-js/qtty/qtty-core/src/units/mod.rs +26 -0
  69. package/qtty-js/qtty/qtty-core/src/units/power.rs +324 -0
  70. package/qtty-js/qtty/qtty-core/src/units/time.rs +667 -0
  71. package/qtty-js/qtty/qtty-core/src/units/unitless.rs +212 -0
  72. package/qtty-js/qtty/qtty-core/src/units/velocity.rs +210 -0
  73. package/qtty-js/qtty/qtty-core/src/units/volume.rs +269 -0
  74. package/qtty-js/qtty/qtty-core/tests/core.rs +628 -0
  75. package/qtty-js/qtty/qtty-core/tests/diesel.rs +461 -0
  76. package/qtty-js/qtty/qtty-core/tests/integers.rs +632 -0
  77. package/qtty-js/qtty/qtty-core/tests/no_cross_unit_ops.rs +35 -0
  78. package/qtty-js/qtty/qtty-core/tests/pyo3.rs +334 -0
  79. package/qtty-js/qtty/qtty-core/tests/quantity_f32.rs +276 -0
  80. package/qtty-js/qtty/qtty-core/tests/scalar_decimal.rs +258 -0
  81. package/qtty-js/qtty/qtty-core/tests/scalar_f32.rs +286 -0
  82. package/qtty-js/qtty/qtty-core/tests/scalar_f64_real.rs +287 -0
  83. package/qtty-js/qtty/qtty-core/tests/scalar_rational.rs +260 -0
  84. package/qtty-js/qtty/qtty-core/tests/serde.rs +256 -0
  85. package/qtty-js/qtty/qtty-core/tests/tiberius.rs +208 -0
  86. package/qtty-js/qtty/qtty-derive/Cargo.toml +23 -0
  87. package/qtty-js/qtty/qtty-derive/README.md +8 -0
  88. package/qtty-js/qtty/qtty-derive/src/lib.rs +340 -0
  89. package/qtty-js/qtty/qtty-ffi/ARCHITECTURE.md +3 -0
  90. package/qtty-js/qtty/qtty-ffi/Cargo.toml +31 -0
  91. package/qtty-js/qtty/qtty-ffi/README.md +9 -0
  92. package/qtty-js/qtty/qtty-ffi/build.rs +326 -0
  93. package/qtty-js/qtty/qtty-ffi/cbindgen.toml +105 -0
  94. package/qtty-js/qtty/qtty-ffi/include/qtty_ffi.h +1126 -0
  95. package/qtty-js/qtty/qtty-ffi/src/ffi.rs +1251 -0
  96. package/qtty-js/qtty/qtty-ffi/src/ffi_serde.rs +294 -0
  97. package/qtty-js/qtty/qtty-ffi/src/helpers.rs +310 -0
  98. package/qtty-js/qtty/qtty-ffi/src/lib.rs +229 -0
  99. package/qtty-js/qtty/qtty-ffi/src/macros.rs +121 -0
  100. package/qtty-js/qtty/qtty-ffi/src/registry.rs +274 -0
  101. package/qtty-js/qtty/qtty-ffi/src/types.rs +620 -0
  102. package/qtty-js/qtty/qtty-ffi/tests/integration_tests.rs +842 -0
  103. package/qtty-js/qtty/qtty-ffi/units.csv +156 -0
  104. package/qtty-js/qtty/qtty-ffi/units.csv.md +3 -0
  105. package/qtty-js/qtty-node/.prettierignore +6 -0
  106. package/qtty-js/qtty-node/.prettierrc.json +6 -0
  107. package/qtty-js/qtty-node/README.md +250 -0
  108. package/qtty-js/qtty-node/c8.config.json +11 -0
  109. package/qtty-js/qtty-node/eslint.config.js +31 -0
  110. package/qtty-js/qtty-node/examples/arithmetic.mjs +64 -0
  111. package/qtty-js/qtty-node/examples/astronomy.mjs +90 -0
  112. package/qtty-js/qtty-node/examples/quickstart.mjs +36 -0
  113. package/qtty-js/qtty-node/examples/serialization.mjs +125 -0
  114. package/qtty-js/qtty-node/examples/unit_factories.mjs +74 -0
  115. package/qtty-js/qtty-node/index.d.ts +219 -0
  116. package/qtty-js/qtty-node/index.js +323 -0
  117. package/qtty-js/qtty-node/lib/DerivedQuantity.js +122 -0
  118. package/qtty-js/qtty-node/lib/Quantity.js +151 -0
  119. package/qtty-js/qtty-node/lib/backend.js +25 -0
  120. package/qtty-js/qtty-node/native.cjs +306 -0
  121. package/qtty-js/qtty-node/package-lock.json +3223 -0
  122. package/qtty-js/qtty-node/package.json +70 -0
  123. package/qtty-js/qtty-node/units.d.ts +299 -0
  124. package/qtty-js/qtty-node/units.js +210 -0
  125. package/qtty-js/qtty-web/Cargo.lock +767 -0
  126. package/qtty-js/qtty-web/Cargo.toml +21 -0
  127. package/qtty-js/qtty-web/index.d.ts +140 -0
  128. package/qtty-js/qtty-web/index.js +20 -0
  129. package/qtty-js/qtty-web/lib/DerivedQuantity.js +58 -0
  130. package/qtty-js/qtty-web/lib/Quantity.js +75 -0
  131. package/qtty-js/qtty-web/lib/backend.js +80 -0
  132. package/qtty-js/qtty-web/package.json +45 -0
  133. package/qtty-js/qtty-web/src/lib.rs +111 -0
  134. package/qtty-js/scripts/ci.sh +73 -0
  135. package/scripts/ci.sh +123 -0
  136. package/siderust-core/Cargo.lock +787 -0
  137. package/siderust-core/Cargo.toml +18 -0
  138. package/siderust-core/DEDUPLICATION.md +124 -0
  139. package/siderust-core/src/body.rs +120 -0
  140. package/siderust-core/src/events.rs +184 -0
  141. package/siderust-core/src/lib.rs +20 -0
  142. package/siderust-core/src/observer.rs +55 -0
  143. package/siderust-core/src/position.rs +213 -0
  144. package/siderust-node/.prettierignore +7 -0
  145. package/siderust-node/.prettierrc.json +6 -0
  146. package/siderust-node/Cargo.lock +906 -0
  147. package/siderust-node/Cargo.toml +29 -0
  148. package/siderust-node/README.md +109 -0
  149. package/siderust-node/__test__/index.test.mjs +248 -0
  150. package/siderust-node/build.rs +5 -0
  151. package/siderust-node/c8.config.json +3 -0
  152. package/siderust-node/eslint.config.js +31 -0
  153. package/siderust-node/examples/01_basic_coordinates.mjs +24 -0
  154. package/siderust-node/examples/02_coordinate_transformations.mjs +25 -0
  155. package/siderust-node/examples/03_all_frames_conversions.mjs +26 -0
  156. package/siderust-node/examples/04_all_center_conversions.mjs +24 -0
  157. package/siderust-node/examples/05_target_tracking.mjs +22 -0
  158. package/siderust-node/examples/06_night_events.mjs +18 -0
  159. package/siderust-node/examples/07_moon_properties.mjs +21 -0
  160. package/siderust-node/examples/08_solar_system.mjs +19 -0
  161. package/siderust-node/examples/09_star_observability.mjs +22 -0
  162. package/siderust-node/examples/10_time_periods.mjs +9 -0
  163. package/siderust-node/examples/11_serialization.mjs +31 -0
  164. package/siderust-node/examples/12_runtime_ephemeris.mjs +27 -0
  165. package/siderust-node/examples/13_coordinate_operations.mjs +20 -0
  166. package/siderust-node/index.d.ts +623 -0
  167. package/siderust-node/index.js +79 -0
  168. package/siderust-node/lib/Observer.js +112 -0
  169. package/siderust-node/lib/Star.js +118 -0
  170. package/siderust-node/lib/backend.js +63 -0
  171. package/siderust-node/lib/wrappers.js +566 -0
  172. package/siderust-node/main.js +20 -0
  173. package/siderust-node/native.cjs +360 -0
  174. package/siderust-node/package-lock.json +3261 -0
  175. package/siderust-node/package.json +71 -0
  176. package/siderust-node/src/body.rs +74 -0
  177. package/siderust-node/src/coordinates.rs +372 -0
  178. package/siderust-node/src/ephemeris.rs +462 -0
  179. package/siderust-node/src/events.rs +577 -0
  180. package/siderust-node/src/lib.rs +43 -0
  181. package/siderust-node/src/observer.rs +132 -0
  182. package/siderust-node/src/phase.rs +218 -0
  183. package/siderust-node/src/position.rs +292 -0
  184. package/siderust-node/src/star.rs +200 -0
  185. package/siderust-web/Cargo.lock +855 -0
  186. package/siderust-web/Cargo.toml +34 -0
  187. package/siderust-web/README.md +100 -0
  188. package/siderust-web/__test__/index.test.mjs +118 -0
  189. package/siderust-web/examples/github-pages/README.md +31 -0
  190. package/siderust-web/examples/github-pages/index.html +135 -0
  191. package/siderust-web/index.d.ts +311 -0
  192. package/siderust-web/index.js +66 -0
  193. package/siderust-web/lib/Observer.js +103 -0
  194. package/siderust-web/lib/Star.js +116 -0
  195. package/siderust-web/lib/backend.js +400 -0
  196. package/siderust-web/lib/wrappers.js +512 -0
  197. package/siderust-web/package.json +55 -0
  198. package/siderust-web/src/body.rs +69 -0
  199. package/siderust-web/src/coordinates.rs +302 -0
  200. package/siderust-web/src/ephemeris.rs +456 -0
  201. package/siderust-web/src/events.rs +520 -0
  202. package/siderust-web/src/lib.rs +51 -0
  203. package/siderust-web/src/observer.rs +117 -0
  204. package/siderust-web/src/phase.rs +190 -0
  205. package/siderust-web/src/position.rs +291 -0
  206. package/siderust-web/src/star.rs +178 -0
  207. package/tempoch-js/.github/workflows/ci.yml +142 -0
  208. package/tempoch-js/.gitmodules +3 -0
  209. package/tempoch-js/CHANGELOG.md +25 -0
  210. package/tempoch-js/LICENSE +661 -0
  211. package/tempoch-js/README.md +126 -0
  212. package/tempoch-js/package.json +20 -0
  213. package/tempoch-js/scripts/ci.sh +73 -0
  214. package/tempoch-js/tempoch/.github/workflows/ci.yml +113 -0
  215. package/tempoch-js/tempoch/CHANGELOG.md +82 -0
  216. package/tempoch-js/tempoch/Cargo.lock +947 -0
  217. package/tempoch-js/tempoch/Cargo.toml +3 -0
  218. package/tempoch-js/tempoch/LICENSE +661 -0
  219. package/tempoch-js/tempoch/README.md +76 -0
  220. package/tempoch-js/tempoch/tempoch/Cargo.toml +27 -0
  221. package/tempoch-js/tempoch/tempoch/examples/periods.rs +45 -0
  222. package/tempoch-js/tempoch/tempoch/examples/quickstart.rs +13 -0
  223. package/tempoch-js/tempoch/tempoch/src/lib.rs +49 -0
  224. package/tempoch-js/tempoch/tempoch/tests/integration.rs +57 -0
  225. package/tempoch-js/tempoch/tempoch-core/Cargo.toml +24 -0
  226. package/tempoch-js/tempoch/tempoch-core/src/delta_t.rs +345 -0
  227. package/tempoch-js/tempoch/tempoch-core/src/instant.rs +811 -0
  228. package/tempoch-js/tempoch/tempoch-core/src/julian_date_ext.rs +142 -0
  229. package/tempoch-js/tempoch/tempoch-core/src/lib.rs +81 -0
  230. package/tempoch-js/tempoch/tempoch-core/src/period.rs +1168 -0
  231. package/tempoch-js/tempoch/tempoch-core/src/scales.rs +779 -0
  232. package/tempoch-js/tempoch/tempoch-ffi/Cargo.lock +889 -0
  233. package/tempoch-js/tempoch/tempoch-ffi/Cargo.toml +26 -0
  234. package/tempoch-js/tempoch/tempoch-ffi/build.rs +24 -0
  235. package/tempoch-js/tempoch/tempoch-ffi/cbindgen.toml +30 -0
  236. package/tempoch-js/tempoch/tempoch-ffi/src/error.rs +19 -0
  237. package/tempoch-js/tempoch/tempoch-ffi/src/lib.rs +82 -0
  238. package/tempoch-js/tempoch/tempoch-ffi/src/period.rs +101 -0
  239. package/tempoch-js/tempoch/tempoch-ffi/src/time.rs +711 -0
  240. package/tempoch-js/tempoch/tempoch-ffi/tests/ffi.rs +265 -0
  241. package/tempoch-js/tempoch-node/.prettierignore +6 -0
  242. package/tempoch-js/tempoch-node/.prettierrc.json +6 -0
  243. package/tempoch-js/tempoch-node/Cargo.lock +496 -0
  244. package/tempoch-js/tempoch-node/Cargo.toml +29 -0
  245. package/tempoch-js/tempoch-node/README.md +265 -0
  246. package/tempoch-js/tempoch-node/__test__/index.test.mjs +598 -0
  247. package/tempoch-js/tempoch-node/build.rs +5 -0
  248. package/tempoch-js/tempoch-node/c8.config.json +3 -0
  249. package/tempoch-js/tempoch-node/eslint.config.js +31 -0
  250. package/tempoch-js/tempoch-node/examples/periods.mjs +79 -0
  251. package/tempoch-js/tempoch-node/examples/quickstart.mjs +71 -0
  252. package/tempoch-js/tempoch-node/examples/timescales.mjs +92 -0
  253. package/tempoch-js/tempoch-node/index.d.ts +280 -0
  254. package/tempoch-js/tempoch-node/index.js +32 -0
  255. package/tempoch-js/tempoch-node/lib/JulianDate.js +176 -0
  256. package/tempoch-js/tempoch-node/lib/ModifiedJulianDate.js +156 -0
  257. package/tempoch-js/tempoch-node/lib/Period.js +133 -0
  258. package/tempoch-js/tempoch-node/lib/backend.js +38 -0
  259. package/tempoch-js/tempoch-node/lib/qttyCompat.js +92 -0
  260. package/tempoch-js/tempoch-node/native.cjs +317 -0
  261. package/tempoch-js/tempoch-node/package-lock.json +3223 -0
  262. package/tempoch-js/tempoch-node/package.json +56 -0
  263. package/tempoch-js/tempoch-node/src/lib.rs +573 -0
  264. package/tempoch-js/tempoch-web/Cargo.toml +23 -0
  265. package/tempoch-js/tempoch-web/index.d.ts +95 -0
  266. package/tempoch-js/tempoch-web/index.js +27 -0
  267. package/tempoch-js/tempoch-web/lib/JulianDate.js +170 -0
  268. package/tempoch-js/tempoch-web/lib/ModifiedJulianDate.js +145 -0
  269. package/tempoch-js/tempoch-web/lib/Period.js +121 -0
  270. package/tempoch-js/tempoch-web/lib/backend.js +118 -0
  271. package/tempoch-js/tempoch-web/package.json +46 -0
  272. 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
+ }