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

zbraniecki / icu4x / 6815798908

09 Nov 2023 05:17PM UTC coverage: 72.607% (-2.4%) from 75.01%
6815798908

push

github

web-flow
Implement `Any/BufferProvider` for some smart pointers (#4255)

Allows storing them as a `Box<dyn Any/BufferProvider>` without using a
wrapper type that implements the trait.

44281 of 60987 relevant lines covered (72.61%)

201375.86 hits per line

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

90.82
/components/datetime/src/skeleton/helpers.rs
1
// This file is part of ICU4X. For terms of use, please see the file
787✔
2
// called LICENSE at the top level of the ICU4X source tree
3
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
4

5
use alloc::vec;
6
use alloc::vec::Vec;
7
use core::cmp::Ordering;
8

9
use crate::{
10
    fields::{self, Field, FieldLength, FieldSymbol},
11
    options::{components, length},
12
    pattern::{
13
        hour_cycle,
14
        runtime::{self, PatternPlurals},
15
        PatternItem, TimeGranularity,
16
    },
17
    provider::calendar::{patterns::GenericLengthPatternsV1, DateSkeletonPatternsV1},
18
};
19

20
// The following scalar values are for testing the suitability of a skeleton's field for the
21
// given input. Per UTS 35, the better the fit of a pattern, the "lower the distance". In this
22
// implementation each distance type is separated by an order of magnitiude. This magnitude needs
23
// to be at minimum a multiple of the max length of fields. As of CLDR 38 (2021-01), the max length
24
// of a skeleton in the "availableFormats" contained a total of 4 fields. The scores use a multiple
25
// of 10, as a number that will contain the range, and be easy to reason with.
26
//
27
// The only exception is on the largest magnitude of values (MISSING_OR_SKELETON_EXTRA_SYMBOL). The
28
// missing or extra count BOTH the requested fields and skeleton fields. This is fine since there
29
// is no higher magnitude.
30

31
const MAX_SKELETON_FIELDS: u32 = 10;
32

33
// Per the skeleton matching algorithm:
34
// https://unicode.org/reports/tr35/tr35-dates.html#Matching_Skeletons
35

36
// > 1. "Input skeleton symbols" are replaced with the best match for a given locale.
37
// >   - Hour: j → {H, k, h, K} + {a, b, B}
38
// >           J → {H, k, h, K}
39
// >           C → j + day period
40

41
// The components::Bag does not support step 1
42

43
// > 2. For fields with symbols representing the same type (year, month, day, etc):
44
// >   A. Most symbols have a small distance from each other.
45
// >     - Months: M ≅ L           (9 ≅ 9)  conjunction, vs stand-alone
46
// >       Week:   E ≅ c           (Tue ≅ 2)
47
// >       Period: a ≅ b ≅ B       (am. ≅ mid. ≅ at night)
48
// >       Hour:   H ≅ k ≅ h ≅ K   (23, 24, 12, 11)
49

50
// For step 2, the components::Bag will not produce "stand-alone" months, as no skeletons
51
// contain stand-alone months.
52

53
const NO_DISTANCE: u32 = 0;
54

55
// B. Width differences among fields, other than those marking text vs numeric, are given small
56
// distance from each other.
57
// - MMM ≅ MMMM  (Sep ≅ September)
58
//   MM ≅ M      (09 ≅ 9)
59
const WIDTH_MISMATCH_DISTANCE: u32 = 1;
60

61
// C. Numeric and text fields are given a larger distance from each other.
62
// - MMM ≈ MM    (Sep ≈ 09)
63
//   MMM
64
const TEXT_VS_NUMERIC_DISTANCE: u32 = 10;
65

66
// D. Symbols representing substantial differences (week of year vs week of month) are given much
67
// larger a distances from each other.
68
// - d ≋ D;     (12 ≋ 345) Day of month vs Day of year
69
const SUBSTANTIAL_DIFFERENCES_DISTANCE: u32 = 100;
70

71
// A skeleton had more symbols than what was requested.
72
const SKELETON_EXTRA_SYMBOL: u32 = 1000;
73

74
// A requested symbol is missing in the skeleton. Note that this final value can be more than
75
// MAX_SKELETON_FIELDS, as it's counting the missing requested fields, which can be longer than
76
// the stored skeletons. There cannot be any cases higher than this one.
77
const REQUESTED_SYMBOL_MISSING: u32 = 10000;
78

79
/// According to the [UTS 35 skeleton matching algorithm](https://unicode.org/reports/tr35/tr35-dates.html#Matching_Skeletons)
80
/// there will be a guaranteed match for a skeleton. However, with this initial implementation,
81
/// there is no attempt to add on missing fields. This enum encodes the variants for the current
82
/// search for a best skeleton.
83
#[derive(Debug, PartialEq, Clone)]
2✔
84
pub enum BestSkeleton<T> {
85
    AllFieldsMatch(T),
×
86
    MissingOrExtraFields(T),
×
87
    NoMatch,
88
}
89

90
/// This function swaps out the time zone name field for the appropriate one. Skeleton matching
91
/// only needs to find a single "v" field, and then the time zone name can expand from there.
92
fn naively_apply_time_zone_name(
304✔
93
    pattern: &mut runtime::Pattern,
94
    time_zone_name: &Option<components::TimeZoneName>,
95
) {
96
    // If there is a preference overriding the hour cycle, apply it now.
97
    if let Some(time_zone_name) = time_zone_name {
304✔
98
        runtime::helpers::maybe_replace_first(pattern, |item| {
92✔
99
            if let PatternItem::Field(fields::Field {
80✔
100
                symbol: fields::FieldSymbol::TimeZone(_),
101
                length: _,
102
            }) = item
103
            {
104
                Some(PatternItem::Field((*time_zone_name).into()))
12✔
105
            } else {
106
                None
68✔
107
            }
108
        });
80✔
109
    }
110
}
304✔
111

112
// TODO - This could return a Cow<'a, Pattern>, but it affects every other part of the API to
113
// add a lifetime here. The pattern returned here could be one that we've already constructed in
114
// the CLDR as an exotic type, or it could be one that was modified to meet the requirements of
115
// the components bag.
116

117
/// Given a set of fields (which represents a skeleton), try to create a best localized pattern
118
// for those fields.
119
///
120
/// * `skeletons` - The skeletons that will be matched against
121
/// * `length_patterns` - Contains information on how to combine date and time patterns.
122
/// * `fields` - The desired fields to match against.
123
/// * `prefer_matched_pattern` - This algorithm does some extra steps of trying to respect
124
///         the desired fields, even if the provider data doesn't completely match. This
125
///         configuration option makes it so that the final pattern won't have additional work
126
///         done to mutate it to match the fields. It will prefer the actual matched pattern.
127
pub fn create_best_pattern_for_fields<'data>(
306✔
128
    skeletons: &DateSkeletonPatternsV1<'data>,
129
    length_patterns: &GenericLengthPatternsV1<'data>,
130
    fields: &[Field],
131
    components: &components::Bag,
132
    prefer_matched_pattern: bool,
133
) -> BestSkeleton<PatternPlurals<'data>> {
134
    let first_pattern_match =
