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

facet-rs / facet / 20107877096

10 Dec 2025 05:40PM UTC coverage: 57.698% (-0.9%) from 58.588%
20107877096

Pull #1220

github

web-flow
Merge 20b27caa2 into fe1531898
Pull Request #1220: feat(cinereus): improve tree matching for leaf nodes and filter no-op moves

1748 of 3613 new or added lines in 18 files covered. (48.38%)

462 existing lines in 6 files now uncovered.

28548 of 49478 relevant lines covered (57.7%)

808.8 hits per line

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

81.37
/facet-args/src/error.rs
1
extern crate alloc;
2

3
use crate::span::Span;
4
use core::fmt;
5
use facet_core::{Field, Shape, Type, UserType, Variant};
6
use facet_reflect::ReflectError;
7
use heck::ToKebabCase;
8
use miette::{Diagnostic, LabeledSpan};
9

10
/// An args parsing error, with input info, so that it can be formatted nicely
11
#[derive(Debug)]
12
pub struct ArgsErrorWithInput {
13
    /// The inner error
14
    pub(crate) inner: ArgsError,
15

16
    /// All CLI arguments joined by a space
17
    pub(crate) flattened_args: String,
18
}
19

20
impl ArgsErrorWithInput {
21
    /// Returns true if this is a help request (not a real error)
22
    pub fn is_help_request(&self) -> bool {
6✔
23
        self.inner.kind.is_help_request()
6✔
24
    }
6✔
25

26
    /// If this is a help request, returns the help text
27
    pub fn help_text(&self) -> Option<&str> {
31✔
28
        self.inner.kind.help_text()
31✔
29
    }
31✔
30
}
31

32
impl core::fmt::Display for ArgsErrorWithInput {
33
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
28✔
34
        // For help requests, just display the help text directly
35
        if let Some(help) = self.help_text() {
28✔
36
            return write!(f, "{}", help);
1✔
37
        }
27✔
38
        write!(f, "Could not parse CLI arguments")
27✔
39
    }
28✔
40
}
41

42
impl core::error::Error for ArgsErrorWithInput {}
43

44
impl Diagnostic for ArgsErrorWithInput {
45
    fn code<'a>(&'a self) -> Option<Box<dyn core::fmt::Display + 'a>> {
26✔
46
        Some(Box::new(self.inner.kind.code()))
26✔
47
    }
26✔
48

49
    fn severity(&self) -> Option<miette::Severity> {
52✔
50
        Some(miette::Severity::Error)
52✔
51
    }
52✔
52

53
    fn help<'a>(&'a self) -> Option<Box<dyn core::fmt::Display + 'a>> {
26✔
54
        self.inner.kind.help()
26✔
55
    }
26✔
56

57
    fn url<'a>(&'a self) -> Option<Box<dyn core::fmt::Display + 'a>> {
26✔
58
        None
26✔
59
    }
26✔
60

61
    fn source_code(&self) -> Option<&dyn miette::SourceCode> {
104✔
62
        Some(&self.flattened_args)
104✔
63
    }
104✔
64

65
    fn labels(&self) -> Option<Box<dyn Iterator<Item = miette::LabeledSpan> + '_>> {
26✔
66
        Some(Box::new(core::iter::once(LabeledSpan::new(
26✔
67
            Some(self.inner.kind.label()),
26✔
68
            self.inner.span.start,
26✔
69
            self.inner.span.len(),
26✔
70
        ))))
26✔
71
    }
26✔
72

73
    fn related<'a>(&'a self) -> Option<Box<dyn Iterator<Item = &'a dyn Diagnostic> + 'a>> {
26✔
74
        None
26✔
75
    }
26✔
76

77
    fn diagnostic_source(&self) -> Option<&dyn Diagnostic> {
26✔
78
        None
26✔
79
    }
26✔
80
}
81

82
/// An args parsing error (without input info)
83
#[derive(Debug)]
84
pub struct ArgsError {
85
    /// Where the error occurred
86
    pub span: Span,
87

88
    /// The specific error that occurred while parsing arguments.
89
    pub kind: ArgsErrorKind,
90
}
91

