use std::{
collections::BTreeMap,
fs::File,
io::{self, BufReader},
path::Path,
};
use displaydoc::Display;
use quick_xml::{events::Event as XmlEvent, Error as XmlError, Reader as XmlReader};
use thiserror::Error;
use super::common::exact::{expect_attribute, expect_end, expect_start, expect_text, Error};
#[derive(Debug, Display, Error)]
pub enum LocaleError {
Io(#[from] io::Error),
Xml(#[from] Error),
}
impl From<XmlError> for LocaleError {
fn from(e: XmlError) -> Self {
Self::Xml(Error::Xml(e))
}
}
#[derive(Debug, Default)]
pub struct LocaleNode {
pub value: Option<String>,
pub int_children: BTreeMap<u32, LocaleNode>,
pub str_children: BTreeMap<String, LocaleNode>,
}
impl LocaleNode {
pub fn get_keys(&self) -> BTreeMap<String, String> {
let mut keys = BTreeMap::new();
for (key, value) in &self.str_children {
value.add_keys(&mut keys, key.clone());
}
for (key, value) in &self.int_children {
value.add_keys(&mut keys, key.to_string());
}
keys
}
fn add_keys(&self, keys: &mut BTreeMap<String, String>, prefix: String) {
for (key, value) in &self.str_children {
let inner = format!("{}_{}", prefix, key);
value.add_keys(keys, inner);
}
for (key, value) in &self.int_children {
let inner = format!("{}_{}", prefix, key);
value.add_keys(keys, inner);
}
if let Some(v) = &self.value {
keys.insert(prefix, v.clone());
}
}
}
pub fn load_locale(path: &Path) -> Result<LocaleNode, LocaleError> {
let file = File::open(path)?;
let file = BufReader::new(file);
let mut root = LocaleNode {
value: None,
int_children: BTreeMap::new(),
str_children: BTreeMap::new(),
};
let mut reader = XmlReader::from_reader(file);
reader.trim_text(true);
let mut buf = Vec::new();
if let Ok(XmlEvent::Decl(_)) = reader.read_event(&mut buf) {}
buf.clear();
let _ = expect_start("localization", &mut reader, &mut buf)?;
buf.clear();
let e_locales = expect_start("locales", &mut reader, &mut buf)?;
let locale_count = expect_attribute("count", &reader, &e_locales)?;
buf.clear();
for _ in 0..locale_count {
let _ = expect_start("locale", &mut reader, &mut buf)?;
buf.clear();
let _locale = expect_text(&mut reader, &mut buf)?;
buf.clear();
let _ = expect_end("locale", &mut reader, &mut buf)?;
buf.clear();
}
let _ = expect_end("locales", &mut reader, &mut buf)?;
buf.clear();
let e_locales = expect_start("phrases", &mut reader, &mut buf)?;
let phrase_count = expect_attribute("count", &reader, &e_locales)?;
buf.clear();
for _ in 0..phrase_count {
let e_phrase = expect_start("phrase", &mut reader, &mut buf)?;
let id: String = expect_attribute("id", &reader, &e_phrase)?;
buf.clear();
let mut translation = None;
loop {
let event = reader.read_event(&mut buf)?;
let e_translation = match event {
XmlEvent::End(e) => {
if e.name() == b"phrase" {
break;
} else {
let name_str = reader.decode(e.name());
return Err(LocaleError::Xml(Error::ExpectedEndTag(
String::from("phrase"),
name_str.into_owned(),
)));
}
}
XmlEvent::Start(e) => {
if e.name() == b"translation" {
e
} else {
let name_str = reader.decode(e.name());
return Err(LocaleError::Xml(Error::ExpectedEndTag(
String::from("translation"),
name_str.into_owned(),
)));
}
}
_ => panic!(),
};
let locale: String = expect_attribute("locale", &reader, &e_translation)?;
buf.clear();
let trans = expect_text(&mut reader, &mut buf)?;
if &locale == "en_US" {
translation = Some(trans);
}
buf.clear();
let _ = expect_end("translation", &mut reader, &mut buf)?;
buf.clear();
}
let mut node = &mut root;
for comp in id.split('_') {
if let Ok(num) = comp.parse::<u32>() {
node = node.int_children.entry(num).or_default();
} else {
node = node.str_children.entry(comp.to_owned()).or_default();
}
}
if let Some(translation) = translation {
node.value = Some(translation);
}
}
let _ = expect_end("phrases", &mut reader, &mut buf)?;
buf.clear();
Ok(root)
}