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

facet-rs / facet / 16482665288

23 Jul 2025 09:56PM UTC coverage: 58.441% (-0.2%) from 58.68%
16482665288

Pull #855

github

web-flow
Merge 1ef12fa0a into 5e8e214d1
Pull Request #855: wip: Remove 'shape lifetime

399 of 571 new or added lines in 70 files covered. (69.88%)

3 existing lines in 3 files now uncovered.

11939 of 20429 relevant lines covered (58.44%)

120.56 hits per line

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

81.48
/facet-args/src/fields.rs
1
use alloc::borrow::Cow;
2
use alloc::string::ToString;
3
use facet_core::{FieldAttribute, Shape, Type, UserType};
4

5
use crate::deserialize::{
6
    DeserErrorKind, Outcome, Raw, Scalar, Span, Spanned, Subspan, SubspanMeta,
7
};
8
use facet_reflect::Partial;
9

10
pub(crate) fn validate_field<'facet>(
38✔
11
    field_name: &str,
38✔
12
    shape: &'static Shape,
38✔
13
    wip: &Partial<'facet>,
38✔
14
) -> Result<(), DeserErrorKind> {
38✔
15
    if let Type::User(UserType::Struct(_)) = &shape.ty {
38✔
16
        if wip.field_index(field_name).is_none() {
38✔
17
            return Err(DeserErrorKind::UnknownField {
2✔
18
                field_name: field_name.to_string(),
2✔
19
                shape,
2✔
20
            });
2✔
21
        }
36✔
22
    }
×
23
    Ok(())
36✔
24
}
38✔
25

26
// Find a positional field
27
pub(crate) fn find_positional_field<'facet>(
9✔
28
    shape: &'static Shape,
9✔
29
    wip: &Partial<'facet>,
9✔
30
) -> Result<&'static str, DeserErrorKind> {
9✔
31
    if let Type::User(UserType::Struct(st)) = &shape.ty {
9✔
32
        for (idx, field) in st.fields.iter().enumerate() {
11✔
33
            for attr in field.attributes.iter() {
12✔
34
                let FieldAttribute::Arbitrary(a) = attr;
12✔
35
                if a.contains("positional") {
12✔
36
                    // Check if this field is already set
37
                    let is_set = wip.is_field_set(idx).unwrap_or(false);
7✔
38
                    if !is_set {
7✔
39
                        return Ok(field.name);
6✔
40
                    }
1✔
41
                }
5✔
42
            }
43
        }
44
    }
×
45
    Err(DeserErrorKind::UnknownField {
3✔
46
        field_name: "positional argument".to_string(),
3✔
47
        shape,
3✔
48
    })
3✔
49
}
9✔
50

51
// Find an unset boolean field for implicit false handling
52
pub(crate) fn find_unset_bool_field<'facet>(
25✔
53
    shape: &'static Shape,
25✔
54
    wip: &Partial<'facet>,
25✔
55
) -> Option<&'static str> {
25✔
56
    if let Type::User(UserType::Struct(st)) = &shape.ty {
25✔
57
        for (idx, field) in st.fields.iter().enumerate() {
52✔
58
            if !wip.is_field_set(idx).unwrap_or(false) && field.shape().is_type::<bool>() {
52✔
59
                return Some(field.name);
1✔
60
            }
51✔
61
        }
62
    }
×
63
    None
24✔
64
}
25✔
65

66
pub(crate) fn handle_unset_bool_field_error<'shape>(
25✔
67
    field_name_opt: Option<&'shape str>,
25✔
68
    span: Span<Raw>,
25✔
69
) -> Result<Spanned<Outcome<'shape>, Raw>, Spanned<DeserErrorKind, Raw>> {
25✔
70
    Ok(Spanned {
71
        node: match field_name_opt {
25✔
72
            Some(field_name) => Outcome::Scalar(Scalar::String(Cow::Borrowed(field_name))),
1✔
73
            None => Outcome::ObjectEnded,
24✔
74
        },
75
        span,
25✔
76
    })
77
}
25✔
78

79
pub(crate) fn find_field_by_short_flag<'shape>(
19✔
80
    key: &str,
19✔
81
    shape: &'static Shape,
19✔
82
) -> Result<&'shape str, DeserErrorKind> {
19✔
83
    match &shape.ty {
19✔
84
        Type::User(UserType::Struct(st)) => st
19✔
85
            .fields
19✔
86
            .iter()
19✔
87
            .find(|field| {
34✔
88
                field.attributes.iter().any(|attr| {
55✔
89
                    matches!(attr, FieldAttribute::Arbitrary(a) if a.contains("short") &&
55✔
90
                            (a.contains(key) || (key.len() == 1 && field.name == key)))
26✔
91
                })
55✔
92
            })
34✔
93
            .map(|field| field.name)
19✔
94
            .ok_or_else(|| DeserErrorKind::UnknownField {
19✔
95
                field_name: key.to_string(),
×
96
                shape,
×
97
            }),
×
98
        _ => Err(DeserErrorKind::UnsupportedType {
×
99
            got: shape,
×
100
            wanted: "struct",
×
101
        }),
×
102
    }
