1#![deny(missing_docs)]
77#![cfg_attr(not(feature = "std"), no_std)]
78#![forbid(unsafe_code)]
79
80#[cfg(not(feature = "std"))]
81extern crate libm;
82
83mod dimension;
88#[cfg(feature = "diesel")]
89mod feature_diesel;
90#[cfg(feature = "pyo3")]
91mod feature_pyo3;
92#[cfg(feature = "serde")]
93mod feature_serde;
94#[cfg(feature = "tiberius")]
95mod feature_tiberius;
96mod macros;
97mod quantity;
98pub mod scalar;
99mod unit;
100
101pub use dimension::{
106 Acceleration,
108 AmountOfSubstance,
110 Angular,
112 Area,
113 Current,
114 Dim,
115 DimDiv,
116 DimMul,
117 Dimension,
118 Dimensionless,
119 DivDim,
120 Energy,
121 Force,
122 FrequencyDim,
123 Length,
124 LuminousIntensity,
125 Mass,
126 MulDim,
127 Power,
128 Temperature,
129 Time,
130 VelocityDim,
131 Volume,
132};
133pub use quantity::{
134 Quantity, Quantity32, Quantity64, QuantityI128, QuantityI16, QuantityI32, QuantityI64,
135 QuantityI8,
136};
137pub use scalar::{Exact, IntegerScalar, Real, Scalar, Transcendental};
138pub use unit::{Per, Prod, Simplify, Unit, Unitless};
139
140#[cfg(feature = "scalar-decimal")]
141pub use quantity::QuantityDecimal;
142
143#[cfg(feature = "scalar-rational")]
144pub use quantity::QuantityRational;
145
146#[cfg(feature = "serde")]
147pub use feature_serde::serde_with_unit;
148
149#[cfg(feature = "serde")]
150pub use feature_serde::serde_scalar;
151
152pub mod units;
161
162pub use units::angular;
163pub use units::area;
164pub use units::frequency;
165pub use units::length;
166pub use units::mass;
167pub use units::power;
168pub use units::time;
169pub use units::unitless;
170pub use units::velocity;
171pub use units::volume;
172
173#[cfg(test)]
174mod tests {
175 use super::*;
176
177 type TestDim = Length;
183
184 #[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
185 pub enum TestUnit {}
186 impl Unit for TestUnit {
187 const RATIO: f64 = 1.0;
188 type Dim = TestDim;
189 const SYMBOL: &'static str = "tu";
190 }
191 impl core::fmt::Display for Quantity<TestUnit> {
192 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
193 write!(f, "{} tu", self.value())
194 }
195 }
196
197 #[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
198 pub enum DoubleTestUnit {}
199 impl Unit for DoubleTestUnit {
200 const RATIO: f64 = 2.0;
201 type Dim = TestDim;
202 const SYMBOL: &'static str = "dtu";
203 }
204 impl core::fmt::Display for Quantity<DoubleTestUnit> {
205 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
206 write!(f, "{} dtu", self.value())
207 }
208 }
209
210 #[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
211 pub enum HalfTestUnit {}
212 impl Unit for HalfTestUnit {
213 const RATIO: f64 = 0.5;
214 type Dim = TestDim;
215 const SYMBOL: &'static str = "htu";
216 }
217 impl core::fmt::Display for Quantity<HalfTestUnit> {
218 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
219 write!(f, "{} htu", self.value())
220 }
221 }
222
223 type TU = Quantity<TestUnit>;
224 type Dtu = Quantity<DoubleTestUnit>;
225
226 #[test]
231 fn quantity_new_and_value() {
232 let q = TU::new(42.0);
233 assert_eq!(q.value(), 42.0);
234 }
235
236 #[test]
237 fn quantity_nan_constant() {
238 assert!(TU::NAN.value().is_nan());
239 }
240
241 #[test]
242 fn quantity_abs() {
243 assert_eq!(TU::new(-5.0).abs().value(), 5.0);
244 assert_eq!(TU::new(5.0).abs().value(), 5.0);
245 assert_eq!(TU::new(0.0).abs().value(), 0.0);
246 }
247
248 #[test]
249 fn quantity_from_f64() {
250 let q: TU = 123.456.into();
251 assert_eq!(q.value(), 123.456);
252 }
253
254 #[test]
259 fn quantity_conversion_to_same_unit() {
260 let q = TU::new(10.0);
261 let converted = q.to::<TestUnit>();
262 assert_eq!(converted.value(), 10.0);
263 }
264
265 #[test]
266 fn quantity_conversion_to_different_unit() {
267 let q = TU::new(10.0);
270 let converted = q.to::<DoubleTestUnit>();
271 assert!((converted.value() - 5.0).abs() < 1e-12);
272 }
273
274 #[test]
275 fn quantity_conversion_roundtrip() {
276 let original = TU::new(100.0);
277 let converted = original.to::<DoubleTestUnit>();
278 let back = converted.to::<TestUnit>();
279 assert!((back.value() - original.value()).abs() < 1e-12);
280 }
281
282 #[test]
287 fn const_add() {
288 let a = TU::new(3.0);
289 let b = TU::new(7.0);
290 assert_eq!(a.const_add(b).value(), 10.0);
291 }
292
293 #[test]
294 fn const_sub() {
295 let a = TU::new(10.0);
296 let b = TU::new(3.0);
297 assert_eq!(a.const_sub(b).value(), 7.0);
298 }
299
300 #[test]
301 fn const_mul() {
302 let a = TU::new(4.0);
303 let b = 5.0;
304 assert_eq!(a.const_mul(b).value(), 20.0);
305 }
306
307 #[test]
308 fn const_div() {
309 let a = TU::new(20.0);
310 let b = 4.0;
311 assert_eq!(a.const_div(b).value(), 5.0);
312 }
313
314 #[test]
315 fn const_min() {
316 let a = TU::new(5.0);
317 let b = TU::new(3.0);
318 assert_eq!(a.min_const(b).value(), 3.0);
319 assert_eq!(b.min_const(a).value(), 3.0);
320 }
321
322 #[test]
327 fn operator_add() {
328 let a = TU::new(3.0);
329 let b = TU::new(7.0);
330 assert_eq!((a + b).value(), 10.0);
331 }
332
333 #[test]
334 fn operator_sub() {
335 let a = TU::new(10.0);
336 let b = TU::new(3.0);
337 assert_eq!((a - b).value(), 7.0);
338 }
339
340 #[test]
341 fn operator_mul_by_f64() {
342 let q = TU::new(5.0);
343 assert_eq!((q * 3.0).value(), 15.0);
344 assert_eq!((3.0 * q).value(), 15.0);
345 }
346
347 #[test]
348 fn operator_div_by_f64() {
349 let q = TU::new(15.0);
350 assert_eq!((q / 3.0).value(), 5.0);
351 }
352
353 #[test]
354 fn operator_neg() {
355 let q = TU::new(5.0);
356 assert_eq!((-q).value(), -5.0);
357 assert_eq!((-(-q)).value(), 5.0);
358 }
359
360 #[test]
361 fn operator_rem() {
362 let q = TU::new(10.0);
363 assert_eq!((q % 3.0).value(), 1.0);
364 }
365
366 #[test]
371 fn operator_add_assign() {
372 let mut q = TU::new(5.0);
373 q += TU::new(3.0);
374 assert_eq!(q.value(), 8.0);
375 }
376
377 #[test]
378 fn operator_sub_assign() {
379 let mut q = TU::new(10.0);
380 q -= TU::new(3.0);
381 assert_eq!(q.value(), 7.0);
382 }
383
384 #[test]
385 fn operator_div_assign() {
386 let mut q = TU::new(20.0);
387 q /= TU::new(4.0);
388 assert_eq!(q.value(), 5.0);
389 }
390
391 #[test]
396 fn partial_eq_f64() {
397 let q = TU::new(5.0);
398 assert!(q == 5.0);
399 assert!(!(q == 4.0));
400 }
401
402 #[test]
407 fn division_creates_per_type() {
408 let num = TU::new(100.0);
409 let den = Dtu::new(20.0);
410 let ratio: Quantity<Per<TestUnit, DoubleTestUnit>> = num / den;
411 assert!((ratio.value() - 5.0).abs() < 1e-12);
412 }
413
414 #[test]
415 fn per_ratio_conversion() {
416 let v1: Quantity<Per<DoubleTestUnit, TestUnit>> = Quantity::new(10.0);
417 let v2: Quantity<Per<TestUnit, TestUnit>> = v1.to();
418 assert!((v2.value() - 20.0).abs() < 1e-12);
419 }
420
421 #[test]
422 fn per_multiplication_recovers_numerator() {
423 let rate: Quantity<Per<TestUnit, DoubleTestUnit>> = Quantity::new(5.0);
424 let time = Dtu::new(4.0);
425 let result: TU = (rate * time).to();
428 assert!((result.value() - 20.0).abs() < 1e-12);
429 }
430
431 #[test]
432 fn per_multiplication_commutative() {
433 let rate: Quantity<Per<TestUnit, DoubleTestUnit>> = Quantity::new(5.0);
434 let time = Dtu::new(4.0);
435 let result1: TU = (rate * time).to();
436 let result2: TU = (time * rate).to();
437 assert!((result1.value() - result2.value()).abs() < 1e-12);
438 }
439
440 #[test]
445 fn simplify_per_u_u_to_unitless() {
446 let ratio: Quantity<Per<TestUnit, TestUnit>> = Quantity::new(1.23456);
447 let unitless: Quantity<Unitless> = ratio.simplify();
448 assert!((unitless.value() - 1.23456).abs() < 1e-12);
449 }
450
451 #[test]
452 fn simplify_per_n_per_n_d_to_d() {
453 let q: Quantity<Per<TestUnit, Per<TestUnit, DoubleTestUnit>>> = Quantity::new(7.5);
454 let simplified: Dtu = q.simplify();
455 assert!((simplified.value() - 7.5).abs() < 1e-12);
456 }
457
458 #[test]
463 fn per_u_u_asin() {
464 let ratio: Quantity<Per<TestUnit, TestUnit>> = Quantity::new(0.5);
465 let result = ratio.asin();
466 assert!((result - 0.5_f64.asin()).abs() < 1e-12);
467 }
468
469 #[test]
470 fn per_u_u_asin_boundary_values() {
471 let one: Quantity<Per<TestUnit, TestUnit>> = Quantity::new(1.0);
472 assert!((one.asin() - core::f64::consts::FRAC_PI_2).abs() < 1e-12);
473
474 let neg_one: Quantity<Per<TestUnit, TestUnit>> = Quantity::new(-1.0);
475 assert!((neg_one.asin() - (-core::f64::consts::FRAC_PI_2)).abs() < 1e-12);
476
477 let zero: Quantity<Per<TestUnit, TestUnit>> = Quantity::new(0.0);
478 assert!((zero.asin() - 0.0).abs() < 1e-12);
479 }
480
481 #[test]
486 fn display_simple_quantity() {
487 let q = TU::new(42.5);
488 let s = format!("{}", q);
489 assert_eq!(s, "42.5 tu");
490 }
491
492 #[test]
493 fn display_per_quantity() {
494 let q: Quantity<Per<TestUnit, DoubleTestUnit>> = Quantity::new(2.5);
495 let s = format!("{}", q);
496 assert_eq!(s, "2.5 tu/dtu");
497 }
498
499 #[test]
500 fn display_negative_value() {
501 let q = TU::new(-99.9);
502 let s = format!("{}", q);
503 assert_eq!(s, "-99.9 tu");
504 }
505
506 #[test]
511 fn edge_case_zero() {
512 let zero = TU::new(0.0);
513 assert_eq!(zero.value(), 0.0);
514 assert_eq!((-zero).value(), 0.0);
515 assert_eq!(zero.abs().value(), 0.0);
516 }
517
518 #[test]
519 fn edge_case_negative_values() {
520 let neg = TU::new(-10.0);
521 let pos = TU::new(5.0);
522
523 assert_eq!((neg + pos).value(), -5.0);
524 assert_eq!((neg - pos).value(), -15.0);
525 assert_eq!((neg * 2.0).value(), -20.0);
526 assert_eq!(neg.abs().value(), 10.0);
527 }
528
529 #[test]
530 fn edge_case_large_values() {
531 let large = TU::new(1e100);
532 let small = TU::new(1e-100);
533 assert_eq!(large.value(), 1e100);
534 assert_eq!(small.value(), 1e-100);
535 }
536
537 #[test]
538 fn edge_case_infinity() {
539 let inf = TU::new(f64::INFINITY);
540 let neg_inf = TU::new(f64::NEG_INFINITY);
541
542 assert!(inf.value().is_infinite());
543 assert!(neg_inf.value().is_infinite());
544 assert_eq!(inf.value().signum(), 1.0);
545 assert_eq!(neg_inf.value().signum(), -1.0);
546 }
547
548 #[cfg(feature = "serde")]
553 mod serde_tests {
554 use super::*;
555 use serde::{Deserialize, Serialize};
556
557 #[test]
558 fn serialize_quantity() {
559 let q = TU::new(42.5);
560 let json = serde_json::to_string(&q).unwrap();
561 assert_eq!(json, "42.5");
562 }
563
564 #[test]
565 fn deserialize_quantity() {
566 let json = "42.5";
567 let q: TU = serde_json::from_str(json).unwrap();
568 assert_eq!(q.value(), 42.5);
569 }
570
571 #[test]
572 fn serde_roundtrip() {
573 let original = TU::new(123.456);
574 let json = serde_json::to_string(&original).unwrap();
575 let restored: TU = serde_json::from_str(&json).unwrap();
576 assert!((restored.value() - original.value()).abs() < 1e-12);
577 }
578
579 #[derive(Serialize, Deserialize, Debug)]
584 struct TestStruct {
585 #[serde(with = "crate::serde_with_unit")]
586 distance: TU,
587 }
588
589 #[test]
590 fn serde_with_unit_serialize() {
591 let data = TestStruct {
592 distance: TU::new(42.5),
593 };
594 let json = serde_json::to_string(&data).unwrap();
595 assert!(json.contains("\"value\""));
596 assert!(json.contains("\"unit\""));
597 assert!(json.contains("42.5"));
598 assert!(json.contains("\"tu\""));
599 }
600
601 #[test]
602 fn serde_with_unit_deserialize() {
603 let json = r#"{"distance":{"value":42.5,"unit":"tu"}}"#;
604 let data: TestStruct = serde_json::from_str(json).unwrap();
605 assert_eq!(data.distance.value(), 42.5);
606 }
607
608 #[test]
609 fn serde_with_unit_deserialize_no_unit_field() {
610 let json = r#"{"distance":{"value":42.5}}"#;
612 let data: TestStruct = serde_json::from_str(json).unwrap();
613 assert_eq!(data.distance.value(), 42.5);
614 }
615
616 #[test]
617 fn serde_with_unit_deserialize_wrong_unit() {
618 let json = r#"{"distance":{"value":42.5,"unit":"wrong"}}"#;
619 let result: Result<TestStruct, _> = serde_json::from_str(json);
620 assert!(result.is_err());
621 let err_msg = result.unwrap_err().to_string();
622 assert!(err_msg.contains("unit mismatch") || err_msg.contains("expected"));
623 }
624
625 #[test]
626 fn serde_with_unit_deserialize_missing_value() {
627 let json = r#"{"distance":{"unit":"tu"}}"#;
628 let result: Result<TestStruct, _> = serde_json::from_str(json);
629 assert!(result.is_err());
630 let err_msg = result.unwrap_err().to_string();
631 assert!(err_msg.contains("missing field") || err_msg.contains("value"));
632 }
633
634 #[test]
635 fn serde_with_unit_deserialize_duplicate_value() {
636 let json = r#"{"distance":{"value":42.5,"value":100.0,"unit":"tu"}}"#;
637 let result: Result<TestStruct, _> = serde_json::from_str(json);
638 let _ = result;
641 }
642
643 #[test]
644 fn serde_with_unit_deserialize_duplicate_unit() {
645 let json = r#"{"distance":{"value":42.5,"unit":"tu","unit":"tu"}}"#;
646 let result: Result<TestStruct, _> = serde_json::from_str(json);
647 let _ = result;
649 }
650
651 #[test]
652 fn serde_with_unit_deserialize_invalid_format() {
653 let json = r#"{"distance":"not_an_object"}"#;
655 let result: Result<TestStruct, _> = serde_json::from_str(json);
656 assert!(result.is_err());
657 }
658
659 #[test]
660 fn serde_with_unit_deserialize_array() {
661 let json = r#"{"distance":[42.5, "tu"]}"#;
663 let result: Result<TestStruct, _> = serde_json::from_str(json);
664 assert!(result.is_err());
665 }
666
667 #[test]
668 fn serde_with_unit_roundtrip() {
669 let original = TestStruct {
670 distance: TU::new(123.456),
671 };
672 let json = serde_json::to_string(&original).unwrap();
673 let restored: TestStruct = serde_json::from_str(&json).unwrap();
674 assert!((restored.distance.value() - original.distance.value()).abs() < 1e-12);
675 }
676
677 #[test]
678 fn serde_with_unit_special_values() {
679 let test_large = TestStruct {
683 distance: TU::new(1e100),
684 };
685 let json = serde_json::to_string(&test_large).unwrap();
686 let restored: TestStruct = serde_json::from_str(&json).unwrap();
687 assert!((restored.distance.value() - 1e100).abs() < 1e88);
688
689 let test_small = TestStruct {
690 distance: TU::new(-1e-100),
691 };
692 let json = serde_json::to_string(&test_small).unwrap();
693 let restored: TestStruct = serde_json::from_str(&json).unwrap();
694 assert!((restored.distance.value() + 1e-100).abs() < 1e-112);
695 }
696 }
697}