306✔
135
        get_best_available_format_pattern(skeletons, fields, prefer_matched_pattern);
306✔
136

137
    // Try to match a skeleton to all of the fields.
138
    if let BestSkeleton::AllFieldsMatch(mut pattern_plurals) = first_pattern_match {
306✔
139
        pattern_plurals.for_each_mut(|pattern| {
406✔
140
            hour_cycle::naively_apply_preferences(pattern, &components.preferences);
206✔
141
            naively_apply_time_zone_name(pattern, &components.time_zone_name);
206✔
142
        });
206✔
143
        return BestSkeleton::AllFieldsMatch(pattern_plurals);
200✔
144
    }
×
145

146
    let FieldsByType { date, time } = group_fields_by_type(fields);
106✔
147

148
    if date.is_empty() || time.is_empty() {
106✔
149
        return match first_pattern_match {
20✔
150
            BestSkeleton::AllFieldsMatch(_) => {
151
                unreachable!("Logic error in implementation. AllFieldsMatch handled above.")
×
152
            }
153
            BestSkeleton::MissingOrExtraFields(mut pattern_plurals) => {
20✔
154
                if date.is_empty() {
20✔
155
                    pattern_plurals.for_each_mut(|pattern| {
24✔
156
                        hour_cycle::naively_apply_preferences(pattern, &components.preferences);
12✔
157
                        naively_apply_time_zone_name(pattern, &components.time_zone_name);
12✔
158
                        append_fractional_seconds(pattern, &time);
12✔
159
                    });
12✔
160
                }
161
                BestSkeleton::MissingOrExtraFields(pattern_plurals)
20✔
162
            }
20✔
163
            BestSkeleton::NoMatch => BestSkeleton::NoMatch,
×
164
        };
165
    }
