#![allow(unused)]
use crate::{Key, Section};
use std::collections::BTreeMap;
use std::io::{self, Read};
use std::{
fs::{self, DirEntry, File},
path::Path,
};
use yaml_rust::{Yaml, YamlLoader};
type FileEntries = Vec<DirEntry>;
type DirEntries = Vec<DirEntry>;
pub(crate) fn handle_root(path: &Path) -> io::Result<Vec<Section>> {
fs::read_dir(path)?
.into_iter()
.filter_map(|entry| {
let e = entry.unwrap();
if e.file_type().unwrap().is_dir() {
Some(parse_section(&e.path()))
} else {
eprintln!(
"cargo:warning=Skipping key in asset root: `{}`!",
e.path().file_name().unwrap().to_str().unwrap()
);
None
}
})
.collect()
}
fn parse_section(path: &Path) -> io::Result<Section> {
let name = path.file_name().unwrap().to_str().unwrap();
let (dirs, files) = split_dir(path)?;
let file_paths: Vec<_> = files.iter().map(|e| e.path()).collect();
let section = load_keys(name, file_paths.iter().map(|p| p.as_path()).collect())
.into_iter()
.fold(Section::new(name), |sec, key| sec.add_key(key));
dirs.iter().map(|e| e.path()).fold(Ok(section), |sec, pb| {
let new = parse_section(pb.as_path())?;
sec.and_then(|sec| Ok(sec.add_child(new)))
})
}
fn split_dir(path: &Path) -> io::Result<(DirEntries, FileEntries)> {
let (a, b): (Vec<_>, Vec<_>) = fs::read_dir(path)?
.into_iter()
.map(|entry| {
let e = entry.unwrap();
if e.file_type().unwrap().is_dir() {
(Some(e), None)
} else {
(None, Some(e))
}
})
.unzip();
Ok((
a.into_iter().filter_map(|e| e).collect(),
b.into_iter().filter_map(|e| e).collect(),
))
}
fn load_keys(section: &str, path: Vec<&Path>) -> Vec<Key> {
let def = crate::env::default();
let mut map: BTreeMap<String, Vec<Yaml>> =
path.into_iter().fold(BTreeMap::new(), |mut map, path| {
let mut content = String::new();
let mut f = File::open(path).unwrap();
f.read_to_string(&mut content).unwrap();
let yamls = YamlLoader::load_from_str(&content).unwrap();
map.insert(path.file_name().unwrap().to_str().unwrap().into(), yamls);
map
});
let mut default = parse_hash_tree(
&def,
map.remove(&format!("{}.yml", def))
.expect(&format!(
"Missing default translations for section `{}`!",
section
))
.remove(0),
);
for (lang, mut yaml) in map.into_iter() {
let lang = lang.replace(".yml", "");
parse_hash_tree(&lang, yaml.remove(0))
.into_iter()
.for_each(|(k, key)| {
let def_key = default.get_mut(&k).expect(&format!(
"Assets error: the key `{}` was not present in the default language.\
The default language must contain all keys. Either remove the key from the secondary language,\
or switch the default language!",
k
));
def_key.merge(&lang, key)
});
}
default.into_iter().map(|(_, k)| k).collect()
}
fn parse_hash_tree(lang: &str, hash: Yaml) -> BTreeMap<String, Key> {
hash.as_hash()
.expect("YAML error: root element must be a map (hash)!")
.into_iter()
.fold(BTreeMap::new(), |mut map, (field_key, field)| {
let name = field_key
.as_str()
.expect("YAML error: only String hash keys are supported!");
map.insert(name.to_string(), parse_field(Key::new(name), lang, field));
map
})
}
fn parse_field(mut k: Key, lang: &str, field: &Yaml) -> Key {
match field {
Yaml::Hash(h) => {
let reflow = h
.get(&Yaml::String("reflow".into()))
.map(|yaml| match yaml {
Yaml::Boolean(b) => *b,
_ => panic!("YAML error: 'reflow' key must be a boolean!"),
})
.unwrap_or(false);
let text = h
.get(&Yaml::String("text".into()))
.map(|yaml| match yaml {
Yaml::String(s) => s.clone(),
_ => panic!("YAML error: 'text' key must be a string!"),
})
.expect("YAML error: 'text' key is missing!");
k.add_value(
lang,
&if reflow {
text.replace("\n", " ")
} else {
text
}
.trim()
.to_string(),
)
}
Yaml::String(s) => k.add_value(lang, &s),
_ => panic!("YAML error: failed to parse key structure, neither string nor map (hash)!"),
}
}