This commit is contained in:
Tsuki 2025-12-24 02:07:44 +08:00
parent 3f2a2bcefc
commit b8b31c4e69
65 changed files with 601 additions and 611 deletions

View File

@ -1,7 +1,11 @@
use crate::{FXY, TableEntry, TableEntryLoader}; use super::EntryLoader;
use rkyv::Archive; use crate::{
FXY,
tables::{BTable, BTableEntry},
};
pub struct BTableCsvLoader; #[derive(Default)]
pub struct BTableLoader;
#[derive(Debug)] #[derive(Debug)]
pub struct RawBTableEntry { pub struct RawBTableEntry {
@ -10,145 +14,62 @@ pub struct RawBTableEntry {
pub y: u16, pub y: u16,
} }
// Helper function to deserialize empty strings as None impl EntryLoader for BTableLoader {
fn deserialize_optional_string<'de, D>(deserializer: D) -> Result<Option<String>, D::Error> type Output = BTableEntry;
where type TableType = BTable;
D: serde::Deserializer<'de>,
{
let s: String = serde::Deserialize::deserialize(deserializer)?;
if s.is_empty() { Ok(None) } else { Ok(Some(s)) }
}
// Helper function to deserialize empty strings as None for u32 fn process_entry(&mut self, raw: csv::StringRecord) -> anyhow::Result<Option<Self::Output>> {
fn deserialize_optional_u32<'de, D>(deserializer: D) -> Result<Option<u32>, D::Error> let f = raw
where .get(0)
D: serde::Deserializer<'de>, .ok_or_else(|| anyhow::anyhow!("Missing F field"))?
{ .parse()?;
let s: String = serde::Deserialize::deserialize(deserializer)?;
if s.is_empty() {
Ok(None)
} else {
s.parse::<u32>().map(Some).map_err(serde::de::Error::custom)
}
}
#[derive( let x = raw
Debug, Clone, serde::Deserialize, serde::Serialize, Archive, rkyv::Serialize, rkyv::Deserialize, .get(1)
)] .ok_or_else(|| anyhow::anyhow!("Missing X field"))?
#[rkyv(compare(PartialEq), derive(Debug))] .parse()?;
pub struct BTableEntry {
fxy: FXY,
class_name_en: String,
element_name_en: String,
bufr_unit: String,
bufr_scale: i32,
bufr_reference_value: i32,
bufr_datawidth_bits: u32,
note_en: Option<String>,
note_ids: Option<String>,
status: String,
}
impl BTableEntry { let y = raw
pub fn fxy(&self) -> FXY { .get(2)
self.fxy .ok_or_else(|| anyhow::anyhow!("Missing Y field"))?
} .parse()?;
pub fn class_name_en(&self) -> &str { let fxy = FXY::new(f, x, y);
&self.class_name_en
}
pub fn element_name_en(&self) -> &str { let class_name_en = raw
&self.element_name_en .get(3)
} .ok_or_else(|| anyhow::anyhow!("Missing Class Name EN"))?
.to_string();
pub fn bufr_unit(&self) -> &str { let bufr_unit = raw
&self.bufr_unit .get(4)
} .ok_or_else(|| anyhow::anyhow!("Missing BUFR Unit"))?
.to_string();
pub fn bufr_scale(&self) -> i32 { let bufr_scale = raw
self.bufr_scale .get(5)
} .ok_or_else(|| anyhow::anyhow!("Missing Scaling Field"))?
.parse()?;
pub fn bufr_reference_value(&self) -> i32 { let bufr_reference_value = raw
self.bufr_reference_value .get(6)
} .ok_or_else(|| anyhow::anyhow!("Missing Reference Value Field"))?
.parse()?;
pub fn bufr_datawidth_bits(&self) -> u32 { let bufr_datawidth_bits = raw
self.bufr_datawidth_bits .get(7)
} .ok_or_else(|| anyhow::anyhow!("Missing Datawidth Bits Field"))?
.parse()?;
pub fn note_en(&self) -> Option<&str> {
self.note_en.as_deref()
}
pub fn note_ids(&self) -> Option<&str> {
self.note_ids.as_deref()
}
pub fn status(&self) -> &str {
&self.status
}
}
impl std::fmt::Display for BTableEntry {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let element_name = if self.element_name_en.len() > 40 {
format!("{}...", &self.element_name_en[..37])
} else {
self.element_name_en.clone()
};
let unit = if self.bufr_unit.len() > 15 {
format!("{}...", &self.bufr_unit[..12])
} else {
self.bufr_unit.clone()
};
write!(
f,
"{:02}{:02}{:03} | {:<40} | {:<15} | {:>5} | {:>8} | {:>8} | {}",
self.fxy.f,
self.fxy.x,
self.fxy.y,
element_name,
unit,
self.bufr_scale,
self.bufr_reference_value,
self.bufr_datawidth_bits,
self.status
)
}
}
impl TableEntryLoader for BTableCsvLoader {
type RawEntry = RawBTableEntry;
type TableEntry = BTableEntry;
const TABLE_TYPE: crate::TableType = crate::TableType::B;
fn process_entry(&mut self, raw: Self::RawEntry) -> anyhow::Result<Option<Self::TableEntry>> {
// Parse FXY string (e.g., "001001") to u32
let fxy = FXY::from_str(&raw.fxy)?;
let entry = BTableEntry { let entry = BTableEntry {
fxy, fxy,
class_name_en: raw.class_name_en, class_name_en: class_name_en.clone(),
element_name_en: raw.element_name_en, element_name_en: class_name_en,
bufr_unit: raw.bufr_unit, bufr_unit,
bufr_scale: raw.bufr_scale, bufr_scale,
bufr_reference_value: raw.bufr_reference_value, bufr_reference_value,
bufr_datawidth_bits: raw.bufr_datawidth_bits, bufr_datawidth_bits,
note_en: raw.note_en, note_en: None,
note_ids: raw.note_ids, note_ids: None,
status: raw.status, status: None,
}; };
Ok(Some(entry)) Ok(Some(entry))
} }
} }
impl TableEntry for BTableEntry {
fn fxy(&self) -> FXY {
self.fxy
}
}

View File

@ -1,34 +1,67 @@
use super::TableEntryLoader; use super::EntryLoader;
use crate::{
FXY,
tables::{DTable, DTableEntry},
};
use csv::StringRecord; use csv::StringRecord;
use rkyv::Archive;
#[derive(Debug, Clone, Default)] #[derive(Default)]
pub struct DTableCsvLoader { pub struct FRDTableLoader {
current_chain: Option<DTableEntry>, current_chain: Option<DTableEntry>,
} }
impl TableEntryLoader for DTableCsvLoader { impl EntryLoader for FRDTableLoader {
type TableEntry = DTableEntry; type Output = DTableEntry;
const TABLE_TYPE: crate::TableType = crate::TableType::D; type TableType = DTable;
fn process_entry(&mut self, raw: &StringRecord) -> anyhow::Result<Option<Self::TableEntry>> { fn process_entry(&mut self, raw: StringRecord) -> anyhow::Result<Option<Self::Output>> {
let f = raw
.get(0)
.ok_or_else(|| anyhow::anyhow!("Missing F field"))?
.parse::<u16>()?;
let x = raw
.get(1)
.ok_or_else(|| anyhow::anyhow!("Missing X field"))?
.parse::<u16>()?;
let y = raw
.get(2)
.ok_or_else(|| anyhow::anyhow!("Missing Y field"))?
.parse::<u16>()?;
let f1 = raw
.get(3)
.ok_or_else(|| anyhow::anyhow!("Missing F1 field"))?;
let x1 = raw
.get(4)
.ok_or_else(|| anyhow::anyhow!("Missing X1 field"))?;
let y1 = raw
.get(5)
.ok_or_else(|| anyhow::anyhow!("Missing Y1 field"))?;
let fxy1 = FXY::new(f1.parse()?, x1.parse()?, y1.parse()?);
let fxy = FXY::new(f, x, y);
// Process the raw entry as needed // Process the raw entry as needed
if self.current_chain.is_none() { if self.current_chain.is_none() {
let entry = DTableEntry { let entry = DTableEntry {
fxy: FXY::from_str(&raw.fxy1)?, fxy,
fxy_chain: vec![FXY::from_str(&raw.fxy2)?], fxy_chain: vec![fxy1],
category: raw.category, category: None,
category_of_sequences_en: raw.category_of_sequences_en, category_of_sequences_en: None,
title_en: raw.title_en, title_en: None,
subtitle_en: raw.subtitle_en, subtitle_en: None,
note_en: raw.note_en, note_en: None,
note_ids: raw.note_ids, note_ids: None,
status: raw.status, status: None,
}; };
self.current_chain = Some(entry); self.current_chain = Some(entry);
return Ok(None); return Ok(None);
} else { } else {
let fxy = FXY::from_str(&raw.fxy1)?; let fxy = FXY::new(f, x, y);
if self.current_chain.as_ref().unwrap().fxy != fxy { if self.current_chain.as_ref().unwrap().fxy != fxy {
// First take out the old completed chain // First take out the old completed chain
let finished = self.current_chain.take(); let finished = self.current_chain.take();
@ -36,116 +69,26 @@ impl TableEntryLoader for DTableCsvLoader {
// Then create and save the new chain // Then create and save the new chain
let entry = DTableEntry { let entry = DTableEntry {
fxy, fxy,
fxy_chain: vec![FXY::from_str(&raw.fxy2)?], fxy_chain: vec![fxy1],
category: raw.category, category: None,
category_of_sequences_en: raw.category_of_sequences_en, category_of_sequences_en: None,
title_en: raw.title_en, title_en: None,
subtitle_en: raw.subtitle_en, subtitle_en: None,
note_en: raw.note_en, note_en: None,
note_ids: raw.note_ids, note_ids: None,
status: raw.status, status: None,
}; };
self.current_chain = Some(entry); self.current_chain = Some(entry);
// Return the old completed chain
return Ok(finished); return Ok(finished);
} else { } else {
self.current_chain self.current_chain.as_mut().unwrap().fxy_chain.push(fxy1);
.as_mut()
.unwrap()
.fxy_chain
.push(FXY::from_str(&raw.fxy2)?);
return Ok(None); return Ok(None);
} }
} }
} }
fn finish(&mut self) -> anyhow::Result<Option<Self::TableEntry>> { fn finish(&mut self) -> anyhow::Result<Option<Self::Output>> {
Ok(self.current_chain.take()) Ok(self.current_chain.take())
} }
} }
#[derive(
Debug, Clone, serde::Deserialize, serde::Serialize, Archive, rkyv::Serialize, rkyv::Deserialize,
)]
#[rkyv(compare(PartialEq), derive(Debug))]
pub struct DTableEntry {
fxy: FXY,
fxy_chain: Vec<FXY>,
category: String,
category_of_sequences_en: String,
title_en: Option<String>,
subtitle_en: Option<String>,
note_en: Option<String>,
note_ids: String,
status: String,
}
impl DTableEntry {
pub fn fxy(&self) -> FXY {
self.fxy
}
pub fn fxy_chain(&self) -> &[FXY] {
&self.fxy_chain
}
pub fn category(&self) -> &str {
&self.category
}
pub fn category_of_sequences_en(&self) -> &str {
&self.category_of_sequences_en
}
pub fn title_en(&self) -> Option<&str> {
self.title_en.as_deref()
}
pub fn subtitle_en(&self) -> Option<&str> {
self.subtitle_en.as_deref()
}
pub fn note_en(&self) -> Option<&str> {
self.note_en.as_deref()
}
pub fn note_ids(&self) -> &str {
&self.note_ids
}
pub fn status(&self) -> &str {
&self.status
}
}
impl std::fmt::Display for DTableEntry {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let fxy_chain_str: String = self
.fxy_chain
.iter()
.map(|fxy| format!("{:02}{:02}{:03}", fxy.f, fxy.x, fxy.y))
.collect::<Vec<_>>()
.join(", ");
let title = self.title_en.as_deref().unwrap_or("N/A");
let truncated_title = if title.len() > 50 {
format!("{}...", &title[..47])
} else {
title.to_string()
};
write!(
f,
"{:02}{:02}{:03} | {:<50} | {:<12} | [{}]",
self.fxy.f, self.fxy.x, self.fxy.y, truncated_title, self.status, fxy_chain_str
)
}
}
impl TableEntry for DTableEntry {
fn fxy(&self) -> FXY {
self.fxy
}
}

