1use std::fmt;
2
3#[cfg(feature = "serde")]
4use serde::{Deserialize, Serialize};
5
6#[derive(Debug, Clone, PartialEq, Eq, Hash)]
11#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
12pub struct Vin(String);
13
14impl Vin {
15 pub fn new(raw: impl Into<String>) -> crate::Result<Self> {
17 let s = raw.into().to_ascii_uppercase();
18 crate::wmi::validate_chars(&s)?;
19 Ok(Vin(s))
20 }
21
22 pub fn as_str(&self) -> &str {
24 &self.0
25 }
26
27 pub fn wmi(&self) -> &str {
29 &self.0[..3]
30 }
31
32 pub fn vds(&self) -> &str {
34 &self.0[3..9]
35 }
36
37 pub fn vis(&self) -> &str {
39 &self.0[9..]
40 }
41
42 pub fn check_digit(&self) -> char {
44 self.0.as_bytes()[8] as char
45 }
46
47 pub fn year_code(&self) -> char {
49 self.0.as_bytes()[9] as char
50 }
51
52 pub fn plant_code(&self) -> char {
54 self.0.as_bytes()[10] as char
55 }
56
57 pub fn region_code(&self) -> char {
59 self.0.as_bytes()[0] as char
60 }
61
62 pub fn country_code(&self) -> &str {
64 &self.0[..2]
65 }
66
67 pub fn squish_vin(&self) -> String {
70 let s = &self.0;
71 let mut out = String::with_capacity(10);
72 out.push_str(&s[..8]);
73 out.push_str(&s[9..11]);
74 out
75 }
76
77 pub fn year_candidates(&self) -> Vec<u32> {
92 let code = self.year_code();
93 let Some(base) = crate::year::year_for_code(code) else {
94 return Vec::new();
95 };
96 if code.is_ascii_digit() {
97 vec![base]
98 } else {
99 vec![base + 30, base]
100 }
101 }
102}
103
104impl fmt::Display for Vin {
105 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
106 f.write_str(&self.0)
107 }
108}
109
110#[derive(Debug, Clone, Default, PartialEq)]
115#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
116pub struct Vehicle {
117 pub vin: String,
119 pub wmi: String,
121 pub make: Option<String>,
123 pub model: Option<String>,
125 pub series: Option<String>,
127 pub trim: Option<String>,
129 pub model_year: Option<u32>,
131 pub body_type: Option<BodyType>,
133 pub fuel_primary: Option<FuelType>,
135 pub fuel_secondary: Option<FuelType>,
137 pub doors: Option<u8>,
139 pub engine_cylinders: Option<u8>,
141 pub engine_model: Option<String>,
143 pub engine_configuration: Option<String>,
145 pub engine_manufacturer: Option<String>,
147 pub displacement_l: Option<f32>,
149 pub turbo: Option<bool>,
151 pub drive_type: Option<String>,
153 pub transmission: Option<String>,
155 pub battery_type: Option<String>,
157 pub charger_level: Option<String>,
159 pub ev_drive_unit: Option<String>,
161 pub brake_system: Option<String>,
163 pub gvwr: Option<String>,
165 pub plant_country: Option<String>,
167 pub plant_city: Option<String>,
169 pub plant_state: Option<String>,
171 pub manufacturer: Option<String>,
173 pub region: Option<String>,
175}
176
177#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
188#[derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
189#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
190#[allow(missing_docs)]
191pub enum BodyType {
192 Saloon, Hatchback, StationWagon, Coupe, Convertible, MultiPurpose, TruckStationWagon, Lorry, Van, TractorUnit, RoadTractor, PickUp, ChassisCab, SingleDeckBus, DoubleDeckBus, ArticulatedSingleDeckBus, ArticulatedDoubleDeckBus, LowFloorSingleDeckBus, LowFloorDoubleDeckBus, ArticulatedLowFloorSingleDeckBus, ArticulatedLowFloorDoubleDeckBus, OpenTopSingleDeckBus, OpenTopDoubleDeckBus, BusChassis, SemiTrailer, DrawbarTrailer, CentreAxleTrailer, RigidDrawbarTrailer, MotorCaravan, ArmouredVehicle, Ambulance, Hearse, TrailerCaravan, MobileCrane, SpecialGroup, WheelchairAccessible, ConverterDolly, ExceptionalLoadTrailer, Unknown,
238}
239
240pub(crate) const BODY_TYPES: &[BodyType] = &[
243 BodyType::Saloon,
244 BodyType::Hatchback,
245 BodyType::StationWagon,
246 BodyType::Coupe,
247 BodyType::Convertible,
248 BodyType::MultiPurpose,
249 BodyType::TruckStationWagon,
250 BodyType::Lorry,
251 BodyType::Van,
252 BodyType::TractorUnit,
253 BodyType::RoadTractor,
254 BodyType::PickUp,
255 BodyType::ChassisCab,
256 BodyType::SingleDeckBus,
257 BodyType::DoubleDeckBus,
258 BodyType::ArticulatedSingleDeckBus,
259 BodyType::ArticulatedDoubleDeckBus,
260 BodyType::LowFloorSingleDeckBus,
261 BodyType::LowFloorDoubleDeckBus,
262 BodyType::ArticulatedLowFloorSingleDeckBus,
263 BodyType::ArticulatedLowFloorDoubleDeckBus,
264 BodyType::OpenTopSingleDeckBus,
265 BodyType::OpenTopDoubleDeckBus,
266 BodyType::BusChassis,
267 BodyType::SemiTrailer,
268 BodyType::DrawbarTrailer,
269 BodyType::CentreAxleTrailer,
270 BodyType::RigidDrawbarTrailer,
271 BodyType::MotorCaravan,
272 BodyType::ArmouredVehicle,
273 BodyType::Ambulance,
274 BodyType::Hearse,
275 BodyType::TrailerCaravan,
276 BodyType::MobileCrane,
277 BodyType::SpecialGroup,
278 BodyType::WheelchairAccessible,
279 BodyType::ConverterDolly,
280 BodyType::ExceptionalLoadTrailer,
281];
282
283impl BodyType {
284 pub fn code(self) -> &'static str {
287 match self {
288 BodyType::Saloon => "AA",
289 BodyType::Hatchback => "AB",
290 BodyType::StationWagon => "AC",
291 BodyType::Coupe => "AD",
292 BodyType::Convertible => "AE",
293 BodyType::MultiPurpose => "AF",
294 BodyType::TruckStationWagon => "AG",
295 BodyType::Lorry => "BA",
296 BodyType::Van => "BB",
297 BodyType::TractorUnit => "BC",
298 BodyType::RoadTractor => "BD",
299 BodyType::PickUp => "BE",
300 BodyType::ChassisCab => "BX",
301 BodyType::SingleDeckBus => "CA",
302 BodyType::DoubleDeckBus => "CB",
303 BodyType::ArticulatedSingleDeckBus => "CC",
304 BodyType::ArticulatedDoubleDeckBus => "CD",
305 BodyType::LowFloorSingleDeckBus => "CE",
306 BodyType::LowFloorDoubleDeckBus => "CF",
307 BodyType::ArticulatedLowFloorSingleDeckBus => "CG",
308 BodyType::ArticulatedLowFloorDoubleDeckBus => "CH",
309 BodyType::OpenTopSingleDeckBus => "CI",
310 BodyType::OpenTopDoubleDeckBus => "CJ",
311 BodyType::BusChassis => "CX",
312 BodyType::SemiTrailer => "DA",
313 BodyType::DrawbarTrailer => "DB",
314 BodyType::CentreAxleTrailer => "DC",
315 BodyType::RigidDrawbarTrailer => "DE",
316 BodyType::MotorCaravan => "SA",
317 BodyType::ArmouredVehicle => "SB",
318 BodyType::Ambulance => "SC",
319 BodyType::Hearse => "SD",
320 BodyType::TrailerCaravan => "SE",
321 BodyType::MobileCrane => "SF",
322 BodyType::SpecialGroup => "SG",
323 BodyType::WheelchairAccessible => "SH",
324 BodyType::ConverterDolly => "SJ",
325 BodyType::ExceptionalLoadTrailer => "SK",
326 BodyType::Unknown => "",
327 }
328 }
329
330 pub fn from_code(code: &str) -> Option<Self> {
335 Some(match code.trim().to_ascii_uppercase().as_str() {
336 "AA" => BodyType::Saloon,
337 "AB" => BodyType::Hatchback,
338 "AC" => BodyType::StationWagon,
339 "AD" => BodyType::Coupe,
340 "AE" => BodyType::Convertible,
341 "AF" => BodyType::MultiPurpose,
342 "AG" => BodyType::TruckStationWagon,
343 "BA" => BodyType::Lorry,
344 "BB" => BodyType::Van,
345 "BC" => BodyType::TractorUnit,
346 "BD" => BodyType::RoadTractor,
347 "BE" => BodyType::PickUp,
348 "BX" => BodyType::ChassisCab,
349 "CA" => BodyType::SingleDeckBus,
350 "CB" => BodyType::DoubleDeckBus,
351 "CC" => BodyType::ArticulatedSingleDeckBus,
352 "CD" => BodyType::ArticulatedDoubleDeckBus,
353 "CE" => BodyType::LowFloorSingleDeckBus,
354 "CF" => BodyType::LowFloorDoubleDeckBus,
355 "CG" => BodyType::ArticulatedLowFloorSingleDeckBus,
356 "CH" => BodyType::ArticulatedLowFloorDoubleDeckBus,
357 "CI" => BodyType::OpenTopSingleDeckBus,
358 "CJ" => BodyType::OpenTopDoubleDeckBus,
359 "CX" => BodyType::BusChassis,
360 "DA" => BodyType::SemiTrailer,
361 "DB" => BodyType::DrawbarTrailer,
362 "DC" => BodyType::CentreAxleTrailer,
363 "DE" => BodyType::RigidDrawbarTrailer,
364 "SA" => BodyType::MotorCaravan,
365 "SB" => BodyType::ArmouredVehicle,
366 "SC" => BodyType::Ambulance,
367 "SD" => BodyType::Hearse,
368 "SE" => BodyType::TrailerCaravan,
369 "SF" => BodyType::MobileCrane,
370 "SG" => BodyType::SpecialGroup,
371 "SH" => BodyType::WheelchairAccessible,
372 "SJ" => BodyType::ConverterDolly,
373 "SK" => BodyType::ExceptionalLoadTrailer,
374 _ => return None,
375 })
376 }
377
378 pub fn parse(s: &str) -> Self {
387 let lc = s.to_ascii_lowercase();
388 let has = |needle: &str| lc.contains(needle);
389 if has("ambulance") {
391 return BodyType::Ambulance;
392 }
393 if has("hearse") || has("lijkwagen") {
394 return BodyType::Hearse;
395 }
396 if has("motor caravan") || has("motorhome") || has("camper") {
397 return BodyType::MotorCaravan;
398 }
399 if has("armoured") || has("armored") {
400 return BodyType::ArmouredVehicle;
401 }
402 if has("wheelchair") {
403 return BodyType::WheelchairAccessible;
404 }
405 if has("crane") {
406 return BodyType::MobileCrane;
407 }
408 if has("semi-trailer") || has("semitrailer") {
410 return BodyType::SemiTrailer;
411 }
412 if has("trailer") {
413 return BodyType::DrawbarTrailer;
414 }
415 if has("double") && (has("deck") || has("decker")) {
416 return BodyType::DoubleDeckBus;
417 }
418 if has("bus") || has("coach") {
419 return BodyType::SingleDeckBus;
420 }
421 if has("pick-up") || has("pickup") || has("pick up") {
423 return BodyType::PickUp;
424 }
425 if has("tractor unit") || has("semi tractor") {
426 return BodyType::TractorUnit;
427 }
428 if has("road tractor") {
429 return BodyType::RoadTractor;
430 }
431 if has("chassis") {
432 return BodyType::ChassisCab;
433 }
434 if has("truck station wagon") {
436 return BodyType::TruckStationWagon;
437 }
438 if has("multi-purpose")
439 || has("multipurpose")
440 || has("mpv")
441 || has("minivan")
442 || has("people carrier")
443 || has("monovolume")
444 || has("suv")
446 || has("sport utility")
447 || has("crossover")
448 || has("cuv")
449 || has("off-road")
450 {
451 return BodyType::MultiPurpose;
452 }
453 if has("station wagon")
454 || has("stationwagen")
455 || has("estate")
456 || has("wagon")
457 || has("kombi")
458 || has("combi")
459 || has("touring")
460 || has("avant")
461 || has("variant")
462 || has("break")
463 {
464 return BodyType::StationWagon;
465 }
466 if has("convertible")
467 || has("cabrio")
468 || has("roadster")
469 || has("spider")
470 || has("spyder")
471 || has("targa")
472 || has("drophead")
473 {
474 return BodyType::Convertible;
475 }
476 if has("coupe") || has("coupé") {
477 return BodyType::Coupe;
478 }
479 if has("hatchback")
480 || has("hatch")
481 || has("liftback")
482 || has("fastback")
483 || has("sportback")
484 {
485 return BodyType::Hatchback;
486 }
487 if has("van") || has("gesloten opbouw") {
488 return BodyType::Van;
489 }
490 if has("saloon")
491 || has("sedan")
492 || has("berlina")
493 || has("berline")
494 || has("limousine")
495 || has("notchback")
496 {
497 return BodyType::Saloon;
498 }
499 if has("lorry") || has("truck") {
501 return BodyType::Lorry;
502 }
503 BodyType::Unknown
504 }
505}
506
507#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
509#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
510#[allow(missing_docs)]
511pub enum FuelType {
512 Gasoline,
513 Diesel,
514 Electric,
515 Hybrid,
516 PluginHybrid,
517 Ethanol,
518 FlexFuel,
519 Cng,
520 Lng,
521 Lpg,
522 Hydrogen,
523 FuelCell,
524 Methanol,
525 NaturalGas,
526 Other,
527}
528
529impl FuelType {
530 pub fn parse(s: &str) -> Self {
532 let lc = s.to_ascii_lowercase();
533 match lc.as_str() {
534 x if x.contains("gasoline") => FuelType::Gasoline,
535 x if x.contains("diesel") => FuelType::Diesel,
536 x if x.contains("plug") => FuelType::PluginHybrid,
537 x if x.contains("hybrid") => FuelType::Hybrid,
538 x if x.contains("methanol") || x.contains("m85") => FuelType::Methanol,
539 x if x.contains("e85") || x.contains("ethanol") => FuelType::Ethanol,
540 x if x.contains("flex") || x.contains("ffv") => FuelType::FlexFuel,
541 x if x.contains("cng") || x.contains("compressed natural") => FuelType::Cng,
542 x if x.contains("lng") || x.contains("liquefied natural") => FuelType::Lng,
543 x if x.contains("lpg") || x.contains("propane") => FuelType::Lpg,
544 x if x.contains("fuel cell") => FuelType::FuelCell,
545 x if x.contains("hydrogen") => FuelType::Hydrogen,
546 x if x.contains("electric") => FuelType::Electric,
547 x if x.contains("natural gas") => FuelType::NaturalGas,
548 _ => FuelType::Other,
549 }
550 }
551}
552
553#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
556#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
557#[allow(missing_docs)]
558pub enum DriveType {
559 Fwd,
560 Rwd,
561 Awd,
562 FourWheelDrive,
563 Other,
564}
565
566impl DriveType {
567 pub fn parse(s: &str) -> Self {
569 let lc = s.to_ascii_lowercase();
570 if lc.contains("front") {
571 DriveType::Fwd
572 } else if lc.contains("rear") {
573 DriveType::Rwd
574 } else if lc.contains("all wheel") || lc.contains("all-wheel") || lc.trim() == "awd" {
575 DriveType::Awd
576 } else if lc.contains("four wheel")
577 || lc.contains("four-wheel")
578 || lc.contains("4wd")
579 || lc.contains("4x4")
580 {
581 DriveType::FourWheelDrive
582 } else {
583 DriveType::Other
584 }
585 }
586}
587
588#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
592#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
593#[allow(missing_docs)]
594pub enum Transmission {
595 Manual,
596 Automatic,
597 DualClutch,
598 Cvt,
599 SingleSpeed,
600 AutomatedManual,
601 Other,
602}
603
604impl Transmission {
605 pub fn parse(s: &str) -> Self {
609 let lc = s.to_ascii_lowercase();
610 if lc.contains("cvt") || lc.contains("continuously variable") {
611 Transmission::Cvt
612 } else if lc.contains("single-speed") || lc.contains("single speed") || lc.contains("1-speed")
613 {
614 Transmission::SingleSpeed
615 } else if lc.contains("dual clutch")
616 || lc.contains("dual-clutch")
617 || lc.contains("twin clutch")
618 || lc.contains("dsg")
619 || lc.contains("dct")
620 || lc.contains("dkg")
621 || lc.contains("pdk")
622 || lc.contains("s tronic")
623 || lc.contains("s-tronic")
624 || lc.contains("powershift")
625 {
626 Transmission::DualClutch
627 } else if lc.contains("automated manual")
628 || lc.contains("automatic manual")
629 || lc.contains("automatized")
630 || lc.contains("semi-automatic")
631 || lc.contains("amt")
632 {
633 Transmission::AutomatedManual
634 } else if lc.contains("manual") {
635 Transmission::Manual
636 } else if lc.contains("automatic") || lc.contains("auto") || lc.contains("tiptronic") {
637 Transmission::Automatic
638 } else {
639 Transmission::Other
640 }
641 }
642}
643
644#[cfg(test)]
645mod tests {
646 use super::*;
647
648 #[test]
649 fn vin_uppercases_input() {
650 let v = Vin::new("1hgcm82633a004352").unwrap();
651 assert_eq!(v.as_str(), "1HGCM82633A004352");
652 }
653
654 #[test]
655 fn vin_section_accessors() {
656 let v = Vin::new("1HGCM82633A004352").unwrap();
657 assert_eq!(v.wmi(), "1HG");
658 assert_eq!(v.vds(), "CM8263");
659 assert_eq!(v.vis(), "3A004352");
660 assert_eq!(v.check_digit(), '3');
661 assert_eq!(v.year_code(), '3');
662 assert_eq!(v.plant_code(), 'A');
663 }
664
665 #[test]
666 fn vin_display_returns_canonical() {
667 let v = Vin::new("1hgcm82633a004352").unwrap();
668 assert_eq!(format!("{}", v), "1HGCM82633A004352");
669 }
670
671 #[test]
672 fn body_type_parse_standard_mappings() {
673 assert_eq!(BodyType::parse("4-Door Sedan"), BodyType::Saloon);
675 assert_eq!(BodyType::parse("2-Door Coupe"), BodyType::Coupe);
676 assert_eq!(BodyType::parse("Hatchback"), BodyType::Hatchback);
677 assert_eq!(BodyType::parse("Station Wagon"), BodyType::StationWagon);
678 assert_eq!(BodyType::parse("Convertible"), BodyType::Convertible);
679 assert_eq!(BodyType::parse("2-Door Cabriolet"), BodyType::Convertible);
680 assert_eq!(BodyType::parse("Roadster"), BodyType::Convertible);
681 assert_eq!(BodyType::parse("Crew Cab Pickup"), BodyType::PickUp);
682 assert_eq!(BodyType::parse("Cargo Van"), BodyType::Van);
683 assert_eq!(BodyType::parse("Berline"), BodyType::Saloon);
685 assert_eq!(BodyType::parse("Kombi"), BodyType::StationWagon);
686 assert_eq!(BodyType::parse("Avant"), BodyType::StationWagon);
687 assert_eq!(BodyType::parse("stationwagen"), BodyType::StationWagon);
688 assert_eq!(BodyType::parse("gesloten opbouw"), BodyType::Van);
689 assert_eq!(BodyType::parse("Minivan"), BodyType::MultiPurpose);
691 assert_eq!(
692 BodyType::parse("Sport Utility Vehicle (SUV)"),
693 BodyType::MultiPurpose
694 );
695 assert_eq!(
696 BodyType::parse("Crossover Utility Vehicle (CUV)"),
697 BodyType::MultiPurpose
698 );
699 assert_eq!(BodyType::parse("Ambulance"), BodyType::Ambulance);
701 assert_eq!(BodyType::parse("Hearse"), BodyType::Hearse);
702 assert_eq!(BodyType::parse("Motorcycle"), BodyType::Unknown);
704 assert_eq!(BodyType::parse("Unknown blob"), BodyType::Unknown);
705 }
706
707 #[test]
708 fn body_type_code_roundtrip() {
709 for &b in BODY_TYPES {
710 assert_eq!(BodyType::from_code(b.code()), Some(b), "roundtrip {b:?}");
711 }
712 assert_eq!(BODY_TYPES.len(), 38);
713 assert_eq!(BodyType::from_code("aa"), Some(BodyType::Saloon)); assert_eq!(BodyType::from_code("ZZ"), None);
715 assert_eq!(BodyType::Unknown.code(), "");
716 }
717
718 #[test]
719 fn drive_type_parse() {
720 assert_eq!(DriveType::parse("Front Wheel Drive"), DriveType::Fwd);
721 assert_eq!(DriveType::parse("Rear Wheel Drive"), DriveType::Rwd);
722 assert_eq!(DriveType::parse("All Wheel Drive"), DriveType::Awd);
723 assert_eq!(
724 DriveType::parse("Four Wheel Drive"),
725 DriveType::FourWheelDrive
726 );
727 assert_eq!(DriveType::parse("4x4"), DriveType::FourWheelDrive);
728 assert_eq!(DriveType::parse(""), DriveType::Other);
729 }
730
731 #[test]
732 fn transmission_parse() {
733 assert_eq!(Transmission::parse("6-Speed Manual"), Transmission::Manual);
734 assert_eq!(
735 Transmission::parse("7-Speed Automatic"),
736 Transmission::Automatic
737 );
738 assert_eq!(
739 Transmission::parse("7-Speed Dual Clutch"),
740 Transmission::DualClutch
741 );
742 assert_eq!(Transmission::parse("6-Speed DSG"), Transmission::DualClutch);
743 assert_eq!(Transmission::parse("7-Speed S tronic"), Transmission::DualClutch);
744 assert_eq!(Transmission::parse("CVT"), Transmission::Cvt);
745 assert_eq!(
746 Transmission::parse("Single-Speed"),
747 Transmission::SingleSpeed
748 );
749 assert_eq!(
750 Transmission::parse("5-Speed Automated Manual"),
751 Transmission::AutomatedManual
752 );
753 assert_eq!(Transmission::parse("Tiptronic"), Transmission::Automatic);
754 assert_eq!(Transmission::parse(""), Transmission::Other);
755 }
756
757 #[test]
758 fn fuel_type_full_coverage() {
759 assert_eq!(FuelType::parse("Gasoline"), FuelType::Gasoline);
760 assert_eq!(FuelType::parse("Diesel"), FuelType::Diesel);
761 assert_eq!(FuelType::parse("Electric"), FuelType::Electric);
762 assert_eq!(FuelType::parse("Plug-in Hybrid"), FuelType::PluginHybrid);
763 assert_eq!(FuelType::parse("Hybrid"), FuelType::Hybrid);
764 assert_eq!(FuelType::parse("E85"), FuelType::Ethanol);
765 assert_eq!(FuelType::parse("Ethanol (E85)"), FuelType::Ethanol);
766 assert_eq!(
767 FuelType::parse("Flexible Fuel Vehicle (FFV)"),
768 FuelType::FlexFuel
769 );
770 assert_eq!(
771 FuelType::parse("Compressed Natural Gas (CNG)"),
772 FuelType::Cng
773 );
774 assert_eq!(
775 FuelType::parse("Liquefied Natural Gas (LNG)"),
776 FuelType::Lng
777 );
778 assert_eq!(
779 FuelType::parse("Liquefied Petroleum Gas (LPG)"),
780 FuelType::Lpg
781 );
782 assert_eq!(
783 FuelType::parse("Compressed Hydrogen/Hydrogen"),
784 FuelType::Hydrogen
785 );
786 assert_eq!(FuelType::parse("Fuel Cell"), FuelType::FuelCell);
787 assert_eq!(FuelType::parse("Methanol (M85)"), FuelType::Methanol);
788 assert_eq!(FuelType::parse("Unknown"), FuelType::Other);
789 }
790}