92
/// An error kind for argument parsing.
93
///
94
/// Stores references to static shape/field/variant info for lazy formatting.
95
#[derive(Debug, Clone)]
96
#[non_exhaustive]
97
pub enum ArgsErrorKind {
98
    /// Help was requested via -h, --help, -help, or /?
99
    ///
100
    /// This is not really an "error" but uses the error path to return
101
    /// help text when the user explicitly requests it.
102
    HelpRequested {
103
        /// The generated help text
104
        help_text: alloc::string::String,
105
    },
106

107
    /// Did not expect a positional argument at this position
108
    UnexpectedPositionalArgument {
109
        /// Fields of the struct/variant being parsed (for help text)
110
        fields: &'static [Field],
111
    },
112

113
    /// Wanted to look up a field, for example `--something` in a struct,
114
    /// but the current shape was not a struct.
115
    NoFields {
116
        /// The shape that was being parsed
117
        shape: &'static Shape,
118
    },
119

120
    /// Found an enum field without the args::subcommand attribute.
121
    /// Enums can only be used as subcommands when explicitly marked.
122
    EnumWithoutSubcommandAttribute {
123
        /// The field that has the enum type
124
        field: &'static Field,
125
    },
126

127
    /// Passed `--something` (see span), no such long flag
128
    UnknownLongFlag {
129
        /// The flag that was passed
130
        flag: String,
131
        /// Fields of the struct/variant being parsed
132
        fields: &'static [Field],
133
    },
134

135
    /// Passed `-j` (see span), no such short flag
136
    UnknownShortFlag {
137
        /// The flag that was passed
138
        flag: String,
139
        /// Fields of the struct/variant being parsed
140
        fields: &'static [Field],
141
        /// Precise span for the invalid flag (used for chained short flags like `-axc` where `x` is invalid)
142
        precise_span: Option<Span>,
143
    },
144

145
    /// Struct/type expected a certain argument to be passed and it wasn't
146
    MissingArgument {
147
        /// The field that was missing
148
        field: &'static Field,
149
    },
150

151
    /// Expected a value of type shape, got EOF
152
    ExpectedValueGotEof {
153
        /// The type that was expected
154
        shape: &'static Shape,
155
    },
156

157
    /// Unknown subcommand name
158
    UnknownSubcommand {
159
        /// The subcommand that was provided
160
        provided: String,
161
        /// Variants of the enum (subcommands)
162
        variants: &'static [Variant],
163
    },
164

165
    /// Required subcommand was not provided
166
    MissingSubcommand {
167
        /// Variants of the enum (available subcommands)
168
        variants: &'static [Variant],
169
    },
170

171
    /// Generic reflection error: something went wrong
172
    ReflectError(ReflectError),
173
}
174

175
impl ArgsErrorKind {
176
    /// Returns a precise span override if the error kind has one.
177
    /// This is used for errors like `UnknownShortFlag` in chained flags
178
    /// where we want to highlight just the invalid character, not the whole arg.
179
    pub fn precise_span(&self) -> Option<Span> {
27✔
180
        match self {
27✔
181
            ArgsErrorKind::UnknownShortFlag { precise_span, .. } => *precise_span,
1✔
182
            _ => None,
26✔
183
        }
184
    }
27✔
185

186
    /// Returns an error code for this error kind.
187
    pub fn code(&self) -> &'static str {
26✔
188
        match self {
26✔
UNCOV
189
            ArgsErrorKind::HelpRequested { .. } => "args::help",
×
190
            ArgsErrorKind::UnexpectedPositionalArgument { .. } => "args::unexpected_positional",
3✔
UNCOV
191
            ArgsErrorKind::NoFields { .. } => "args::no_fields",
×
192
            ArgsErrorKind::EnumWithoutSubcommandAttribute { .. } => {
UNCOV
193
                "args::enum_without_subcommand_attribute"
×
194
            }
195
            ArgsErrorKind::UnknownLongFlag { .. } => "args::unknown_long_flag",
3✔
196
            ArgsErrorKind::UnknownShortFlag { .. } => "args::unknown_short_flag",
1✔
197
            ArgsErrorKind::MissingArgument { .. } => "args::missing_argument",
2✔
198
            ArgsErrorKind::ExpectedValueGotEof { .. } => "args::expected_value",
2✔
199
            ArgsErrorKind::UnknownSubcommand { .. } => "args::unknown_subcommand",
2✔
200
            ArgsErrorKind::MissingSubcommand { .. } => "args::missing_subcommand",
2✔
201
            ArgsErrorKind::ReflectError(_) => "args::reflect_error",
11✔
202
        }
203
    }
