fluent_testing/scenarios/
structs.rs

1use fluent_fallback::types::ResourceId;
2
3pub struct FileSource {
4    pub name: String,
5    pub locales: Vec<String>,
6    pub path_scheme: String,
7}
8
9impl Default for FileSource {
10    fn default() -> Self {
11        Self {
12            name: "default".to_string(),
13            path_scheme: "{locale}/".to_string(),
14            locales: vec!["en-US".to_string()],
15        }
16    }
17}
18
19impl FileSource {
20    pub fn new<S: ToString>(name: S, path_scheme: S, locales: Vec<S>) -> Self {
21        Self {
22            name: name.to_string(),
23            path_scheme: path_scheme.to_string(),
24            locales: locales
25                .iter()
26                .map(|l| l.to_string().parse().unwrap())
27                .collect(),
28        }
29    }
30}
31
32#[derive(Debug)]
33pub struct L10nAttribute {
34    pub name: String,
35    pub value: String,
36}
37
38impl L10nAttribute {
39    pub fn new<S: ToString>(name: S, value: S) -> Self {
40        Self {
41            name: name.to_string(),
42            value: value.to_string(),
43        }
44    }
45}
46
47#[derive(Debug)]
48pub struct L10nMessage {
49    pub value: Option<String>,
50    pub attributes: Option<Vec<L10nAttribute>>,
51}
52
53impl L10nMessage {
54    pub fn new(value: Option<&str>, attributes: Option<Vec<L10nAttribute>>) -> Self {
55        Self {
56            value: value.map(|v| v.to_string()),
57            attributes,
58        }
59    }
60}
61
62impl From<&str> for L10nMessage {
63    fn from(value: &str) -> Self {
64        Self {
65            value: Some(value.to_string()),
66            attributes: None,
67        }
68    }
69}
70
71#[derive(Debug)]
72pub struct L10nArgument {
73    pub id: String,
74    pub value: String,
75}
76
77impl L10nArgument {
78    pub fn new<S: ToString>(id: S, value: S) -> Self {
79        Self {
80            id: id.to_string(),
81            value: value.to_string(),
82        }
83    }
84}
85
86#[derive(Debug)]
87pub struct L10nKey {
88    pub id: String,
89    pub args: Option<Vec<L10nArgument>>,
90}
91
92impl L10nKey {
93    pub fn new<S: ToString>(id: S, args: Option<Vec<L10nArgument>>) -> Self {
94        Self {
95            id: id.to_string(),
96            args,
97        }
98    }
99}
100
101impl From<&str> for L10nKey {
102    fn from(input: &str) -> Self {
103        Self {
104            id: input.to_string(),
105            args: None,
106        }
107    }
108}
109
110#[derive(Clone, Copy, Debug)]
111pub enum ExceptionalContext {
112    /// There is no exceptional context for this query (happy path).
113    None,
114    /// A value is missing from a resource and should cause a fallback.
115    ValueMissingFromResource,
116    /// A value is missing from all resources in all locales.
117    ValueMissingFromAllResources,
118    /// An optional resource is missing from the top locale.
119    OptionalResourceMissingFromLocale,
120    /// An optional resource is missing from all locales.
121    OptionalResourceMissingFromAllLocales,
122    /// A required resource is missing from the top locale.
123    RequiredResourceMissingFromLocale,
124    /// A required resource is missing from all locales.
125    RequiredResourceMissingFromAllLocales,
126}
127
128impl ExceptionalContext {
129    /// This is a query for a value in a missing required resource.
130    pub fn missing_required_resource(self) -> bool {
131        matches!(
132            self,
133            Self::RequiredResourceMissingFromLocale | Self::RequiredResourceMissingFromAllLocales,
134        )
135    }
136
137    /// This query should cause a format error to be appended to the errors Vec.
138    pub fn causes_reported_format_error(self) -> bool {
139        matches!(
140            self,
141            Self::ValueMissingFromResource
142                | Self::ValueMissingFromAllResources
143                | Self::OptionalResourceMissingFromLocale
144                | Self::OptionalResourceMissingFromAllLocales
145                | Self::RequiredResourceMissingFromAllLocales,
146        )
147    }
148
149    /// This query should cause a failed value lookup.
150    pub fn causes_failed_value_lookup(self) -> bool {
151        matches!(
152            self,
153            Self::ValueMissingFromAllResources
154                | Self::OptionalResourceMissingFromAllLocales
155                | Self::RequiredResourceMissingFromAllLocales,
156        )
157    }
158
159    /// This query should result in no bundles being generated.
160    pub fn blocks_bundle_generation(self) -> bool {
161        matches!(self, Self::RequiredResourceMissingFromAllLocales,)
162    }
163}
164
165#[derive(Debug)]
166pub struct Query {
167    pub input: L10nKey,
168    pub output: Option<L10nMessage>,
169    pub exceptional_context: ExceptionalContext,
170}
171
172impl Query {
173    pub fn new<K: Into<L10nKey>>(input: K, output: Option<L10nMessage>) -> Self {
174        Self {
175            input: input.into(),
176            output,
177            exceptional_context: ExceptionalContext::None,
178        }
179    }
180}
181
182impl From<(&str, &str)> for Query {
183    fn from(i: (&str, &str)) -> Self {
184        Self {
185            input: i.0.into(),
186            output: Some(i.1.into()),
187            exceptional_context: ExceptionalContext::None,
188        }
189    }
190}
191
192impl From<(&str, &str, ExceptionalContext)> for Query {
193    fn from(i: (&str, &str, ExceptionalContext)) -> Self {
194        Self {
195            input: i.0.into(),
196            output: Some(i.1.into()),
197            exceptional_context: i.2,
198        }
199    }
200}
201
202impl From<(&str, L10nMessage)> for Query {
203    fn from(i: (&str, L10nMessage)) -> Self {
204        Self {
205            input: i.0.into(),
206            output: Some(i.1),
207            exceptional_context: ExceptionalContext::None,
208        }
209    }
210}
211
212impl From<(L10nKey, L10nMessage)> for Query {
213    fn from(i: (L10nKey, L10nMessage)) -> Self {
214        Self {
215            input: i.0,
216            output: Some(i.1),
217            exceptional_context: ExceptionalContext::None,
218        }
219    }
220}
221
222impl From<&str> for Query {
223    fn from(i: &str) -> Self {
224        Self {
225            input: i.into(),
226            output: None,
227            exceptional_context: ExceptionalContext::None,
228        }
229    }
230}
231
232impl From<L10nKey> for Query {
233    fn from(key: L10nKey) -> Self {
234        Self {
235            input: key,
236            output: None,
237            exceptional_context: ExceptionalContext::None,
238        }
239    }
240}
241
242pub struct Queries(pub Vec<Query>);
243
244impl From<Vec<&str>> for Queries {
245    fn from(input: Vec<&str>) -> Self {
246        Self(input.into_iter().map(|q| q.into()).collect())
247    }
248}
249
250impl From<Vec<(&str, &str)>> for Queries {
251    fn from(input: Vec<(&str, &str)>) -> Self {
252        Self(input.into_iter().map(|q| q.into()).collect())
253    }
254}
255
256impl std::ops::Deref for Queries {
257    type Target = Vec<Query>;
258
259    fn deref(&self) -> &Self::Target {
260        &self.0
261    }
262}
263
264pub struct Scenario {
265    pub name: String,
266    pub file_sources: Vec<FileSource>,
267    pub locales: Vec<String>,
268    pub res_ids: Vec<ResourceId>,
269    pub queries: Queries,
270}
271
272impl Scenario {
273    pub fn new<S: ToString, R: Into<ResourceId>, Q: Into<Queries>>(
274        name: S,
275        file_sources: Vec<FileSource>,
276        locales: Vec<S>,
277        res_ids: Vec<R>,
278        queries: Q,
279    ) -> Self {
280        Self {
281            name: name.to_string(),
282            file_sources,
283            locales: locales
284                .into_iter()
285                .map(|l| l.to_string().parse().unwrap())
286                .collect(),
287            res_ids: res_ids.into_iter().map(|id| id.into()).collect(),
288            queries: queries.into(),
289        }
290    }
291}