166

167
    // Match the date and time, and then simplify the combinatorial logic of the results into
168
    // an optional values of the results, and a boolean value.
169
    let (date_patterns, date_missing_or_extra): (Option<PatternPlurals<'data>>, bool) =
86✔
170
        match get_best_available_format_pattern(skeletons, &date, prefer_matched_pattern) {
86✔
171
            BestSkeleton::MissingOrExtraFields(fields) => (Some(fields), true),
×
172
            BestSkeleton::AllFieldsMatch(fields) => (Some(fields), false),
86✔
173
            BestSkeleton::NoMatch => (None, true),
×
174
        };
175

176
    let (time_patterns, time_missing_or_extra): (Option<PatternPlurals<'data>>, bool) =
86✔
177
        match get_best_available_format_pattern(skeletons, &time, prefer_matched_pattern) {
86✔
178
            BestSkeleton::MissingOrExtraFields(fields) => (Some(fields), true),
1✔
179
            BestSkeleton::AllFieldsMatch(fields) => (Some(fields), false),
85✔
180
            BestSkeleton::NoMatch => (None, true),
×
181
        };
182
    let time_pattern: Option<runtime::Pattern<'data>> = time_patterns.map(|pattern_plurals| {
172✔
183
        let mut pattern =
184
            pattern_plurals.expect_pattern("Only date patterns can contain plural variants");
86✔
185
        hour_cycle::naively_apply_preferences(&mut pattern, &components.preferences);
86✔
186
        naively_apply_time_zone_name(&mut pattern, &components.time_zone_name);
86✔
187
        append_fractional_seconds(&mut pattern, &time);
86✔
188
        pattern
189
    });
86✔
190

191
    // Determine how to combine the date and time.
192
    let patterns: Option<PatternPlurals<'data>> = match (date_patterns, time_pattern) {
86✔
193
        (Some(mut date_patterns), Some(time_pattern)) => {
86✔
194
            let month_field = fields
86✔
195
                .iter()
196
                .find(|f| matches!(f.symbol, FieldSymbol::Month(_)));
261✔
197

198
            // Per UTS-35, choose a "length" pattern for combining the date and time.
199
            // https://unicode.org/reports/tr35/tr35-dates.html#Matching_Skeletons
200
            //
201
            // 1. If the requested date fields include Wide month and weekday name of any length, use length::Date::Full
202
            // 2. Otherwise, if the requested date fields include wide month, use length::Date::Long
203
            // 3. Otherwise, if the requested date fields include abbreviated month, use length::Date::Medium
204
            // 4. Otherwise use length::Date::Short
205
            let length = match month_field {
86✔
206
                Some(field) => match field.length {
81✔
207
                    FieldLength::Wide => {
208
                        let weekday = fields
78✔
209
                            .iter()
210
                            .find(|f| matches!(f.symbol, FieldSymbol::Weekday(_)));
382✔
211

212
                        if weekday.is_some() {
78✔
213
                            length::Date::Full
75✔
214
                        } else {
215
                            length::Date::Long
3✔
216
                        }
217
                    }
218
                    FieldLength::Abbreviated => length::Date::Medium,
2✔
219
                    _ => length::Date::Short,
1✔
220
                },
221
                None => length::Date::Short,
5✔
222
            };
223

224
            use crate::pattern::runtime::GenericPattern;
225
            let dt_pattern: &GenericPattern<'data> = match length {
172✔
226
                length::Date::Full => &length_patterns.full,
75✔
227
                length::Date::Long => &length_patterns.long,
3✔
228
                length::Date::Medium => &length_patterns.medium,
2✔
229
                length::Date::Short => &length_patterns.short,
6✔
230
            };
231

232
            date_patterns.for_each_mut(|pattern| {
173✔
233
                let date = pattern.clone();
87✔
234
                let time = time_pattern.clone();
87✔
235

236
                // TODO(#2626) - Since this is fallible, we should make this method fallible.
237
                #[allow(clippy::expect_used)] // Generic pattern combination should never fail.
238
                let dt = dt_pattern
87✔
239
                    .clone()
240
                    .combined(date, time)
87✔
241
                    .expect("Failed to combine date and time");
242
                *pattern = dt;
87✔
243
            });
