• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

projectfluent / fluent-rs / 15122392209

19 May 2025 08:19PM UTC coverage: 89.142% (-0.5%) from 89.654%
15122392209

Pull #382

github

web-flow
Merge d10428072 into 10302d0ad
Pull Request #382: Bump deps in prep for release

6 of 6 new or added lines in 3 files covered. (100.0%)

2 existing lines in 2 files now uncovered.

3604 of 4043 relevant lines covered (89.14%)

3834.26 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

61.67
/fluent-fallback/src/bundles.rs
1
use crate::{
2
    cache::{AsyncCache, Cache},
3
    env::LocalesProvider,
4
    errors::LocalizationError,
5
    generator::{BundleGenerator, BundleIterator, BundleStream},
6
    types::{L10nAttribute, L10nKey, L10nMessage, ResourceId},
7
};
8
use fluent_bundle::{FluentArgs, FluentBundle, FluentError};
9
use rustc_hash::FxHashSet;
10
use std::borrow::Cow;
11

12
pub enum BundlesInner<G>
13
where
14
    G: BundleGenerator,
15
{
16
    Iter(Cache<G::Iter, G::Resource>),
17
    Stream(AsyncCache<G::Stream, G::Resource>),
18
}
19

20
pub struct Bundles<G>(BundlesInner<G>)
21
where
22
    G: BundleGenerator;
23

24
impl<G> Bundles<G>
25
where
26
    G: BundleGenerator,
27
    G::Iter: BundleIterator,
28
{
29
    pub fn prefetch_sync(&self) {
×
30
        match &self.0 {
×
31
            BundlesInner::Iter(iter) => iter.prefetch(),
×
32
            BundlesInner::Stream(_) => panic!("Can't prefetch a sync bundle set asynchronously"),
×
33
        }
34
    }
×
35
}
36

37
impl<G> Bundles<G>
38
where
39
    G: BundleGenerator,
40
    G::Stream: BundleStream,
41
{
42
    pub async fn prefetch_async(&self) {
×
43
        match &self.0 {
×
44
            BundlesInner::Iter(_) => panic!("Can't prefetch a async bundle set synchronously"),
×
45
            BundlesInner::Stream(stream) => stream.prefetch().await,
×
46
        }
47
    }
×
48
}
49

50
impl<G> Bundles<G>
51
where
52
    G: BundleGenerator,
53
{
54
    pub fn new<P>(sync: bool, res_ids: FxHashSet<ResourceId>, generator: &G, provider: &P) -> Self
11✔
55
    where
11✔
56
        G: BundleGenerator<LocalesIter = P::Iter>,
11✔
57
        P: LocalesProvider,
11✔
58
    {
59
        Self(if sync {
11✔
60
            BundlesInner::Iter(Cache::new(
10✔
61
                generator.bundles_iter(provider.locales(), res_ids),
10✔
62
            ))
10✔
63
        } else {
64
            BundlesInner::Stream(AsyncCache::new(
1✔
65
                generator.bundles_stream(provider.locales(), res_ids),
1✔
66
            ))
1✔
67
        })
68
    }
11✔
69

70
    pub async fn format_value<'l>(
1✔
71
        &'l self,
1✔
72
        id: &'l str,
1✔
73
        args: Option<&'l FluentArgs<'_>>,
1✔
74
        errors: &mut Vec<LocalizationError>,
1✔
75
    ) -> Option<Cow<'l, str>> {
1✔
76
        match &self.0 {
1✔
77
            BundlesInner::Iter(cache) => Self::format_value_from_iter(cache, id, args, errors),
×
78
            BundlesInner::Stream(stream) => {
1✔
79
                Self::format_value_from_stream(stream, id, args, errors).await
1✔
80
            }
81
        }
82
    }
1✔
83

84
    pub async fn format_values<'l>(
×
85
        &'l self,
×
86
        keys: &'l [L10nKey<'l>],
×
87
        errors: &mut Vec<LocalizationError>,
×
88
    ) -> Vec<Option<Cow<'l, str>>> {
×
89
        match &self.0 {
×
90
            BundlesInner::Iter(cache) => Self::format_values_from_iter(cache, keys, errors),
×
91
            BundlesInner::Stream(stream) => {
×
92
                Self::format_values_from_stream(stream, keys, errors).await
×
93
            }
94
        }
95
    }
×
96

97
    pub async fn format_messages<'l>(
×
98
        &'l self,
