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]
328 fn operator_add() {
329 let a = TU::new(3.0);
330 let b = TU::new(7.0);
331 assert_eq!((a + b).value(), 10.0);
332 }
333
334 #[test]
335 fn operator_sub() {
336 let a = TU::new(10.0);
337 let b = TU::new(3.0);
338 assert_eq!((a - b).value(), 7.0);
339 }
340
341 #[test]
342 fn operator_mul_by_f64() {
343 let q = TU::new(5.0);
344 assert_eq!((q * 3.0).value(), 15.0);
345 assert_eq!((3.0 * q).value(), 15.0);
346 }
347
348 #[test]
349 fn operator_div_by_f64() {
350 let q = TU::new(15.0);
351 assert_eq!((q / 3.0).value(), 5.0);
352 }
353
354 #[test]
355 fn operator_neg() {
356 let q = TU::new(5.0);
357 assert_eq!((-q).value(), -5.0);
358 assert_eq!((-(-q)).value(), 5.0);
359 }
360
361 #[test]
362 fn operator_rem() {
363 let q = TU::new(10.0);
364 assert_eq!((q % 3.0).value(), 1.0);
365 }
366
367 #[test]
372 fn operator_add_assign() {
373 let mut q = TU::new(5.0);
374 q += TU::new(3.0);
375 assert_eq!(q.value(), 8.0);
376 }
377
378 #[test]
379 fn operator_sub_assign() {
380 let mut q = TU::new(10.0);
381 q -= TU::new(3.0);
382 assert_eq!(q.value(), 7.0);
383 }
384
385 #[test]
386 fn operator_div_assign() {
387 let mut q = TU::new(20.0);
388 q /= 4.0;
389 assert_eq!(q.value(), 5.0);
390 }
391
392 #[test]
397 fn partial_eq_f64() {
398 let q = TU::new(5.0);
399 assert!(q == 5.0);
400 assert!(!(q == 4.0));
401 }
402
403 #[test]
408 fn division_creates_per_type() {
409 let num = TU::new(100.0);
410 let den = Dtu::new(20.0);
411 let ratio: Quantity<Per<TestUnit, DoubleTestUnit>> = num / den;
412 assert!((ratio.value() - 5.0).abs() < 1e-12);
413 }
414
415 #[test]
416 fn per_ratio_conversion() {
417 let v1: Quantity<Per<DoubleTestUnit, TestUnit>> = Quantity::new(10.0);
418 let v2: Quantity<Per<TestUnit, TestUnit>> = v1.to();
419 assert!((v2.value() - 20.0).abs() < 1e-12);
420 }
421
422 #[test]
423 fn per_multiplication_recovers_numerator() {
424 let rate: Quantity<Per<TestUnit, DoubleTestUnit>> = Quantity::new(5.0);
425 let time = Dtu::new(4.0);
426 let result: TU = (rate * time).to();
429 assert!((result.value() - 20.0).abs() < 1e-12);
430 }
431
432 #[test]
433 fn per_multiplication_commutative() {
434 let rate: Quantity<Per<TestUnit, DoubleTestUnit>> = Quantity::new(5.0);
435 let time = Dtu::new(4.0);
436 let result1: TU = (rate * time).to();
437 let result2: TU = (time * rate).to();
438 assert!((result1.value() - result2.value()).abs() < 1e-12);
439 }
440
441 #[test]
446 fn simplify_per_u_u_to_unitless() {
447 let ratio: Quantity<Per<TestUnit, TestUnit>> = Quantity::new(1.23456);
448 let unitless: Quantity<Unitless> = ratio.simplify();
449 assert!((unitless.value() - 1.23456).abs() < 1e-12);
450 }
451
452 #[test]
453 fn simplify_per_n_per_n_d_to_d() {
454 let q: Quantity<Per<TestUnit, Per<TestUnit, DoubleTestUnit>>> = Quantity::new(7.5);
455 let simplified: Dtu = q.simplify();
456 assert!((simplified.value() - 7.5).abs() < 1e-12);
457 }
458
459 #[test]
464 fn per_u_u_asin() {
465 let ratio: Quantity<Per<TestUnit, TestUnit>> = Quantity::new(0.5);
466 let result = ratio.asin();
467 assert!((result - 0.5_f64.asin()).abs() < 1e-12);
468 }
469
470 #[test]
471 fn per_u_u_asin_boundary_values() {
472 let one: Quantity<Per<TestUnit, TestUnit>> = Quantity::new(1.0);
473 assert!((one.asin() - core::f64::consts::FRAC_PI_2).abs() < 1e-12);
474
475 let neg_one: Quantity<Per<TestUnit, TestUnit>> = Quantity::new(-1.0);
476 assert!((neg_one.asin() - (-core::f64::consts::FRAC_PI_2)).abs() < 1e-12);
477
478 let zero: Quantity<Per<TestUnit, TestUnit>> = Quantity::new(0.0);
479 assert!((zero.asin() - 0.0).abs() < 1e-12);
480 }
481
482 #[test]
487 fn display_simple_quantity() {
488 let q = TU::new(42.5);
489 let s = format!("{}", q);
490 assert_eq!(s, "42.5 tu");
491 }
492
493 #[test]
494 fn display_per_quantity() {
495 let q: Quantity<Per<TestUnit, DoubleTestUnit>> = Quantity::new(2.5);
496 let s = format!("{}", q);
497 assert_eq!(s, "2.5 tu/dtu");
498 }
499
500 #[test]
501 fn display_negative_value() {
502 let q = TU::new(-99.9);
503 let s = format!("{}", q);
504 assert_eq!(s, "-99.9 tu");
505 }
506
507 #[test]
512 fn edge_case_zero() {
513 let zero = TU::new(0.0);
514 assert_eq!(zero.value(), 0.0);
515 assert_eq!((-zero).value(), 0.0);
516 assert_eq!(zero.abs().value(), 0.0);
517 }
518
519 #[test]
520 fn edge_case_negative_values() {
521 let neg = TU::new(-10.0);
522 let pos = TU::new(5.0);
523
524 assert_eq!((neg + pos).value(), -5.0);
525 assert_eq!((neg - pos).value(), -15.0);
526 assert_eq!((neg * 2.0).value(), -20.0);
527 assert_eq!(neg.abs().value(), 10.0);
528 }
529
530 #[test]
531 fn edge_case_large_values() {
532 let large = TU::new(1e100);
533 let small = TU::new(1e-100);
534 assert_eq!(large.value(), 1e100);
535 assert_eq!(small.value(), 1e-100);
536 }
537
538 #[test]
539 fn edge_case_infinity() {
540 let inf = TU::new(f64::INFINITY);
541 let neg_inf = TU::new(f64::NEG_INFINITY);
542
543 assert!(inf.value().is_infinite());
544 assert!(neg_inf.value().is_infinite());
545 assert_eq!(inf.value().signum(), 1.0);
546 assert_eq!(neg_inf.value().signum(), -1.0);
547 }
548
549 #[cfg(feature = "serde")]
554 mod serde_tests {
555 use super::*;
556 use serde::{Deserialize, Serialize};
557
558 #[test]
559 fn serialize_quantity() {
560 let q = TU::new(42.5);
561 let json = serde_json::to_string(&q).unwrap();
562 assert_eq!(json, "42.5");
563 }
564
565 #[test]
566 fn deserialize_quantity() {
567 let json = "42.5";
568 let q: TU = serde_json::from_str(json).unwrap();
569 assert_eq!(q.value(), 42.5);
570 }
571
572 #[test]
573 fn serde_roundtrip() {
574 let original = TU::new(123.456);
575 let json = serde_json::to_string(&original).unwrap();
576 let restored: TU = serde_json::from_str(&json).unwrap();
577 assert!((restored.value() - original.value()).abs() < 1e-12);
578 }
579
580 #[derive(Serialize, Deserialize, Debug)]
585 struct TestStruct {
586 #[serde(with = "crate::serde_with_unit")]
587 distance: TU,
588 }
589
590 #[test]
591 fn serde_with_unit_serialize() {
592 let data = TestStruct {
593 distance: TU::new(42.5),
594 };
595 let json = serde_json::to_string(&data).unwrap();
596 assert!(json.contains("\"value\""));
597 assert!(json.contains("\"unit\""));
598 assert!(json.contains("42.5"));
599 assert!(json.contains("\"tu\""));
600 }
601
602 #[test]
603 fn serde_with_unit_deserialize() {
604 let json = r#"{"distance":{"value":42.5,"unit":"tu"}}"#;
605 let data: TestStruct = serde_json::from_str(json).unwrap();
606 assert_eq!(data.distance.value(), 42.5);
607 }
608
609 #[test]
610 fn serde_with_unit_deserialize_no_unit_field() {
611 let json = r#"{"distance":{"value":42.5}}"#;
613 let data: TestStruct = serde_json::from_str(json).unwrap();
614 assert_eq!(data.distance.value(), 42.5);
615 }
616
617 #[test]
618 fn serde_with_unit_deserialize_wrong_unit() {
619 let json = r#"{"distance":{"value":42.5,"unit":"wrong"}}"#;
620 let result: Result<TestStruct, _> = serde_json::from_str(json);
621 assert!(result.is_err());
622 let err_msg = result.unwrap_err().to_string();
623 assert!(err_msg.contains("unit mismatch") || err_msg.contains("expected"));
624 }
625
626 #[test]
627 fn serde_with_unit_deserialize_missing_value() {
628 let json = r#"{"distance":{"unit":"tu"}}"#;
629 let result: Result<TestStruct, _> = serde_json::from_str(json);
630 assert!(result.is_err());
631 let err_msg = result.unwrap_err().to_string();
632 assert!(err_msg.contains("missing field") || err_msg.contains("value"));
633 }
634
635 #[test]
636 fn serde_with_unit_deserialize_duplicate_value() {
637 let json = r#"{"distance":{"value":42.5,"value":100.0,"unit":"tu"}}"#;
638 let result: Result<TestStruct, _> = serde_json::from_str(json);
639 let _ = result;
642 }
643
644 #[test]
645 fn serde_with_unit_deserialize_duplicate_unit() {
646 let json = r#"{"distance":{"value":42.5,"unit":"tu","unit":"tu"}}"#;
647 let result: Result<TestStruct, _> = serde_json::from_str(json);
648 let _ = result;
650 }
651
652 #[test]
653 fn serde_with_unit_deserialize_invalid_format() {
654 let json = r#"{"distance":"not_an_object"}"#;
656 let result: Result<TestStruct, _> = serde_json::from_str(json);
657 assert!(result.is_err());
658 }
659
660 #[test]
661 fn serde_with_unit_deserialize_array() {
662 let json = r#"{"distance":[42.5, "tu"]}"#;
664 let result: Result<TestStruct, _> = serde_json::from_str(json);
665 assert!(result.is_err());
666 }
667
668 #[test]
669 fn serde_with_unit_roundtrip() {
670 let original = TestStruct {
671 distance: TU::new(123.456),
672 };
673 let json = serde_json::to_string(&original).unwrap();
674 let restored: TestStruct = serde_json::from_str(&json).unwrap();
675 assert!((restored.distance.value() - original.distance.value()).abs() < 1e-12);
676 }
677
678 #[test]
679 fn serde_with_unit_special_values() {
680 let test_large = TestStruct {
684 distance: TU::new(1e100),
685 };
686 let json = serde_json::to_string(&test_large).unwrap();
687 let restored: TestStruct = serde_json::from_str(&json).unwrap();
688 assert!((restored.distance.value() - 1e100).abs() < 1e88);
689
690 let test_small = TestStruct {
691 distance: TU::new(-1e-100),
692 };
693 let json = serde_json::to_string(&test_small).unwrap();
694 let restored: TestStruct = serde_json::from_str(&json).unwrap();
695 assert!((restored.distance.value() + 1e-100).abs() < 1e-112);
696 }
697 }
698}