26✔
204

205
    /// Returns a short label for the error (shown inline in the source)
206
    pub fn label(&self) -> String {
26✔
207
        match self {
26✔
UNCOV
208
            ArgsErrorKind::HelpRequested { .. } => "help requested".to_string(),
×
209
            ArgsErrorKind::UnexpectedPositionalArgument { .. } => {
210
                "unexpected positional argument".to_string()
3✔
211
            }
UNCOV
212
            ArgsErrorKind::NoFields { shape } => {
×
UNCOV
213
                format!("cannot parse arguments into `{}`", shape.type_identifier)
×
214
            }
UNCOV
215
            ArgsErrorKind::EnumWithoutSubcommandAttribute { field } => {
×
UNCOV
216
                format!(
×
217
                    "enum field `{}` must be marked with `#[facet(args::subcommand)]` to be used as subcommands",
218
                    field.name
219
                )
220
            }
221
            ArgsErrorKind::UnknownLongFlag { flag, .. } => {
3✔
222
                format!("unknown flag `--{flag}`")
3✔
223
            }
224
            ArgsErrorKind::UnknownShortFlag { flag, .. } => {
1✔
225
                format!("unknown flag `-{flag}`")
1✔
226
            }
227
            ArgsErrorKind::ExpectedValueGotEof { shape } => {
2✔
228
                // Unwrap Option to show the inner type
229
                let inner_type = unwrap_option_type(shape);
2✔
230
                format!("expected `{inner_type}` value")
2✔
231
            }
232
            ArgsErrorKind::ReflectError(err) => format_reflect_error(err),
11✔
233
            ArgsErrorKind::MissingArgument { field } => {
2✔
234
                let doc_hint = field
2✔
235
                    .doc
2✔
236
                    .first()
2✔
237
                    .map(|d| format!(" ({})", d.trim()))
2✔
238
                    .unwrap_or_default();
2✔
239
                let positional = field.has_attr(Some("args"), "positional");
2✔
240
                let arg_name = if positional {
2✔
241
                    format!("<{}>", field.name.to_kebab_case())
2✔
242
                } else {
243
                    format!("--{}", field.name.to_kebab_case())
×
244
                };
245
                format!("missing required argument `{arg_name}`{doc_hint}")
2✔
246
            }
247
            ArgsErrorKind::UnknownSubcommand { provided, .. } => {
2✔
248
                format!("unknown subcommand `{provided}`")
2✔
249
            }
250
            ArgsErrorKind::MissingSubcommand { .. } => "expected a subcommand".to_string(),
2✔
251
        }
252
    }
26✔
253

254
    /// Returns help text for this error
255
    pub fn help(&self) -> Option<Box<dyn core::fmt::Display + '_>> {
