1use crate::error::{CoreError, CoreResult, ErrorContext};
7use crate::numeric::ScientificNumber;
8use std::collections::HashMap;
9use std::fmt;
10
11#[derive(Debug, Clone, PartialEq, Eq, Hash)]
13pub enum Dimension {
14 Length,
15 Time,
16 Mass,
17 Temperature,
18 Current,
19 Amount, LuminousIntensity,
21 Angle,
22 SolidAngle,
23 Area, Volume, Velocity, Acceleration, Force, Energy, Power, Pressure, Frequency, Voltage, Resistance, Capacitance, Inductance, MagneticField, Dimensionless, }
40
41impl fmt::Display for Dimension {
42 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
43 let name = match self {
44 Self::Length => "Length",
45 Self::Time => "Time",
46 Self::Mass => "Mass",
47 Self::Temperature => "Temperature",
48 Self::Current => "Current",
49 Self::Amount => "Amount",
50 Self::LuminousIntensity => "Luminous Intensity",
51 Self::Angle => "Angle",
52 Self::SolidAngle => "Solid Angle",
53 Self::Area => "Area",
54 Self::Volume => "Volume",
55 Self::Velocity => "Velocity",
56 Self::Acceleration => "Acceleration",
57 Self::Force => "Force",
58 Self::Energy => "Energy",
59 Self::Power => "Power",
60 Self::Pressure => "Pressure",
61 Self::Frequency => "Frequency",
62 Self::Voltage => "Voltage",
63 Self::Resistance => "Resistance",
64 Self::Capacitance => "Capacitance",
65 Self::Inductance => "Inductance",
66 Self::MagneticField => "Magnetic Field",
67 Self::Dimensionless => "Dimensionless",
68 };
69 write!(f, "{name}")
70 }
71}
72
73#[derive(Debug, Clone, PartialEq, Eq, Hash)]
75pub enum UnitSystem {
76 SI, Imperial, CGS, Natural, Atomic, Planck, }
83
84impl fmt::Display for UnitSystem {
85 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
86 let name = match self {
87 Self::SI => "SI",
88 Self::Imperial => "Imperial",
89 Self::CGS => "CGS",
90 Self::Natural => "Natural",
91 Self::Atomic => "Atomic",
92 Self::Planck => "Planck",
93 };
94 write!(f, "{name}")
95 }
96}
97
98#[derive(Debug, Clone)]
100pub struct UnitDefinition {
101 pub name: String,
103 pub symbol: String,
105 pub dimension: Dimension,
107 pub system: UnitSystem,
109 pub si_factor: f64,
111 pub si_offset: f64,
113 pub isbase: bool,
115}
116
117impl UnitDefinition {
118 pub fn new(
120 name: String,
121 symbol: String,
122 dimension: Dimension,
123 system: UnitSystem,
124 si_factor: f64,
125 si_offset: f64,
126 isbase: bool,
127 ) -> Self {
128 Self {
129 name,
130 symbol,
131 dimension,
132 system,
133 si_factor,
134 si_offset,
135 isbase,
136 }
137 }
138
139 pub fn to_si(&self, value: f64) -> f64 {
141 value * self.si_factor + self.si_offset
142 }
143
144 pub fn from_si(&self, value: f64) -> f64 {
146 (value - self.si_offset) / self.si_factor
147 }
148}
149
150impl fmt::Display for UnitDefinition {
151 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
152 write!(
153 f,
154 "{} ({}) - {} [{}]",
155 self.name, self.symbol, self.dimension, self.system
156 )
157 }
158}
159
160#[derive(Debug, Clone)]
162pub struct UnitValue<T: ScientificNumber> {
163 value: T,
164 unit: String, }
166
167impl<T: ScientificNumber> UnitValue<T> {
168 pub fn new(value: T, unit: String) -> Self {
170 Self { value, unit }
171 }
172
173 pub fn value(&self) -> T {
175 self.value
176 }
177
178 pub fn unit(&self) -> &str {
180 &self.unit
181 }
182}
183
184impl<T: ScientificNumber + fmt::Display> fmt::Display for UnitValue<T> {
185 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
186 write!(f, "{} {}", self.value, self.unit)
187 }
188}
189
190pub struct UnitRegistry {
192 units: HashMap<String, UnitDefinition>,
193 aliases: HashMap<String, String>, }
195
196impl UnitRegistry {
197 pub fn new() -> Self {
199 let mut registry = Self {
200 units: HashMap::new(),
201 aliases: HashMap::new(),
202 };
203
204 registry.register_standard_units();
205 registry
206 }
207
208 pub fn register_unit(&mut self, unit: UnitDefinition) {
210 self.units.insert(unit.symbol.clone(), unit);
211 }
212
213 pub fn register_alias(&mut self, alias: String, canonical: String) {
215 self.aliases.insert(alias, canonical);
216 }
217
218 pub fn get_unit(&self, symbol: &str) -> Option<&UnitDefinition> {
220 if let Some(unit) = self.units.get(symbol) {
222 return Some(unit);
223 }
224
225 if let Some(canonical) = self.aliases.get(symbol) {
227 return self.units.get(canonical);
228 }
229
230 None
231 }
232
233 pub fn are_compatible(&self, unit1: &str, unit2: &str) -> bool {
235 if let (Some(u1), Some(u2)) = (self.get_unit(unit1), self.get_unit(unit2)) {
236 u1.dimension == u2.dimension
237 } else {
238 false
239 }
240 }
241
242 pub fn convert(&self, value: f64, from_unit: &str, tounit: &str) -> CoreResult<f64> {
244 let from = self.get_unit(from_unit).ok_or_else(|| {
245 CoreError::InvalidArgument(ErrorContext::new(format!("Unknown unit: {from_unit}")))
246 })?;
247
248 let to = self.get_unit(tounit).ok_or_else(|| {
249 CoreError::InvalidArgument(ErrorContext::new(format!("Unknown unit: {tounit}")))
250 })?;
251
252 if from.dimension != to.dimension {
253 return Err(CoreError::InvalidArgument(ErrorContext::new(format!(
254 "Cannot convert {} ({}) to {} ({}): incompatible dimensions",
255 from_unit, from.dimension, tounit, to.dimension
256 ))));
257 }
258
259 let si_value = from.to_si(value);
261 let result = to.from_si(si_value);
262
263 Ok(result)
264 }
265
266 pub fn convert_value<T>(&self, value: &UnitValue<T>, tounit: &str) -> CoreResult<UnitValue<T>>
268 where
269 T: ScientificNumber + TryFrom<f64>,
270 f64: From<T>,
271 {
272 let converted_f64 = self.convert(value.value.into(), &value.unit, tounit)?;
273
274 let converted_value = T::try_from(converted_f64).map_err(|_| {
275 CoreError::TypeError(ErrorContext::new(
276 "Failed to convert back to original numeric type",
277 ))
278 })?;
279
280 Ok(UnitValue::new(converted_value, tounit.to_string()))
281 }
282
283 pub fn get_units_for_dimension(&self, dimension: &Dimension) -> Vec<&UnitDefinition> {
285 self.units
286 .values()
287 .filter(|unit| unit.dimension == *dimension)
288 .collect()
289 }
290
291 pub fn get_units_for_system(&self, system: &UnitSystem) -> Vec<&UnitDefinition> {
293 self.units
294 .values()
295 .filter(|unit| unit.system == *system)
296 .collect()
297 }
298
299 pub fn list_units(&self) -> Vec<&UnitDefinition> {
301 self.units.values().collect()
302 }
303
304 fn register_standard_units(&mut self) {
306 self.register_unit(UnitDefinition::new(
308 "meter".to_string(),
309 "m".to_string(),
310 Dimension::Length,
311 UnitSystem::SI,
312 1.0,
313 0.0,
314 true,
315 ));
316 self.register_unit(UnitDefinition::new(
317 "second".to_string(),
318 "s".to_string(),
319 Dimension::Time,
320 UnitSystem::SI,
321 1.0,
322 0.0,
323 true,
324 ));
325 self.register_unit(UnitDefinition::new(
326 "kilogram".to_string(),
327 "kg".to_string(),
328 Dimension::Mass,
329 UnitSystem::SI,
330 1.0,
331 0.0,
332 true,
333 ));
334 self.register_unit(UnitDefinition::new(
335 "kelvin".to_string(),
336 "K".to_string(),
337 Dimension::Temperature,
338 UnitSystem::SI,
339 1.0,
340 0.0,
341 true,
342 ));
343 self.register_unit(UnitDefinition::new(
344 "ampere".to_string(),
345 "A".to_string(),
346 Dimension::Current,
347 UnitSystem::SI,
348 1.0,
349 0.0,
350 true,
351 ));
352 self.register_unit(UnitDefinition::new(
353 "mole".to_string(),
354 "mol".to_string(),
355 Dimension::Amount,
356 UnitSystem::SI,
357 1.0,
358 0.0,
359 true,
360 ));
361 self.register_unit(UnitDefinition::new(
362 "candela".to_string(),
363 "cd".to_string(),
364 Dimension::LuminousIntensity,
365 UnitSystem::SI,
366 1.0,
367 0.0,
368 true,
369 ));
370
371 self.register_unit(UnitDefinition::new(
373 "radian".to_string(),
374 "rad".to_string(),
375 Dimension::Angle,
376 UnitSystem::SI,
377 1.0,
378 0.0,
379 false,
380 ));
381 self.register_unit(UnitDefinition::new(
382 "hertz".to_string(),
383 "Hz".to_string(),
384 Dimension::Frequency,
385 UnitSystem::SI,
386 1.0,
387 0.0,
388 false,
389 ));
390 self.register_unit(UnitDefinition::new(
391 "newton".to_string(),
392 "N".to_string(),
393 Dimension::Force,
394 UnitSystem::SI,
395 1.0,
396 0.0,
397 false,
398 ));
399 self.register_unit(UnitDefinition::new(
400 "joule".to_string(),
401 "J".to_string(),
402 Dimension::Energy,
403 UnitSystem::SI,
404 1.0,
405 0.0,
406 false,
407 ));
408 self.register_unit(UnitDefinition::new(
409 "watt".to_string(),
410 "W".to_string(),
411 Dimension::Power,
412 UnitSystem::SI,
413 1.0,
414 0.0,
415 false,
416 ));
417 self.register_unit(UnitDefinition::new(
418 "pascal".to_string(),
419 "Pa".to_string(),
420 Dimension::Pressure,
421 UnitSystem::SI,
422 1.0,
423 0.0,
424 false,
425 ));
426 self.register_unit(UnitDefinition::new(
427 "volt".to_string(),
428 "V".to_string(),
429 Dimension::Voltage,
430 UnitSystem::SI,
431 1.0,
432 0.0,
433 false,
434 ));
435
436 self.register_unit(UnitDefinition::new(
438 "centimeter".to_string(),
439 "cm".to_string(),
440 Dimension::Length,
441 UnitSystem::CGS,
442 0.01,
443 0.0,
444 false,
445 ));
446 self.register_unit(UnitDefinition::new(
447 "millimeter".to_string(),
448 "mm".to_string(),
449 Dimension::Length,
450 UnitSystem::SI,
451 0.001,
452 0.0,
453 false,
454 ));
455 self.register_unit(UnitDefinition::new(
456 "kilometer".to_string(),
457 "km".to_string(),
458 Dimension::Length,
459 UnitSystem::SI,
460 1000.0,
461 0.0,
462 false,
463 ));
464 self.register_unit(UnitDefinition::new(
465 "inch".to_string(),
466 "in".to_string(),
467 Dimension::Length,
468 UnitSystem::Imperial,
469 0.0254,
470 0.0,
471 false,
472 ));
473 self.register_unit(UnitDefinition::new(
474 "foot".to_string(),
475 "ft".to_string(),
476 Dimension::Length,
477 UnitSystem::Imperial,
478 0.3048,
479 0.0,
480 false,
481 ));
482 self.register_unit(UnitDefinition::new(
483 "yard".to_string(),
484 "yd".to_string(),
485 Dimension::Length,
486 UnitSystem::Imperial,
487 0.9144,
488 0.0,
489 false,
490 ));
491 self.register_unit(UnitDefinition::new(
492 "mile".to_string(),
493 "mi".to_string(),
494 Dimension::Length,
495 UnitSystem::Imperial,
496 1609.344,
497 0.0,
498 false,
499 ));
500
501 self.register_unit(UnitDefinition::new(
503 "minute".to_string(),
504 "min".to_string(),
505 Dimension::Time,
506 UnitSystem::SI,
507 60.0,
508 0.0,
509 false,
510 ));
511 self.register_unit(UnitDefinition::new(
512 "hour".to_string(),
513 "h".to_string(),
514 Dimension::Time,
515 UnitSystem::SI,
516 3600.0,
517 0.0,
518 false,
519 ));
520 self.register_unit(UnitDefinition::new(
521 "day".to_string(),
522 "d".to_string(),
523 Dimension::Time,
524 UnitSystem::SI,
525 86400.0,
526 0.0,
527 false,
528 ));
529 self.register_unit(UnitDefinition::new(
530 "year".to_string(),
531 "yr".to_string(),
532 Dimension::Time,
533 UnitSystem::SI,
534 31_557_600.0,
535 0.0,
536 false,
537 )); self.register_unit(UnitDefinition::new(
541 "gram".to_string(),
542 "g".to_string(),
543 Dimension::Mass,
544 UnitSystem::CGS,
545 0.001,
546 0.0,
547 false,
548 ));
549 self.register_unit(UnitDefinition::new(
550 "pound".to_string(),
551 "lb".to_string(),
552 Dimension::Mass,
553 UnitSystem::Imperial,
554 0.453_592_37,
555 0.0,
556 false,
557 ));
558 self.register_unit(UnitDefinition::new(
559 "ounce".to_string(),
560 "oz".to_string(),
561 Dimension::Mass,
562 UnitSystem::Imperial,
563 0.028_349_523_125,
564 0.0,
565 false,
566 ));
567 self.register_unit(UnitDefinition::new(
568 "ton".to_string(),
569 "t".to_string(),
570 Dimension::Mass,
571 UnitSystem::SI,
572 1000.0,
573 0.0,
574 false,
575 ));
576
577 self.register_unit(UnitDefinition::new(
579 "celsius".to_string(),
580 "°C".to_string(),
581 Dimension::Temperature,
582 UnitSystem::SI,
583 1.0,
584 273.15,
585 false,
586 ));
587 self.register_unit(UnitDefinition::new(
588 "fahrenheit".to_string(),
589 "°F".to_string(),
590 Dimension::Temperature,
591 UnitSystem::Imperial,
592 5.0 / 9.0,
593 459.67 * 5.0 / 9.0,
594 false,
595 ));
596
597 self.register_unit(UnitDefinition::new(
599 "degree".to_string(),
600 "°".to_string(),
601 Dimension::Angle,
602 UnitSystem::SI,
603 std::f64::consts::PI / 180.0,
604 0.0,
605 false,
606 ));
607
608 self.register_unit(UnitDefinition::new(
610 "calorie".to_string(),
611 "cal".to_string(),
612 Dimension::Energy,
613 UnitSystem::CGS,
614 4.184,
615 0.0,
616 false,
617 ));
618 self.register_unit(UnitDefinition::new(
619 "kilocalorie".to_string(),
620 "kcal".to_string(),
621 Dimension::Energy,
622 UnitSystem::SI,
623 4184.0,
624 0.0,
625 false,
626 ));
627 self.register_unit(UnitDefinition::new(
628 "electron_volt".to_string(),
629 "eV".to_string(),
630 Dimension::Energy,
631 UnitSystem::Atomic,
632 1.602_176_634e-19,
633 0.0,
634 false,
635 ));
636 self.register_unit(UnitDefinition::new(
637 "kilowatt_hour".to_string(),
638 "kWh".to_string(),
639 Dimension::Energy,
640 UnitSystem::SI,
641 3_600_000.0,
642 0.0,
643 false,
644 ));
645
646 self.register_unit(UnitDefinition::new(
648 "horsepower".to_string(),
649 "hp".to_string(),
650 Dimension::Power,
651 UnitSystem::Imperial,
652 745.7,
653 0.0,
654 false,
655 ));
656
657 self.register_unit(UnitDefinition::new(
659 "atmosphere".to_string(),
660 "atm".to_string(),
661 Dimension::Pressure,
662 UnitSystem::SI,
663 101_325.0,
664 0.0,
665 false,
666 ));
667 self.register_unit(UnitDefinition::new(
668 "bar".to_string(),
669 "bar".to_string(),
670 Dimension::Pressure,
671 UnitSystem::SI,
672 100_000.0,
673 0.0,
674 false,
675 ));
676 self.register_unit(UnitDefinition::new(
677 "torr".to_string(),
678 "Torr".to_string(),
679 Dimension::Pressure,
680 UnitSystem::SI,
681 133.322,
682 0.0,
683 false,
684 ));
685 self.register_unit(UnitDefinition::new(
686 "pounds_per_square_inch".to_string(),
687 "psi".to_string(),
688 Dimension::Pressure,
689 UnitSystem::Imperial,
690 6894.76,
691 0.0,
692 false,
693 ));
694
695 self.register_alias("metre".to_string(), "m".to_string());
697 self.register_alias("meters".to_string(), "m".to_string());
698 self.register_alias("metres".to_string(), "m".to_string());
699 self.register_alias("seconds".to_string(), "s".to_string());
700 self.register_alias("minutes".to_string(), "min".to_string());
701 self.register_alias("hours".to_string(), "h".to_string());
702 self.register_alias("days".to_string(), "d".to_string());
703 self.register_alias("years".to_string(), "yr".to_string());
704 self.register_alias("degrees".to_string(), "°".to_string());
705 self.register_alias("deg".to_string(), "°".to_string());
706 self.register_alias("radians".to_string(), "rad".to_string());
707 self.register_alias("inches".to_string(), "in".to_string());
708 self.register_alias("feet".to_string(), "ft".to_string());
709 self.register_alias("pounds".to_string(), "lb".to_string());
710 self.register_alias("lbs".to_string(), "lb".to_string());
711 }
712}
713
714impl Default for UnitRegistry {
715 fn default() -> Self {
716 Self::new()
717 }
718}
719
720static GLOBAL_UNIT_REGISTRY: std::sync::LazyLock<std::sync::RwLock<UnitRegistry>> =
722 std::sync::LazyLock::new(|| std::sync::RwLock::new(UnitRegistry::new()));
723
724#[allow(dead_code)]
726pub fn global_unit_registry() -> &'static std::sync::RwLock<UnitRegistry> {
727 &GLOBAL_UNIT_REGISTRY
728}
729
730#[allow(dead_code)]
732pub fn convert(value: f64, from_unit: &str, tounit: &str) -> CoreResult<f64> {
733 let registry = global_unit_registry().read().map_err(|_| {
734 CoreError::ComputationError(ErrorContext::new(
735 "Failed to acquire read lock on unit registry",
736 ))
737 })?;
738 registry.convert(value, from_unit, tounit)
739}
740
741#[allow(dead_code)]
743pub fn unit_value<T: ScientificNumber>(value: T, unit: &str) -> UnitValue<T> {
744 UnitValue::new(value, unit.to_string())
745}
746
747pub mod conversions {
749
750 pub fn celsius_to_fahrenheit(celsius: f64) -> f64 {
752 celsius * 9.0 / 5.0 + 32.0
753 }
754
755 pub fn fahrenheit_to_celsius(fahrenheit: f64) -> f64 {
756 (fahrenheit - 32.0) * 5.0 / 9.0
757 }
758
759 pub fn celsius_to_kelvin(celsius: f64) -> f64 {
760 celsius + 273.15
761 }
762
763 pub fn kelvin_to_celsius(kelvin: f64) -> f64 {
764 kelvin - 273.15
765 }
766
767 pub fn meters_to_feet(meters: f64) -> f64 {
769 meters / 0.3048
770 }
771
772 pub fn feet_to_meters(feet: f64) -> f64 {
773 feet * 0.3048
774 }
775
776 pub fn inches_to_cm(inches: f64) -> f64 {
777 inches * 2.54
778 }
779
780 pub fn cm_to_inches(cm: f64) -> f64 {
781 cm / 2.54
782 }
783
784 pub fn kg_to_lbs(kg: f64) -> f64 {
786 kg / 0.453_592_37
787 }
788
789 pub fn lbs_to_kg(lbs: f64) -> f64 {
790 lbs * 0.453_592_37
791 }
792
793 pub fn degrees_to_radians(degrees: f64) -> f64 {
795 degrees * std::f64::consts::PI / 180.0
796 }
797
798 pub fn radians_to_degrees(radians: f64) -> f64 {
799 radians * 180.0 / std::f64::consts::PI
800 }
801
802 pub fn joules_to_calories(joules: f64) -> f64 {
804 joules / 4.184
805 }
806
807 pub fn calories_to_joules(calories: f64) -> f64 {
808 calories * 4.184
809 }
810
811 pub fn ev_to_joules(ev: f64) -> f64 {
812 ev * 1.602_176_634e-19
813 }
814
815 pub fn joules_to_ev(joules: f64) -> f64 {
816 joules / 1.602_176_634e-19
817 }
818}
819
820#[macro_export]
822macro_rules! convert_units {
823 ($value:expr, $from:expr => $to:expr) => {
824 $crate::units::convert($value, $from, $to)
825 };
826}
827
828#[cfg(test)]
829mod tests {
830 use super::*;
831
832 #[test]
833 fn test_unit_registry_creation() {
834 let registry = UnitRegistry::new();
835
836 assert!(registry.get_unit("m").is_some());
837 assert!(registry.get_unit("kg").is_some());
838 assert!(registry.get_unit("s").is_some());
839 assert!(registry.get_unit("K").is_some());
840 }
841
842 #[test]
843 fn test_unit_compatibility() {
844 let registry = UnitRegistry::new();
845
846 assert!(registry.are_compatible("m", "km"));
847 assert!(registry.are_compatible("m", "ft"));
848 assert!(!registry.are_compatible("m", "kg"));
849 assert!(!registry.are_compatible("s", "m"));
850 }
851
852 #[test]
853 fn test_length_conversions() {
854 let registry = UnitRegistry::new();
855
856 let result = registry.convert(1000.0, "m", "km").unwrap();
858 assert!((result - 1.0).abs() < 1e-10);
859
860 let result = registry.convert(1.0, "m", "ft").unwrap();
862 assert!((result - 3.280_839_895).abs() < 1e-6);
863
864 let result = registry.convert(1.0, "in", "cm").unwrap();
866 assert!((result - 2.54).abs() < 1e-10);
867 }
868
869 #[test]
870 fn test_temperature_conversions() {
871 let registry = UnitRegistry::new();
872
873 let result = registry.convert(0.0, "°C", "K").unwrap();
875 assert!((result - 273.15).abs() < 1e-10);
876
877 let result = registry.convert(32.0, "°F", "°C").unwrap();
879 assert!((result - 0.0).abs() < 1e-10);
880
881 let result = registry.convert(32.0, "°F", "K").unwrap();
883 assert!((result - 273.15).abs() < 1e-10);
884 }
885
886 #[test]
887 fn test_angle_conversions() {
888 let registry = UnitRegistry::new();
889
890 let result = registry.convert(180.0, "°", "rad").unwrap();
892 assert!((result - std::f64::consts::PI).abs() < 1e-10);
893
894 let result = registry
896 .convert(std::f64::consts::PI / 2.0, "rad", "°")
897 .unwrap();
898 assert!((result - 90.0).abs() < 1e-10);
899 }
900
901 #[test]
902 fn test_energy_conversions() {
903 let registry = UnitRegistry::new();
904
905 let result = registry.convert(4.184, "J", "cal").unwrap();
907 assert!((result - 1.0).abs() < 1e-10);
908
909 let result = registry.convert(1.0, "eV", "J").unwrap();
911 assert!((result - 1.602_176_634e-19).abs() < 1e-25);
912 }
913
914 #[test]
915 fn test_unit_value() {
916 let value = UnitValue::new(5.0, "m".to_string());
917 assert_eq!(value.value(), 5.0);
918 assert_eq!(value.unit(), "m");
919
920 let formatted = format!("{value}");
921 assert!(formatted.contains("5"));
922 assert!(formatted.contains("m"));
923 }
924
925 #[test]
926 fn test_unit_value_conversion() {
927 let registry = UnitRegistry::new();
928 let value = UnitValue::new(1000.0, "m".to_string());
929
930 let converted: UnitValue<f64> = registry.convert_value(&value, "km").unwrap();
931 assert!((converted.value() - 1.0).abs() < 1e-10);
932 assert_eq!(converted.unit(), "km");
933 }
934
935 #[test]
936 fn test_incompatible_conversion() {
937 let registry = UnitRegistry::new();
938
939 let result = registry.convert(1.0, "m", "kg");
940 assert!(result.is_err());
941 }
942
943 #[test]
944 fn test_unknown_unit() {
945 let registry = UnitRegistry::new();
946
947 let result = registry.convert(1.0, "unknown", "m");
948 assert!(result.is_err());
949 }
950
951 #[test]
952 fn test_aliases() {
953 let registry = UnitRegistry::new();
954
955 assert!(registry.get_unit("meters").is_some());
957 assert!(registry.get_unit("degrees").is_some());
958 assert!(registry.get_unit("feet").is_some());
959 }
960
961 #[test]
962 fn test_dimension_filtering() {
963 let registry = UnitRegistry::new();
964
965 let length_units = registry.get_units_for_dimension(&Dimension::Length);
966 assert!(length_units.len() > 5); let si_units = registry.get_units_for_system(&UnitSystem::SI);
969 assert!(si_units.len() > 10);
970 }
971
972 #[test]
973 fn test_global_convert_function() {
974 let result = convert(1000.0, "m", "km").unwrap();
975 assert!((result - 1.0).abs() < 1e-10);
976 }
977
978 #[test]
979 fn test_conversion_utilities() {
980 use conversions::*;
981
982 assert!((celsius_to_fahrenheit(0.0) - 32.0).abs() < 1e-10);
983 assert!((fahrenheit_to_celsius(32.0) - 0.0).abs() < 1e-10);
984 assert!((celsius_to_kelvin(0.0) - 273.15).abs() < 1e-10);
985
986 assert!((degrees_to_radians(180.0) - std::f64::consts::PI).abs() < 1e-10);
987 assert!((radians_to_degrees(std::f64::consts::PI) - 180.0).abs() < 1e-10);
988
989 assert!((meters_to_feet(1.0) - 3.280_839_895).abs() < 1e-6);
990 assert!((feet_to_meters(1.0) - 0.3048).abs() < 1e-10);
991 }
992}