pub mod btable; pub mod dtable; use crate::{ TableConverter, tables::{TableEntryFull, TableTypeTrait}, }; pub use btable::BTableCsvLoader as WMOBTableLoader; use csv::ReaderBuilder; pub use dtable::DTableCsvLoader as WMODTableLoader; use std::fmt::Debug; #[derive(Default)] pub struct TableLoader { _marker: std::marker::PhantomData, } impl TableLoader { pub fn load_table>( &self, path: P, loader: &mut C, ) -> anyhow::Result> { let mut entries = vec![]; let mut rdr = ReaderBuilder::new() .has_headers(true) .delimiter(b',') .flexible(true) // Allow variable number of fields .from_path(path.as_ref())?; let mut line_num = 1; // Start at 1 for header for result in rdr.deserialize() { line_num += 1; match result { Ok(record) => { let record: C::RawEntry = record; if let Some(processed_entry) = loader.process_entry(record)? { entries.push(processed_entry); } } Err(e) => { // Log the error but continue processing eprintln!( "Warning: Skipping line {} in {}: {}", line_num, path.as_ref().display(), e ); } } } if let Some(processed_entry) = loader.finish()? { entries.push(processed_entry); } Ok(entries) } } pub trait EntryLoader: Default { type Output: TableEntryFull; type RawEntry: for<'de> serde::Deserialize<'de> + Debug; type TableType: TableTypeTrait; fn process_entry(&mut self, raw: Self::RawEntry) -> anyhow::Result>; fn finish(&mut self) -> anyhow::Result> { Ok(None) } } impl TableConverter for TableLoader { type OutputEntry = T::Output; type TableType = T::TableType; fn convert>( &self, path: P, ) -> anyhow::Result> { let mut loader = T::default(); self.load_table(path, &mut loader) } }