26✔
256
        match self {
26✔
257
            ArgsErrorKind::UnexpectedPositionalArgument { fields } => {
3✔
258
                if fields.is_empty() {
3✔
259
                    return Some(Box::new(
×
UNCOV
260
                        "this command does not accept positional arguments",
×
UNCOV
261
                    ));
×
262
                }
3✔
263

264
                // Check if any of the fields are enums without subcommand attributes
265
                if let Some(enum_field) = fields.iter().find(|f| {
3✔
266
                    matches!(f.shape().ty, Type::User(UserType::Enum(_)))
3✔
267
                        && !f.has_attr(Some("args"), "subcommand")
×
268
                }) {
3✔
269
                    return Some(Box::new(format!(
×
UNCOV
270
                        "available options:\n{}\n\nnote: field `{}` is an enum but missing `#[facet(args::subcommand)]` attribute. Enums must be marked as subcommands to accept positional arguments.",
×
UNCOV
271
                        format_available_flags(fields),
×
272
                        enum_field.name
×
UNCOV
273
                    )));
×
274
                }
3✔
275

276
                let flags = format_available_flags(fields);
3✔
277
                Some(Box::new(format!("available options:\n{flags}")))
3✔
278
            }
279
            ArgsErrorKind::UnknownLongFlag { flag, fields } => {
3✔
280
                // Try to find a similar flag
281
                if let Some(suggestion) = find_similar_flag(flag, fields) {
3✔
282
                    return Some(Box::new(format!("did you mean `--{suggestion}`?")));
3✔
UNCOV
283
                }
×
284
                if fields.is_empty() {
×
285
                    return None;
×
286
                }
×
UNCOV
287
                let flags = format_available_flags(fields);
×
UNCOV
288
                Some(Box::new(format!("available options:\n{flags}")))
×
289
            }
290
            ArgsErrorKind::UnknownShortFlag { flag, fields, .. } => {
1✔
291
                // Try to find what flag the user might have meant
292
                let short_char = flag.chars().next();
1✔
293
                if let Some(field) = fields.iter().find(|f| get_short_flag(f) == short_char) {
2✔
UNCOV
294
                    return Some(Box::new(format!(
×
295
                        "`-{}` is `--{}`",
×
UNCOV
296
                        flag,
×
UNCOV
297
                        field.name.to_kebab_case()
×
UNCOV
298
                    )));
×
299
                }
1✔
300
                if fields.is_empty() {
1✔
301
                    return None;
×
302
                }
1✔
303
                let flags = format_available_flags(fields);
1✔
304
                Some(Box::new(format!("available options:\n{flags}")))
1✔
305
            }
306
            ArgsErrorKind::MissingArgument { field } => {
2✔
307
                let kebab = field.name.to_kebab_case();
2✔
308
                let type_name = field.shape().type_identifier;
2✔
309
                let positional = field.has_attr(Some("args"), "positional");
2✔
310
                if positional {
2✔
311
                    Some(Box::new(format!("provide a value for `<{kebab}>`")))
2✔
312
                } else {
UNCOV
313
                    Some(Box::new(format!(
×
UNCOV
314
                        "provide a value with `--{kebab} <{type_name}>`"
×
UNCOV
315
                    )))
×
316
                }
317
            }
318
            ArgsErrorKind::UnknownSubcommand { provided, variants } => {
2✔
319
                if variants.is_empty() {
2✔
UNCOV
320
                    return None;
×
321
                }
2✔
322
                // Try to find a similar subcommand
323
                if let Some(suggestion) = find_similar_subcommand(provided, variants) {
2✔
UNCOV
324
                    return Some(Box::new(format!("did you mean `{suggestion}`?")));
×
325
                }
2✔
326
                let cmds = format_available_subcommands(variants);
2✔
327
                Some(Box::new(format!("available subcommands:\n{cmds}")))
2✔
328
            }
329
            ArgsErrorKind::MissingSubcommand { variants } => {
2✔
330
                if variants.is_empty() {
2✔
UNCOV
331
                    return None;
×
332
                }
2✔
333
                let cmds = format_available_subcommands(variants);
2✔
334
                Some(Box::new(format!("available subcommands:\n{cmds}")))
2✔
335
            }
336
            ArgsErrorKind::ExpectedValueGotEof { .. } => {
337
                Some(Box::new("provide a value after the flag"))
2✔
338
            }
339
            ArgsErrorKind::HelpRequested { .. }
340
            | ArgsErrorKind::NoFields { .. }
341
            | ArgsErrorKind::EnumWithoutSubcommandAttribute { .. }
342
            | ArgsErrorKind::ReflectError(_) => None,
11✔
343
        }
344
    }
26✔
345

346
    /// Returns true if this is a help request (not a real error)
347
    pub fn is_help_request(&self) -> bool {
6✔
348
        matches!(self, ArgsErrorKind::HelpRequested { .. })
6✔
349
    }
6✔
350

351
    /// If this is a help request, returns the help text
352
    pub fn help_text(&self) -> Option<&str> {
31✔
353
        match self {
31✔
354
            ArgsErrorKind::HelpRequested { help_text } => Some(help_text),
4✔
355
            _ => None,
27✔
356
        }
357
    }
31✔
358
}
359

