use serde::{Deserialize, Serialize}; use serde_repr::{Deserialize_repr, Serialize_repr}; /// The data was extracted from the 1974 Motor Trend US magazine, and comprises fuel consumption and 10 aspects of automobile design and performance for 32 automobiles (1973–74 models). #[derive(Serialize, Deserialize, PartialEq, Debug)] pub struct MtCar { /// Vehicle make and model pub name: String, /// Miles per gallon pub mpg: f64, /// Number of cylinders #[serde(rename = "cyl")] pub cylinders: u8, /// Displacement (cu. in.) #[serde(rename = "disp")] pub displacement: f64, /// Gross horsepower pub hp: u16, /// Rear axle ratio #[serde(rename = "drat")] pub rear_axle_ratio: f64, /// Weight (per 1000 lbs) #[serde(rename = "wt")] pub weight: f64, /// Quarter mile time #[serde(rename = "qsec")] pub quarter_mile_time: f64, #[serde(rename = "vs")] pub engine_configuration: EngineConfiguration, #[serde(rename = "am")] pub transmission: Transmission, #[serde(rename = "gear")] /// Number of forward gears pub gear_count: u8, /// Number of carburetor barrels #[serde(rename = "carb")] pub carb_count: u8, } /// Engine configuration, whether the engine is v-shaped or straight line #[derive(Serialize_repr, Deserialize_repr, PartialEq, Eq, Debug)] #[repr(u8)] pub enum EngineConfiguration { VShaped = 0, Straight = 1, } /// Transmission type #[derive(Serialize_repr, Deserialize_repr, PartialEq, Eq, Debug)] #[repr(u8)] pub enum Transmission { Automatic = 0, Manual = 1, } #[cfg(test)] mod tests { use std::error::Error; use csv::Reader; use super::*; const FIRST_RECORD_DATA: &str = "name,mpg,cyl,disp,hp,drat,wt,qsec,vs,am,gear,carb\nMazda RX4,21,6,160,110,3.9,2.62,16.46,0,1,4,4"; /// First record in the dataset fn first_record() -> MtCar { MtCar { name: "Mazda RX4".to_string(), mpg: 21.0, cylinders: 6, displacement: 160.0, hp: 110, rear_axle_ratio: 3.9, weight: 2.62, quarter_mile_time: 16.46, engine_configuration: EngineConfiguration::VShaped, transmission: Transmission::Manual, gear_count: 4, carb_count: 4, } } #[test] fn test_deserialize() -> Result<(), Box> { let mut rdr = Reader::from_reader(FIRST_RECORD_DATA.as_bytes()); let mut iter = rdr.deserialize(); let res = iter.next().unwrap(); let record: MtCar = res?; assert_eq!(record, first_record()); Ok(()) } /// Writing is not exactly the same due to differences in decimal points #[test] fn test_serialize() -> Result<(), Box> { let mut writer = csv::Writer::from_writer(vec![]); writer.serialize(first_record())?; // with headers let data = String::from_utf8(writer.into_inner()?)?; // trim is to remove the Unix trailing newline let (hdrs, record) = data.trim_end().split_once('\n').unwrap(); // no trailing newline here let expected = FIRST_RECORD_DATA.split_once('\n').unwrap(); // headers should match assert_eq!(hdrs, expected.0); // record doesn't match expected because of decimal points. In this particular test, serializing inserts a .0 at the end of displacement (160.0 instead of 160) assert_ne!(record, expected.1); assert_eq!(record, "Mazda RX4,21.0,6,160.0,110,3.9,2.62,16.46,0,1,4,4"); Ok(()) } }