87✔
244
            Some(date_patterns)
86✔
245
        }
86✔
246
        (None, Some(pattern)) => Some(pattern.into()),
×
247
        (Some(patterns), None) => Some(patterns),
×
248
        (None, None) => None,
×
249
    };
×
250

251
    match patterns {
86✔
252
        Some(patterns) => {
86✔
253
            if date_missing_or_extra || time_missing_or_extra {
86✔
254
                BestSkeleton::MissingOrExtraFields(patterns)
1✔
255
            } else {
256
                BestSkeleton::AllFieldsMatch(patterns)
85✔
257
            }
258
        }
259
        None => BestSkeleton::NoMatch,
×
260
    }
261
}
306✔
262

263
struct FieldsByType {
264
    pub date: Vec<Field>,
265
    pub time: Vec<Field>,
266
}
267

268
fn group_fields_by_type(fields: &[Field]) -> FieldsByType {
106✔
269
    let mut date = Vec::new();
106✔
270
    let mut time = Vec::new();
106✔
271

272
    for field in fields {
827✔
273
        match field.symbol {
721✔
274
            // Date components:
275
            // Note: Weekdays are included in both time and date skeletons.
276
            //  - Time examples: "EBhm" "EBhms" "Ed" "Ehm" "EHm" "Ehms" "EHms"
277
            //  - Date examples: "GyMMMEd" "MEd" "MMMEd" "MMMMEd" "yMEd" "yMMMEd"
278
            //  - Solo example: "E"
279
            FieldSymbol::Era
280
            | FieldSymbol::Year(_)
281
            | FieldSymbol::Month(_)
282
            | FieldSymbol::Week(_)
283
            | FieldSymbol::Day(_)
284
            | FieldSymbol::Weekday(_) => date.push(*field),
721✔
285

286
            // Time components:
287
            FieldSymbol::DayPeriod(_)
288
            | FieldSymbol::Hour(_)
289
            | FieldSymbol::Minute
290
            | FieldSymbol::Second(_)
291
            | FieldSymbol::TimeZone(_) => time.push(*field),
×
292
            // Other components
293
            // TODO(#486)
294
            // FieldSymbol::Era(_) => other.push(*field),
295
            // Plus others...
296
        };
297
    }
298

299
    FieldsByType { date, time }
500✔
300
}
500✔
301

302
/// Alters given Pattern so that its fields have the same length as 'fields'.
303
///
304
///  For example the "d MMM y" pattern will be changed to "d MMMM y" given fields ["y", "MMMM", "d"].
305
fn adjust_pattern_field_lengths(fields: &[Field], pattern: &mut runtime::Pattern) {
298✔
306
    runtime::helpers::maybe_replace(pattern, |item| {
3,050✔
307
        if let PatternItem::Field(pattern_field) = item {
2,752✔
308
            if let Some(requested_field) = fields
2,846✔
309
                .iter()
310
                .find(|field| field.symbol.discriminant_cmp(&pattern_field.symbol).is_eq())
5,399✔
311
            {
312
                if requested_field.length != pattern_field.length
1,381✔
313
                    && requested_field.get_length_type() == pattern_field.get_length_type()
651✔
314
                {
315
                    return Some(PatternItem::Field(*requested_field));
640✔
316
                }
317
            }
318
        }
319
        None
2,112✔
320
    })
2,752✔
321
}
298✔
322

323
/// Alters given Pattern so that it will have a fractional second field if it was requested.
324
///
325
/// If the requested skeleton included both seconds and fractional seconds and the dateFormatItem
326
/// skeleton included seconds but not fractional seconds, then the seconds field of the corresponding
327
/// pattern should be adjusted by appending the locale’s decimal separator, followed by the sequence
328
/// of ‘S’ characters from the requested skeleton.
329
/// (see <https://unicode.org/reports/tr35/tr35-dates.html#Matching_Skeletons>)
330
fn append_fractional_seconds(pattern: &mut runtime::Pattern, fields: &[Field]) {
98✔
331
    if let Some(requested_field) = fields
107✔
332
        .iter()
333
        .find(|field| field.symbol == FieldSymbol::Second(fields::Second::FractionalSecond))
303✔
334
    {
335
        let mut items = pattern.items.to_vec();
9✔
336
        if let Some(pos) = items.iter().position(|&item| match item {
54✔
337
            PatternItem::Field(field) => {
27✔
338
                matches!(field.symbol, FieldSymbol::Second(fields::Second::Second))
27✔
339
            }
340
            _ => false,
18✔
341
        }) {
45✔
342
            if let FieldLength::Fixed(p) = requested_field.length {
9✔
343
                if p > 0 {
9✔
344
                    items.insert(pos + 1, PatternItem::Field(*requested_field));
7✔
345
                }
346
            }
347
        }
348
        *pattern = runtime::Pattern::from(items);
9✔
349
        pattern.time_granularity = TimeGranularity::Nanoseconds;
9✔
350
    }
9✔
351
}
98✔
352