360
/// Format a two-column list with aligned descriptions
361
fn format_two_column_list(
8✔
362
    items: impl IntoIterator<Item = (String, Option<&'static str>)>,
8✔
363
) -> String {
8✔
364
    use core::fmt::Write;
365

366
    let items: Vec<_> = items.into_iter().collect();
8✔
367

368
    // Find max width for alignment
369
    let max_width = items.iter().map(|(name, _)| name.len()).max().unwrap_or(0);
11✔
370

371
    let mut lines = Vec::new();
8✔
372
    for (name, doc) in items {
11✔
373
        let mut line = String::new();
11✔
374
        write!(line, "  {name}").unwrap();
11✔
375

376
        // Pad to alignment
377
        let padding = max_width.saturating_sub(name.len());
11✔
378
        for _ in 0..padding {
11✔
379
            line.push(' ');
2✔
380
        }
2✔
381

382
        if let Some(doc) = doc {
11✔
383
            write!(line, "  {}", doc.trim()).unwrap();
6✔
384
        }
6✔
385

386
        lines.push(line);
11✔
387
    }
388
    lines.join("\n")
8✔
389
}
8✔
390

391
/// Format available flags for help text (from static field info)
392
fn format_available_flags(fields: &'static [Field]) -> String {
4✔
393
    let items = fields.iter().filter_map(|field| {
5✔
394
        if field.has_attr(Some("args"), "subcommand") {
5✔
UNCOV
395
            return None;
×
396
        }
5✔
397

398
        let short = get_short_flag(field);
5✔
399
        let positional = field.has_attr(Some("args"), "positional");
5✔
400
        let kebab = field.name.to_kebab_case();
5✔
401

402
        let name = if positional {
5✔
UNCOV
403
            match short {
×
UNCOV
404
                Some(s) => format!("-{s}, <{kebab}>"),
×
UNCOV
405
                None => format!("    <{kebab}>"),
×
406
            }
407
        } else {
408
            match short {
5✔
409
                Some(s) => format!("-{s}, --{kebab}"),
2✔
410
                None => format!("    --{kebab}"),
3✔
411
            }
412
        };
413

414
        Some((name, field.doc.first().copied()))
5✔
415
    });
5✔
416

417
    format_two_column_list(items)
4✔
418
}
4✔
419

420
/// Format available subcommands for help text (from static variant info)
421
fn format_available_subcommands(variants: &'static [Variant]) -> String {
4✔
422
    let items = variants.iter().map(|variant| {
6✔
423
        let name = variant
6✔
424
            .get_builtin_attr("rename")
6✔
425
            .and_then(|attr| attr.get_as::<&str>())
6✔
426
            .map(|s| (*s).to_string())
6✔
427
            .unwrap_or_else(|| variant.name.to_kebab_case());
6✔
428

429
        (name, variant.doc.first().copied())
6✔
430
    });
6✔
431

432
    format_two_column_list(items)
4✔
433
}
4✔
434

435
/// Get the short flag character for a field, if any
436
fn get_short_flag(field: &Field) -> Option<char> {
7✔
437
    field
7✔
438
        .get_attr(Some("args"), "short")
7✔
439
        .and_then(|attr| attr.get_as::<crate::Attr>())
7✔
440
        .and_then(|attr| {
7✔
441
            if let crate::Attr::Short(c) = attr {
4✔
442
                // If explicit char provided, use it; otherwise use first char of field name
443
                c.or_else(|| field.name.chars().next())
4✔
444
            } else {
UNCOV
445
                None
×
446
            }
447
        })
4✔
448
}
7✔
449

450
/// Find a similar flag name using simple heuristics
451
fn find_similar_flag(input: &str, fields: &'static [Field]) -> Option<String> {
3✔
452
    for field in fields {
4✔
453
        let kebab = field.name.to_kebab_case();
4✔
454
        if is_similar(input, &kebab) {
4✔
455
            return Some(kebab);
3✔
456
        }
1✔
457
    }
UNCOV
458
    None
×
459
}
3✔
460

461
/// Find a similar subcommand name using simple heuristics
462
fn find_similar_subcommand(input: &str, variants: &'static [Variant]) -> Option<String> {
2✔
463
    for variant in variants {
3✔
464
        // Check for rename attribute first
465
        let name = variant
3✔
466
            .get_builtin_attr("rename")
3✔
467
            .and_then(|attr| attr.get_as::<&str>())
3✔
468
            .map(|s| (*s).to_string())
3✔
469
            .unwrap_or_else(|| variant.name.to_kebab_case());
3✔
470
        if is_similar(input, &name) {
3✔
UNCOV
471
            return Some(name);
×
472
        }
3✔
473
    }
474
    None
2✔
475
}
2✔
476

477
/// Check if two strings are similar (differ by at most 2 edits)
478
fn is_similar(a: &str, b: &str) -> bool {
7✔
479
    if a == b {
7✔
UNCOV
480
        return true;
×
481
    }
7✔
482
    let len_diff = (a.len() as isize - b.len() as isize).abs();
7✔
483
    if len_diff > 2 {
7✔
484
        return false;
1✔
485
    }
6✔
486

487
    // Simple check: count character differences
488
    let mut diffs = 0;
6✔
489
    let a_chars: Vec<char> = a.chars().collect();
6✔
490
    let b_chars: Vec<char> = b.chars().collect();
6✔
491

492
    for (ac, bc) in a_chars.iter().zip(b_chars.iter()) {
34✔
493
        if ac != bc {
34✔
494
            diffs += 1;
17✔
495
        }
17✔
496
    }
497
    diffs += len_diff as usize;
6✔
498
    diffs <= 2
6✔
499
}
7✔
500

501
/// Get the inner type identifier, unwrapping Option if present
502
fn unwrap_option_type(shape: &'static Shape) -> &'static str {
13✔
503
    match shape.def {
13✔
504
        facet_core::Def::Option(opt_def) => opt_def.t.type_identifier,
×
505
        _ => shape.type_identifier,
13✔
506
    }
507
}
13✔
508

509
/// Format a ReflectError into a user-friendly message
510
fn format_reflect_error(err: &ReflectError) -> String {
11✔
511
    use facet_reflect::ReflectError::*;
512
    match err {
11✔
513
        OperationFailed { shape, operation } => {
11✔
514
            // Improve common operation failure messages
515
            // Unwrap Option to show the inner type
516
            let inner_type = unwrap_option_type(shape);
11✔
517

518
            // Check for subcommand-specific error message
519
            if operation.starts_with("Subcommands must be provided") {
11✔
520
                return operation.to_string();
1✔
521
            }
10✔
522

523
            match *operation {
10✔
524
                "Type does not support parsing from string" => {
10✔
525
                    format!("`{inner_type}` cannot be parsed from a string value")
2✔
526
                }
527
                "Failed to parse string value" => {
8✔
528
                    format!("invalid value for `{inner_type}`")
7✔
529
                }
530
                _ => format!("`{inner_type}`: {operation}"),
1✔
531
            }
532
        }
533
        UninitializedField { shape, field_name } => {
×
534
            format!(
×
535
                "field `{}` of `{}` was not provided",
536
                field_name, shape.type_identifier
537
            )
538
        }
UNCOV
539
        WrongShape { expected, actual } => {
×
UNCOV
540
            format!(
×
541
                "expected `{}`, got `{}`",
542
                expected.type_identifier, actual.type_identifier
543
            )
544
        }
UNCOV
545
        _ => format!("{err}"),
×
546
    }
547
}
11✔
548

549
impl core::fmt::Display for ArgsErrorKind {
UNCOV
550
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
×
UNCOV
551
        write!(f, "{}", self.label())
×
UNCOV
552
    }
×
553
}
554

555
impl From<ReflectError> for ArgsErrorKind {
556
    fn from(error: ReflectError) -> Self {
9✔
557
        ArgsErrorKind::ReflectError(error)
9✔
558
    }
9✔
559
}
560

561
impl ArgsError {
562
    /// Creates a new args error
563
    pub fn new(kind: ArgsErrorKind, span: Span) -> Self {
33✔
564
        Self { span, kind }
33✔
565
    }
33✔
566
}
567

568
impl fmt::Display for ArgsError {
UNCOV
569
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
×
UNCOV
570
        fmt::Debug::fmt(self, f)
×
UNCOV
571
    }
×
572
}
573

574
/// Extract variants from a shape (if it's an enum)
575
pub(crate) fn get_variants_from_shape(shape: &'static Shape) -> &'static [Variant] {
2✔
576
    if let Type::User(UserType::Enum(enum_type)) = shape.ty {
2✔
577
        enum_type.variants
2✔
578
    } else {
UNCOV
579
        &[]
×
580
    }
581
}
2✔
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

© 2026 Coveralls, Inc