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,41 @@
1
+ [package]
2
+ name = "qtty-core"
3
+ description = "Core types for zero-cost strongly-typed physical quantities."
4
+ version.workspace = true
5
+ edition.workspace = true
6
+ authors.workspace = true
7
+ license.workspace = true
8
+ repository.workspace = true
9
+ keywords.workspace = true
10
+ categories.workspace = true
11
+ readme = "README.md"
12
+ documentation = "https://docs.rs/qtty-core"
13
+
14
+ [features]
15
+ default = ["std", "cross-unit-ops"]
16
+ std = []
17
+ cross-unit-ops = []
18
+ serde = ["dep:serde"]
19
+
20
+ # Optional support
21
+ scalar-decimal = ["rust_decimal"]
22
+ scalar-rational = ["num-rational"]
23
+ pyo3 = ["dep:pyo3"]
24
+ tiberius = ["dep:tiberius"]
25
+ diesel = ["dep:diesel"]
26
+
27
+ [dependencies]
28
+ rust_decimal = { version = "1.36", optional = true, features = ["maths"] }
29
+ num-rational = { version = "0.4", optional = true, default-features = false }
30
+ serde = { version = "1.0", default-features = false, features = ["derive", "alloc"], optional = true }
31
+ pyo3 = { version = "0.28.2", optional = true, features = ["auto-initialize"] }
32
+ tiberius = { version = "0.12", default-features = false, optional = true }
33
+ diesel = { version = "2.1", default-features = false, optional = true }
34
+ libm = "0.2"
35
+ typenum = "1.17"
36
+ qtty-derive = { version = "0.4.0", path = "../qtty-derive" }
37
+
38
+ [dev-dependencies]
39
+ approx = "0.5"
40
+ proptest = "1.4"
41
+ serde_json = "1.0"
@@ -0,0 +1,8 @@
1
+ # qtty-core crate
2
+
3
+ Centralized documentation lives under the repository `doc/` tree.
4
+
5
+ - Workspace overview: [`../../doc/users/rust-workspace.md`](../../doc/users/rust-workspace.md)
6
+ - Repository layout: [`../../doc/architecture/repository-layout.md`](../../doc/architecture/repository-layout.md)
7
+
8
+ This README remains as the crate metadata entrypoint.
@@ -0,0 +1,145 @@
1
+ //! Example showing how to use `qtty_core::Quantity` types with Diesel ORM.
2
+ //!
3
+ //! This example demonstrates:
4
+ //! - Using `Quantity<U>` types (e.g., `Degrees`, `Meters`) directly in Diesel models
5
+ //! - Automatic serialization/deserialization to/from SQL DOUBLE PRECISION columns
6
+ //! - Type-safe queries with strongly-typed physical quantities
7
+ //!
8
+ //! # Running this example
9
+ //!
10
+ //! ```bash
11
+ //! cargo run -p qtty-core --example diesel_integration --features diesel
12
+ //! ```
13
+ //!
14
+ //! Note: This is a conceptual example showing the API usage. For a real database integration,
15
+ //! you would need to set up an actual database connection and schema.
16
+
17
+ #[cfg(feature = "diesel")]
18
+ fn main() {
19
+ use qtty_core::angular::Degrees;
20
+ use qtty_core::length::Meters;
21
+
22
+ println!("=== Diesel ORM Integration Example ===\n");
23
+
24
+ // Example 1: Conceptual Diesel model using Quantity types
25
+ println!("1. Diesel Model with Quantity Types:\n");
26
+
27
+ #[derive(Debug)]
28
+ #[allow(dead_code)]
29
+ struct Observatory {
30
+ id: i32,
31
+ name: String,
32
+ latitude: Degrees, // Instead of f64!
33
+ longitude: Degrees, // Type-safe angles
34
+ elevation: Meters, // Type-safe length
35
+ }
36
+
37
+ let obs = Observatory {
38
+ id: 1,
39
+ name: "Mauna Kea".to_string(),
40
+ latitude: Degrees::new(19.8207),
41
+ longitude: Degrees::new(-155.4681),
42
+ elevation: Meters::new(4207.0),
43
+ };
44
+
45
+ println!("Observatory: {}", obs.name);
46
+ println!(
47
+ " Location: {:.4}° N, {:.4}° W",
48
+ obs.latitude.value(),
49
+ obs.longitude.value().abs()
50
+ );
51
+ println!(" Elevation: {:.0} m", obs.elevation.value());
52
+
53
+ // Example 2: Type conversion
54
+ println!("\n2. Type Conversions:");
55
+ use qtty_core::angular::Radians;
56
+ use qtty_core::length::Kilometers;
57
+
58
+ let lat_rad: Radians = obs.latitude.to();
59
+ let elev_km: Kilometers = obs.elevation.to();
60
+
61
+ println!(" Latitude: {:.6} rad", lat_rad.value());
62
+ println!(" Elevation: {:.3} km", elev_km.value());
63
+
64
+ // Example 3: Conceptual schema definition
65
+ println!("\n3. Conceptual Diesel Schema:");
66
+ println!(
67
+ r#"
68
+ // In your Diesel schema.rs:
69
+ table! {{
70
+ observatories (id) {{
71
+ id -> Int4,
72
+ name -> Text,
73
+ latitude -> Float8, // Maps to qtty::Degrees
74
+ longitude -> Float8, // Maps to qtty::Degrees
75
+ elevation -> Float8, // Maps to qtty::Meters
76
+ }}
77
+ }}
78
+
79
+ // In your models.rs:
80
+ use qtty::{{Degrees, Meters}};
81
+
82
+ #[derive(Queryable, Selectable)]
83
+ #[diesel(table_name = observatories)]
84
+ pub struct Observatory {{
85
+ pub id: i32,
86
+ pub name: String,
87
+ pub latitude: Degrees, // Direct use of Quantity types!
88
+ pub longitude: Degrees,
89
+ pub elevation: Meters,
90
+ }}
91
+
92
+ #[derive(Insertable)]
93
+ #[diesel(table_name = observatories)]
94
+ pub struct NewObservatory {{
95
+ pub name: String,
96
+ pub latitude: Degrees,
97
+ pub longitude: Degrees,
98
+ pub elevation: Meters,
99
+ }}
100
+ "#
101
+ );
102
+
103
+ // Example 4: Optional fields
104
+ println!("\n4. Optional Quantity Fields:");
105
+
106
+ #[derive(Debug)]
107
+ #[allow(dead_code)]
108
+ struct Telescope {
109
+ id: i32,
110
+ name: String,
111
+ min_altitude: Option<Degrees>, // Optional constraint
112
+ max_altitude: Option<Degrees>,
113
+ }
114
+
115
+ let telescope = Telescope {
116
+ id: 1,
117
+ name: "Keck I".to_string(),
118
+ min_altitude: Some(Degrees::new(15.0)),
119
+ max_altitude: Some(Degrees::new(85.0)),
120
+ };
121
+
122
+ println!("Telescope: {}", telescope.name);
123
+ if let Some(min_alt) = telescope.min_altitude {
124
+ println!(" Min altitude: {}°", min_alt.value());
125
+ }
126
+ if let Some(max_alt) = telescope.max_altitude {
127
+ println!(" Max altitude: {}°", max_alt.value());
128
+ }
129
+
130
+ // Example 5: Benefits summary
131
+ println!("\n5. Benefits of Using Quantity Types:");
132
+ println!(" ✓ Compile-time unit checking");
133
+ println!(" ✓ No manual unit conversions in database layer");
134
+ println!(" ✓ Self-documenting code (field type shows units)");
135
+ println!(" ✓ Automatic serialization/deserialization");
136
+ println!(" ✓ Zero runtime overhead (newtype pattern)");
137
+
138
+ println!("\n=== Example Complete ===");
139
+ }
140
+
141
+ #[cfg(not(feature = "diesel"))]
142
+ fn main() {
143
+ println!("This example requires the 'diesel' feature to be enabled.");
144
+ println!("Run with: cargo run -p qtty-core --example diesel_integration --features diesel");
145
+ }
@@ -0,0 +1,215 @@
1
+ //! Example showing how to use `qtty_core::Quantity` types with `serde` and (optionally) Tiberius DB.
2
+ //!
3
+ //! This demonstrates:
4
+ //! - Default serde behavior: `Quantity<U>` serializes as a raw `f64` by default
5
+ //! - `qtty_core::serde_with_unit`: serialize with unit information using `#[serde(with = "...")]`
6
+ //! - `tiberius` feature: bind/extract `Quantity<U>` directly (conceptual example)
7
+ //!
8
+ //! Run with:
9
+ //! - `cargo run -p qtty-core --example quantity_db_serde --features serde`
10
+ //! - `cargo run -p qtty-core --example quantity_db_serde --features serde,tiberius`
11
+
12
+ #[cfg(feature = "serde")]
13
+ use qtty_core::angular::Degrees;
14
+ #[cfg(feature = "serde")]
15
+ use qtty_core::time::Seconds;
16
+ #[cfg(feature = "serde")]
17
+ use serde::{Deserialize, Serialize};
18
+
19
+ // ─────────────────────────────────────────────────────────────────────────────
20
+ // Example 1: Serde with default f64 serialization
21
+ // ─────────────────────────────────────────────────────────────────────────────
22
+
23
+ #[cfg(feature = "serde")]
24
+ #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
25
+ struct SchedulingConstraints {
26
+ /// Minimum altitude in degrees (serializes as raw f64 by default)
27
+ pub min_altitude: Degrees,
28
+
29
+ /// Maximum altitude in degrees (serializes as raw f64 by default)
30
+ pub max_altitude: Degrees,
31
+
32
+ /// Minimum azimuth in degrees (serializes as raw f64 by default)
33
+ pub min_azimuth: Degrees,
34
+
35
+ /// Maximum azimuth in degrees (serializes as raw f64 by default)
36
+ pub max_azimuth: Degrees,
37
+
38
+ /// Minimum observation time in seconds (serializes as raw f64 by default)
39
+ pub min_observation_time: Seconds,
40
+ }
41
+
42
+ #[cfg(feature = "serde")]
43
+ fn example_serde() {
44
+ println!("=== Default Serde Example (compact f64) ===\n");
45
+
46
+ // Create constraints with typed quantities
47
+ let constraints = SchedulingConstraints {
48
+ min_altitude: Degrees::new(30.0),
49
+ max_altitude: Degrees::new(90.0),
50
+ min_azimuth: Degrees::new(0.0),
51
+ max_azimuth: Degrees::new(360.0),
52
+ min_observation_time: Seconds::new(1200.0),
53
+ };
54
+
55
+ // Serialize to JSON (as raw f64 values - compact!)
56
+ let json = serde_json::to_string_pretty(&constraints).unwrap();
57
+ println!("Serialized JSON:\n{}\n", json);
58
+
59
+ // Deserialize back
60
+ let parsed: SchedulingConstraints = serde_json::from_str(&json).unwrap();
61
+ println!("Deserialized successfully!");
62
+ assert_eq!(
63
+ constraints.min_altitude.value(),
64
+ parsed.min_altitude.value()
65
+ );
66
+ println!("✓ Round-trip successful\n");
67
+ }
68
+
69
+ // ─────────────────────────────────────────────────────────────────────────────
70
+ // Example 1b: Serde with unit information (for self-documenting APIs)
71
+ // ─────────────────────────────────────────────────────────────────────────────
72
+
73
+ #[cfg(feature = "serde")]
74
+ #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
75
+ struct ConfigWithUnits {
76
+ /// Maximum speed with unit information preserved
77
+ #[serde(with = "qtty_core::serde_with_unit")]
78
+ pub max_altitude: Degrees,
79
+ }
80
+
81
+ #[cfg(feature = "serde")]
82
+ fn example_serde_with_unit() {
83
+ println!("=== Serde with Unit Information (self-documenting) ===\n");
84
+
85
+ let config = ConfigWithUnits {
86
+ max_altitude: Degrees::new(90.0),
87
+ };
88
+
89
+ // Serialize to JSON with unit information
90
+ let json = serde_json::to_string_pretty(&config).unwrap();
91
+ println!("Serialized JSON:\n{}\n", json);
92
+ println!("✓ Unit symbol preserved in output for clarity!\n");
93
+
94
+ // Deserialize back
95
+ let parsed: ConfigWithUnits = serde_json::from_str(&json).unwrap();
96
+ assert_eq!(config.max_altitude.value(), parsed.max_altitude.value());
97
+ println!("✓ Round-trip successful\n");
98
+ }
99
+
100
+ // ─────────────────────────────────────────────────────────────────────────────
101
+ // Example 2: Direct DB usage (conceptual - requires actual DB connection)
102
+ // ─────────────────────────────────────────────────────────────────────────────
103
+
104
+ #[cfg(all(feature = "serde", feature = "tiberius"))]
105
+ fn example_tiberius_concept() {
106
+ println!("=== Tiberius DB Example (conceptual) ===\n");
107
+
108
+ // In real code, you would:
109
+ // 1. Bind Quantity types directly to queries:
110
+ println!("// Binding Quantity to query:");
111
+ println!("let min_alt = Degrees::new(30.0);");
112
+ println!("query.bind(min_alt); // Works directly!\n");
113
+
114
+ // 2. Extract Quantity types from result rows:
115
+ println!("// Extracting Quantity from DB row:");
116
+ println!("let altitude: Degrees = row.try_get(\"altitude\")?.unwrap();");
117
+ println!("// No manual f64 → Degrees conversion needed!\n");
118
+ }
119
+
120
+ // ─────────────────────────────────────────────────────────────────────────────
121
+ // Example 3: Migration comparison
122
+ // ─────────────────────────────────────────────────────────────────────────────
123
+
124
+ #[cfg(feature = "serde")]
125
+ fn example_migration_comparison() {
126
+ println!("=== Migration Comparison ===\n");
127
+
128
+ // Old approach: store as f64, convert on access
129
+ struct OldConstraints {
130
+ min_alt: f64,
131
+ max_alt: f64,
132
+ }
133
+
134
+ impl OldConstraints {
135
+ fn min_alt(&self) -> Degrees {
136
+ Degrees::new(self.min_alt)
137
+ }
138
+
139
+ fn max_alt(&self) -> Degrees {
140
+ Degrees::new(self.max_alt)
141
+ }
142
+ }
143
+
144
+ let old = OldConstraints {
145
+ min_alt: 30.0,
146
+ max_alt: 90.0,
147
+ };
148
+
149
+ println!("Old approach:");
150
+ println!(" - Store: f64");
151
+ println!(" - Access: .min_alt() -> Degrees");
152
+ println!(
153
+ " - Values: {}..{} degrees\n",
154
+ old.min_alt().value(),
155
+ old.max_alt().value()
156
+ );
157
+
158
+ // New approach: store as Quantity directly
159
+ #[derive(Serialize, Deserialize)]
160
+ struct NewConstraints {
161
+ min_alt: Degrees,
162
+ max_alt: Degrees,
163
+ }
164
+
165
+ let new = NewConstraints {
166
+ min_alt: Degrees::new(30.0),
167
+ max_alt: Degrees::new(90.0),
168
+ };
169
+
170
+ println!("New approach:");
171
+ println!(" - Store: Degrees (typed!)");
172
+ println!(" - Access: .min_alt (direct)");
173
+ println!(
174
+ " - Values: {}..{} degrees",
175
+ new.min_alt.value(),
176
+ new.max_alt.value()
177
+ );
178
+ println!(" ✓ No conversion methods needed!");
179
+ println!(" ✓ Type safety at compile time!\n");
180
+ }
181
+
182
+ // ─────────────────────────────────────────────────────────────────────────────
183
+ // Main
184
+ // ─────────────────────────────────────────────────────────────────────────────
185
+
186
+ #[cfg(feature = "serde")]
187
+ fn run() {
188
+ println!("\n╔═══════════════════════════════════════════════════════╗");
189
+ println!("║ qtty-core: Serde and DB Integration Examples ║");
190
+ println!("╚═══════════════════════════════════════════════════════╝\n");
191
+
192
+ example_serde();
193
+ example_serde_with_unit();
194
+
195
+ #[cfg(feature = "tiberius")]
196
+ example_tiberius_concept();
197
+
198
+ example_migration_comparison();
199
+
200
+ println!("═══════════════════════════════════════════════════════");
201
+ println!("All examples completed successfully!");
202
+ println!("═══════════════════════════════════════════════════════\n");
203
+ }
204
+
205
+ fn main() {
206
+ #[cfg(feature = "serde")]
207
+ {
208
+ run();
209
+ }
210
+
211
+ #[cfg(not(feature = "serde"))]
212
+ {
213
+ eprintln!("This example requires `--features serde`.");
214
+ }
215
+ }
@@ -0,0 +1,249 @@
1
+ //! Dimension types and traits.
2
+ //!
3
+ //! Dimensions are modelled as a single generic struct [`Dim`] parameterised by eight
4
+ //! [`typenum`] signed integers representing the exponents of the fundamental physical
5
+ //! dimensions. Multiplying two dimensions adds exponents; dividing subtracts them.
6
+ //! Because typenum arithmetic resolves at compile time, compound types like `Length * Length`
7
+ //! automatically become `Area` (exponent 2), and `Area / Length` collapses back to `Length`
8
+ //! (exponent 1) — all verified by the type checker.
9
+ //!
10
+ //! # Layout
11
+ //!
12
+ //! The eight exponent slots are:
13
+ //!
14
+ //! | Position | Base quantity | SI symbol |
15
+ //! |----------|--------------------------|-----------|
16
+ //! | `L` | Length | m |
17
+ //! | `T` | Time | s |
18
+ //! | `M` | Mass | kg |
19
+ //! | `Th` | Thermodynamic temperature| K |
20
+ //! | `I` | Electric current | A |
21
+ //! | `N` | Amount of substance | mol |
22
+ //! | `J` | Luminous intensity | cd |
23
+ //! | `A` | Plane angle (auxiliary) | rad/deg |
24
+
25
+ use core::marker::PhantomData;
26
+ use core::ops::{Add, Sub};
27
+ use typenum::Integer;
28
+
29
+ /// Marker trait for **dimensions**.
30
+ ///
31
+ /// Implemented automatically for every [`Dim<L,T,M,Th,I,N,J,A>`] whose type
32
+ /// parameters satisfy the required bounds.
33
+ pub trait Dimension: 'static {}
34
+
35
+ // ─────────────────────────────────────────────────────────────────────────────
36
+ // Core dimension struct
37
+ // ─────────────────────────────────────────────────────────────────────────────
38
+
39
+ /// A physical dimension encoded as eight typenum integer exponents.
40
+ ///
41
+ /// This is a zero-sized type: no runtime cost.
42
+ #[derive(Clone, Copy, Debug, PartialEq, Eq)]
43
+ pub struct Dim<L, T, M, Th, I, N, J, A>(
44
+ PhantomData<L>,
45
+ PhantomData<T>,
46
+ PhantomData<M>,
47
+ PhantomData<Th>,
48
+ PhantomData<I>,
49
+ PhantomData<N>,
50
+ PhantomData<J>,
51
+ PhantomData<A>,
52
+ )
53
+ where
54
+ L: Integer,
55
+ T: Integer,
56
+ M: Integer,
57
+ Th: Integer,
58
+ I: Integer,
59
+ N: Integer,
60
+ J: Integer,
61
+ A: Integer;
62
+
63
+ impl<L, T, M, Th, I, N, J, A> Dimension for Dim<L, T, M, Th, I, N, J, A>
64
+ where
65
+ L: Integer + 'static,
66
+ T: Integer + 'static,
67
+ M: Integer + 'static,
68
+ Th: Integer + 'static,
69
+ I: Integer + 'static,
70
+ N: Integer + 'static,
71
+ J: Integer + 'static,
72
+ A: Integer + 'static,
73
+ {
74
+ }
75
+
76
+ // ─────────────────────────────────────────────────────────────────────────────
77
+ // Dimension multiplication (adds exponents)
78
+ // ─────────────────────────────────────────────────────────────────────────────
79
+
80
+ /// Trait for multiplying two dimensions (adds exponents).
81
+ pub trait DimMul<Rhs: Dimension>: Dimension {
82
+ /// The resulting dimension.
83
+ type Output: Dimension;
84
+ }
85
+
86
+ impl<L1, T1, M1, Th1, I1, N1, J1, A1, L2, T2, M2, Th2, I2, N2, J2, A2>
87
+ DimMul<Dim<L2, T2, M2, Th2, I2, N2, J2, A2>> for Dim<L1, T1, M1, Th1, I1, N1, J1, A1>
88
+ where
89
+ L1: Integer + Add<L2> + 'static,
90
+ T1: Integer + Add<T2> + 'static,
91
+ M1: Integer + Add<M2> + 'static,
92
+ Th1: Integer + Add<Th2> + 'static,
93
+ I1: Integer + Add<I2> + 'static,
94
+ N1: Integer + Add<N2> + 'static,
95
+ J1: Integer + Add<J2> + 'static,
96
+ A1: Integer + Add<A2> + 'static,
97
+ L2: Integer + 'static,
98
+ T2: Integer + 'static,
99
+ M2: Integer + 'static,
100
+ Th2: Integer + 'static,
101
+ I2: Integer + 'static,
102
+ N2: Integer + 'static,
103
+ J2: Integer + 'static,
104
+ A2: Integer + 'static,
105
+ <L1 as Add<L2>>::Output: Integer + 'static,
106
+ <T1 as Add<T2>>::Output: Integer + 'static,
107
+ <M1 as Add<M2>>::Output: Integer + 'static,
108
+ <Th1 as Add<Th2>>::Output: Integer + 'static,
109
+ <I1 as Add<I2>>::Output: Integer + 'static,
110
+ <N1 as Add<N2>>::Output: Integer + 'static,
111
+ <J1 as Add<J2>>::Output: Integer + 'static,
112
+ <A1 as Add<A2>>::Output: Integer + 'static,
113
+ {
114
+ type Output = Dim<
115
+ <L1 as Add<L2>>::Output,
116
+ <T1 as Add<T2>>::Output,
117
+ <M1 as Add<M2>>::Output,
118
+ <Th1 as Add<Th2>>::Output,
119
+ <I1 as Add<I2>>::Output,
120
+ <N1 as Add<N2>>::Output,
121
+ <J1 as Add<J2>>::Output,
122
+ <A1 as Add<A2>>::Output,
123
+ >;
124
+ }
125
+
126
+ // ─────────────────────────────────────────────────────────────────────────────
127
+ // Dimension division (subtracts exponents)
128
+ // ─────────────────────────────────────────────────────────────────────────────
129
+
130
+ /// Trait for dividing two dimensions (subtracts exponents).
131
+ pub trait DimDiv<Rhs: Dimension>: Dimension {
132
+ /// The resulting dimension.
133
+ type Output: Dimension;
134
+ }
135
+
136
+ impl<L1, T1, M1, Th1, I1, N1, J1, A1, L2, T2, M2, Th2, I2, N2, J2, A2>
137
+ DimDiv<Dim<L2, T2, M2, Th2, I2, N2, J2, A2>> for Dim<L1, T1, M1, Th1, I1, N1, J1, A1>
138
+ where
139
+ L1: Integer + Sub<L2> + 'static,
140
+ T1: Integer + Sub<T2> + 'static,
141
+ M1: Integer + Sub<M2> + 'static,
142
+ Th1: Integer + Sub<Th2> + 'static,
143
+ I1: Integer + Sub<I2> + 'static,
144
+ N1: Integer + Sub<N2> + 'static,
145
+ J1: Integer + Sub<J2> + 'static,
146
+ A1: Integer + Sub<A2> + 'static,
147
+ L2: Integer + 'static,
148
+ T2: Integer + 'static,
149
+ M2: Integer + 'static,
150
+ Th2: Integer + 'static,
151
+ I2: Integer + 'static,
152
+ N2: Integer + 'static,
153
+ J2: Integer + 'static,
154
+ A2: Integer + 'static,
155
+ <L1 as Sub<L2>>::Output: Integer + 'static,
156
+ <T1 as Sub<T2>>::Output: Integer + 'static,
157
+ <M1 as Sub<M2>>::Output: Integer + 'static,
158
+ <Th1 as Sub<Th2>>::Output: Integer + 'static,
159
+ <I1 as Sub<I2>>::Output: Integer + 'static,
160
+ <N1 as Sub<N2>>::Output: Integer + 'static,
161
+ <J1 as Sub<J2>>::Output: Integer + 'static,
162
+ <A1 as Sub<A2>>::Output: Integer + 'static,
163
+ {
164
+ type Output = Dim<
165
+ <L1 as Sub<L2>>::Output,
166
+ <T1 as Sub<T2>>::Output,
167
+ <M1 as Sub<M2>>::Output,
168
+ <Th1 as Sub<Th2>>::Output,
169
+ <I1 as Sub<I2>>::Output,
170
+ <N1 as Sub<N2>>::Output,
171
+ <J1 as Sub<J2>>::Output,
172
+ <A1 as Sub<A2>>::Output,
173
+ >;
174
+ }
175
+
176
+ // ─────────────────────────────────────────────────────────────────────────────
177
+ // Base dimension aliases
178
+ // ─────────────────────────────────────────────────────────────────────────────
179
+
180
+ use typenum::{N1, N2, N3, P1, P2, P3, Z0};
181
+
182
+ /// Dimensionless (all exponents zero).
183
+ pub type Dimensionless = Dim<Z0, Z0, Z0, Z0, Z0, Z0, Z0, Z0>;
184
+
185
+ /// Length (L¹).
186
+ pub type Length = Dim<P1, Z0, Z0, Z0, Z0, Z0, Z0, Z0>;
187
+
188
+ /// Time (T¹).
189
+ pub type Time = Dim<Z0, P1, Z0, Z0, Z0, Z0, Z0, Z0>;
190
+
191
+ /// Mass (M¹).
192
+ pub type Mass = Dim<Z0, Z0, P1, Z0, Z0, Z0, Z0, Z0>;
193
+
194
+ /// Thermodynamic temperature (Θ¹).
195
+ pub type Temperature = Dim<Z0, Z0, Z0, P1, Z0, Z0, Z0, Z0>;
196
+
197
+ /// Electric current (I¹).
198
+ pub type Current = Dim<Z0, Z0, Z0, Z0, P1, Z0, Z0, Z0>;
199
+
200
+ /// Amount of substance (N¹).
201
+ pub type AmountOfSubstance = Dim<Z0, Z0, Z0, Z0, Z0, P1, Z0, Z0>;
202
+
203
+ /// Luminous intensity (J¹).
204
+ pub type LuminousIntensity = Dim<Z0, Z0, Z0, Z0, Z0, Z0, P1, Z0>;
205
+
206
+ /// Plane angle (A¹) — treated as an independent dimension for type safety.
207
+ pub type Angular = Dim<Z0, Z0, Z0, Z0, Z0, Z0, Z0, P1>;
208
+
209
+ // ─────────────────────────────────────────────────────────────────────────────
210
+ // Derived dimension aliases
211
+ // ─────────────────────────────────────────────────────────────────────────────
212
+
213
+ /// Area (L²).
214
+ pub type Area = Dim<P2, Z0, Z0, Z0, Z0, Z0, Z0, Z0>;
215
+
216
+ /// Volume (L³).
217
+ pub type Volume = Dim<P3, Z0, Z0, Z0, Z0, Z0, Z0, Z0>;
218
+
219
+ /// Velocity (L¹ · T⁻¹).
220
+ pub type VelocityDim = Dim<P1, N1, Z0, Z0, Z0, Z0, Z0, Z0>;
221
+
222
+ /// Acceleration (L¹ · T⁻²).
223
+ pub type Acceleration = Dim<P1, N2, Z0, Z0, Z0, Z0, Z0, Z0>;
224
+
225
+ /// Force (M¹ · L¹ · T⁻²).
226
+ pub type Force = Dim<P1, N2, P1, Z0, Z0, Z0, Z0, Z0>;
227
+
228
+ /// Energy (M¹ · L² · T⁻²).
229
+ pub type Energy = Dim<P2, N2, P1, Z0, Z0, Z0, Z0, Z0>;
230
+
231
+ /// Power (M¹ · L² · T⁻³).
232
+ pub type Power = Dim<P2, N3, P1, Z0, Z0, Z0, Z0, Z0>;
233
+
234
+ /// Frequency — angular per time (A¹ · T⁻¹).
235
+ pub type FrequencyDim = Dim<Z0, N1, Z0, Z0, Z0, Z0, Z0, P1>;
236
+
237
+ // ─────────────────────────────────────────────────────────────────────────────
238
+ // Legacy compatibility alias
239
+ // ─────────────────────────────────────────────────────────────────────────────
240
+
241
+ /// Backward-compatible alias: `DivDim<N, D>` resolves to `<N as DimDiv<D>>::Output`.
242
+ ///
243
+ /// This preserves source compatibility for code that previously used `DivDim<Length, Time>`.
244
+ /// It now resolves to the same concrete type as the `Velocity` alias, since typenum
245
+ /// arithmetic produces the identical `Dim<P1, N1, …>`.
246
+ pub type DivDim<N, D> = <N as DimDiv<D>>::Output;
247
+
248
+ /// Backward-compatible alias: `MulDim<A, B>` resolves to `<A as DimMul<B>>::Output`.
249
+ pub type MulDim<A, B> = <A as DimMul<B>>::Output;