local_fmt_macros_internal/def_local_fmt/internal/file/
mod.rs1use crate::{def_local_fmt::arg::ArgFileType, utils::hierarchy::Hierarchy};
2
3use super::{arg::MessageValue, ArgPath, LangMessage};
4
5#[cfg(feature = "json")]
6pub mod json;
7
8#[cfg(feature = "toml")]
9pub mod toml;
10
11#[cfg(feature = "yaml")]
12pub mod yaml;
13
14pub fn parse(file_type: ArgFileType, path: ArgPath) -> Vec<LangMessage> {
15 macro_rules! from_path {
16 ($file_type:ident, $path:ident, {$($pattern:pat => ($feature:literal, $mod:ident::$loader:ident),)+}) => {
17 use ArgFileType::*;
18 match $file_type {
19 $(
20 $pattern => {
21 #[cfg(feature = $feature)]
22 {
23 $mod::$loader::from_path($path)
24 }
25 #[cfg(not(feature = $feature))]
26 {
27 panic!(concat!($feature, " feature is not enabled failed to parse {:#?} file"), $path)
28 }
29 },
30 )+
31 }
32 };
33 }
34
35 from_path! { file_type, path, {
36 Toml => ("toml", toml::TomlMessageLoader),
37 Json => ("json", json::JsonMessageLoader),
38 Yaml => ("yaml", yaml::YamlMessageLoader),
39 } }
40}
41
42pub trait MessageLoader: Sized {
43 const EXTENSION: &'static str;
44
45 type Value;
46 type NestValue;
47 const NEST_VALUE_NAME: &'static str;
48
49 fn value_to_nest(value: Self::Value) -> Option<Self::NestValue>;
50 fn value_as_str(value: &Self::Value) -> Option<&str>;
51 fn value_from_str(content: &str) -> Result<Self::Value, String>;
52 fn iter_nested(value: Self::NestValue) -> impl Iterator<Item = (String, Self::Value)>;
53
54 fn from_path(path: ArgPath) -> Vec<LangMessage> {
55 match path {
56 ArgPath::File(file) => Self::from_file(file),
57 ArgPath::Folder(folder) => Self::from_folder(folder),
58 }
59 }
60
61 fn from_file(file: std::path::PathBuf) -> Vec<LangMessage> {
62 let extension = file.extension().unwrap_or_else(|| {
63 panic!(
64 "Failed to retrieve file extension for path: {}",
65 file.display()
66 )
67 });
68 if extension != Self::EXTENSION {
69 panic!(
70 "Expected a {} file, but got {} file: {}",
71 Self::EXTENSION,
72 extension.to_string_lossy(),
73 file.display()
74 )
75 }
76
77 let content = std::fs::read_to_string(&file)
78 .unwrap_or_else(|_| panic!("failed to read {}", file.display()));
79
80 let value = Self::value_from_str(&content).unwrap_or_else(|e| {
81 panic!(
82 "failed to parse {} in {}: {}",
83 Self::EXTENSION,
84 file.display(),
85 e
86 )
87 });
88
89 let nest = Self::value_to_nest(value).unwrap_or_else(|| {
90 panic!(
91 "Expected a {} in the {} file: {}",
92 Self::NEST_VALUE_NAME,
93 Self::EXTENSION,
94 file.display()
95 )
96 });
97
98 let mut lang_messages = Vec::new();
99 for (lang, value) in Self::iter_nested(nest) {
100 let nest = Self::value_to_nest(value).unwrap_or_else(|| {
101 panic!(
102 "Expected a {} in the {} file: {}",
103 Self::NEST_VALUE_NAME,
104 Self::EXTENSION,
105 file.display()
106 )
107 });
108 let messages = Self::internal(&lang, &mut Hierarchy::new(), nest);
109 lang_messages.push(LangMessage { lang, messages });
110 }
111 lang_messages
112 }
113
114 fn from_folder(folder: std::path::PathBuf) -> Vec<LangMessage> {
115 let files = folder.read_dir().unwrap_or_else(|_| {
116 panic!(
117 "Failed to read directory entry in folder: {}",
118 folder.display()
119 )
120 });
121 let mut lang_messages = Vec::new();
122 for entry in files {
123 let entry =
124 entry.unwrap_or_else(|_| panic!("failed to read entry in {}", folder.display()));
125 let path = entry.path();
126 let exn = path.extension().unwrap_or_else(|| {
127 panic!(
128 "Failed to retrieve file extension for path: {}",
129 path.display()
130 )
131 });
132 if exn != Self::EXTENSION {
133 continue;
134 }
135 let lang = path
136 .file_stem()
137 .unwrap_or_else(|| panic!("failed to get file stem in {}", path.display()))
138 .to_string_lossy();
139
140 let content = std::fs::read_to_string(&path)
141 .unwrap_or_else(|_| panic!("failed to read {}", path.display()));
142 let value = Self::value_from_str(&content).unwrap_or_else(|e| {
143 panic!(
144 "failed to parse {} in {}: {}",
145 Self::EXTENSION,
146 path.display(),
147 e
148 )
149 });
150
151 let nest = Self::value_to_nest(value).unwrap_or_else(|| {
152 panic!(
153 "Expected a {} in the {} file: {}",
154 Self::NEST_VALUE_NAME,
155 Self::EXTENSION,
156 path.display()
157 )
158 });
159
160 let messages = Self::internal(&lang, &mut Hierarchy::new(), nest);
161 lang_messages.push(LangMessage {
162 lang: lang.to_string(),
163 messages,
164 });
165 }
166 lang_messages
167 }
168
169 fn internal(
170 lang: &str,
171 hierarchy: &mut Hierarchy<String>,
172 value: Self::NestValue,
173 ) -> Vec<super::Message> {
174 let mut messages = Vec::new();
175 for (key, value) in Self::iter_nested(value) {
176 if let Some(value) = Self::value_as_str(&value) {
177 messages.push(super::Message {
178 value: MessageValue::Token(value.parse().unwrap_or_else(|e| {
179 let key = hierarchy.join(value);
180 panic!(
181 "Failed to parse message token for language '{}' and key '{}': {}",
182 lang, key, e
183 )
184 })),
185 key,
186 });
187 continue;
188 }
189 let nest = Self::value_to_nest(value).unwrap_or_else(|| {
190 let display_key = hierarchy.join(&key);
191 panic!(
192 "Expected a string or {} for language '{}' and key '{}'",
193 Self::NEST_VALUE_NAME,
194 lang,
195 display_key
196 )
197 });
198 let temp_key = key.clone();
199 let nest_messages =
200 hierarchy.process(temp_key, |hierarchy| Self::internal(lang, hierarchy, nest));
201 messages.push(super::Message {
202 key,
203 value: MessageValue::Nested(nest_messages),
204 });
205 }
206 messages
207 }
208}