×
99
        keys: &'l [L10nKey<'l>],
×
100
        errors: &mut Vec<LocalizationError>,
×
101
    ) -> Vec<Option<L10nMessage<'l>>> {
×
102
        match &self.0 {
×
103
            BundlesInner::Iter(cache) => Self::format_messages_from_iter(cache, keys, errors),
×
104
            BundlesInner::Stream(stream) => {
×
105
                Self::format_messages_from_stream(stream, keys, errors).await
×
106
            }
107
        }
108
    }
×
109

110
    pub fn format_value_sync<'l>(
13✔
111
        &'l self,
13✔
112
        id: &'l str,
13✔
113
        args: Option<&'l FluentArgs>,
13✔
114
        errors: &mut Vec<LocalizationError>,
13✔
115
    ) -> Result<Option<Cow<'l, str>>, LocalizationError> {
13✔
116
        match &self.0 {
13✔
117
            BundlesInner::Iter(cache) => Ok(Self::format_value_from_iter(cache, id, args, errors)),
13✔
118
            BundlesInner::Stream(_) => Err(LocalizationError::SyncRequestInAsyncMode),
×
119
        }
120
    }
13✔
121

122
    pub fn format_values_sync<'l>(
2✔
123
        &'l self,
2✔
124
        keys: &'l [L10nKey<'l>],
2✔
125
        errors: &mut Vec<LocalizationError>,
2✔
126
    ) -> Result<Vec<Option<Cow<'l, str>>>, LocalizationError> {
2✔
127
        match &self.0 {
2✔
128
            BundlesInner::Iter(cache) => Ok(Self::format_values_from_iter(cache, keys, errors)),
2✔
129
            BundlesInner::Stream(_) => Err(LocalizationError::SyncRequestInAsyncMode),
×
130
        }
131
    }
2✔
132

133
    pub fn format_messages_sync<'l>(
3✔
134
        &'l self,
3✔
135
        keys: &'l [L10nKey<'l>],
3✔
136
        errors: &mut Vec<LocalizationError>,
3✔
137
    ) -> Result<Vec<Option<L10nMessage<'l>>>, LocalizationError> {
3✔
138
        match &self.0 {
3✔
139
            BundlesInner::Iter(cache) => Ok(Self::format_messages_from_iter(cache, keys, errors)),
3✔
140
            BundlesInner::Stream(_) => Err(LocalizationError::SyncRequestInAsyncMode),
×
141
        }
142
    }
3✔
143
}
144

145
macro_rules! format_value_from_inner {
146
    ($step:expr, $id:expr, $args:expr, $errors:expr) => {
147
        let mut found_message = false;
148

149
        while let Some(bundle) = $step {
150
            let bundle = bundle.as_ref().unwrap_or_else(|(bundle, err)| {
×
151
                $errors.extend(err.iter().cloned().map(Into::into));
×
152
                bundle
×
153
            });
×
154

155
            if let Some(msg) = bundle.get_message($id) {
156
                found_message = true;
157
                if let Some(value) = msg.value() {
158
                    let mut format_errors = vec![];
159
                    let result = bundle.format_pattern(value, $args, &mut format_errors);
160
                    if !format_errors.is_empty() {
161
                        $errors.push(LocalizationError::Resolver {
162
                            id: $id.to_string(),
163
                            locale: bundle.locales[0].clone(),
164
                            errors: format_errors,
165
                        });
166
                    }
167
                    return Some(result);
168
                } else {
169
                    $errors.push(LocalizationError::MissingValue {
170
                        id: $id.to_string(),
171
                        locale: Some(bundle.locales[0].clone()),
172
                    });
173
                }
174
            } else {
175
                $errors.push(LocalizationError::MissingMessage {
176
                    id: $id.to_string(),
177
                    locale: Some(bundle.locales[0].clone()),
178
                });
179
            }
180
        }
181
        if found_message {
182
            $errors.push(LocalizationError::MissingValue {
183
                id: $id.to_string(),
184
                locale: None,
185
            });
186
        } else {
187
            $errors.push(LocalizationError::MissingMessage {
188
                id: $id.to_string(),
189
                locale: None,
190
            });
191
        }
192
        return None;
193
    };
194
}
195

196
#[derive(Clone)]
197
enum Value<'l> {
198
    Present(Cow<'l, str>),
199
    Missing,
200
    None,
201
}
202

