embedded_lang/
language.rs1use serde::{Deserialize, Serialize};
2use std::collections::HashMap;
3
4#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)]
6#[serde(untagged)]
7pub enum LanguageStringObject {
8 Direct(String),
10
11 Category(HashMap<String, LanguageStringObject>),
13}
14
15impl LanguageStringObject {
16 pub fn flatten(&self, own_key: &str) -> HashMap<String, String> {
18 let mut map = HashMap::<String, String>::default();
19 match self {
20 LanguageStringObject::Direct(s) => {
21 map.insert(own_key.to_string(), s.clone());
22 }
23 LanguageStringObject::Category(c) => map.extend(Self::flatten_all(c, Some(own_key))),
24 };
25 map
26 }
27
28 fn flatten_all(
29 c: &HashMap<String, LanguageStringObject>,
30 root_key: Option<&str>,
31 ) -> HashMap<String, String> {
32 let mut map = HashMap::<String, String>::default();
33 c.iter().for_each(|e| {
34 let key = if root_key.is_some() {
35 format!("{}\\{}", root_key.unwrap(), e.0)
36 } else {
37 e.0.clone()
38 };
39 map.extend(e.1.flatten(&key))
40 });
41 map
42 }
43}
44
45#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)]
47pub struct Language {
48 name: String,
49 short_name: String,
50 strings: HashMap<String, LanguageStringObject>,
51
52 #[serde(default)]
53 resources: HashMap<String, Vec<u8>>,
54}
55
56impl Language {
57 pub fn new(
64 name: String,
65 short_name: String,
66 strings: HashMap<String, LanguageStringObject>,
67 resources: HashMap<String, Vec<u8>>,
68 ) -> Self {
69 Self {
70 name,
71 short_name,
72 strings,
73 resources,
74 }
75 }
76
77 pub fn new_from_string(
82 json: &str,
83 resources: HashMap<String, Vec<u8>>,
84 ) -> Result<Self, String> {
85 match serde_json::from_str::<Self>(json) {
86 Ok(mut lang) => {
87 lang.resources = resources;
88 Ok(lang)
89 }
90 Err(e) => Err(e.to_string()),
91 }
92 }
93
94 pub fn new_from_file(path: &str, resources: HashMap<String, Vec<u8>>) -> Result<Self, String> {
99 match std::fs::read_to_string(path) {
100 Ok(json) => Self::new_from_string(&json, resources),
101 Err(e) => Err(e.to_string()),
102 }
103 }
104
105 pub fn name(&self) -> &str {
107 &self.name
108 }
109
110 pub fn short_name(&self) -> &str {
112 &self.short_name
113 }
114
115 pub fn strings(&self) -> HashMap<String, String> {
117 LanguageStringObject::flatten_all(&self.strings, None)
118 }
119
120 pub fn get(&self, name: &str) -> Option<&str> {
125 let mut path = name.split("\\").peekable();
126 if path.peek().is_none() {
127 return None;
128 }
129
130 let mut pos = self.strings.get(path.next().unwrap());
131 for item in path {
132 if pos.is_none() {
133 return None;
134 }
135 match pos.unwrap() {
136 LanguageStringObject::Direct(s) => return Some(s),
137 LanguageStringObject::Category(c) => pos = c.get(item),
138 }
139 }
140
141 if let Some(pos) = pos {
142 match pos {
143 LanguageStringObject::Direct(s) => Some(s),
144 LanguageStringObject::Category(_) => None,
145 }
146 } else {
147 None
148 }
149 }
150
151 pub fn utf8_resource(&self, name: &str) -> Option<&str> {
153 self.resources
154 .get(name)
155 .and_then(|bytes| std::str::from_utf8(&bytes.as_slice()).ok())
156 }
157
158 pub fn binary_resource(&self, name: &str) -> Option<&[u8]> {
160 self.resources
161 .get(name)
162 .and_then(|bytes| Some(bytes.as_slice()))
163 }
164}
165
166#[cfg(test)]
167mod test_token {
168 use super::*;
169 use crate as embedded_lang;
170 use crate::embedded_language;
171
172 #[test]
173 fn test_new_from_string() {
174 if let Ok(s) = std::fs::read_to_string("examples/en.lang.json") {
175 let lang = Language::new_from_string(&s, HashMap::default()).unwrap();
176 assert_eq!(lang.short_name(), "en");
177 }
178 }
179
180 #[test]
181 fn test_new_from_file() {
182 let lang = Language::new_from_file("examples/en.lang.json", HashMap::default()).unwrap();
183 assert_eq!(lang.short_name(), "en");
184 }
185
186 #[test]
187 fn test_short_name() {
188 let lang = embedded_language!("../examples/en.lang.json");
189 assert_eq!(lang.short_name(), "en");
190 }
191
192 #[test]
193 fn test_name() {
194 let lang = embedded_language!("../examples/en.lang.json");
195 assert_eq!(lang.name(), "English");
196 }
197
198 #[test]
199 fn test_get() {
200 let lang = embedded_language!("../examples/en.lang.json");
201
202 assert_eq!(lang.get("hello_msg"), Some("hello world!"));
203 assert_eq!(lang.get("goodbye_msg"), None);
204 }
205}