353
/// A partial implementation of the [UTS 35 skeleton matching algorithm](https://unicode.org/reports/tr35/tr35-dates.html#Matching_Skeletons).
354
///
355
/// The following is implemented:
356
///
357
///  * Compute a score based on the best possible match for the given fields.
358
///  * Select the skeleton with highest score.
359
///  * Modify the resulting pattern to have fields of the same length. For example requesting
360
///      a skeleton "yMMMMd" can have a best match of ["yMMMd", "d MMM y"]. This pattern should
361
///      then be modified to use the requested length to produce a pattern "d MMMM y".
362
///      However, fields should not be changed from numeric to text.
363
///
364
/// The following is not implemented:
365
///
366
///  * 2.6.2.2 Missing Skeleton Fields
367
///    - TODO(#586) - Using the CLDR appendItems field. Note: There is not agreement yet on how
368
///      much of this step to implement. See the issue for more information.
369
///
370
/// # Panics
371
///
372
/// Panics if `prefer_matched_pattern` is set to true in a non-datagen mode.
373
pub fn get_best_available_format_pattern<'data>(
482✔
374
    skeletons: &DateSkeletonPatternsV1<'data>,
375
    fields: &[Field],
376
    prefer_matched_pattern: bool,
377
) -> BestSkeleton<PatternPlurals<'data>> {
378
    let mut closest_format_pattern = None;
482✔
379
    let mut closest_distance: u32 = u32::MAX;
482✔
380
    let mut closest_missing_fields = 0;
482✔
381

