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,318 @@
1
+ //! Diesel ORM support for `Quantity` types (feature-gated).
2
+ //!
3
+ //! This module is enabled by the `diesel` feature. It provides serialization, deserialization,
4
+ //! and query support for `Quantity<U, S>` types when used with Diesel ORM.
5
+ //!
6
+ //! # Supported Scalar Types
7
+ //!
8
+ //! - `f64` - maps to SQL DOUBLE PRECISION
9
+ //! - `f32` - maps to SQL REAL (FLOAT)
10
+ //!
11
+ //! Note: Decimal and Rational scalar types are not supported for Diesel integration as they
12
+ //! don't have direct SQL type representations. Use f64 or f32 for database storage.
13
+ //!
14
+ //! # Supported Operations
15
+ //!
16
+ //! - **Serialization/Deserialization**: `Quantity<U, S>` maps to SQL DOUBLE PRECISION or REAL
17
+ //! - **Nullable columns**: `Option<Quantity<U, S>>` automatically supported
18
+ //! - **Query parameters**: Use in WHERE clauses and INSERT statements
19
+ //! - **Result loading**: Use in SELECT queries with `Queryable` structs
20
+ //! - **Backend-agnostic**: Works with PostgreSQL, SQLite, MySQL, and other Diesel backends
21
+ //!
22
+ //! # Examples
23
+ //!
24
+ //! ```rust,ignore
25
+ //! use qtty::Degrees;
26
+ //! use diesel::prelude::*;
27
+ //!
28
+ //! #[derive(Queryable, Selectable)]
29
+ //! #[diesel(table_name = observations)]
30
+ //! pub struct Observation {
31
+ //! pub id: i32,
32
+ //! pub altitude: Degrees, // Direct use of Quantity type
33
+ //! pub azimuth: Degrees,
34
+ //! pub min_altitude: Option<Degrees>, // Optional fields work too
35
+ //! }
36
+ //!
37
+ //! #[derive(Insertable)]
38
+ //! #[diesel(table_name = observations)]
39
+ //! pub struct NewObservation {
40
+ //! pub altitude: Degrees,
41
+ //! pub azimuth: Degrees,
42
+ //! pub min_altitude: Option<Degrees>,
43
+ //! }
44
+ //! ```
45
+
46
+ use crate::scalar::Real;
47
+ use crate::{Quantity, Unit};
48
+ use diesel::{
49
+ backend::Backend,
50
+ deserialize::{self, FromSql as DieselFromSql},
51
+ expression::AsExpression,
52
+ query_builder::QueryId,
53
+ serialize::{self, Output, ToSql as DieselToSql},
54
+ sql_types::{Double, Float, Nullable},
55
+ Queryable,
56
+ };
57
+
58
+ // ─────────────────────────────────────────────────────────────────────────────
59
+ // Core FromSql/ToSql implementations for f64 (Double)
60
+ // ─────────────────────────────────────────────────────────────────────────────
61
+
62
+ /// Deserialize `Quantity<U, f64>` from SQL DOUBLE PRECISION (f64) for any Diesel backend.
63
+ impl<U, DB> DieselFromSql<Double, DB> for Quantity<U, f64>
64
+ where
65
+ U: Unit,
66
+ DB: Backend,
67
+ f64: DieselFromSql<Double, DB>,
68
+ {
69
+ fn from_sql(bytes: DB::RawValue<'_>) -> deserialize::Result<Self> {
70
+ let value = f64::from_sql(bytes)?;
71
+ Ok(Quantity::new(value))
72
+ }
73
+ }
74
+
75
+ /// Serialize `Quantity<U, f64>` to SQL DOUBLE PRECISION (f64) for any Diesel backend.
76
+ impl<U, DB> DieselToSql<Double, DB> for Quantity<U, f64>
77
+ where
78
+ U: Unit,
79
+ DB: Backend,
80
+ f64: DieselToSql<Double, DB>,
81
+ {
82
+ fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, DB>) -> serialize::Result {
83
+ <f64 as DieselToSql<Double, DB>>::to_sql(self.value_ref(), out)
84
+ }
85
+ }
86
+
87
+ // ─────────────────────────────────────────────────────────────────────────────
88
+ // Core FromSql/ToSql implementations for f32 (Float)
89
+ // ─────────────────────────────────────────────────────────────────────────────
90
+
91
+ /// Deserialize `Quantity<U, f32>` from SQL REAL (f32) for any Diesel backend.
92
+ impl<U, DB> DieselFromSql<Float, DB> for Quantity<U, f32>
93
+ where
94
+ U: Unit,
95
+ DB: Backend,
96
+ f32: DieselFromSql<Float, DB>,
97
+ {
98
+ fn from_sql(bytes: DB::RawValue<'_>) -> deserialize::Result<Self> {
99
+ let value = f32::from_sql(bytes)?;
100
+ Ok(Quantity::new(value))
101
+ }
102
+ }
103
+
104
+ /// Serialize `Quantity<U, f32>` to SQL REAL (f32) for any Diesel backend.
105
+ impl<U, DB> DieselToSql<Float, DB> for Quantity<U, f32>
106
+ where
107
+ U: Unit,
108
+ DB: Backend,
109
+ f32: DieselToSql<Float, DB>,
110
+ {
111
+ fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, DB>) -> serialize::Result {
112
+ <f32 as DieselToSql<Float, DB>>::to_sql(self.value_ref(), out)
113
+ }
114
+ }
115
+
116
+ // ─────────────────────────────────────────────────────────────────────────────
117
+ // Nullable column support for f64
118
+ // ─────────────────────────────────────────────────────────────────────────────
119
+
120
+ /// Support for nullable columns: `Option<Quantity<U, f64>>` maps to SQL DOUBLE PRECISION NULL.
121
+ impl<U, DB> DieselFromSql<Nullable<Double>, DB> for Quantity<U, f64>
122
+ where
123
+ U: Unit,
124
+ DB: Backend,
125
+ f64: DieselFromSql<Double, DB>,
126
+ {
127
+ fn from_sql(bytes: DB::RawValue<'_>) -> deserialize::Result<Self> {
128
+ let value = f64::from_sql(bytes)?;
129
+ Ok(Quantity::new(value))
130
+ }
131
+ }
132
+
133
+ impl<U, DB> DieselToSql<Nullable<Double>, DB> for Quantity<U, f64>
134
+ where
135
+ U: Unit,
136
+ DB: Backend,
137
+ f64: DieselToSql<Double, DB>,
138
+ {
139
+ fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, DB>) -> serialize::Result {
140
+ <f64 as DieselToSql<Double, DB>>::to_sql(self.value_ref(), out)
141
+ }
142
+ }
143
+
144
+ // ─────────────────────────────────────────────────────────────────────────────
145
+ // Nullable column support for f32
146
+ // ─────────────────────────────────────────────────────────────────────────────
147
+
148
+ /// Support for nullable columns: `Option<Quantity<U, f32>>` maps to SQL REAL NULL.
149
+ impl<U, DB> DieselFromSql<Nullable<Float>, DB> for Quantity<U, f32>
150
+ where
151
+ U: Unit,
152
+ DB: Backend,
153
+ f32: DieselFromSql<Float, DB>,
154
+ {
155
+ fn from_sql(bytes: DB::RawValue<'_>) -> deserialize::Result<Self> {
156
+ let value = f32::from_sql(bytes)?;
157
+ Ok(Quantity::new(value))
158
+ }
159
+ }
160
+
161
+ impl<U, DB> DieselToSql<Nullable<Float>, DB> for Quantity<U, f32>
162
+ where
163
+ U: Unit,
164
+ DB: Backend,
165
+ f32: DieselToSql<Float, DB>,
166
+ {
167
+ fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, DB>) -> serialize::Result {
168
+ <f32 as DieselToSql<Float, DB>>::to_sql(self.value_ref(), out)
169
+ }
170
+ }
171
+
172
+ // ─────────────────────────────────────────────────────────────────────────────
173
+ // AsExpression implementations for f64
174
+ // ─────────────────────────────────────────────────────────────────────────────
175
+
176
+ /// Enable `Quantity<U, f64>` in WHERE clauses and INSERT statements.
177
+ impl<U: Unit> AsExpression<Double> for Quantity<U, f64> {
178
+ type Expression = <f64 as AsExpression<Double>>::Expression;
179
+
180
+ fn as_expression(self) -> Self::Expression {
181
+ AsExpression::<Double>::as_expression(self.value())
182
+ }
183
+ }
184
+
185
+ impl<U: Unit> AsExpression<Double> for &Quantity<U, f64> {
186
+ type Expression = <f64 as AsExpression<Double>>::Expression;
187
+
188
+ fn as_expression(self) -> Self::Expression {
189
+ AsExpression::<Double>::as_expression(self.value())
190
+ }
191
+ }
192
+
193
+ /// Enable `Quantity<U, f64>` in nullable (Option) columns.
194
+ impl<U: Unit> AsExpression<Nullable<Double>> for Quantity<U, f64> {
195
+ type Expression = <f64 as AsExpression<Nullable<Double>>>::Expression;
196
+
197
+ fn as_expression(self) -> Self::Expression {
198
+ AsExpression::<Nullable<Double>>::as_expression(self.value())
199
+ }
200
+ }
201
+
202
+ impl<U: Unit> AsExpression<Nullable<Double>> for &Quantity<U, f64> {
203
+ type Expression = <f64 as AsExpression<Nullable<Double>>>::Expression;
204
+
205
+ fn as_expression(self) -> Self::Expression {
206
+ AsExpression::<Nullable<Double>>::as_expression(self.value())
207
+ }
208
+ }
209
+
210
+ // ─────────────────────────────────────────────────────────────────────────────
211
+ // AsExpression implementations for f32
212
+ // ─────────────────────────────────────────────────────────────────────────────
213
+
214
+ /// Enable `Quantity<U, f32>` in WHERE clauses and INSERT statements.
215
+ impl<U: Unit> AsExpression<Float> for Quantity<U, f32> {
216
+ type Expression = <f32 as AsExpression<Float>>::Expression;
217
+
218
+ fn as_expression(self) -> Self::Expression {
219
+ AsExpression::<Float>::as_expression(self.value())
220
+ }
221
+ }
222
+
223
+ impl<U: Unit> AsExpression<Float> for &Quantity<U, f32> {
224
+ type Expression = <f32 as AsExpression<Float>>::Expression;
225
+
226
+ fn as_expression(self) -> Self::Expression {
227
+ AsExpression::<Float>::as_expression(self.value())
228
+ }
229
+ }
230
+
231
+ /// Enable `Quantity<U, f32>` in nullable (Option) columns.
232
+ impl<U: Unit> AsExpression<Nullable<Float>> for Quantity<U, f32> {
233
+ type Expression = <f32 as AsExpression<Nullable<Float>>>::Expression;
234
+
235
+ fn as_expression(self) -> Self::Expression {
236
+ AsExpression::<Nullable<Float>>::as_expression(self.value())
237
+ }
238
+ }
239
+
240
+ impl<U: Unit> AsExpression<Nullable<Float>> for &Quantity<U, f32> {
241
+ type Expression = <f32 as AsExpression<Nullable<Float>>>::Expression;
242
+
243
+ fn as_expression(self) -> Self::Expression {
244
+ AsExpression::<Nullable<Float>>::as_expression(self.value())
245
+ }
246
+ }
247
+
248
+ // ─────────────────────────────────────────────────────────────────────────────
249
+ // Queryable implementations for f64
250
+ // ─────────────────────────────────────────────────────────────────────────────
251
+
252
+ /// Enable `Quantity<U, f64>` to be used in Diesel's `Queryable` derive.
253
+ impl<U, DB> Queryable<Double, DB> for Quantity<U, f64>
254
+ where
255
+ U: Unit,
256
+ DB: Backend,
257
+ f64: Queryable<Double, DB>,
258
+ {
259
+ type Row = <f64 as Queryable<Double, DB>>::Row;
260
+
261
+ fn build(row: Self::Row) -> deserialize::Result<Self> {
262
+ let value = <f64 as Queryable<Double, DB>>::build(row)?;
263
+ Ok(Quantity::new(value))
264
+ }
265
+ }
266
+
267
+ impl<U, DB> Queryable<Nullable<Double>, DB> for Quantity<U, f64>
268
+ where
269
+ U: Unit,
270
+ DB: Backend,
271
+ f64: Queryable<Nullable<Double>, DB>,
272
+ {
273
+ type Row = <f64 as Queryable<Nullable<Double>, DB>>::Row;
274
+
275
+ fn build(row: Self::Row) -> deserialize::Result<Self> {
276
+ let value = <f64 as Queryable<Nullable<Double>, DB>>::build(row)?;
277
+ Ok(Quantity::new(value))
278
+ }
279
+ }
280
+
281
+ // ─────────────────────────────────────────────────────────────────────────────
282
+ // Queryable implementations for f32
283
+ // ─────────────────────────────────────────────────────────────────────────────
284
+
285
+ /// Enable `Quantity<U, f32>` to be used in Diesel's `Queryable` derive.
286
+ impl<U, DB> Queryable<Float, DB> for Quantity<U, f32>
287
+ where
288
+ U: Unit,
289
+ DB: Backend,
290
+ f32: Queryable<Float, DB>,
291
+ {
292
+ type Row = <f32 as Queryable<Float, DB>>::Row;
293
+
294
+ fn build(row: Self::Row) -> deserialize::Result<Self> {
295
+ let value = <f32 as Queryable<Float, DB>>::build(row)?;
296
+ Ok(Quantity::new(value))
297
+ }
298
+ }
299
+
300
+ impl<U, DB> Queryable<Nullable<Float>, DB> for Quantity<U, f32>
301
+ where
302
+ U: Unit,
303
+ DB: Backend,
304
+ f32: Queryable<Nullable<Float>, DB>,
305
+ {
306
+ type Row = <f32 as Queryable<Nullable<Float>, DB>>::Row;
307
+
308
+ fn build(row: Self::Row) -> deserialize::Result<Self> {
309
+ let value = <f32 as Queryable<Nullable<Float>, DB>>::build(row)?;
310
+ Ok(Quantity::new(value))
311
+ }
312
+ }
313
+
314
+ /// QueryId implementation for query caching support.
315
+ impl<U: Unit, S: Real> QueryId for Quantity<U, S> {
316
+ type QueryId = Self;
317
+ const HAS_STATIC_QUERY_ID: bool = false;
318
+ }
@@ -0,0 +1,27 @@
1
+ //! PyO3 trait implementations for `Quantity` types (feature-gated).
2
+ //!
3
+ //! This module is enabled by the `pyo3` feature. It provides `IntoPyObject` and `FromPyObject`
4
+ //! implementations that convert `Quantity<U, S>` to/from Python floats.
5
+
6
+ use crate::scalar::Real;
7
+ use crate::{Quantity, Unit};
8
+ use pyo3::prelude::*;
9
+
10
+ impl<'py, U: Unit, S: Real> pyo3::conversion::IntoPyObject<'py> for Quantity<U, S> {
11
+ type Target = pyo3::types::PyFloat;
12
+ type Output = pyo3::Bound<'py, pyo3::types::PyFloat>;
13
+ type Error = core::convert::Infallible;
14
+
15
+ fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
16
+ Ok(pyo3::types::PyFloat::new(py, self.value().to_f64()))
17
+ }
18
+ }
19
+
20
+ impl<'a, 'py, U: Unit, S: Real> pyo3::conversion::FromPyObject<'a, 'py> for Quantity<U, S> {
21
+ type Error = pyo3::PyErr;
22
+
23
+ fn extract(obj: pyo3::Borrowed<'a, 'py, PyAny>) -> Result<Self, Self::Error> {
24
+ let value = <f64 as pyo3::conversion::FromPyObject<'a, 'py>>::extract(obj)?;
25
+ Ok(Quantity::new(S::from_f64(value)))
26
+ }
27
+ }
@@ -0,0 +1,203 @@
1
+ //! Serde support for `Quantity` types (feature-gated).
2
+ //!
3
+ //! This module is enabled by the `serde` feature. It provides serialization and deserialization
4
+ //! for `Quantity<U, S>` types, including helper modules for different serialization formats.
5
+
6
+ use crate::scalar::{Real, Scalar};
7
+ use crate::{Quantity, Unit};
8
+ use serde::{Deserialize, Deserializer, Serialize, Serializer};
9
+
10
+ // Default serde: serialize as f64 (backward compatible)
11
+ impl<U: Unit, S: Real> Serialize for Quantity<U, S> {
12
+ fn serialize<Ser>(&self, serializer: Ser) -> core::result::Result<Ser::Ok, Ser::Error>
13
+ where
14
+ Ser: Serializer,
15
+ {
16
+ // Strategy A: Always serialize as f64 for backward compatibility
17
+ self.value().to_f64().serialize(serializer)
18
+ }
19
+ }
20
+
21
+ impl<'de, U: Unit, S: Real> Deserialize<'de> for Quantity<U, S> {
22
+ fn deserialize<D>(deserializer: D) -> core::result::Result<Self, D::Error>
23
+ where
24
+ D: Deserializer<'de>,
25
+ {
26
+ // Strategy A: Deserialize from f64 and convert
27
+ let value = f64::deserialize(deserializer)?;
28
+ Ok(Quantity::new(S::from_f64(value)))
29
+ }
30
+ }
31
+
32
+ /// Serde helper module for serializing the raw scalar value.
33
+ ///
34
+ /// This module serializes the scalar type directly instead of converting to f64.
35
+ /// Use this when you need to preserve the exact scalar representation.
36
+ ///
37
+ /// # Example
38
+ ///
39
+ /// ```rust,ignore
40
+ /// use qtty_core::length::Meters;
41
+ /// use serde::{Serialize, Deserialize};
42
+ ///
43
+ /// #[derive(Serialize, Deserialize)]
44
+ /// struct Config {
45
+ /// #[serde(with = "qtty_core::serde_scalar")]
46
+ /// distance: Meters, // Serializes the scalar directly
47
+ /// }
48
+ /// ```
49
+ pub mod serde_scalar {
50
+ use super::*;
51
+
52
+ /// Serializes the quantity's scalar value directly.
53
+ #[allow(dead_code)]
54
+ pub fn serialize<U, S, Ser>(
55
+ quantity: &Quantity<U, S>,
56
+ serializer: Ser,
57
+ ) -> Result<Ser::Ok, Ser::Error>
58
+ where
59
+ U: Unit,
60
+ S: Scalar + Serialize,
61
+ Ser: Serializer,
62
+ {
63
+ quantity.value_ref().serialize(serializer)
64
+ }
65
+
66
+ /// Deserializes the quantity from a scalar value directly.
67
+ #[allow(dead_code)]
68
+ pub fn deserialize<'de, U, S, D>(deserializer: D) -> Result<Quantity<U, S>, D::Error>
69
+ where
70
+ U: Unit,
71
+ S: Scalar + Deserialize<'de>,
72
+ D: Deserializer<'de>,
73
+ {
74
+ let value = S::deserialize(deserializer)?;
75
+ Ok(Quantity::new(value))
76
+ }
77
+ }
78
+
79
+ /// Serde helper module for serializing quantities with unit information.
80
+ ///
81
+ /// Use this with the `#[serde(with = "...")]` attribute to preserve unit symbols
82
+ /// in serialized data. This is useful for external APIs, configuration files, or
83
+ /// self-documenting data formats.
84
+ ///
85
+ /// # Examples
86
+ ///
87
+ /// ```rust
88
+ /// use qtty_core::length::Meters;
89
+ /// use serde::{Serialize, Deserialize};
90
+ ///
91
+ /// #[derive(Serialize, Deserialize)]
92
+ /// struct Config {
93
+ /// #[serde(with = "qtty_core::serde_with_unit")]
94
+ /// max_distance: Meters, // Serializes as {"value": 100.0, "unit": "m"}
95
+ ///
96
+ /// min_distance: Meters, // Serializes as 50.0 (default, compact)
97
+ /// }
98
+ /// ```
99
+ #[cfg(feature = "std")]
100
+ pub mod serde_with_unit {
101
+ extern crate alloc;
102
+ use alloc::format;
103
+ use alloc::string::String;
104
+
105
+ use super::*;
106
+ use serde::de::{self, Deserializer, MapAccess, Visitor};
107
+ use serde::ser::{SerializeStruct, Serializer};
108
+
109
+ /// Serializes a `Quantity<U, S>` as a struct with `value` and `unit` fields.
110
+ ///
111
+ /// # Example JSON Output
112
+ /// ```json
113
+ /// {"value": 42.5, "unit": "m"}
114
+ /// ```
115
+ pub fn serialize<U, S, Ser>(
116
+ quantity: &Quantity<U, S>,
117
+ serializer: Ser,
118
+ ) -> Result<Ser::Ok, Ser::Error>
119
+ where
120
+ U: Unit,
121
+ S: Real,
122
+ Ser: Serializer,
123
+ {
124
+ let mut state = serializer.serialize_struct("Quantity", 2)?;
125
+ state.serialize_field("value", &quantity.value().to_f64())?;
126
+ state.serialize_field("unit", U::SYMBOL)?;
127
+ state.end()
128
+ }
129
+
130
+ /// Deserializes a `Quantity<U, S>` from a struct with `value` and optionally `unit` fields.
131
+ ///
132
+ /// The `unit` field is validated if present but not required for backwards compatibility.
133
+ /// If provided and doesn't match `U::SYMBOL`, an error is returned.
134
+ pub fn deserialize<'de, U, S, D>(deserializer: D) -> Result<Quantity<U, S>, D::Error>
135
+ where
136
+ U: Unit,
137
+ S: Real,
138
+ D: Deserializer<'de>,
139
+ {
140
+ #[derive(Deserialize)]
141
+ #[serde(field_identifier, rename_all = "lowercase")]
142
+ enum Field {
143
+ Value,
144
+ Unit,
145
+ }
146
+
147
+ struct QuantityVisitor<U, S>(core::marker::PhantomData<(U, S)>);
148
+
149
+ impl<'de, U: Unit, S: Real> Visitor<'de> for QuantityVisitor<U, S> {
150
+ type Value = Quantity<U, S>;
151
+
152
+ fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
153
+ formatter.write_str("struct Quantity with value and unit fields")
154
+ }
155
+
156
+ fn visit_map<V>(self, mut map: V) -> Result<Quantity<U, S>, V::Error>
157
+ where
158
+ V: MapAccess<'de>,
159
+ {
160
+ let mut value: Option<f64> = None;
161
+ let mut unit: Option<String> = None;
162
+
163
+ while let Some(key) = map.next_key()? {
164
+ match key {
165
+ Field::Value => {
166
+ if value.is_some() {
167
+ return Err(de::Error::duplicate_field("value"));
168
+ }
169
+ value = Some(map.next_value()?);
170
+ }
171
+ Field::Unit => {
172
+ if unit.is_some() {
173
+ return Err(de::Error::duplicate_field("unit"));
174
+ }
175
+ unit = Some(map.next_value()?);
176
+ }
177
+ }
178
+ }
179
+
180
+ let value = value.ok_or_else(|| de::Error::missing_field("value"))?;
181
+
182
+ // Validate unit if provided (optional for backwards compatibility)
183
+ if let Some(ref unit_str) = unit {
184
+ if unit_str != U::SYMBOL {
185
+ return Err(de::Error::custom(format!(
186
+ "unit mismatch: expected '{}', found '{}'",
187
+ U::SYMBOL,
188
+ unit_str
189
+ )));
190
+ }
191
+ }
192
+
193
+ Ok(Quantity::new(S::from_f64(value)))
194
+ }
195
+ }
196
+
197
+ deserializer.deserialize_struct(
198
+ "Quantity",
199
+ &["value", "unit"],
200
+ QuantityVisitor(core::marker::PhantomData),
201
+ )
202
+ }
203
+ }
@@ -0,0 +1,28 @@
1
+ //! Tiberius SQL Server support for `Quantity` types (feature-gated).
2
+ //!
3
+ //! This module is enabled by the `tiberius` feature. It provides SQL Server database
4
+ //! integration for `Quantity<U, S>` types through the Tiberius driver.
5
+
6
+ use crate::scalar::Real;
7
+ use crate::{Quantity, Unit};
8
+ use tiberius::{ColumnData, FromSql, ToSql};
9
+
10
+ impl<U: Unit + Send + Sync, S: Real + Send + Sync> ToSql for Quantity<U, S> {
11
+ fn to_sql(&self) -> ColumnData<'_> {
12
+ ColumnData::F64(Some(self.value().to_f64()))
13
+ }
14
+ }
15
+
16
+ impl<U: Unit, S: Real> FromSql<'_> for Quantity<U, S> {
17
+ fn from_sql(value: &ColumnData<'_>) -> tiberius::Result<Option<Self>> {
18
+ match value {
19
+ ColumnData::F64(Some(val)) => Ok(Some(Quantity::new(S::from_f64(*val)))),
20
+ ColumnData::F32(Some(val)) => Ok(Some(Quantity::new(S::from_f64(*val as f64)))),
21
+ ColumnData::I16(Some(val)) => Ok(Some(Quantity::new(S::from_f64(*val as f64)))),
22
+ ColumnData::I32(Some(val)) => Ok(Some(Quantity::new(S::from_f64(*val as f64)))),
23
+ ColumnData::I64(Some(val)) => Ok(Some(Quantity::new(S::from_f64(*val as f64)))),
24
+ ColumnData::U8(Some(val)) => Ok(Some(Quantity::new(S::from_f64(*val as f64)))),
25
+ _ => Ok(None),
26
+ }
27
+ }
28
+ }