1#![deny(missing_docs)]
78#![cfg_attr(not(feature = "std"), no_std)]
79#![forbid(unsafe_code)]
80
81#[cfg(not(feature = "std"))]
82extern crate libm;
83
84mod dimension;
89#[cfg(feature = "diesel")]
90mod feature_diesel;
91#[cfg(feature = "pyo3")]
92mod feature_pyo3;
93#[cfg(feature = "serde")]
94mod feature_serde;
95#[cfg(feature = "tiberius")]
96mod feature_tiberius;
97mod macros;
98mod quantity;
99pub mod scalar;
100mod unit;
101
102pub use dimension::{
107 Acceleration,
109 AmountOfSubstance,
111 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};
134pub use quantity::{
135 Quantity, Quantity32, Quantity64, QuantityI128, QuantityI16, QuantityI32, QuantityI64,
136 QuantityI8,
137};
138pub use scalar::{Exact, IntegerScalar, Real, Scalar, Transcendental};
139pub use unit::{Per, Prod, Simplify, Unit, Unitless};
140
141#[cfg(feature = "scalar-decimal")]
142pub use quantity::QuantityDecimal;
143
144#[cfg(feature = "scalar-rational")]
145pub use quantity::QuantityRational;
146
147#[cfg(feature = "serde")]
148pub use feature_serde::serde_with_unit;
149
150#[cfg(feature = "serde")]
151pub use feature_serde::serde_scalar;
152
153pub mod units;
162
163pub use units::angular;
164pub use units::area;
165pub use units::frequency;
166pub use units::length;
167pub use units::mass;
168pub use units::power;
169pub use units::time;
170pub use units::unitless;
171pub use units::velocity;
172pub use units::volume;
173
174#[cfg(test)]
175mod tests {
176 use super::*;
177
178 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 #[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 #[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 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 #[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 assert_eq!(a.max_const(b).value(), 5.0);
329 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 #[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 #[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 #[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 #[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 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 #[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 #[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 #[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 #[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 #[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 #[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 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 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 let _ = result;
696 }
697
698 #[test]
699 fn serde_with_unit_deserialize_invalid_format() {
700 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 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 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}