103
}
19✔
104

105
// Create a missing value error
106
pub(crate) fn create_missing_value_error(field: &str) -> DeserErrorKind {
4✔
107
    DeserErrorKind::MissingValue {
4✔
108
        expected: "argument value",
4✔
109
        field: field.to_string(),
4✔
110
    }
4✔
111
}
4✔
112

113
// Handle boolean value parsing
114
pub(crate) fn handle_bool_value(args_available: bool) -> Result<Scalar<'static>, DeserErrorKind> {
6✔
115
    Ok(Scalar::Bool(args_available))
6✔
116
}
6✔
117

118
// Check if a value is available and valid (not a flag)
119
pub(crate) fn validate_value_available<'input>(
46✔
120
    arg_idx: usize,
46✔
121
    args: &[&'input str],
46✔
122
) -> Result<&'input str, DeserErrorKind> {
46✔
123
    if arg_idx >= args.len() {
46✔
124
        return Err(create_missing_value_error(args[arg_idx.saturating_sub(1)]));
2✔
125
    }
44✔
126

127
    let arg = args[arg_idx];
44✔
128
    if arg.starts_with('-') {
44✔
129
        return Err(create_missing_value_error(args[arg_idx.saturating_sub(1)]));
2✔
130
    }
42✔
131

132
    Ok(arg)
42✔
133
}
46✔
134

135
// Check if a list has reached its end
136
pub(crate) fn is_list_ended(arg_idx: usize, args: &[&str]) -> bool {
×
137
    arg_idx >= args.len() || args[arg_idx].starts_with('-')
×
138
}
×
139

140
// Validate a struct type and return appropriate error if it's not a struct
141
pub(crate) fn validate_struct_type(shape: &'static Shape) -> Result<(), DeserErrorKind> {
42✔
142
    if !matches!(shape.ty, Type::User(UserType::Struct(_))) {
42✔
143
        Err(DeserErrorKind::UnsupportedType {
1✔
144
            got: shape,
1✔
145
            wanted: "struct",
1✔
146
        })
1✔
147
    } else {
148
        Ok(())
41✔
149
    }
150
}
42✔
151

NEW
152
pub(crate) fn create_unknown_field_error(
×
153
    field_name: &str,
×
NEW
154
    shape: &'static Shape,
×
NEW
155
) -> DeserErrorKind {
×
156
    DeserErrorKind::UnknownField {
×
157
        field_name: field_name.to_string(),
×
158
        shape,
×
159
    }
×
160
}
×
161

162
/// Create subspans by splitting at all occurrences of a delimiter
163
pub(crate) fn create_delimited_subspans(
10✔
164
    arg: &str,
10✔
165
    delimiter: char,
10✔
166
    meta: Option<SubspanMeta>,
10✔
167
) -> Vec<Subspan> {
10✔
168
    // Find all positions of the delimiter
169
    let positions: Vec<usize> = arg.match_indices(delimiter).map(|(idx, _)| idx).collect();
10✔
170

171
    // Create ranges between delimiters
172
    let ranges = {
10✔
173
        let mut ranges = Vec::with_capacity(positions.len() + 1);
10✔
174

175
        // First range: from start to first delimiter (or end if no delimiters)
176
        let first_end = positions.first().copied().unwrap_or(arg.len());
10✔
177
        ranges.push(0..first_end);
10✔
178

179
        // Middle ranges: between consecutive delimiters
180
        for window in positions.windows(2) {
10✔
181
            let start = window[0] + delimiter.len_utf8();
×
182
            let end = window[1];
×
183
            ranges.push(start..end);
×
184
        }
×
185

186
        // Last range: from last delimiter to end (if there were any delimiters)
187
        if let Some(&last_pos) = positions.last() {
10✔
188
            ranges.push((last_pos + delimiter.len_utf8())..arg.len());
10✔
189
        }
10✔
190

191
        ranges
10✔
192
    };
193

194
    // Map ranges to subspans
195
    ranges
10✔
196
        .into_iter()
10✔
197
        .map(|range| Subspan {
10✔
198
            offset: range.start,
20✔
199
            len: range.end - range.start,
20✔
200
            meta,
20✔
201
        })
20✔
202
        .collect()
10✔
203
}
10✔
204

205
/// Create key-value subspans from an argument with an equals sign
206
pub(crate) fn create_key_value_subspans(arg: &str) -> Option<Vec<Subspan>> {
10✔
207
    if arg.contains('=') {
10✔
208
        Some(create_delimited_subspans(
10✔
209
            arg,
10✔
210
            '=',
10✔
211
            Some(SubspanMeta::KeyValue),
10✔
212
        ))
10✔
213
    } else {
214
        None
×
215
    }
216
}
10✔
217

218
#[allow(unused)]
219
/// Create comma-separated value subspans
220
pub(crate) fn create_csv_subspans(arg: &str) -> Vec<Subspan> {
×
221
    create_delimited_subspans(arg, ',', Some(SubspanMeta::Delimiter(',')))
×
222
}
×
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