203
macro_rules! format_values_from_inner {
204
    ($step:expr, $keys:expr, $errors:expr) => {
205
        let mut cells = vec![Value::None; $keys.len()];
206

207
        while let Some(bundle) = $step {
208
            let bundle = bundle.as_ref().unwrap_or_else(|(bundle, err)| {
×
209
                $errors.extend(err.iter().cloned().map(Into::into));
×
210
                bundle
×
211
            });
×
212

213
            let mut has_missing = false;
214

215
            for (key, cell) in $keys
216
                .iter()
217
                .zip(&mut cells)
218
                .filter(|(_, cell)| !matches!(cell, Value::Present(_)))
6✔
219
            {
220
                if let Some(msg) = bundle.get_message(&key.id) {
221
                    if let Some(value) = msg.value() {
222
                        let mut format_errors = vec![];
223
                        *cell = Value::Present(bundle.format_pattern(
224
                            value,
225
                            key.args.as_ref(),
226
                            &mut format_errors,
227
                        ));
228
                        if !format_errors.is_empty() {
229
                            $errors.push(LocalizationError::Resolver {
230
                                id: key.id.to_string(),
231
                                locale: bundle.locales[0].clone(),
232
                                errors: format_errors,
233
                            });
234
                        }
235
                    } else {
236
                        *cell = Value::Missing;
237
                        has_missing = true;
238
                        $errors.push(LocalizationError::MissingValue {
239
                            id: key.id.to_string(),
240
                            locale: Some(bundle.locales[0].clone()),
241
                        });
242
                    }
243
                } else {
244
                    has_missing = true;
245
                    $errors.push(LocalizationError::MissingMessage {
246
                        id: key.id.to_string(),
247
                        locale: Some(bundle.locales[0].clone()),
248
                    });
249
                }
250
            }
251
            if !has_missing {
252
                break;
253
            }
254
        }
255

256
        return $keys
257
            .iter()
258
            .zip(cells)
259
            .map(|(key, value)| match value {
3✔
260
                Value::Present(value) => Some(value),
×
261
                Value::Missing => {
262
                    $errors.push(LocalizationError::MissingValue {
1✔
263
                        id: key.id.to_string(),
1✔
264
                        locale: None,
1✔
265
                    });
1✔
266
                    None
1✔
267
                }
268
                Value::None => {
269
                    $errors.push(LocalizationError::MissingMessage {
2✔
270
                        id: key.id.to_string(),
2✔
271
                        locale: None,
2✔
272
                    });
2✔
273
                    None
2✔
274
                }
275
            })
3✔
276
            .collect();
277
    };
278
}
279

280
macro_rules! format_messages_from_inner {
281
    ($step:expr, $keys:expr, $errors:expr) => {
282
        let mut result = vec![None; $keys.len()];
283

284
        let mut is_complete = false;
285

286
        while let Some(bundle) = $step {
287
            let bundle = bundle.as_ref().unwrap_or_else(|(bundle, err)| {
×
288
                $errors.extend(err.iter().cloned().map(Into::into));
×
289
                bundle
×
290
            });
×
291

292
            let mut has_missing = false;
293
            for (key, cell) in $keys
294
                .iter()
295
                .zip(&mut result)
296
                .filter(|(_, cell)| cell.is_none())
6✔
297
            {
298
                let mut format_errors = vec![];
299
                let msg = Self::format_message_from_bundle(bundle, key, &mut format_errors);
300

301
                if msg.is_none() {
302
                    has_missing = true;
303
                    $errors.push(LocalizationError::MissingMessage {
304
                        id: key.id.to_string(),
305
                        locale: Some(bundle.locales[0].clone()),
306
                    });
307
                } else if !format_errors.is_empty() {
308
                    $errors.push(LocalizationError::Resolver {
309
                        id: key.id.to_string(),
310
                        locale: bundle.locales.get(0).cloned().unwrap(),
311
                        errors: format_errors,
312
                    });
313
                }
314

315
                *cell = msg;
316
            }
317
            if !has_missing {
318
                is_complete = true;
319
                break;
320
            }
321
        }
322

323
        if !is_complete {
324
            for (key, _) in $keys
325
                .iter()
326
                .zip(&mut result)
327
                .filter(|(_, cell)| cell.is_none())
2✔
328
            {
329
                $errors.push(LocalizationError::MissingMessage {
330
                    id: key.id.to_string(),
331
                    locale: None,
332
                });
333
            }
334
        }
335

336
        return result;
337
    };
338
}
339

