1use anyhow::{anyhow, Result};
2use std::collections::HashMap;
3
4#[derive(Debug, Clone, PartialEq)]
6enum UnitCategory {
7 Length,
8 Mass,
9 Temperature,
10 Volume,
11 Time,
12 Area,
13 Speed,
14 Pressure,
15}
16
17#[derive(Debug, Clone)]
19struct Unit {
20 category: UnitCategory,
21 to_si_factor: f64, to_si_offset: f64, from_si_factor: f64, from_si_offset: f64, }
26
27impl Unit {
28 fn simple(category: UnitCategory, to_si_factor: f64) -> Self {
30 Unit {
31 category,
32 to_si_factor,
33 to_si_offset: 0.0,
34 from_si_factor: 1.0 / to_si_factor,
35 from_si_offset: 0.0,
36 }
37 }
38
39 fn temperature(
41 to_si_factor: f64,
42 to_si_offset: f64,
43 from_si_factor: f64,
44 from_si_offset: f64,
45 ) -> Self {
46 Unit {
47 category: UnitCategory::Temperature,
48 to_si_factor,
49 to_si_offset,
50 from_si_factor,
51 from_si_offset,
52 }
53 }
54}
55
56pub struct UnitConverter {
58 units: HashMap<String, Unit>,
59}
60
61impl UnitConverter {
62 pub fn new() -> Self {
63 let mut units = HashMap::new();
64
65 units.insert("m".to_string(), Unit::simple(UnitCategory::Length, 1.0));
67 units.insert("meter".to_string(), Unit::simple(UnitCategory::Length, 1.0));
68 units.insert(
69 "meters".to_string(),
70 Unit::simple(UnitCategory::Length, 1.0),
71 );
72 units.insert("metre".to_string(), Unit::simple(UnitCategory::Length, 1.0));
73 units.insert(
74 "metres".to_string(),
75 Unit::simple(UnitCategory::Length, 1.0),
76 );
77
78 units.insert("km".to_string(), Unit::simple(UnitCategory::Length, 1000.0));
80 units.insert(
81 "kilometer".to_string(),
82 Unit::simple(UnitCategory::Length, 1000.0),
83 );
84 units.insert(
85 "kilometers".to_string(),
86 Unit::simple(UnitCategory::Length, 1000.0),
87 );
88 units.insert("cm".to_string(), Unit::simple(UnitCategory::Length, 0.01));
89 units.insert(
90 "centimeter".to_string(),
91 Unit::simple(UnitCategory::Length, 0.01),
92 );
93 units.insert(
94 "centimeters".to_string(),
95 Unit::simple(UnitCategory::Length, 0.01),
96 );
97 units.insert("mm".to_string(), Unit::simple(UnitCategory::Length, 0.001));
98 units.insert(
99 "millimeter".to_string(),
100 Unit::simple(UnitCategory::Length, 0.001),
101 );
102 units.insert(
103 "millimeters".to_string(),
104 Unit::simple(UnitCategory::Length, 0.001),
105 );
106 units.insert("nm".to_string(), Unit::simple(UnitCategory::Length, 1e-9));
107 units.insert(
108 "nanometer".to_string(),
109 Unit::simple(UnitCategory::Length, 1e-9),
110 );
111 units.insert("um".to_string(), Unit::simple(UnitCategory::Length, 1e-6));
112 units.insert(
113 "micrometer".to_string(),
114 Unit::simple(UnitCategory::Length, 1e-6),
115 );
116
117 units.insert(
119 "mi".to_string(),
120 Unit::simple(UnitCategory::Length, 1609.344),
121 );
122 units.insert(
123 "mile".to_string(),
124 Unit::simple(UnitCategory::Length, 1609.344),
125 );
126 units.insert(
127 "miles".to_string(),
128 Unit::simple(UnitCategory::Length, 1609.344),
129 );
130 units.insert("yd".to_string(), Unit::simple(UnitCategory::Length, 0.9144));
131 units.insert(
132 "yard".to_string(),
133 Unit::simple(UnitCategory::Length, 0.9144),
134 );
135 units.insert(
136 "yards".to_string(),
137 Unit::simple(UnitCategory::Length, 0.9144),
138 );
139 units.insert("ft".to_string(), Unit::simple(UnitCategory::Length, 0.3048));
140 units.insert(
141 "foot".to_string(),
142 Unit::simple(UnitCategory::Length, 0.3048),
143 );
144 units.insert(
145 "feet".to_string(),
146 Unit::simple(UnitCategory::Length, 0.3048),
147 );
148 units.insert("in".to_string(), Unit::simple(UnitCategory::Length, 0.0254));
149 units.insert(
150 "inch".to_string(),
151 Unit::simple(UnitCategory::Length, 0.0254),
152 );
153 units.insert(
154 "inches".to_string(),
155 Unit::simple(UnitCategory::Length, 0.0254),
156 );
157
158 units.insert(
160 "nmi".to_string(),
161 Unit::simple(UnitCategory::Length, 1852.0),
162 );
163 units.insert(
164 "nautical_mile".to_string(),
165 Unit::simple(UnitCategory::Length, 1852.0),
166 );
167
168 units.insert("kg".to_string(), Unit::simple(UnitCategory::Mass, 1.0));
170 units.insert(
171 "kilogram".to_string(),
172 Unit::simple(UnitCategory::Mass, 1.0),
173 );
174 units.insert(
175 "kilograms".to_string(),
176 Unit::simple(UnitCategory::Mass, 1.0),
177 );
178
179 units.insert("g".to_string(), Unit::simple(UnitCategory::Mass, 0.001));
181 units.insert("gram".to_string(), Unit::simple(UnitCategory::Mass, 0.001));
182 units.insert("grams".to_string(), Unit::simple(UnitCategory::Mass, 0.001));
183 units.insert("mg".to_string(), Unit::simple(UnitCategory::Mass, 0.000001));
184 units.insert(
185 "milligram".to_string(),
186 Unit::simple(UnitCategory::Mass, 0.000001),
187 );
188 units.insert(
189 "milligrams".to_string(),
190 Unit::simple(UnitCategory::Mass, 0.000001),
191 );
192 units.insert("ug".to_string(), Unit::simple(UnitCategory::Mass, 1e-9));
193 units.insert(
194 "microgram".to_string(),
195 Unit::simple(UnitCategory::Mass, 1e-9),
196 );
197 units.insert("t".to_string(), Unit::simple(UnitCategory::Mass, 1000.0));
198 units.insert(
199 "tonne".to_string(),
200 Unit::simple(UnitCategory::Mass, 1000.0),
201 );
202 units.insert(
203 "metric_ton".to_string(),
204 Unit::simple(UnitCategory::Mass, 1000.0),
205 );
206
207 units.insert(
209 "lb".to_string(),
210 Unit::simple(UnitCategory::Mass, 0.45359237),
211 );
212 units.insert(
213 "lbs".to_string(),
214 Unit::simple(UnitCategory::Mass, 0.45359237),
215 );
216 units.insert(
217 "pound".to_string(),
218 Unit::simple(UnitCategory::Mass, 0.45359237),
219 );
220 units.insert(
221 "pounds".to_string(),
222 Unit::simple(UnitCategory::Mass, 0.45359237),
223 );
224 units.insert(
225 "oz".to_string(),
226 Unit::simple(UnitCategory::Mass, 0.028349523125),
227 );
228 units.insert(
229 "ounce".to_string(),
230 Unit::simple(UnitCategory::Mass, 0.028349523125),
231 );
232 units.insert(
233 "ounces".to_string(),
234 Unit::simple(UnitCategory::Mass, 0.028349523125),
235 );
236 units.insert(
237 "ton".to_string(),
238 Unit::simple(UnitCategory::Mass, 907.18474),
239 );
240 units.insert(
241 "short_ton".to_string(),
242 Unit::simple(UnitCategory::Mass, 907.18474),
243 );
244 units.insert(
245 "long_ton".to_string(),
246 Unit::simple(UnitCategory::Mass, 1016.0469088),
247 );
248 units.insert(
249 "stone".to_string(),
250 Unit::simple(UnitCategory::Mass, 6.35029318),
251 );
252
253 units.insert(
255 "k".to_string(),
256 Unit::simple(UnitCategory::Temperature, 1.0),
257 );
258 units.insert(
259 "kelvin".to_string(),
260 Unit::simple(UnitCategory::Temperature, 1.0),
261 );
262
263 units.insert(
265 "c".to_string(),
266 Unit::temperature(1.0, 273.15, 1.0, -273.15),
267 );
268 units.insert(
269 "celsius".to_string(),
270 Unit::temperature(1.0, 273.15, 1.0, -273.15),
271 );
272 units.insert(
273 "centigrade".to_string(),
274 Unit::temperature(1.0, 273.15, 1.0, -273.15),
275 );
276
277 units.insert(
279 "f".to_string(),
280 Unit::temperature(5.0 / 9.0, 459.67 * 5.0 / 9.0, 9.0 / 5.0, -459.67),
281 );
282 units.insert(
283 "fahrenheit".to_string(),
284 Unit::temperature(5.0 / 9.0, 459.67 * 5.0 / 9.0, 9.0 / 5.0, -459.67),
285 );
286
287 units.insert("l".to_string(), Unit::simple(UnitCategory::Volume, 1.0));
289 units.insert("liter".to_string(), Unit::simple(UnitCategory::Volume, 1.0));
290 units.insert(
291 "liters".to_string(),
292 Unit::simple(UnitCategory::Volume, 1.0),
293 );
294 units.insert("litre".to_string(), Unit::simple(UnitCategory::Volume, 1.0));
295 units.insert(
296 "litres".to_string(),
297 Unit::simple(UnitCategory::Volume, 1.0),
298 );
299
300 units.insert("ml".to_string(), Unit::simple(UnitCategory::Volume, 0.001));
302 units.insert(
303 "milliliter".to_string(),
304 Unit::simple(UnitCategory::Volume, 0.001),
305 );
306 units.insert(
307 "milliliters".to_string(),
308 Unit::simple(UnitCategory::Volume, 0.001),
309 );
310 units.insert("m3".to_string(), Unit::simple(UnitCategory::Volume, 1000.0));
311 units.insert(
312 "cubic_meter".to_string(),
313 Unit::simple(UnitCategory::Volume, 1000.0),
314 );
315 units.insert("cm3".to_string(), Unit::simple(UnitCategory::Volume, 0.001));
316 units.insert(
317 "cubic_centimeter".to_string(),
318 Unit::simple(UnitCategory::Volume, 0.001),
319 );
320 units.insert("cc".to_string(), Unit::simple(UnitCategory::Volume, 0.001));
321
322 units.insert(
324 "gal".to_string(),
325 Unit::simple(UnitCategory::Volume, 3.785411784),
326 );
327 units.insert(
328 "gallon".to_string(),
329 Unit::simple(UnitCategory::Volume, 3.785411784),
330 );
331 units.insert(
332 "gallons".to_string(),
333 Unit::simple(UnitCategory::Volume, 3.785411784),
334 );
335 units.insert(
336 "qt".to_string(),
337 Unit::simple(UnitCategory::Volume, 0.946352946),
338 );
339 units.insert(
340 "quart".to_string(),
341 Unit::simple(UnitCategory::Volume, 0.946352946),
342 );
343 units.insert(
344 "quarts".to_string(),
345 Unit::simple(UnitCategory::Volume, 0.946352946),
346 );
347 units.insert(
348 "pt".to_string(),
349 Unit::simple(UnitCategory::Volume, 0.473176473),
350 );
351 units.insert(
352 "pint".to_string(),
353 Unit::simple(UnitCategory::Volume, 0.473176473),
354 );
355 units.insert(
356 "pints".to_string(),
357 Unit::simple(UnitCategory::Volume, 0.473176473),
358 );
359 units.insert(
360 "cup".to_string(),
361 Unit::simple(UnitCategory::Volume, 0.2365882365),
362 );
363 units.insert(
364 "cups".to_string(),
365 Unit::simple(UnitCategory::Volume, 0.2365882365),
366 );
367 units.insert(
368 "fl_oz".to_string(),
369 Unit::simple(UnitCategory::Volume, 0.0295735295625),
370 );
371 units.insert(
372 "fluid_ounce".to_string(),
373 Unit::simple(UnitCategory::Volume, 0.0295735295625),
374 );
375 units.insert(
376 "tbsp".to_string(),
377 Unit::simple(UnitCategory::Volume, 0.01478676478125),
378 );
379 units.insert(
380 "tablespoon".to_string(),
381 Unit::simple(UnitCategory::Volume, 0.01478676478125),
382 );
383 units.insert(
384 "tsp".to_string(),
385 Unit::simple(UnitCategory::Volume, 0.00492892159375),
386 );
387 units.insert(
388 "teaspoon".to_string(),
389 Unit::simple(UnitCategory::Volume, 0.00492892159375),
390 );
391
392 units.insert(
394 "imperial_gal".to_string(),
395 Unit::simple(UnitCategory::Volume, 4.54609),
396 );
397 units.insert(
398 "imperial_gallon".to_string(),
399 Unit::simple(UnitCategory::Volume, 4.54609),
400 );
401 units.insert(
402 "imperial_qt".to_string(),
403 Unit::simple(UnitCategory::Volume, 1.1365225),
404 );
405 units.insert(
406 "imperial_pt".to_string(),
407 Unit::simple(UnitCategory::Volume, 0.56826125),
408 );
409
410 units.insert("s".to_string(), Unit::simple(UnitCategory::Time, 1.0));
412 units.insert("sec".to_string(), Unit::simple(UnitCategory::Time, 1.0));
413 units.insert("second".to_string(), Unit::simple(UnitCategory::Time, 1.0));
414 units.insert("seconds".to_string(), Unit::simple(UnitCategory::Time, 1.0));
415
416 units.insert("ms".to_string(), Unit::simple(UnitCategory::Time, 0.001));
417 units.insert(
418 "millisecond".to_string(),
419 Unit::simple(UnitCategory::Time, 0.001),
420 );
421 units.insert(
422 "milliseconds".to_string(),
423 Unit::simple(UnitCategory::Time, 0.001),
424 );
425 units.insert("us".to_string(), Unit::simple(UnitCategory::Time, 0.000001));
426 units.insert(
427 "microsecond".to_string(),
428 Unit::simple(UnitCategory::Time, 0.000001),
429 );
430 units.insert(
431 "microseconds".to_string(),
432 Unit::simple(UnitCategory::Time, 0.000001),
433 );
434 units.insert(
435 "ns".to_string(),
436 Unit::simple(UnitCategory::Time, 0.000000001),
437 );
438 units.insert(
439 "nanosecond".to_string(),
440 Unit::simple(UnitCategory::Time, 0.000000001),
441 );
442 units.insert(
443 "nanoseconds".to_string(),
444 Unit::simple(UnitCategory::Time, 0.000000001),
445 );
446
447 units.insert("min".to_string(), Unit::simple(UnitCategory::Time, 60.0));
448 units.insert("minute".to_string(), Unit::simple(UnitCategory::Time, 60.0));
449 units.insert(
450 "minutes".to_string(),
451 Unit::simple(UnitCategory::Time, 60.0),
452 );
453 units.insert("hr".to_string(), Unit::simple(UnitCategory::Time, 3600.0));
454 units.insert("hour".to_string(), Unit::simple(UnitCategory::Time, 3600.0));
455 units.insert(
456 "hours".to_string(),
457 Unit::simple(UnitCategory::Time, 3600.0),
458 );
459 units.insert("day".to_string(), Unit::simple(UnitCategory::Time, 86400.0));
460 units.insert(
461 "days".to_string(),
462 Unit::simple(UnitCategory::Time, 86400.0),
463 );
464 units.insert(
465 "week".to_string(),
466 Unit::simple(UnitCategory::Time, 604800.0),
467 );
468 units.insert(
469 "weeks".to_string(),
470 Unit::simple(UnitCategory::Time, 604800.0),
471 );
472 units.insert(
473 "month".to_string(),
474 Unit::simple(UnitCategory::Time, 2629746.0),
475 ); units.insert(
477 "months".to_string(),
478 Unit::simple(UnitCategory::Time, 2629746.0),
479 );
480 units.insert(
481 "year".to_string(),
482 Unit::simple(UnitCategory::Time, 31557600.0),
483 ); units.insert(
485 "years".to_string(),
486 Unit::simple(UnitCategory::Time, 31557600.0),
487 );
488
489 units.insert("m2".to_string(), Unit::simple(UnitCategory::Area, 1.0));
491 units.insert("sqm".to_string(), Unit::simple(UnitCategory::Area, 1.0));
492 units.insert(
493 "square_meter".to_string(),
494 Unit::simple(UnitCategory::Area, 1.0),
495 );
496 units.insert(
497 "square_meters".to_string(),
498 Unit::simple(UnitCategory::Area, 1.0),
499 );
500
501 units.insert(
502 "km2".to_string(),
503 Unit::simple(UnitCategory::Area, 1000000.0),
504 );
505 units.insert(
506 "square_kilometer".to_string(),
507 Unit::simple(UnitCategory::Area, 1000000.0),
508 );
509 units.insert("cm2".to_string(), Unit::simple(UnitCategory::Area, 0.0001));
510 units.insert(
511 "square_centimeter".to_string(),
512 Unit::simple(UnitCategory::Area, 0.0001),
513 );
514
515 units.insert(
516 "sq_ft".to_string(),
517 Unit::simple(UnitCategory::Area, 0.09290304),
518 );
519 units.insert(
520 "square_foot".to_string(),
521 Unit::simple(UnitCategory::Area, 0.09290304),
522 );
523 units.insert(
524 "square_feet".to_string(),
525 Unit::simple(UnitCategory::Area, 0.09290304),
526 );
527 units.insert(
528 "sq_in".to_string(),
529 Unit::simple(UnitCategory::Area, 0.00064516),
530 );
531 units.insert(
532 "square_inch".to_string(),
533 Unit::simple(UnitCategory::Area, 0.00064516),
534 );
535 units.insert(
536 "sq_mi".to_string(),
537 Unit::simple(UnitCategory::Area, 2589988.110336),
538 );
539 units.insert(
540 "square_mile".to_string(),
541 Unit::simple(UnitCategory::Area, 2589988.110336),
542 );
543 units.insert(
544 "acre".to_string(),
545 Unit::simple(UnitCategory::Area, 4046.8564224),
546 );
547 units.insert(
548 "acres".to_string(),
549 Unit::simple(UnitCategory::Area, 4046.8564224),
550 );
551 units.insert(
552 "hectare".to_string(),
553 Unit::simple(UnitCategory::Area, 10000.0),
554 );
555 units.insert(
556 "hectares".to_string(),
557 Unit::simple(UnitCategory::Area, 10000.0),
558 );
559
560 units.insert("mps".to_string(), Unit::simple(UnitCategory::Speed, 1.0));
562 units.insert("m/s".to_string(), Unit::simple(UnitCategory::Speed, 1.0));
563 units.insert(
564 "meters_per_second".to_string(),
565 Unit::simple(UnitCategory::Speed, 1.0),
566 );
567
568 units.insert(
569 "kph".to_string(),
570 Unit::simple(UnitCategory::Speed, 0.277777778),
571 );
572 units.insert(
573 "kmh".to_string(),
574 Unit::simple(UnitCategory::Speed, 0.277777778),
575 );
576 units.insert(
577 "km/h".to_string(),
578 Unit::simple(UnitCategory::Speed, 0.277777778),
579 );
580 units.insert(
581 "kilometers_per_hour".to_string(),
582 Unit::simple(UnitCategory::Speed, 0.277777778),
583 );
584
585 units.insert(
586 "mph".to_string(),
587 Unit::simple(UnitCategory::Speed, 0.44704),
588 );
589 units.insert(
590 "mi/h".to_string(),
591 Unit::simple(UnitCategory::Speed, 0.44704),
592 );
593 units.insert(
594 "miles_per_hour".to_string(),
595 Unit::simple(UnitCategory::Speed, 0.44704),
596 );
597
598 units.insert(
599 "knot".to_string(),
600 Unit::simple(UnitCategory::Speed, 0.514444444),
601 );
602 units.insert(
603 "knots".to_string(),
604 Unit::simple(UnitCategory::Speed, 0.514444444),
605 );
606 units.insert(
607 "kt".to_string(),
608 Unit::simple(UnitCategory::Speed, 0.514444444),
609 );
610
611 units.insert("fps".to_string(), Unit::simple(UnitCategory::Speed, 0.3048));
612 units.insert(
613 "ft/s".to_string(),
614 Unit::simple(UnitCategory::Speed, 0.3048),
615 );
616 units.insert(
617 "feet_per_second".to_string(),
618 Unit::simple(UnitCategory::Speed, 0.3048),
619 );
620
621 units.insert(
624 "light_speed".to_string(),
625 Unit::simple(UnitCategory::Speed, 299792458.0),
626 );
627
628 units.insert("pa".to_string(), Unit::simple(UnitCategory::Pressure, 1.0));
630 units.insert(
631 "pascal".to_string(),
632 Unit::simple(UnitCategory::Pressure, 1.0),
633 );
634 units.insert(
635 "pascals".to_string(),
636 Unit::simple(UnitCategory::Pressure, 1.0),
637 );
638
639 units.insert(
640 "kpa".to_string(),
641 Unit::simple(UnitCategory::Pressure, 1000.0),
642 );
643 units.insert(
644 "kilopascal".to_string(),
645 Unit::simple(UnitCategory::Pressure, 1000.0),
646 );
647 units.insert(
648 "mpa".to_string(),
649 Unit::simple(UnitCategory::Pressure, 1000000.0),
650 );
651 units.insert(
652 "megapascal".to_string(),
653 Unit::simple(UnitCategory::Pressure, 1000000.0),
654 );
655 units.insert(
656 "gpa".to_string(),
657 Unit::simple(UnitCategory::Pressure, 1000000000.0),
658 );
659 units.insert(
660 "gigapascal".to_string(),
661 Unit::simple(UnitCategory::Pressure, 1000000000.0),
662 );
663
664 units.insert(
665 "bar".to_string(),
666 Unit::simple(UnitCategory::Pressure, 100000.0),
667 );
668 units.insert(
669 "bars".to_string(),
670 Unit::simple(UnitCategory::Pressure, 100000.0),
671 );
672 units.insert(
673 "mbar".to_string(),
674 Unit::simple(UnitCategory::Pressure, 100.0),
675 );
676 units.insert(
677 "millibar".to_string(),
678 Unit::simple(UnitCategory::Pressure, 100.0),
679 );
680
681 units.insert(
682 "atm".to_string(),
683 Unit::simple(UnitCategory::Pressure, 101325.0),
684 );
685 units.insert(
686 "atmosphere".to_string(),
687 Unit::simple(UnitCategory::Pressure, 101325.0),
688 );
689 units.insert(
690 "atmospheres".to_string(),
691 Unit::simple(UnitCategory::Pressure, 101325.0),
692 );
693
694 units.insert(
695 "psi".to_string(),
696 Unit::simple(UnitCategory::Pressure, 6894.757293168),
697 );
698 units.insert(
699 "pounds_per_square_inch".to_string(),
700 Unit::simple(UnitCategory::Pressure, 6894.757293168),
701 );
702
703 units.insert(
704 "torr".to_string(),
705 Unit::simple(UnitCategory::Pressure, 133.322368421),
706 );
707 units.insert(
708 "mmhg".to_string(),
709 Unit::simple(UnitCategory::Pressure, 133.322368421),
710 );
711 units.insert(
712 "mm_hg".to_string(),
713 Unit::simple(UnitCategory::Pressure, 133.322368421),
714 );
715
716 UnitConverter { units }
717 }
718
719 pub fn convert(&self, value: f64, from_unit: &str, to_unit: &str) -> Result<f64> {
721 let from_key = from_unit.to_lowercase();
723 let to_key = to_unit.to_lowercase();
724
725 let from = self
727 .units
728 .get(&from_key)
729 .ok_or_else(|| anyhow!("Unknown unit: {}", from_unit))?;
730 let to = self
731 .units
732 .get(&to_key)
733 .ok_or_else(|| anyhow!("Unknown unit: {}", to_unit))?;
734
735 if from.category != to.category {
737 return Err(anyhow!(
738 "Cannot convert between different unit types: {} ({:?}) to {} ({:?})",
739 from_unit,
740 from.category,
741 to_unit,
742 to.category
743 ));
744 }
745
746 let si_value = value * from.to_si_factor + from.to_si_offset;
748
749 let result = si_value * to.from_si_factor + to.from_si_offset;
751
752 Ok(result)
753 }
754
755 pub fn get_units_for_category(&self, category: &str) -> Vec<String> {
757 let cat = match category.to_lowercase().as_str() {
758 "length" => Some(UnitCategory::Length),
759 "mass" | "weight" => Some(UnitCategory::Mass),
760 "temperature" | "temp" => Some(UnitCategory::Temperature),
761 "volume" => Some(UnitCategory::Volume),
762 "time" => Some(UnitCategory::Time),
763 "area" => Some(UnitCategory::Area),
764 "speed" | "velocity" => Some(UnitCategory::Speed),
765 "pressure" => Some(UnitCategory::Pressure),
766 _ => None,
767 };
768
769 if let Some(category) = cat {
770 self.units
771 .iter()
772 .filter(|(_, unit)| unit.category == category)
773 .map(|(name, _)| name.clone())
774 .collect()
775 } else {
776 Vec::new()
777 }
778 }
779}
780
781lazy_static::lazy_static! {
783 static ref CONVERTER: UnitConverter = UnitConverter::new();
784}
785
786pub fn convert_units(value: f64, from_unit: &str, to_unit: &str) -> Result<f64> {
788 CONVERTER.convert(value, from_unit, to_unit)
789}
790
791pub fn list_units(category: &str) -> Vec<String> {
793 CONVERTER.get_units_for_category(category)
794}
795
796#[cfg(test)]
797mod tests {
798 use super::*;
799
800 #[test]
801 fn test_length_conversions() {
802 let converter = UnitConverter::new();
803
804 let result = converter.convert(100.0, "km", "miles").unwrap();
806 assert!((result - 62.137).abs() < 0.01);
807
808 let result = converter.convert(100.0, "ft", "m").unwrap();
810 assert!((result - 30.48).abs() < 0.01);
811
812 let result = converter.convert(12.0, "in", "cm").unwrap();
814 assert!((result - 30.48).abs() < 0.01);
815 }
816
817 #[test]
818 fn test_mass_conversions() {
819 let converter = UnitConverter::new();
820
821 let result = converter.convert(100.0, "lb", "kg").unwrap();
823 assert!((result - 45.359).abs() < 0.01);
824
825 let result = converter.convert(16.0, "oz", "g").unwrap();
827 assert!((result - 453.592).abs() < 0.01);
828 }
829
830 #[test]
831 fn test_temperature_conversions() {
832 let converter = UnitConverter::new();
833
834 let result = converter.convert(32.0, "F", "C").unwrap();
836 assert!((result - 0.0).abs() < 0.01);
837
838 let result = converter.convert(212.0, "F", "C").unwrap();
839 assert!((result - 100.0).abs() < 0.01);
840
841 let result = converter.convert(0.0, "C", "K").unwrap();
843 assert!((result - 273.15).abs() < 0.01);
844 }
845
846 #[test]
847 fn test_volume_conversions() {
848 let converter = UnitConverter::new();
849
850 let result = converter.convert(1.0, "gal", "L").unwrap();
852 assert!((result - 3.785).abs() < 0.01);
853
854 let result = converter.convert(1.0, "cup", "ml").unwrap();
856 assert!((result - 236.588).abs() < 0.01);
857 }
858
859 #[test]
860 fn test_invalid_conversion() {
861 let converter = UnitConverter::new();
862
863 let result = converter.convert(100.0, "km", "kg");
865 assert!(result.is_err());
866
867 let result = converter.convert(100.0, "xyz", "m");
869 assert!(result.is_err());
870 }
871
872 #[test]
873 fn test_case_insensitive() {
874 let converter = UnitConverter::new();
875
876 let result1 = converter.convert(100.0, "KM", "MILES").unwrap();
878 let result2 = converter.convert(100.0, "km", "miles").unwrap();
879 let result3 = converter.convert(100.0, "Km", "Miles").unwrap();
880
881 assert!((result1 - result2).abs() < 0.001);
882 assert!((result2 - result3).abs() < 0.001);
883 }
884}