use std::io; use std::path::Path; pub use satrs_core::res_code::ResultU16; use serde::{Deserialize, Serialize}; use serde_hex::{SerHex, StrictCapPfx}; #[derive(Debug, Copy, Clone, Serialize)] pub struct ResultU16Info { pub name: &'static str, pub result: &'static ResultU16, pub group_str: &'static str, pub info: &'static str, } impl ResultU16Info { pub const fn const_new( name: &'static str, result: &'static ResultU16, group_str: &'static str, info: &'static str, ) -> Self { Self { name, result, group_str, info, } } } #[derive(Debug, Serialize, Deserialize)] pub struct ResultU16InfoSerializable { #[serde(with = "SerHex::")] raw: u16, #[serde(with = "SerHex::")] group_id: u8, #[serde(with = "SerHex::")] unique_id: u8, name: &'static str, group_str: &'static str, info: &'static str, } impl From for ResultU16InfoSerializable { fn from(v: ResultU16Info) -> Self { Self { raw: v.result.raw(), group_id: v.result.group_id(), unique_id: v.result.unique_id(), name: v.name, group_str: v.group_str, info: v.info, } } } pub fn print_resultcodes_as_csv(codes: &[ResultU16Info]) -> Result<(), csv::Error> { let mut wtr = csv::Writer::from_writer(io::stdout()); for result in codes { wtr.serialize(ResultU16InfoSerializable::from(*result))?; } wtr.flush()?; Ok(()) } pub fn write_resultcodes_to_csv( results: &[ResultU16Info], path: impl AsRef, ) -> Result<(), csv::Error> { let mut wtr = csv::Writer::from_path(path)?; for result_code in results { wtr.serialize(ResultU16InfoSerializable::from(*result_code))?; } wtr.flush()?; Ok(()) } #[cfg(test)] mod tests { use super::*; use std::fs::File; use std::io::{BufRead, BufReader}; // Special solution for this crate because the code generated by a macro will use // satrs_mib::res_code::* use crate as satrs_mib; use satrs_core::res_code::ResultU16; use satrs_macros::resultcode; #[derive(Debug)] #[allow(dead_code)] pub enum GroupId { Tmtc = 0, } #[resultcode] pub const INVALID_PUS_SERVICE: ResultU16 = ResultU16::const_new(GroupId::Tmtc as u8, 0); #[resultcode] pub const INVALID_PUS_SUBSERVICE: ResultU16 = ResultU16::const_new(GroupId::Tmtc as u8, 1); #[resultcode(info = "Not enough data inside the TC application data field")] pub const NOT_ENOUGH_APP_DATA: ResultU16 = ResultU16::const_new(GroupId::Tmtc as u8, 2); pub const TMTC_RESULTS: &[ResultU16Info] = &[ INVALID_PUS_SERVICE_EXT, INVALID_PUS_SUBSERVICE_EXT, NOT_ENOUGH_APP_DATA_EXT, ]; const CSV_NAME: &'static str = "dummy.csv"; #[test] fn test_printout() { print_resultcodes_as_csv(TMTC_RESULTS).expect("Priting result codes failed"); } #[test] fn test_csv_export() { let csvpath = Path::new(CSV_NAME); write_resultcodes_to_csv(TMTC_RESULTS, csvpath).expect("CSV export failed"); assert!(csvpath.exists()); let file = File::open(csvpath).expect("Opening CSV file failed"); let buf_reader = BufReader::new(file); for (idx, line) in buf_reader.lines().enumerate() { match idx { 0 => { assert!(line.is_ok()); let line = line.unwrap(); assert_eq!(line, "raw,group_id,unique_id,name,group_str,info"); } 1 => { assert!(line.is_ok()); let line = line.unwrap(); assert_eq!(line, "0x0000,0x00,0x00,INVALID_PUS_SERVICE,,"); } 2 => { assert!(line.is_ok()); let line = line.unwrap(); assert_eq!(line, "0x0001,0x00,0x01,INVALID_PUS_SUBSERVICE,,"); } 3 => { assert!(line.is_ok()); let line = line.unwrap(); assert_eq!(line, "0x0002,0x00,0x02,NOT_ENOUGH_APP_DATA,,Not enough data inside the TC application data field"); } _ => (), } } std::fs::remove_file(Path::new("dummy.csv")).expect("Removing dummy csv failed"); } }