340
impl<G> Bundles<G>
341
where
342
    G: BundleGenerator,
343
{
344
    fn format_value_from_iter<'l>(
13✔
345
        cache: &'l Cache<G::Iter, G::Resource>,
13✔
346
        id: &'l str,
13✔
347
        args: Option<&'l FluentArgs>,
13✔
348
        errors: &mut Vec<LocalizationError>,
13✔
349
    ) -> Option<Cow<'l, str>> {
13✔
350
        let mut bundle_iter = cache.into_iter();
13✔
351
        format_value_from_inner!(bundle_iter.next(), id, args, errors);
27✔
352
    }
13✔
353

354
    async fn format_value_from_stream<'l>(
1✔
355
        stream: &'l AsyncCache<G::Stream, G::Resource>,
1✔
356
        id: &'l str,
1✔
357
        args: Option<&'l FluentArgs<'_>>,
1✔
358
        errors: &mut Vec<LocalizationError>,
1✔
359
    ) -> Option<Cow<'l, str>> {
1✔
360
        use futures::StreamExt;
361

362
        let mut bundle_stream = stream.stream();
1✔
363
        format_value_from_inner!(bundle_stream.next().await, id, args, errors);
2✔
364
    }
1✔
365

366
    async fn format_messages_from_stream<'l>(
×
367
        stream: &'l AsyncCache<G::Stream, G::Resource>,
×
368
        keys: &'l [L10nKey<'l>],
×
369
        errors: &mut Vec<LocalizationError>,
×
370
    ) -> Vec<Option<L10nMessage<'l>>> {
×
371
        use futures::StreamExt;
372
        let mut bundle_stream = stream.stream();
×
373
        format_messages_from_inner!(bundle_stream.next().await, keys, errors);
×
374
    }
×
375

376
    async fn format_values_from_stream<'l>(
×
377
        stream: &'l AsyncCache<G::Stream, G::Resource>,
×
378
        keys: &'l [L10nKey<'l>],
×
379
        errors: &mut Vec<LocalizationError>,
×
380
    ) -> Vec<Option<Cow<'l, str>>> {
×
381
        use futures::StreamExt;
382
        let mut bundle_stream = stream.stream();
×
383

384
        format_values_from_inner!(bundle_stream.next().await, keys, errors);
×
385
    }
×
386

387
    fn format_message_from_bundle<'l>(
6✔
388
        bundle: &'l FluentBundle<G::Resource>,
6✔
389
        key: &'l L10nKey,
6✔
390
        format_errors: &mut Vec<FluentError>,
6✔
391
    ) -> Option<L10nMessage<'l>> {
6✔
392
        let msg = bundle.get_message(&key.id)?;
6✔
393
        let value = msg
2✔
394
            .value()
2✔
395
            .map(|pattern| bundle.format_pattern(pattern, key.args.as_ref(), format_errors));
2✔
396
        let attributes = msg
2✔
397
            .attributes()
2✔
398
            .map(|attr| {
2✔
399
                let value = bundle.format_pattern(attr.value(), key.args.as_ref(), format_errors);
×
400
                L10nAttribute {
×
401
                    name: attr.id().into(),
×
402
                    value,
×
403
                }
×
UNCOV
404
            })
×
405
            .collect();
2✔
406
        Some(L10nMessage { value, attributes })
2✔
407
    }
6✔
408

409
    fn format_messages_from_iter<'l>(
3✔
410
        cache: &'l Cache<G::Iter, G::Resource>,
3✔
411
        keys: &'l [L10nKey<'l>],
3✔
412
        errors: &mut Vec<LocalizationError>,
3✔
413
    ) -> Vec<Option<L10nMessage<'l>>> {
3✔
414
        let mut bundle_iter = cache.into_iter();
3✔
415
        format_messages_from_inner!(bundle_iter.next(), keys, errors);
5✔
416
    }
3✔
417

418
    fn format_values_from_iter<'l>(
2✔
419
        cache: &'l Cache<G::Iter, G::Resource>,
2✔
420
        keys: &'l [L10nKey<'l>],
2✔
421
        errors: &mut Vec<LocalizationError>,
2✔
422
    ) -> Vec<Option<Cow<'l, str>>> {
2✔
423
        let mut bundle_iter = cache.into_iter();
2✔
424
        format_values_from_inner!(bundle_iter.next(), keys, errors);
6✔
425
    }
2✔
426
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc