sync
This commit is contained in:
parent
3f2a2bcefc
commit
b8b31c4e69
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
161
gen/src/lib.rs
161
gen/src/lib.rs
@ -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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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());
|
||||||
|
|||||||
@ -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
238
gen/src/tables.rs
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -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>> {
|
||||||
|
|||||||
@ -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())
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue
Block a user