382
    for (skeleton, pattern) in skeletons.0.iter() {
18,972✔
383
        debug_assert!(
18,490✔
384
            skeleton.0.fields_len() <= MAX_SKELETON_FIELDS as usize,
18,490✔
385
            "The distance mechanism assumes skeletons are less than MAX_SKELETON_FIELDS in length."
386
        );
387
        let mut missing_fields = 0;
18,490✔
388
        let mut distance: u32 = 0;
18,490✔
389
        // The distance should fit into a u32.
390

391
        let mut requested_fields = fields.iter().peekable();
18,490✔
392
        let mut skeleton_fields = skeleton.0.fields_iter().peekable();
18,490✔
393

394
        let mut matched_seconds = false;
18,490✔
395
        loop {
18,490✔
396
            let next = (requested_fields.peek(), skeleton_fields.peek());
113,479✔
397

398
            // Try to find matching symbols.
399
            match next {
113,479✔
400
                (Some(requested_field), Some(skeleton_field)) => {
57,029✔
401
                    debug_assert!(
57,029✔
402
                        // As of the time of this writing, stand-alone months are not in the CLDR
403
                        // skeleton data. The components::Bag could produce stand-alone month fields,
404
                        // but since the CLDR does not have them, only Month::Format symbols are
405
                        // used for matching.
406
                        skeleton_field.symbol != FieldSymbol::Month(fields::Month::StandAlone)
57,029✔
407
                    );
408

409
                    match skeleton_field
114,058✔
410
                        .symbol
411
                        .discriminant_cmp(&requested_field.symbol)
57,029✔
412
                    {
413
                        Ordering::Less => {
414
                            // Keep searching for a matching skeleton field.
415
                            skeleton_fields.next();
12,795✔
416
                            distance += SKELETON_EXTRA_SYMBOL;
12,795✔
417
                            continue;
12,795✔
418
                        }
419
                        Ordering::Greater => {
420
                            // https://unicode.org/reports/tr35/tr35-dates.html#Matching_Skeletons
421
                            // A requested skeleton that includes both seconds and fractional seconds (e.g. “mmssSSS”) is allowed
422
                            // to match a dateFormatItem skeleton that includes seconds but not fractional seconds (e.g. “ms”).
423
                            if !(matched_seconds
22,245✔
424
                                && requested_field.symbol
×
425
                                    == FieldSymbol::Second(fields::Second::FractionalSecond))
426
                            {
427
                                // The requested field symbol is missing from the skeleton.
428
                                distance += REQUESTED_SYMBOL_MISSING;
22,244✔
429
                                missing_fields += 1;
22,244✔
430
                                requested_fields.next();
22,244✔
431
                                continue;
432
                            }
433
                        }
434
                        _ => (),
435
                    }
436

437
                    if requested_field.symbol
21,990✔
438
                        == FieldSymbol::Second(fields::Second::FractionalSecond)
439
                        && skeleton_field.symbol
×
440
                            == FieldSymbol::Second(fields::Second::FractionalSecond)
441
                    {
442
                        matched_seconds = true;
×
443
                    }
444

445
                    distance += if requested_field == skeleton_field {
34,973✔
446
                        NO_DISTANCE
9,007✔
447
                    } else if requested_field.symbol != skeleton_field.symbol {
12,983✔
448
                        SUBSTANTIAL_DIFFERENCES_DISTANCE
1,764✔
449
                    } else if requested_field.get_length_type() != skeleton_field.get_length_type()
11,219✔
450
                    {
451
                        TEXT_VS_NUMERIC_DISTANCE
1,981✔
452
                    } else {
453
                        WIDTH_MISMATCH_DISTANCE
9,238✔
454
                    };
455

456
                    requested_fields.next();
21,990✔
457
                    skeleton_fields.next();
21,990✔
458
                }
459
                (None, Some(_)) => {
460
                    // The skeleton has additional fields that we are not matching.
461
                    distance += SKELETON_EXTRA_SYMBOL;
12,026✔
462
                    skeleton_fields.next();
12,026✔
463
                }
464
                (Some(_), None) => {
25,934✔
465
                    // The skeleton is missing requested fields.
466
                    distance += REQUESTED_SYMBOL_MISSING;
25,934✔
467
                    requested_fields.next();
25,934✔
468
                    missing_fields += 1;
25,934✔
469
                }
470
                (None, None) => {
471
                    break;
472
                }
473
            }
474
        }
475

476
        if distance < closest_distance {
18,490✔
477
            closest_format_pattern = Some(pattern);
2,331✔
478
            closest_distance = distance;
2,331✔
479
            closest_missing_fields = missing_fields;
2,331✔
480
        }
481
    }
482

483
    if !prefer_matched_pattern && closest_distance >= TEXT_VS_NUMERIC_DISTANCE {
482✔
484
        if let [field] = fields {
137✔
485
            // A single field was requested and the best pattern either includes extra fields or can't be adjusted to match
486
            // (e.g. text vs numeric). We return the field instead of the matched pattern.
487
            return BestSkeleton::AllFieldsMatch(
11✔
488
                runtime::Pattern::from(vec![PatternItem::Field(*field)]).into(),
11✔
489
            );
490
        }
491
    }
492

493
    let mut closest_format_pattern = if let Some(pattern) = closest_format_pattern {
471✔
494
        pattern.clone()
471✔
495
    } else {
496
        return BestSkeleton::NoMatch;
×
497
    };
498

499
    if closest_missing_fields == fields.len() {
471✔
500
        return BestSkeleton::NoMatch;
2✔
501
    }
502

503
    if closest_distance == NO_DISTANCE {
469✔
504
        return BestSkeleton::AllFieldsMatch(closest_format_pattern);
177✔
505
    }
506

507
    // Modify the resulting pattern to have fields of the same length.
508
    #[allow(clippy::panic)] // guards against running this branch in non-datagen mode.
509
    if prefer_matched_pattern {
292✔
510
        #[cfg(not(feature = "datagen"))]
511
        panic!("This code branch should only be run when transforming provider code.");
512
    } else {
513
        closest_format_pattern.for_each_mut(|pattern| {
590✔
514
            adjust_pattern_field_lengths(fields, pattern);
298✔
515
        });
298✔
516
    }
517

518
    if closest_distance >= SKELETON_EXTRA_SYMBOL {
292✔
519
        return BestSkeleton::MissingOrExtraFields(closest_format_pattern);
109✔
520
    }
521

522
    BestSkeleton::AllFieldsMatch(closest_format_pattern)
183✔
523
}
482✔
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