View File

@ -1,24 +1,34 @@
use crate::{
TableConverter,
tables::{TableEntry, TableEntryFull, TableTypeTrait},
};
use csv::{ReaderBuilder, StringRecord}; use csv::{ReaderBuilder, StringRecord};
use std::path::Path;
pub mod btable; pub mod btable;
pub mod dtable; pub mod dtable;
pub struct TableLoader; pub type FRDTableLoader = TableLoader<dtable::FRDTableLoader>;
pub type FRBTableLoader = TableLoader<btable::BTableLoader>;
impl TableLoader { #[derive(Default)]
pub fn load_table<P: AsRef<Path>, T: TableEntryLoader>( pub struct TableLoader<C: EntryLoader> {
_marker: std::marker::PhantomData<C>,
}
impl<C: EntryLoader> TableLoader<C> {
pub fn load_table<P: AsRef<std::path::Path>>(
&self, &self,
path: P, path: P,
loader: &mut T, loader: &mut C,
) -> anyhow::Result<Vec<T::TableEntry>> { ) -> anyhow::Result<Vec<C::Output>> {
let path = path.as_ref();
let mut entries = vec![]; let mut entries = vec![];
let mut rdr = ReaderBuilder::new() let mut rdr = ReaderBuilder::new()
.has_headers(false) .has_headers(false)
.delimiter(b';') .delimiter(b';')
.flexible(false) // Allow variable number of fields .flexible(false)
.from_path(path.as_ref())?; .from_path(path)?;
let mut line_num = 1; // Start at 1 for header let mut line_num = 1;
for result in rdr.records() { for result in rdr.records() {
line_num += 1; line_num += 1;
match result { match result {
@ -28,11 +38,10 @@ impl TableLoader {
} }
} }
Err(e) => { Err(e) => {
// Log the error but continue processing
eprintln!( eprintln!(
"Warning: Skipping line {} in {}: {}", "Warning: Skipping line {} in {}: {}",
line_num, line_num,
path.as_ref().display(), path.display(),
e e
); );
} }
@ -47,15 +56,25 @@ impl TableLoader {
} }
} }
pub trait TableEntryLoader: Sized { pub trait EntryLoader: Default {
type TableEntry: TableEntry; type Output: TableEntryFull;
type TableType: TableTypeTrait;
const TABLE_TYPE: TableType; fn process_entry(&mut self, raw: StringRecord) -> anyhow::Result<Option<Self::Output>>;
fn finish(&mut self) -> anyhow::Result<Option<Self::Output>> {
/// Process a single entry from the CSV file
fn process_entry(&mut self, raw: &StringRecord) -> anyhow::Result<Option<Self::TableEntry>>;
fn finish(&mut self) -> anyhow::Result<Option<Self::TableEntry>> {
Ok(None) Ok(None)
} }
} }
impl<T: EntryLoader> TableConverter for TableLoader<T> {
type OutputEntry = T::Output;
type TableType = T::TableType;
fn convert<P: AsRef<std::path::Path>>(
&self,
path: P,
) -> anyhow::Result<Vec<Self::OutputEntry>> {
let mut loader = T::default();
self.load_table(path, &mut loader)
}
}

View File

@ -1,117 +1,44 @@
// pub mod fr; pub mod fr;
pub mod prelude; pub mod prelude;
pub mod tables;
mod utils; mod utils;
pub mod wmo; pub mod wmo;
use anyhow::Context; use anyhow::Context;
use memmap2::Mmap; use memmap2::Mmap;
use ph::fmph::GOFunction; use ph::fmph::GOFunction;
use rkyv::Archive; use rkyv::Archive;
use rkyv::api::high::{HighDeserializer, HighSerializer};
use rkyv::rancor::Error; use rkyv::rancor::Error;
use serde::de::DeserializeOwned;
use serde::{Deserialize as SerdeDeserialize, Serialize as SerdeSerialize}; use serde::{Deserialize as SerdeDeserialize, Serialize as SerdeSerialize};
use std::fmt::Debug; use std::fmt::Debug;
use std::io::Write; use std::io::Write;
use std::path::Path; use std::path::Path;
use csv::ReaderBuilder; use crate::tables::{TableEntryFull, TableTypeTrait};
pub struct TableLoader; pub trait TableConverter {
type OutputEntry: TableEntryFull;
type TableType: TableTypeTrait;
fn convert<P: AsRef<Path>>(&self, path: P) -> anyhow::Result<Vec<Self::OutputEntry>>;
impl TableLoader { fn table_type(&self) -> crate::TableType {
pub fn load_table<P: AsRef<Path>, T: TableEntryLoader>( Self::TableType::TABLE_TYPE
&self,
path: P,
loader: &mut T,
) -> anyhow::Result<Vec<T::TableEntry>> {
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: T::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 TableEntryLoader: Sized struct BufrTableMph<T: TableEntryFull> {
where
Self::TableEntry: for<'a> rkyv::Serialize<
HighSerializer<rkyv::util::AlignedVec, rkyv::ser::allocator::ArenaHandle<'a>, Error>,
>,
<Self::TableEntry as Archive>::Archived:
rkyv::Deserialize<Self::TableEntry, HighDeserializer<Error>>,
{
/// The raw CSV entry type that will be deserialized
type RawEntry: for<'de> serde::Deserialize<'de> + Debug;
type TableEntry: TableEntry;
const TABLE_TYPE: TableType;
/// Process a single entry from the CSV file
fn process_entry(&mut self, raw: Self::RawEntry) -> anyhow::Result<Option<Self::TableEntry>>;
fn finish(&mut self) -> anyhow::Result<Option<Self::TableEntry>> {
Ok(None)
}
}
pub trait TableEntry: SerdeSerialize + DeserializeOwned + Debug + Clone + Archive {
fn fxy(&self) -> FXY;
}
struct BufrTableMph<T: TableEntryLoader> {
mphf: GOFunction, mphf: GOFunction,
offsets: Vec<u64>, offsets: Vec<u64>,
mmap: Mmap, mmap: Mmap,
_marker: std::marker::PhantomData<T>, _marker: std::marker::PhantomData<T>,
} }
struct BUFRTableM { impl<T: TableEntryFull> BufrTableMph<T> {
mphf: GOFunction, fn build(entries: Vec<T>, output_path: &str) -> std::io::Result<Self> {
}
impl<T: TableEntryLoader> BufrTableMph<T>
where
for<'a> T::TableEntry: rkyv::Serialize<
HighSerializer<rkyv::util::AlignedVec, rkyv::ser::allocator::ArenaHandle<'a>, Error>,
>,
<T::TableEntry as Archive>::Archived: rkyv::Deserialize<T::TableEntry, HighDeserializer<Error>>,
{
fn build(entries: Vec<T::TableEntry>, output_path: &str) -> std::io::Result<Self> {
println!("Building MPH table with {} entries...", entries.len()); println!("Building MPH table with {} entries...", entries.len());
let keys: Vec<FXY> = entries.iter().map(|e| e.fxy()).collect(); let keys: Vec<FXY> = entries.iter().map(|e| e.fxy()).collect();
let mphf = GOFunction::from_slice(&keys); let mphf = GOFunction::from_slice(&keys);
let mut sorted_entries: Vec<(usize, T::TableEntry)> = entries let mut sorted_entries: Vec<(usize, T)> = entries
.into_iter() .into_iter()
.map(|e| (mphf.get(&(e.fxy())).unwrap() as usize, e)) .map(|e| (mphf.get(&(e.fxy())).unwrap() as usize, e))
.collect(); .collect();
@ -212,7 +139,7 @@ where
} }
/// 获取拥有的版本 /// 获取拥有的版本
fn get(&self, fxy: FXY) -> Option<T::TableEntry> { fn get(&self, fxy: FXY) -> Option<T> {
let hash = self.mphf.get(&fxy)? as usize; let hash = self.mphf.get(&fxy)? as usize;
let offset = *self.offsets.get(hash)? as usize; let offset = *self.offsets.get(hash)? as usize;
@ -221,13 +148,12 @@ where
let data = self.mmap.get(offset + 4..offset + 4 + len)?; let data = self.mmap.get(offset + 4..offset + 4 + len)?;
let archived = let archived = unsafe { rkyv::access_unchecked::<<T as TableEntryFull>::Archived>(data) };
unsafe { rkyv::access_unchecked::<<T::TableEntry as Archive>::Archived>(data) }; rkyv::deserialize::<T, Error>(archived).ok()
rkyv::deserialize::<T::TableEntry, Error>(archived).ok()
} }
/// 获取所有条目 /// 获取所有条目
fn get_all(&self) -> Vec<T::TableEntry> { fn get_all(&self) -> Vec<T> {
let mut entries = Vec::new(); let mut entries = Vec::new();
for offset in &self.offsets { for offset in &self.offsets {
let offset = *offset as usize; let offset = *offset as usize;
@ -236,9 +162,9 @@ where
let len = u32::from_le_bytes(len_bytes_array) as usize; let len = u32::from_le_bytes(len_bytes_array) as usize;
if let Some(data) = self.mmap.get(offset + 4..offset + 4 + len) { if let Some(data) = self.mmap.get(offset + 4..offset + 4 + len) {
let archived = unsafe { let archived = unsafe {
rkyv::access_unchecked::<<T::TableEntry as Archive>::Archived>(data) rkyv::access_unchecked::<<T as TableEntryFull>::Archived>(data)
}; };
if let Ok(entry) = rkyv::deserialize::<T::TableEntry, Error>(archived) { if let Ok(entry) = rkyv::deserialize::<T, Error>(archived) {
entries.push(entry); entries.push(entry);
} }
} }
@ -269,15 +195,6 @@ pub struct FXY {
pub y: u16, pub y: u16,
} }
// // Custom Hash implementation to work around boomphf's overflow bug
// // We implement Hash by converting to u32 first
// impl std::hash::Hash for FXY {
// fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
// // Convert FXY to a simple u32 value to avoid complex hashing
// self.to_u32().hash(state);
// }
// }
impl FXY { impl FXY {
pub fn new(f: u16, x: u16, y: u16) -> Self { pub fn new(f: u16, x: u16, y: u16) -> Self {
FXY { f, x, y } FXY { f, x, y }
@ -319,32 +236,37 @@ impl FXY {
} }
} }
pub struct BUFRTableMPH<T: TableEntryLoader> { pub struct BUFRTableMPH<T: TableTypeTrait> {
inner: BufrTableMph<T>, inner: BufrTableMph<T::EntryType>,
} }
impl<T: TableEntryLoader> BUFRTableMPH<T> { impl<T: TableTypeTrait> BUFRTableMPH<T> {
pub fn build_from_csv<P: AsRef<Path>>( pub fn build_from_csv<P: AsRef<Path>, L: TableConverter>(
mut loader: T, loader: L,
csv_path: P, path: P,
output_path: P, output_path: P,
) -> anyhow::Result<Self> { ) -> anyhow::Result<Self>
let entries = TableLoader.load_table(csv_path, &mut loader)?; where
let bhm = BufrTableMph::<T>::build(entries, output_path.as_ref().to_str().unwrap())?; L: TableConverter<OutputEntry = T::EntryType>,
L: TableConverter<TableType = T>,
{
let entries = loader.convert(path)?;
let bhm =
BufrTableMph::<T::EntryType>::build(entries, output_path.as_ref().to_str().unwrap())?;
Ok(BUFRTableMPH { inner: bhm }) Ok(BUFRTableMPH { inner: bhm })
} }
pub fn load_from_disk<P: AsRef<Path>>(path: P) -> anyhow::Result<Self> { pub fn load_from_disk<P: AsRef<Path>>(path: P) -> anyhow::Result<Self> {
let bhm: BufrTableMph<T> = BufrTableMph::load(path)?; let bhm: BufrTableMph<T::EntryType> = BufrTableMph::load(path)?;
Ok(BUFRTableMPH { inner: bhm }) Ok(BUFRTableMPH { inner: bhm })
} }
pub fn lookup(&self, fxy: FXY) -> anyhow::Result<Option<T::TableEntry>> { pub fn lookup(&self, fxy: FXY) -> anyhow::Result<Option<T::EntryType>> {
Ok(self.inner.get(fxy)) Ok(self.inner.get(fxy))
} }
pub fn get_all_entries(&self) -> Vec<T::TableEntry> { pub fn get_all_entries(&self) -> Vec<T::EntryType> {
self.inner.get_all() self.inner.get_all()
} }
} }
@ -358,16 +280,7 @@ pub enum TableType {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use crate::{BUFRTableMPH, dtable::DTableCsvLoader};
#[test] #[test]
fn test() { fn test() {}
let d_loader = DTableCsvLoader::default();
BUFRTableMPH::build_from_csv(
d_loader,
"/Users/xiang.li1/projects/rbufr/BUFR4/BUFR_TableD_en_00.csv",
"test_table_d",
)
.unwrap();
}
} }

View File

@ -1,6 +1,9 @@
use anyhow::{Context, Result}; use anyhow::{Context, Result, anyhow};
use clap::{Parser, Subcommand}; use clap::{Parser, Subcommand};
use genlib::{BUFRTableMPH, btable::BTableCsvLoader, dtable::DTableCsvLoader}; use genlib::{
TableType,
prelude::{BUFRTableB, BUFRTableD},
};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
#[derive(Parser)] #[derive(Parser)]
@ -196,16 +199,63 @@ fn convert_single_file(input_path: &Path, output_path: &Path, table_type: &str)
Ok(()) Ok(())
} }
type BuildFn = fn(&Path, &Path) -> Result<()>;
fn run_with_fallbacks(
kind: TableType,
input_path: &Path,
output_path: &Path,
attempts: &[(&str, BuildFn)],
) -> Result<()> {
let mut errors = Vec::new();
for (label, build_fn) in attempts {
match build_fn(input_path, output_path) {
Ok(()) => return Ok(()),
Err(err) => errors.push(format!("{label} failed: {err:#}")),
}
}
Err(anyhow!(
"all {:?} loaders failed:\n{}",
kind,
errors.join("\n---\n")
))
}
fn build_wmo_d(input_path: &Path, output_path: &Path) -> Result<()> {
let loader = genlib::wmo::TableLoader::<genlib::wmo::WMODTableLoader>::default();
BUFRTableD::build_from_csv(loader, input_path, output_path).map(|_| ())
}
fn build_fr_d(input_path: &Path, output_path: &Path) -> Result<()> {
let loader = genlib::fr::FRDTableLoader::default();
BUFRTableD::build_from_csv(loader, input_path, output_path).map(|_| ())
}
fn convert_table_d(input_path: &Path, output_path: &Path) -> Result<()> { fn convert_table_d(input_path: &Path, output_path: &Path) -> Result<()> {
let loader = DTableCsvLoader::default(); const ATTEMPTS: &[(&str, BuildFn)] = &[
BUFRTableMPH::build_from_csv(loader, input_path, output_path)?; ("WMO Table D loader", build_wmo_d),
Ok(()) ("FR Table D loader", build_fr_d),
];
run_with_fallbacks(TableType::D, input_path, output_path, ATTEMPTS)
}
fn build_wmo_b(input_path: &Path, output_path: &Path) -> Result<()> {
let loader = genlib::wmo::TableLoader::<genlib::wmo::WMOBTableLoader>::default();
BUFRTableB::build_from_csv(loader, input_path, output_path).map(|_| ())
}
fn build_fr_b(input_path: &Path, output_path: &Path) -> Result<()> {
let loader = genlib::fr::FRBTableLoader::default();
BUFRTableB::build_from_csv(loader, input_path, output_path).map(|_| ())
} }
fn convert_table_b(input_path: &Path, output_path: &Path) -> Result<()> { fn convert_table_b(input_path: &Path, output_path: &Path) -> Result<()> {
let loader = BTableCsvLoader; const ATTEMPTS: &[(&str, BuildFn)] = &[
BUFRTableMPH::build_from_csv(loader, input_path, output_path)?; ("WMO Table B loader", build_wmo_b),
Ok(()) ("FR Table B loader", build_fr_b),
];
run_with_fallbacks(TableType::B, input_path, output_path, ATTEMPTS)
} }
fn print_table(input_path: &Path, table_type: &str, limit: Option<usize>) -> Result<()> { fn print_table(input_path: &Path, table_type: &str, limit: Option<usize>) -> Result<()> {
@ -219,11 +269,9 @@ fn print_table(input_path: &Path, table_type: &str, limit: Option<usize>) -> Res
} }
fn print_table_d(input_path: &Path, limit: Option<usize>) -> Result<()> { fn print_table_d(input_path: &Path, limit: Option<usize>) -> Result<()> {
use genlib::dtable::DTableEntry;
println!("Loading Table D from: {}", input_path.display()); println!("Loading Table D from: {}", input_path.display());
let table: BUFRTableMPH<DTableCsvLoader> = BUFRTableMPH::load_from_disk(input_path)?; let table: BUFRTableD = BUFRTableD::load_from_disk(input_path)?;
let entries = table.get_all_entries(); let entries = table.get_all_entries();
println!("\nTable D Entries (Total: {})", entries.len()); println!("\nTable D Entries (Total: {})", entries.len());
@ -254,11 +302,9 @@ fn print_table_d(input_path: &Path, limit: Option<usize>) -> Result<()> {
} }
fn print_table_b(input_path: &Path, limit: Option<usize>) -> Result<()> { fn print_table_b(input_path: &Path, limit: Option<usize>) -> Result<()> {
use genlib::btable::BTableEntry;
println!("Loading Table B from: {}", input_path.display()); println!("Loading Table B from: {}", input_path.display());
let table: BUFRTableMPH<BTableCsvLoader> = BUFRTableMPH::load_from_disk(input_path)?; let table: BUFRTableB = BUFRTableB::load_from_disk(input_path)?;
let entries = table.get_all_entries(); let entries = table.get_all_entries();
println!("\nTable B Entries (Total: {})", entries.len()); println!("\nTable B Entries (Total: {})", entries.len());

View File

@ -1,6 +1,8 @@
use crate::tables::BTable;
use crate::tables::DTable;
pub use crate::wmo; pub use crate::wmo;
// pub type BUFRTableD = crate::BUFRTableMPH<DTableCsvLoader>; pub type BUFRTableD = crate::BUFRTableMPH<DTable>;
// pub type BUFRTableB = crate::BUFRTableMPH<crate::btable::BTableCsvLoader>; pub type BUFRTableB = crate::BUFRTableMPH<BTable>;
pub use crate::BUFRTableMPH; pub use crate::BUFRTableMPH;
pub use crate::FXY; pub use crate::FXY;
pub use crate::TableType; pub use crate::TableType;

238
gen/src/tables.rs Normal file
View File

@ -0,0 +1,238 @@
use crate::FXY;
use rkyv::Archive;
use rkyv::api::high::{HighDeserializer, HighSerializer};
use rkyv::de::Pool;
use rkyv::rancor::{Error, Strategy};
use serde::Serialize as SerdeSerialize;
use serde::de::DeserializeOwned;
use std::fmt::Debug;
pub struct BTable;
pub struct DTable;
pub trait TableTypeTrait {
type EntryType: TableEntryFull;
const TABLE_TYPE: crate::TableType;
}
impl TableTypeTrait for BTable {
type EntryType = crate::tables::BTableEntry;
const TABLE_TYPE: crate::TableType = crate::TableType::B;
}
impl TableTypeTrait for DTable {
type EntryType = crate::tables::DTableEntry;
const TABLE_TYPE: crate::TableType = crate::TableType::D;
}
pub trait TableEntry:
SerdeSerialize
+ DeserializeOwned
+ Debug
+ Clone
+ Sized
+ Archive
+ for<'a> rkyv::Serialize<
HighSerializer<rkyv::util::AlignedVec, rkyv::ser::allocator::ArenaHandle<'a>, Error>,
>
{
fn fxy(&self) -> FXY;
}
pub trait TableEntryFull: TableEntry {
type Archived: for<'a> rkyv::Deserialize<Self, HighDeserializer<Error>>
+ rkyv::Deserialize<Self, Strategy<Pool, rkyv::rancor::Error>>
+ rkyv::Portable;
}
impl<T> TableEntryFull for T
where
T: TableEntry,
<T as Archive>::Archived: for<'a> rkyv::Deserialize<T, HighDeserializer<Error>>
+ rkyv::Deserialize<T, Strategy<Pool, rkyv::rancor::Error>>,
{
type Archived = <T as Archive>::Archived;
}
#[derive(
Debug, Clone, serde::Deserialize, serde::Serialize, Archive, rkyv::Serialize, rkyv::Deserialize,
)]
#[rkyv(compare(PartialEq), derive(Debug))]
pub struct BTableEntry {
pub fxy: FXY,
pub class_name_en: String,
pub element_name_en: String,
pub bufr_unit: String,
pub bufr_scale: i32,
pub bufr_reference_value: i32,
pub bufr_datawidth_bits: u32,
pub note_en: Option<String>,
pub note_ids: Option<String>,
pub status: Option<String>,
}
impl BTableEntry {
pub fn fxy(&self) -> FXY {
self.fxy
}
pub fn class_name_en(&self) -> &str {
&self.class_name_en
}
pub fn element_name_en(&self) -> &str {
&self.element_name_en
}
pub fn bufr_unit(&self) -> &str {
&self.bufr_unit
}
pub fn bufr_scale(&self) -> i32 {
self.bufr_scale
}
pub fn bufr_reference_value(&self) -> i32 {
self.bufr_reference_value
}
pub fn bufr_datawidth_bits(&self) -> u32 {
self.bufr_datawidth_bits
}
pub fn note_en(&self) -> Option<&str> {
self.note_en.as_deref()
}
pub fn note_ids(&self) -> Option<&str> {
self.note_ids.as_deref()
}
pub fn status(&self) -> Option<&str> {
self.status.as_deref()
}
}
impl std::fmt::Display for BTableEntry {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let element_name = if self.element_name_en.len() > 40 {
format!("{}...", &self.element_name_en[..37])
} else {
self.element_name_en.clone()
};
let unit = if self.bufr_unit.len() > 15 {
format!("{}...", &self.bufr_unit[..12])
} else {
self.bufr_unit.clone()
};
write!(
f,
"{:02}{:02}{:03} | {:<40} | {:<15} | {:>5} | {:>8} | {:>8} | {}",
self.fxy.f,
self.fxy.x,
self.fxy.y,
element_name,
unit,
self.bufr_scale,
self.bufr_reference_value,
self.bufr_datawidth_bits,
self.status().unwrap_or("N/A")
)
}
}
#[derive(
Debug, Clone, serde::Deserialize, serde::Serialize, Archive, rkyv::Serialize, rkyv::Deserialize,
)]
#[rkyv(compare(PartialEq), derive(Debug))]
pub struct DTableEntry {
pub fxy: FXY,
pub fxy_chain: Vec<FXY>,
pub category: Option<String>,
pub category_of_sequences_en: Option<String>,
pub title_en: Option<String>,
pub subtitle_en: Option<String>,
pub note_en: Option<String>,
pub note_ids: Option<String>,
pub status: Option<String>,
}
impl DTableEntry {
pub fn fxy(&self) -> FXY {
self.fxy
}
pub fn fxy_chain(&self) -> &[FXY] {
&self.fxy_chain
}
pub fn category(&self) -> Option<&str> {
self.category.as_deref()
}
pub fn category_of_sequences_en(&self) -> Option<&str> {
self.category_of_sequences_en.as_deref()
}
pub fn title_en(&self) -> Option<&str> {
self.title_en.as_deref()
}
pub fn subtitle_en(&self) -> Option<&str> {
self.subtitle_en.as_deref()
}
pub fn note_en(&self) -> Option<&str> {
self.note_en.as_deref()
}
pub fn note_ids(&self) -> Option<&str> {
self.note_ids.as_deref()
}
pub fn status(&self) -> Option<&str> {
self.status.as_deref()
}
}
impl std::fmt::Display for DTableEntry {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let fxy_chain_str: String = self
.fxy_chain
.iter()
.map(|fxy| format!("{:02}{:02}{:03}", fxy.f, fxy.x, fxy.y))
.collect::<Vec<_>>()
.join(", ");
let title = self.title_en.as_deref().unwrap_or("N/A");
let truncated_title = if title.len() > 50 {
format!("{}...", &title[..47])
} else {
title.to_string()
};
write!(
f,
"{:02}{:02}{:03} | {:<50} | {:<12} | [{}]",
self.fxy.f,
self.fxy.x,
self.fxy.y,
truncated_title,
self.status().unwrap_or("N/A"),
fxy_chain_str
)
}
}
impl TableEntry for DTableEntry {
fn fxy(&self) -> FXY {
self.fxy
}
}
impl TableEntry for BTableEntry {
fn fxy(&self) -> FXY {
self.fxy
}
}

View File

@ -1,6 +1,10 @@
use crate::{FXY, TableEntry, TableEntryLoader}; use super::EntryLoader;
use rkyv::Archive; use crate::{
FXY,
tables::{BTable, BTableEntry},
};
#[derive(Default)]
pub struct BTableCsvLoader; pub struct BTableCsvLoader;
#[derive(Debug, serde::Deserialize)] #[derive(Debug, serde::Deserialize)]
@ -32,7 +36,7 @@ pub struct RawBTableEntry {
#[serde(rename = "noteIDs")] #[serde(rename = "noteIDs")]
pub note_ids: Option<String>, pub note_ids: Option<String>,
#[serde(rename = "Status")] #[serde(rename = "Status")]
pub status: String, pub status: Option<String>,
} }
// Helper function to deserialize empty strings as None // Helper function to deserialize empty strings as None
@ -57,102 +61,12 @@ where
} }
} }
#[derive( impl EntryLoader for BTableCsvLoader {
Debug, Clone, serde::Deserialize, serde::Serialize, Archive, rkyv::Serialize, rkyv::Deserialize,
)]
#[rkyv(compare(PartialEq), derive(Debug))]
pub struct BTableEntry {
fxy: FXY,
class_name_en: String,
element_name_en: String,
bufr_unit: String,
bufr_scale: i32,
bufr_reference_value: i32,
bufr_datawidth_bits: u32,
note_en: Option<String>,
note_ids: Option<String>,
status: String,
}
impl BTableEntry {
pub fn fxy(&self) -> FXY {
self.fxy
}
pub fn class_name_en(&self) -> &str {
&self.class_name_en
}
pub fn element_name_en(&self) -> &str {
&self.element_name_en
}
pub fn bufr_unit(&self) -> &str {
&self.bufr_unit
}
pub fn bufr_scale(&self) -> i32 {
self.bufr_scale
}
pub fn bufr_reference_value(&self) -> i32 {
self.bufr_reference_value
}
pub fn bufr_datawidth_bits(&self) -> u32 {
self.bufr_datawidth_bits
}
pub fn note_en(&self) -> Option<&str> {
self.note_en.as_deref()
}
pub fn note_ids(&self) -> Option<&str> {
self.note_ids.as_deref()
}
pub fn status(&self) -> &str {
&self.status
}
}
impl std::fmt::Display for BTableEntry {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let element_name = if self.element_name_en.len() > 40 {
format!("{}...", &self.element_name_en[..37])
} else {
self.element_name_en.clone()
};
let unit = if self.bufr_unit.len() > 15 {
format!("{}...", &self.bufr_unit[..12])
} else {
self.bufr_unit.clone()
};
write!(
f,
"{:02}{:02}{:03} | {:<40} | {:<15} | {:>5} | {:>8} | {:>8} | {}",
self.fxy.f,
self.fxy.x,
self.fxy.y,
element_name,
unit,
self.bufr_scale,
self.bufr_reference_value,
self.bufr_datawidth_bits,
self.status
)
}
}
impl TableEntryLoader for BTableCsvLoader {
type RawEntry = RawBTableEntry; type RawEntry = RawBTableEntry;
type TableEntry = BTableEntry; type Output = BTableEntry;
const TABLE_TYPE: crate::TableType = crate::TableType::B; type TableType = BTable;
fn process_entry(&mut self, raw: Self::RawEntry) -> anyhow::Result<Option<Self::TableEntry>> { fn process_entry(&mut self, raw: Self::RawEntry) -> anyhow::Result<Option<Self::Output>> {
// Parse FXY string (e.g., "001001") to u32
let fxy = FXY::from_str(&raw.fxy)?; let fxy = FXY::from_str(&raw.fxy)?;
let entry = BTableEntry { let entry = BTableEntry {
@ -171,9 +85,3 @@ impl TableEntryLoader for BTableCsvLoader {
Ok(Some(entry)) Ok(Some(entry))
} }
} }
impl TableEntry for BTableEntry {
fn fxy(&self) -> FXY {
self.fxy
}
}

View File

@ -1,5 +1,8 @@
use crate::{FXY, TableEntry, TableEntryLoader}; use super::EntryLoader;
use rkyv::Archive; use crate::{
FXY,
tables::{DTable, DTableEntry},
};
#[derive(Debug, Clone, Default)] #[derive(Debug, Clone, Default)]
pub struct DTableCsvLoader { pub struct DTableCsvLoader {
@ -9,9 +12,9 @@ pub struct DTableCsvLoader {
#[derive(Debug, serde::Deserialize)] #[derive(Debug, serde::Deserialize)]
pub struct RawDTableEntry { pub struct RawDTableEntry {
#[serde(rename = "Category")] #[serde(rename = "Category")]
pub category: String, pub category: Option<String>,
#[serde(rename = "CategoryOfSequences_en")] #[serde(rename = "CategoryOfSequences_en")]
pub category_of_sequences_en: String, pub category_of_sequences_en: Option<String>,
#[serde(rename = "FXY1")] #[serde(rename = "FXY1")]
pub fxy1: String, pub fxy1: String,
#[serde(rename = "Title_en")] #[serde(rename = "Title_en")]
@ -27,17 +30,17 @@ pub struct RawDTableEntry {
#[serde(rename = "Note_en")] #[serde(rename = "Note_en")]
pub note_en: Option<String>, pub note_en: Option<String>,
#[serde(rename = "noteIDs")] #[serde(rename = "noteIDs")]
pub note_ids: String, pub note_ids: Option<String>,
#[serde(rename = "Status")] #[serde(rename = "Status")]
pub status: String, pub status: Option<String>,
} }
impl TableEntryLoader for DTableCsvLoader { impl EntryLoader for DTableCsvLoader {
type RawEntry = RawDTableEntry; type RawEntry = RawDTableEntry;
type TableEntry = DTableEntry; type Output = DTableEntry;
const TABLE_TYPE: crate::TableType = crate::TableType::D; type TableType = DTable;
fn process_entry(&mut self, raw: Self::RawEntry) -> anyhow::Result<Option<Self::TableEntry>> { fn process_entry(&mut self, raw: Self::RawEntry) -> anyhow::Result<Option<Self::Output>> {
// Process the raw entry as needed // Process the raw entry as needed
if self.current_chain.is_none() { if self.current_chain.is_none() {
let entry = DTableEntry { let entry = DTableEntry {
@ -87,91 +90,7 @@ impl TableEntryLoader for DTableCsvLoader {
} }
} }
fn finish(&mut self) -> anyhow::Result<Option<Self::TableEntry>> { fn finish(&mut self) -> anyhow::Result<Option<Self::Output>> {
Ok(self.current_chain.take()) Ok(self.current_chain.take())
} }
} }
#[derive(
Debug, Clone, serde::Deserialize, serde::Serialize, Archive, rkyv::Serialize, rkyv::Deserialize,
)]
#[rkyv(compare(PartialEq), derive(Debug))]
pub struct DTableEntry {
fxy: FXY,
fxy_chain: Vec<FXY>,
category: String,
category_of_sequences_en: String,
title_en: Option<String>,
subtitle_en: Option<String>,
note_en: Option<String>,
note_ids: String,
status: String,
}
impl DTableEntry {
pub fn fxy(&self) -> FXY {
self.fxy
}
pub fn fxy_chain(&self) -> &[FXY] {
&self.fxy_chain
}
pub fn category(&self) -> &str {
&self.category
}
pub fn category_of_sequences_en(&self) -> &str {
&self.category_of_sequences_en
}
pub fn title_en(&self) -> Option<&str> {
self.title_en.as_deref()
}
pub fn subtitle_en(&self) -> Option<&str> {
self.subtitle_en.as_deref()
}
pub fn note_en(&self) -> Option<&str> {
self.note_en.as_deref()
}
pub fn note_ids(&self) -> &str {
&self.note_ids
}
pub fn status(&self) -> &str {
&self.status
}
}
impl std::fmt::Display for DTableEntry {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let fxy_chain_str: String = self
.fxy_chain
.iter()
.map(|fxy| format!("{:02}{:02}{:03}", fxy.f, fxy.x, fxy.y))
.collect::<Vec<_>>()
.join(", ");
let title = self.title_en.as_deref().unwrap_or("N/A");
let truncated_title = if title.len() > 50 {
format!("{}...", &title[..47])
} else {
title.to_string()
};
write!(
f,
"{:02}{:02}{:03} | {:<50} | {:<12} | [{}]",
self.fxy.f, self.fxy.x, self.fxy.y, truncated_title, self.status, fxy_chain_str
)
}
}
impl TableEntry for DTableEntry {
fn fxy(&self) -> FXY {
self.fxy
}
}

View File

@ -1,2 +1,82 @@
pub mod btable; pub mod btable;
pub mod dtable; 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<C: EntryLoader> {
_marker: std::marker::PhantomData<C>,
}
impl<C: EntryLoader> TableLoader<C> {
pub fn load_table<P: AsRef<std::path::Path>>(
&self,
path: P,
loader: &mut C,
) -> anyhow::Result<Vec<C::Output>> {
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<Option<Self::Output>>;
fn finish(&mut self) -> anyhow::Result<Option<Self::Output>> {
Ok(None)
}
}
impl<T: EntryLoader> TableConverter for TableLoader<T> {
type OutputEntry = T::Output;
type TableType = T::TableType;
fn convert<P: AsRef<std::path::Path>>(
&self,
path: P,
) -> anyhow::Result<Vec<Self::OutputEntry>> {
let mut loader = T::default();
self.load_table(path, &mut loader)
}
}

View File

@ -1,4 +1,5 @@
use genlib::{BUFRTableMPH, TableEntryLoader}; use genlib::BUFRTableMPH;
use genlib::tables::TableTypeTrait;
use crate::errors::Result; use crate::errors::Result;
use crate::structs::versions::BUFRMessage; use crate::structs::versions::BUFRMessage;
@ -44,7 +45,7 @@ impl MessageBlock {
// master_b_table.load_table(TT::Standard); // master_b_table.load_table(TT::Standard);
} }
fn load_first_validable_table<E: TableEntryLoader>( fn load_first_validable_table<E: TableTypeTrait>(
&self, &self,
table_version: u8, table_version: u8,
) -> Result<BUFRTableMPH<E>> { ) -> Result<BUFRTableMPH<E>> {

View File

@ -1,6 +1,6 @@
use crate::errors::Result; use crate::errors::Result;
pub use genlib::prelude::{BUFRTableB, BUFRTableD, TableType}; pub use genlib::prelude::{BUFRTableB, BUFRTableD, TableType};
use genlib::{TableEntryLoader, prelude::*}; use genlib::{prelude::*, tables::TableTypeTrait};
use std::path::PathBuf; use std::path::PathBuf;
pub trait TableTrait { pub trait TableTrait {
@ -94,7 +94,7 @@ pub struct TableLoader;
impl TableLoader { impl TableLoader {
pub fn load_table<T>(&self, table_type: impl TableTrait) -> Result<BUFRTableMPH<T>> pub fn load_table<T>(&self, table_type: impl TableTrait) -> Result<BUFRTableMPH<T>>
where where
T: TableEntryLoader, T: TableTypeTrait,
{ {
let path = table_type.file_path(T::TABLE_TYPE); let path = table_type.file_path(T::TABLE_TYPE);
BUFRTableMPH::<T>::load_from_disk(path).map_err(|e| e.into()) BUFRTableMPH::<T>::load_from_disk(path).map_err(|e| e.into())