lingora_core/config/
toml.rs1use std::path::{Path, PathBuf};
2
3use serde::{Deserialize, Serialize};
4
5use crate::{
6 config::{args::CoreArgs, config_inclusion_style::ConfigInclusionStyle},
7 domain::Locale,
8 error::LingoraError,
9};
10
11#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
12#[serde(default, deny_unknown_fields)]
13pub(crate) struct EngineSettings {
14 pub(crate) fluent_sources: Vec<PathBuf>,
15 pub(crate) canonical: Locale,
16 pub(crate) primaries: Vec<Locale>,
17}
18
19#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
20#[serde(default, deny_unknown_fields)]
21pub(crate) struct DioxusI18nSettings {
22 pub(crate) rust_sources: Vec<PathBuf>,
23 pub(crate) config_inclusion: ConfigInclusionStyle,
24}
25
26#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
28#[serde(default, deny_unknown_fields)]
29pub struct LingoraToml {
30 pub(crate) lingora: EngineSettings,
31 pub(crate) dioxus_i18n: DioxusI18nSettings,
32}
33
34impl std::str::FromStr for LingoraToml {
35 type Err = LingoraError;
36
37 fn from_str(s: &str) -> Result<Self, Self::Err> {
38 let toml = toml::from_str(s)?;
39 Ok(toml)
40 }
41}
42
43impl TryFrom<&Path> for LingoraToml {
44 type Error = LingoraError;
45
46 fn try_from(path: &Path) -> Result<Self, Self::Error> {
47 use std::str::FromStr;
48 let content = std::fs::read_to_string(path)?;
49 Self::from_str(&content)
50 }
51}
52
53impl TryFrom<&CoreArgs> for LingoraToml {
54 type Error = LingoraError;
55
56 fn try_from(args: &CoreArgs) -> Result<Self, Self::Error> {
57 let default_toml_path = Path::new("Lingora.toml");
58
59 let mut toml = if let Some(requested_toml_path) = &args.config {
60 Self::try_from(requested_toml_path.as_path())
61 } else if std::fs::exists(default_toml_path).unwrap_or(false) {
62 Self::try_from(default_toml_path)
63 } else {
64 Ok(Self::default())
65 }?;
66
67 toml.lingora
68 .fluent_sources
69 .append(&mut args.fluent_sources.clone());
70
71 if let Some(locale) = &args.canonical {
72 toml.lingora.canonical = locale.clone();
73 }
74
75 toml.lingora.primaries.append(&mut args.primaries.clone());
76
77 toml.dioxus_i18n
78 .rust_sources
79 .append(&mut args.rust_sources.clone());
80
81 if let Some(style) = &args.config_inclusion {
82 toml.dioxus_i18n.config_inclusion = *style;
83 }
84
85 Ok(toml)
86 }
87}
88
89impl Default for LingoraToml {
90 fn default() -> Self {
91 let fluent_sources = vec![Path::new("./i18n").to_path_buf()];
92 let canonical = Locale::default();
93 let primaries = Vec::default();
94 let rust_sources = vec![Path::new("./src").to_path_buf()];
95 let config_inclusion = ConfigInclusionStyle::Auto;
96
97 Self {
98 lingora: EngineSettings {
99 fluent_sources,
100 canonical,
101 primaries,
102 },
103 dioxus_i18n: DioxusI18nSettings {
104 rust_sources,
105 config_inclusion,
106 },
107 }
108 }
109}
110
111impl std::fmt::Display for LingoraToml {
112 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
113 let toml =
114 toml::to_string_pretty(self).unwrap_or("failed to re-create lingora toml text".into());
115 toml.fmt(f)
116 }
117}
118
119#[cfg(test)]
120mod test {
121 use std::{path::Path, str::FromStr};
122
123 use crate::{
124 config::toml::{ConfigInclusionStyle, LingoraToml},
125 domain::Locale,
126 error::LingoraError,
127 };
128
129 #[test]
130 fn will_load_from_toml_file() {
131 let path = Path::new("./tests/data/toml/Lingora.toml");
132 let toml = LingoraToml::try_from(path).expect("failed to load from toml");
133 assert_eq!(toml.lingora.fluent_sources, [Path::new("./i18n")]);
134 assert_eq!(toml.lingora.canonical, Locale::from_str("en-GB").unwrap());
135 assert!(toml.lingora.primaries.is_empty());
136 assert_eq!(toml.dioxus_i18n.rust_sources, [Path::new("./src")]);
137 assert_eq!(
138 toml.dioxus_i18n.config_inclusion,
139 ConfigInclusionStyle::Auto
140 );
141 }
142
143 #[test]
144 fn will_not_load_from_non_existing_toml_file() {
145 let path = Path::new("./non_existing_lingora.toml");
146 let error = LingoraToml::try_from(path).expect_err("failed to detect error");
147 assert!(matches!(error, LingoraError::Io(_)));
148 }
149
150 #[test]
151 fn will_not_load_from_invalid_toml_file() {
152 let path = Path::new("./tests/data/toml/Lingora_error.toml");
153 let error = LingoraToml::try_from(path).expect_err("failed to detect error");
154 assert!(matches!(error, LingoraError::TomlParse(_)));
155 }
156
157 #[test]
158 fn will_default_when_no_file_provided() {
159 let toml = LingoraToml::default();
160 assert_eq!(toml.lingora.fluent_sources, [Path::new("./i18n")]);
161 assert_eq!(toml.lingora.canonical, Locale::default());
162 assert!(toml.lingora.primaries.is_empty());
163 assert_eq!(toml.dioxus_i18n.rust_sources, [Path::new("./src")]);
164 assert_eq!(
165 toml.dioxus_i18n.config_inclusion,
166 ConfigInclusionStyle::Auto
167 );
168 }
169
170 #[test]
171 fn will_load_from_str() {
172 let content = std::fs::read_to_string(Path::new("./tests/data/toml/Lingora.toml"))
173 .expect("read test file");
174 let toml = LingoraToml::from_str(&content).expect("failed to parse toml");
175 assert_eq!(toml.lingora.fluent_sources, [Path::new("./i18n")]);
176 assert_eq!(toml.lingora.canonical, Locale::from_str("en-GB").unwrap());
177 assert!(toml.lingora.primaries.is_empty());
178 assert_eq!(toml.dioxus_i18n.rust_sources, [Path::new("./src")]);
179 assert_eq!(
180 toml.dioxus_i18n.config_inclusion,
181 ConfigInclusionStyle::Auto
182 );
183 }
184
185 #[test]
186 fn will_load_from_args_config() {
187 use crate::config::args::CoreArgs;
188 let args =
189 CoreArgs::from_str("app_name --config=./tests/data/toml/Lingora_args.toml").unwrap();
190 let toml = LingoraToml::try_from(&args).unwrap();
191 assert_eq!(toml.lingora.fluent_sources, [Path::new("./args/i18n")]);
192 assert_eq!(toml.lingora.canonical, Locale::from_str("de-DE").unwrap());
193 assert_eq!(toml.lingora.primaries, [Locale::from_str("en-AU").unwrap()]);
194 assert_eq!(toml.dioxus_i18n.rust_sources, [Path::new("./args/src")]);
195 assert_eq!(
196 toml.dioxus_i18n.config_inclusion,
197 ConfigInclusionStyle::IncludeStr
198 );
199 }
200
201 #[test]
202 fn will_load_from_args_overridden() {
203 use crate::config::args::CoreArgs;
204 let args =
205 CoreArgs::from_str("app_name --config=./tests/data/toml/Lingora_args.toml --fluent-sources=./also/i18n,./also/branding --canonical=ja-JP --primaries=sk-SK,sr-Cryl-RS,bn-IN --rust-sources=./also/src --config-inclusion=pathbuf").unwrap();
206 let toml = LingoraToml::try_from(&args).unwrap();
207 assert_eq!(
208 toml.lingora.fluent_sources,
209 [
210 Path::new("./args/i18n"),
211 Path::new("./also/i18n"),
212 Path::new("./also/branding")
213 ]
214 );
215 assert_eq!(toml.lingora.canonical, Locale::from_str("ja-JP").unwrap());
216 assert_eq!(
217 toml.lingora.primaries,
218 [
219 Locale::from_str("en-AU").unwrap(),
220 Locale::from_str("sk-SK").unwrap(),
221 Locale::from_str("sr-Cryl-RS").unwrap(),
222 Locale::from_str("bn-IN").unwrap()
223 ]
224 );
225 assert_eq!(
226 toml.dioxus_i18n.rust_sources,
227 [Path::new("./args/src"), Path::new("./also/src")]
228 );
229 assert_eq!(
230 toml.dioxus_i18n.config_inclusion,
231 ConfigInclusionStyle::PathBuf
232 );
233 }
234}