use std::collections::BTreeMap;
use std::path::Path;
use crate::release::{Release, ReleaseError, Table, csv_at, field};
#[derive(Debug, Clone, PartialEq, Eq)]
#[expect(
clippy::struct_field_names,
reason = "the fields are the LOINC linguistic variant columns, named as the release names them"
)]
pub struct Translation {
pub long_common_name: Option<String>,
pub short_name: Option<String>,
pub display_name: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Variant {
pub language: String,
pub terms: BTreeMap<String, Translation>,
}
#[must_use]
pub fn language_of(path: &Path) -> Option<String> {
let name = path.file_name()?.to_str()?;
let bytes = name.as_bytes();
if bytes.len() < 4
|| !bytes.get(0..2)?.iter().all(u8::is_ascii_lowercase)
|| !bytes.get(2..4)?.iter().all(u8::is_ascii_uppercase)
{
return None;
}
Some(format!("{}-{}", name.get(0..2)?, name.get(2..4)?))
}
pub fn read(release: &Release) -> Result<Vec<Variant>, ReleaseError> {
let mut out = Vec::new();
for path in release.variants() {
let Some(language) = language_of(path) else {
continue;
};
let mut table = Table::open(path)?;
let code_at = table.column("LOINC_NUM")?;
let columns = table.columns();
let at = |name: &str| columns.iter().position(|c| c.eq_ignore_ascii_case(name));
let long_at = at("LONG_COMMON_NAME");
let short_at = at("SHORTNAME");
let display_at = at("LinguisticVariantDisplayName");
let mut terms = BTreeMap::new();
let path = table.path.clone();
for record in table.reader.records() {
let record = record.map_err(|e| csv_at(&path, e))?;
let text = |at: Option<usize>| {
at.map(|i| field(&record, i))
.filter(|v| !v.is_empty())
.map(str::to_owned)
};
terms.insert(
field(&record, code_at).to_owned(),
Translation {
long_common_name: text(long_at),
short_name: text(short_at),
display_name: text(display_at),
},
);
}
out.push(Variant { language, terms });
}
Ok(out)
}
#[cfg(test)]
mod tests {
use std::path::Path;
use super::language_of;
#[test]
fn the_language_is_read_from_the_file_name() {
assert_eq!(
language_of(Path::new("LinguisticVariants/nlNL22LinguisticVariant.csv")).as_deref(),
Some("nl-NL")
);
assert_eq!(
language_of(Path::new("deAT24LinguisticVariant.csv")).as_deref(),
Some("de-AT")
);
assert_eq!(language_of(Path::new("LinguisticVariant.csv")), None);
}
}