local_fmt_macros_internal/def_local_fmt/internal/file/
mod.rs

1use 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}