assembly_xml/
localization.rs

1//! # The XML `<localization>` format
2//!
3//! This is used in:
4//! - the `locale/locale.xml` file
5
6use std::{
7    collections::BTreeMap,
8    fs::File,
9    io::{self, BufReader},
10    path::Path,
11};
12
13use displaydoc::Display;
14use quick_xml::{events::Event as XmlEvent, Error as XmlError, Reader as XmlReader};
15use thiserror::Error;
16
17use super::common::exact::{expect_attribute, expect_end, expect_start, expect_text, Error};
18
19#[derive(Debug, Display, Error)]
20/// Some problem with loading a locale file
21pub enum LocaleError {
22    /// I/O Error
23    Io(#[from] io::Error),
24    /// Xml
25    Xml(#[from] Error),
26}
27
28impl From<XmlError> for LocaleError {
29    fn from(e: XmlError) -> Self {
30        Self::Xml(Error::Xml(e))
31    }
32}
33#[derive(Debug, Default)]
34/// A node in the locale tree
35pub struct LocaleNode {
36    /// The translation at the current node
37    pub value: Option<String>,
38    /// The (optional) children with a numeric key
39    pub int_children: BTreeMap<u32, LocaleNode>,
40    /// The (optional) children with a non-numeric key
41    pub str_children: BTreeMap<String, LocaleNode>,
42}
43
44impl LocaleNode {
45    /// Return all keys that correspond to this node
46    ///
47    /// This returns a flat map of locale values
48    pub fn get_keys(&self) -> BTreeMap<String, String> {
49        let mut keys = BTreeMap::new();
50        for (key, value) in &self.str_children {
51            value.add_keys(&mut keys, key.clone());
52        }
53        for (key, value) in &self.int_children {
54            value.add_keys(&mut keys, key.to_string());
55        }
56        keys
57    }
58
59    fn add_keys(&self, keys: &mut BTreeMap<String, String>, prefix: String) {
60        for (key, value) in &self.str_children {
61            let inner = format!("{}_{}", prefix, key);
62            value.add_keys(keys, inner);
63        }
64        for (key, value) in &self.int_children {
65            let inner = format!("{}_{}", prefix, key);
66            value.add_keys(keys, inner);
67        }
68        if let Some(v) = &self.value {
69            keys.insert(prefix, v.clone());
70        }
71    }
72}
73
74/// Load a locale file
75pub fn load_locale(path: &Path) -> Result<LocaleNode, LocaleError> {
76    let file = File::open(path)?;
77    let file = BufReader::new(file);
78
79    let mut root = LocaleNode {
80        value: None,
81        int_children: BTreeMap::new(),
82        str_children: BTreeMap::new(),
83    };
84
85    let mut reader = XmlReader::from_reader(file);
86    reader.trim_text(true);
87
88    let mut buf = Vec::new();
89
90    // The `Reader` does not implement `Iterator` because it outputs borrowed data (`Cow`s)
91    if let Ok(XmlEvent::Decl(_)) = reader.read_event(&mut buf) {}
92    buf.clear();
93
94    let _ = expect_start("localization", &mut reader, &mut buf)?;
95    //println!("<localization>");
96    buf.clear();
97
98    let e_locales = expect_start("locales", &mut reader, &mut buf)?;
99    //println!("<locales>");
100    let locale_count = expect_attribute("count", &reader, &e_locales)?;
101    buf.clear();
102
103    for _ in 0..locale_count {
104        let _ = expect_start("locale", &mut reader, &mut buf)?;
105        //print!("<locale>");
106        buf.clear();
107
108        let _locale = expect_text(&mut reader, &mut buf)?;
109        //print!("{}", locale);
110        buf.clear();
111
112        let _ = expect_end("locale", &mut reader, &mut buf)?;
113        //println!("</locale>");
114        buf.clear();
115    }
116
117    let _ = expect_end("locales", &mut reader, &mut buf)?;
118    buf.clear();
119    //println!("</locales>");
120
121    let e_locales = expect_start("phrases", &mut reader, &mut buf)?;
122    //println!("<phrases>");
123    let phrase_count = expect_attribute("count", &reader, &e_locales)?;
124    buf.clear();
125
126    for _ in 0..phrase_count {
127        let e_phrase = expect_start("phrase", &mut reader, &mut buf)?;
128        let id: String = expect_attribute("id", &reader, &e_phrase)?;
129
130        //let key = id.strip_prefix(&opt.prefix).map(|x| x.to_owned());
131        buf.clear();
132
133        let mut translation = None;
134
135        loop {
136            let event = reader.read_event(&mut buf)?;
137            let e_translation = match event {
138                XmlEvent::End(e) => {
139                    if e.name() == b"phrase" {
140                        break;
141                    } else {
142                        let name_str = reader.decode(e.name());
143                        return Err(LocaleError::Xml(Error::ExpectedEndTag(
144                            String::from("phrase"),
145                            name_str.into_owned(),
146                        )));
147                    }
148                }
149                XmlEvent::Start(e) => {
150                    if e.name() == b"translation" {
151                        e
152                    } else {
153                        let name_str = reader.decode(e.name());
154                        return Err(LocaleError::Xml(Error::ExpectedEndTag(
155                            String::from("translation"),
156                            name_str.into_owned(),
157                        )));
158                    }
159                }
160                _ => panic!(),
161            };
162            let locale: String = expect_attribute("locale", &reader, &e_translation)?;
163            buf.clear();
164
165            let trans = expect_text(&mut reader, &mut buf)?;
166            if &locale == "en_US" {
167                translation = Some(trans);
168            }
169            buf.clear();
170
171            let _ = expect_end("translation", &mut reader, &mut buf)?;
172            buf.clear();
173        }
174
175        let mut node = &mut root;
176        for comp in id.split('_') {
177            if let Ok(num) = comp.parse::<u32>() {
178                node = node.int_children.entry(num).or_default();
179            } else {
180                node = node.str_children.entry(comp.to_owned()).or_default();
181            }
182        }
183        if let Some(translation) = translation {
184            node.value = Some(translation);
185        }
186    }
187
188    let _ = expect_end("phrases", &mut reader, &mut buf)?;
189    //println!("</phrases>");
190    buf.clear();
191
192    Ok(root)
193}