use std::collections::BTreeMap;
use serde::Deserialize;
use super::AllophonyRule;
#[derive(Debug, Clone, Deserialize)]
pub struct Variety {
pub id: String,
#[serde(default = "default_kind")]
pub kind: String, #[serde(default)]
pub axis: String, #[serde(default)]
pub prestige: Option<String>,
#[serde(default)]
pub sound_changes: Vec<AllophonyRule>,
#[serde(default)]
pub lexicon: BTreeMap<String, String>,
#[serde(default)]
pub note: Option<String>,
}
fn default_kind() -> String {
"dialect".to_string()
}
impl Variety {
pub fn summary(&self) -> String {
let mut bits = vec![self.kind.clone()];
if !self.axis.is_empty() {
bits.push(self.axis.clone());
}
if let Some(p) = &self.prestige {
bits.push(format!("{p} prestige"));
}
bits.push(format!("{} sound change(s)", self.sound_changes.len()));
if !self.lexicon.is_empty() {
bits.push(format!("{} word override(s)", self.lexicon.len()));
}
bits.join(", ")
}
}
#[derive(Debug, Clone, Default, Deserialize)]
pub struct Varieties {
#[serde(default)]
pub varieties: Vec<Variety>,
}
impl Varieties {
pub fn from_hjson(body: &str) -> Result<Option<Self>, String> {
if body.trim().is_empty() {
return Ok(None);
}
let block = crate::language_entry::extract_hjson_block(body).unwrap_or(body);
match serde_hjson::from_str::<Self>(block) {
Ok(v) if !v.varieties.is_empty() => Ok(Some(v)),
Ok(_) => Ok(None),
Err(e) => Err(format!("varieties HJSON parse failed: {e}")),
}
}
pub fn get(&self, id: &str) -> Option<&Variety> {
self.varieties.iter().find(|v| v.id.eq_ignore_ascii_case(id))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parses_a_dialect_with_sound_changes_and_overrides() {
let body = r#"{ varieties: [
{ id: "lowland", kind: "dialect", axis: "region", prestige: "low",
sound_changes: [ { rule: "t > d / V _ V" } ],
lexicon: { "water": "moru" } }
] }"#;
let v = Varieties::from_hjson(body).unwrap().unwrap();
assert_eq!(v.varieties.len(), 1);
let d = v.get("LOWLAND").unwrap(); assert_eq!(d.kind, "dialect");
assert_eq!(d.sound_changes.len(), 1);
assert_eq!(d.lexicon.get("water").unwrap(), "moru");
assert!(d.summary().contains("region"));
}
#[test]
fn empty_block_is_none() {
assert!(Varieties::from_hjson("{ varieties: [] }").unwrap().is_none());
assert!(Varieties::from_hjson("").unwrap().is_none());
}
}