Coverage Report

Created: 2025-06-02 07:01

/rust/registry/src/index.crates.io-6f17d22bba15001f/bitflags-2.5.0/src/parser.rs
Line
Count
Source (jump to first uncovered line)
1
/*!
2
Parsing flags from text.
3
4
Format and parse a flags value as text using the following grammar:
5
6
- _Flags:_ (_Whitespace_ _Flag_ _Whitespace_)`|`*
7
- _Flag:_ _Name_ | _Hex Number_
8
- _Name:_ The name of any defined flag
9
- _Hex Number_: `0x`([0-9a-fA-F])*
10
- _Whitespace_: (\s)*
11
12
As an example, this is how `Flags::A | Flags::B | 0x0c` can be represented as text:
13
14
```text
15
A | B | 0x0c
16
```
17
18
Alternatively, it could be represented without whitespace:
19
20
```text
21
A|B|0x0C
22
```
23
24
Note that identifiers are *case-sensitive*, so the following is *not equivalent*:
25
26
```text
27
a|b|0x0C
28
```
29
*/
30
31
#![allow(clippy::let_unit_value)]
32
33
use core::fmt::{self, Write};
34
35
use crate::{Bits, Flags};
36
37
/**
38
Write a flags value as text.
39
40
Any bits that aren't part of a contained flag will be formatted as a hex number.
41
*/
42
0
pub fn to_writer<B: Flags>(flags: &B, mut writer: impl Write) -> Result<(), fmt::Error>
43
0
where
44
0
    B::Bits: WriteHex,
45
0
{
46
0
    // A formatter for bitflags that produces text output like:
47
0
    //
48
0
    // A | B | 0xf6
49
0
    //
50
0
    // The names of set flags are written in a bar-separated-format,
51
0
    // followed by a hex number of any remaining bits that are set
52
0
    // but don't correspond to any flags.
53
0
54
0
    // Iterate over known flag values
55
0
    let mut first = true;
56
0
    let mut iter = flags.iter_names();
57
0
    for (name, _) in &mut iter {
58
0
        if !first {
59
0
            writer.write_str(" | ")?;
60
0
        }
61
62
0
        first = false;
63
0
        writer.write_str(name)?;
64
    }
65
66
    // Append any extra bits that correspond to flags to the end of the format
67
0
    let remaining = iter.remaining().bits();
68
0
    if remaining != B::Bits::EMPTY {
69
0
        if !first {
70
0
            writer.write_str(" | ")?;
71
0
        }
72
73
0
        writer.write_str("0x")?;
74
0
        remaining.write_hex(writer)?;
75
0
    }
76
77
0
    fmt::Result::Ok(())
78
0
}
Unexecuted instantiation: bitflags::parser::to_writer::<rustix::fs::ioctl::IFlags, &mut core::fmt::Formatter>
Unexecuted instantiation: bitflags::parser::to_writer::<rustix::fs::statx::StatxFlags, &mut core::fmt::Formatter>
Unexecuted instantiation: bitflags::parser::to_writer::<rustix::fs::statx::StatxAttributes, &mut core::fmt::Formatter>
Unexecuted instantiation: bitflags::parser::to_writer::<rustix::fs::xattr::XattrFlags, &mut core::fmt::Formatter>
Unexecuted instantiation: bitflags::parser::to_writer::<rustix::backend::fs::types::MemfdFlags, &mut core::fmt::Formatter>
Unexecuted instantiation: bitflags::parser::to_writer::<rustix::backend::fs::types::RenameFlags, &mut core::fmt::Formatter>
Unexecuted instantiation: bitflags::parser::to_writer::<rustix::backend::fs::types::ResolveFlags, &mut core::fmt::Formatter>
Unexecuted instantiation: bitflags::parser::to_writer::<rustix::backend::fs::types::FallocateFlags, &mut core::fmt::Formatter>
Unexecuted instantiation: bitflags::parser::to_writer::<rustix::backend::fs::types::StatVfsMountFlags, &mut core::fmt::Formatter>
Unexecuted instantiation: bitflags::parser::to_writer::<rustix::backend::fs::types::Mode, &mut core::fmt::Formatter>
Unexecuted instantiation: bitflags::parser::to_writer::<rustix::backend::fs::types::Access, &mut core::fmt::Formatter>
Unexecuted instantiation: bitflags::parser::to_writer::<rustix::backend::fs::types::OFlags, &mut core::fmt::Formatter>
Unexecuted instantiation: bitflags::parser::to_writer::<rustix::backend::fs::types::AtFlags, &mut core::fmt::Formatter>
Unexecuted instantiation: bitflags::parser::to_writer::<rustix::backend::fs::types::SealFlags, &mut core::fmt::Formatter>
Unexecuted instantiation: bitflags::parser::to_writer::<rustix::backend::fs::inotify::WatchFlags, &mut core::fmt::Formatter>
Unexecuted instantiation: bitflags::parser::to_writer::<rustix::backend::fs::inotify::CreateFlags, &mut core::fmt::Formatter>
Unexecuted instantiation: bitflags::parser::to_writer::<rustix::backend::fs::inotify::ReadFlags, &mut core::fmt::Formatter>
Unexecuted instantiation: bitflags::parser::to_writer::<rustix::backend::io::types::ReadWriteFlags, &mut core::fmt::Formatter>
Unexecuted instantiation: bitflags::parser::to_writer::<rustix::backend::io::types::FdFlags, &mut core::fmt::Formatter>
Unexecuted instantiation: bitflags::parser::to_writer::<rustix::backend::io::types::DupFlags, &mut core::fmt::Formatter>
Unexecuted instantiation: bitflags::parser::to_writer::<pulldown_cmark::Options, &mut core::fmt::Formatter>
Unexecuted instantiation: bitflags::parser::to_writer::<pulldown_cmark::Options, &mut core::fmt::Formatter>
Unexecuted instantiation: bitflags::parser::to_writer::<_, _>
79
80
#[cfg(feature = "serde")]
81
pub(crate) struct AsDisplay<'a, B>(pub(crate) &'a B);
82
83
#[cfg(feature = "serde")]
84
impl<'a, B: Flags> fmt::Display for AsDisplay<'a, B>
85
where
86
    B::Bits: WriteHex,
87
{
88
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
89
        to_writer(self.0, f)
90
    }
91
}
92
93
/**
94
Parse a flags value from text.
95
96
This function will fail on any names that don't correspond to defined flags.
97
Unknown bits will be retained.
98
*/
99
0
pub fn from_str<B: Flags>(input: &str) -> Result<B, ParseError>
100
0
where
101
0
    B::Bits: ParseHex,
102
0
{
103
0
    let mut parsed_flags = B::empty();
104
0
105
0
    // If the input is empty then return an empty set of flags
106
0
    if input.trim().is_empty() {
107
0
        return Ok(parsed_flags);
108
0
    }
109
110
0
    for flag in input.split('|') {
111
0
        let flag = flag.trim();
112
0
113
0
        // If the flag is empty then we've got missing input
114
0
        if flag.is_empty() {
115
0
            return Err(ParseError::empty_flag());
116
0
        }
117
118
        // If the flag starts with `0x` then it's a hex number
119
        // Parse it directly to the underlying bits type
120
0
        let parsed_flag = if let Some(flag) = flag.strip_prefix("0x") {
121
0
            let bits =
122
0
                <B::Bits>::parse_hex(flag).map_err(|_| ParseError::invalid_hex_flag(flag))?;
Unexecuted instantiation: bitflags::parser::from_str::<rustix::fs::ioctl::IFlags>::{closure#0}
Unexecuted instantiation: bitflags::parser::from_str::<rustix::fs::statx::StatxFlags>::{closure#0}
Unexecuted instantiation: bitflags::parser::from_str::<rustix::fs::statx::StatxAttributes>::{closure#0}
Unexecuted instantiation: bitflags::parser::from_str::<rustix::fs::xattr::XattrFlags>::{closure#0}
Unexecuted instantiation: bitflags::parser::from_str::<rustix::backend::fs::types::MemfdFlags>::{closure#0}
Unexecuted instantiation: bitflags::parser::from_str::<rustix::backend::fs::types::RenameFlags>::{closure#0}
Unexecuted instantiation: bitflags::parser::from_str::<rustix::backend::fs::types::ResolveFlags>::{closure#0}
Unexecuted instantiation: bitflags::parser::from_str::<rustix::backend::fs::types::FallocateFlags>::{closure#0}
Unexecuted instantiation: bitflags::parser::from_str::<rustix::backend::fs::types::StatVfsMountFlags>::{closure#0}
Unexecuted instantiation: bitflags::parser::from_str::<rustix::backend::fs::types::Mode>::{closure#0}
Unexecuted instantiation: bitflags::parser::from_str::<rustix::backend::fs::types::Access>::{closure#0}
Unexecuted instantiation: bitflags::parser::from_str::<rustix::backend::fs::types::OFlags>::{closure#0}
Unexecuted instantiation: bitflags::parser::from_str::<rustix::backend::fs::types::AtFlags>::{closure#0}
Unexecuted instantiation: bitflags::parser::from_str::<rustix::backend::fs::types::SealFlags>::{closure#0}
Unexecuted instantiation: bitflags::parser::from_str::<rustix::backend::fs::inotify::WatchFlags>::{closure#0}
Unexecuted instantiation: bitflags::parser::from_str::<rustix::backend::fs::inotify::CreateFlags>::{closure#0}
Unexecuted instantiation: bitflags::parser::from_str::<rustix::backend::fs::inotify::ReadFlags>::{closure#0}
Unexecuted instantiation: bitflags::parser::from_str::<rustix::backend::io::types::ReadWriteFlags>::{closure#0}
Unexecuted instantiation: bitflags::parser::from_str::<rustix::backend::io::types::FdFlags>::{closure#0}
Unexecuted instantiation: bitflags::parser::from_str::<rustix::backend::io::types::DupFlags>::{closure#0}
Unexecuted instantiation: bitflags::parser::from_str::<pulldown_cmark::Options>::{closure#0}
Unexecuted instantiation: bitflags::parser::from_str::<pulldown_cmark::Options>::{closure#0}
Unexecuted instantiation: bitflags::parser::from_str::<_>::{closure#0}
123
124
0
            B::from_bits_retain(bits)
125
        }
126
        // Otherwise the flag is a name
127
        // The generated flags type will determine whether
128
        // or not it's a valid identifier
129
        else {
130
0
            B::from_name(flag).ok_or_else(|| ParseError::invalid_named_flag(flag))?
Unexecuted instantiation: bitflags::parser::from_str::<rustix::fs::ioctl::IFlags>::{closure#1}
Unexecuted instantiation: bitflags::parser::from_str::<rustix::fs::statx::StatxFlags>::{closure#1}
Unexecuted instantiation: bitflags::parser::from_str::<rustix::fs::statx::StatxAttributes>::{closure#1}
Unexecuted instantiation: bitflags::parser::from_str::<rustix::fs::xattr::XattrFlags>::{closure#1}
Unexecuted instantiation: bitflags::parser::from_str::<rustix::backend::fs::types::MemfdFlags>::{closure#1}
Unexecuted instantiation: bitflags::parser::from_str::<rustix::backend::fs::types::RenameFlags>::{closure#1}
Unexecuted instantiation: bitflags::parser::from_str::<rustix::backend::fs::types::ResolveFlags>::{closure#1}
Unexecuted instantiation: bitflags::parser::from_str::<rustix::backend::fs::types::FallocateFlags>::{closure#1}
Unexecuted instantiation: bitflags::parser::from_str::<rustix::backend::fs::types::StatVfsMountFlags>::{closure#1}
Unexecuted instantiation: bitflags::parser::from_str::<rustix::backend::fs::types::Mode>::{closure#1}
Unexecuted instantiation: bitflags::parser::from_str::<rustix::backend::fs::types::Access>::{closure#1}
Unexecuted instantiation: bitflags::parser::from_str::<rustix::backend::fs::types::OFlags>::{closure#1}
Unexecuted instantiation: bitflags::parser::from_str::<rustix::backend::fs::types::AtFlags>::{closure#1}
Unexecuted instantiation: bitflags::parser::from_str::<rustix::backend::fs::types::SealFlags>::{closure#1}
Unexecuted instantiation: bitflags::parser::from_str::<rustix::backend::fs::inotify::WatchFlags>::{closure#1}
Unexecuted instantiation: bitflags::parser::from_str::<rustix::backend::fs::inotify::CreateFlags>::{closure#1}
Unexecuted instantiation: bitflags::parser::from_str::<rustix::backend::fs::inotify::ReadFlags>::{closure#1}
Unexecuted instantiation: bitflags::parser::from_str::<rustix::backend::io::types::ReadWriteFlags>::{closure#1}
Unexecuted instantiation: bitflags::parser::from_str::<rustix::backend::io::types::FdFlags>::{closure#1}
Unexecuted instantiation: bitflags::parser::from_str::<rustix::backend::io::types::DupFlags>::{closure#1}
Unexecuted instantiation: bitflags::parser::from_str::<pulldown_cmark::Options>::{closure#1}
Unexecuted instantiation: bitflags::parser::from_str::<pulldown_cmark::Options>::{closure#1}
Unexecuted instantiation: bitflags::parser::from_str::<_>::{closure#1}
131
        };
132
133
0
        parsed_flags.insert(parsed_flag);
134
    }
135
136
0
    Ok(parsed_flags)
137
0
}
Unexecuted instantiation: bitflags::parser::from_str::<rustix::fs::ioctl::IFlags>
Unexecuted instantiation: bitflags::parser::from_str::<rustix::fs::statx::StatxFlags>
Unexecuted instantiation: bitflags::parser::from_str::<rustix::fs::statx::StatxAttributes>
Unexecuted instantiation: bitflags::parser::from_str::<rustix::fs::xattr::XattrFlags>
Unexecuted instantiation: bitflags::parser::from_str::<rustix::backend::fs::types::MemfdFlags>
Unexecuted instantiation: bitflags::parser::from_str::<rustix::backend::fs::types::RenameFlags>
Unexecuted instantiation: bitflags::parser::from_str::<rustix::backend::fs::types::ResolveFlags>
Unexecuted instantiation: bitflags::parser::from_str::<rustix::backend::fs::types::FallocateFlags>
Unexecuted instantiation: bitflags::parser::from_str::<rustix::backend::fs::types::StatVfsMountFlags>
Unexecuted instantiation: bitflags::parser::from_str::<rustix::backend::fs::types::Mode>
Unexecuted instantiation: bitflags::parser::from_str::<rustix::backend::fs::types::Access>
Unexecuted instantiation: bitflags::parser::from_str::<rustix::backend::fs::types::OFlags>
Unexecuted instantiation: bitflags::parser::from_str::<rustix::backend::fs::types::AtFlags>
Unexecuted instantiation: bitflags::parser::from_str::<rustix::backend::fs::types::SealFlags>
Unexecuted instantiation: bitflags::parser::from_str::<rustix::backend::fs::inotify::WatchFlags>
Unexecuted instantiation: bitflags::parser::from_str::<rustix::backend::fs::inotify::CreateFlags>
Unexecuted instantiation: bitflags::parser::from_str::<rustix::backend::fs::inotify::ReadFlags>
Unexecuted instantiation: bitflags::parser::from_str::<rustix::backend::io::types::ReadWriteFlags>
Unexecuted instantiation: bitflags::parser::from_str::<rustix::backend::io::types::FdFlags>
Unexecuted instantiation: bitflags::parser::from_str::<rustix::backend::io::types::DupFlags>
Unexecuted instantiation: bitflags::parser::from_str::<pulldown_cmark::Options>
Unexecuted instantiation: bitflags::parser::from_str::<pulldown_cmark::Options>
Unexecuted instantiation: bitflags::parser::from_str::<_>
138
139
/**
140
Write a flags value as text, ignoring any unknown bits.
141
*/
142
0
pub fn to_writer_truncate<B: Flags>(flags: &B, writer: impl Write) -> Result<(), fmt::Error>
143
0
where
144
0
    B::Bits: WriteHex,
145
0
{
146
0
    to_writer(&B::from_bits_truncate(flags.bits()), writer)
147
0
}
148
149
/**
150
Parse a flags value from text.
151
152
This function will fail on any names that don't correspond to defined flags.
153
Unknown bits will be ignored.
154
*/
155
0
pub fn from_str_truncate<B: Flags>(input: &str) -> Result<B, ParseError>
156
0
where
157
0
    B::Bits: ParseHex,
158
0
{
159
0
    Ok(B::from_bits_truncate(from_str::<B>(input)?.bits()))
160
0
}
161
162
/**
163
Write only the contained, defined, named flags in a flags value as text.
164
*/
165
0
pub fn to_writer_strict<B: Flags>(flags: &B, mut writer: impl Write) -> Result<(), fmt::Error> {
166
0
    // This is a simplified version of `to_writer` that ignores
167
0
    // any bits not corresponding to a named flag
168
0
169
0
    let mut first = true;
170
0
    let mut iter = flags.iter_names();
171
0
    for (name, _) in &mut iter {
172
0
        if !first {
173
0
            writer.write_str(" | ")?;
174
0
        }
175
176
0
        first = false;
177
0
        writer.write_str(name)?;
178
    }
179
180
0
    fmt::Result::Ok(())
181
0
}
182
183
/**
184
Parse a flags value from text.
185
186
This function will fail on any names that don't correspond to defined flags.
187
This function will fail to parse hex values.
188
*/
189
0
pub fn from_str_strict<B: Flags>(input: &str) -> Result<B, ParseError> {
190
0
    // This is a simplified version of `from_str` that ignores
191
0
    // any bits not corresponding to a named flag
192
0
193
0
    let mut parsed_flags = B::empty();
194
0
195
0
    // If the input is empty then return an empty set of flags
196
0
    if input.trim().is_empty() {
197
0
        return Ok(parsed_flags);
198
0
    }
199
200
0
    for flag in input.split('|') {
201
0
        let flag = flag.trim();
202
0
203
0
        // If the flag is empty then we've got missing input
204
0
        if flag.is_empty() {
205
0
            return Err(ParseError::empty_flag());
206
0
        }
207
0
208
0
        // If the flag starts with `0x` then it's a hex number
209
0
        // These aren't supported in the strict parser
210
0
        if flag.starts_with("0x") {
211
0
            return Err(ParseError::invalid_hex_flag("unsupported hex flag value"));
212
0
        }
213
214
0
        let parsed_flag = B::from_name(flag).ok_or_else(|| ParseError::invalid_named_flag(flag))?;
215
216
0
        parsed_flags.insert(parsed_flag);
217
    }
218
219
0
    Ok(parsed_flags)
220
0
}
221
222
/**
223
Encode a value as a hex string.
224
225
Implementors of this trait should not write the `0x` prefix.
226
*/
227
pub trait WriteHex {
228
    /// Write the value as hex.
229
    fn write_hex<W: fmt::Write>(&self, writer: W) -> fmt::Result;
230
}
231
232
/**
233
Parse a value from a hex string.
234
*/
235
pub trait ParseHex {
236
    /// Parse the value from hex.
237
    fn parse_hex(input: &str) -> Result<Self, ParseError>
238
    where
239
        Self: Sized;
240
}
241
242
/// An error encountered while parsing flags from text.
243
#[derive(Debug)]
244
pub struct ParseError(ParseErrorKind);
245
246
#[derive(Debug)]
247
#[allow(clippy::enum_variant_names)]
248
enum ParseErrorKind {
249
    EmptyFlag,
250
    InvalidNamedFlag {
251
        #[cfg(not(feature = "std"))]
252
        got: (),
253
        #[cfg(feature = "std")]
254
        got: String,
255
    },
256
    InvalidHexFlag {
257
        #[cfg(not(feature = "std"))]
258
        got: (),
259
        #[cfg(feature = "std")]
260
        got: String,
261
    },
262
}
263
264
impl ParseError {
265
    /// An invalid hex flag was encountered.
266
0
    pub fn invalid_hex_flag(flag: impl fmt::Display) -> Self {
267
0
        let _flag = flag;
268
0
269
0
        let got = {
270
0
            #[cfg(feature = "std")]
271
0
            {
272
0
                _flag.to_string()
273
0
            }
274
0
        };
275
0
276
0
        ParseError(ParseErrorKind::InvalidHexFlag { got })
277
0
    }
278
279
    /// A named flag that doesn't correspond to any on the flags type was encountered.
280
0
    pub fn invalid_named_flag(flag: impl fmt::Display) -> Self {
281
0
        let _flag = flag;
282
0
283
0
        let got = {
284
0
            #[cfg(feature = "std")]
285
0
            {
286
0
                _flag.to_string()
287
0
            }
288
0
        };
289
0
290
0
        ParseError(ParseErrorKind::InvalidNamedFlag { got })
291
0
    }
Unexecuted instantiation: <bitflags::parser::ParseError>::invalid_named_flag::<&str>
Unexecuted instantiation: <bitflags::parser::ParseError>::invalid_named_flag::<&str>
Unexecuted instantiation: <bitflags::parser::ParseError>::invalid_named_flag::<&str>
Unexecuted instantiation: <bitflags::parser::ParseError>::invalid_named_flag::<_>
292
293
    /// A hex or named flag wasn't found between separators.
294
0
    pub const fn empty_flag() -> Self {
295
0
        ParseError(ParseErrorKind::EmptyFlag)
296
0
    }
297
}
298
299
impl fmt::Display for ParseError {
300
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
301
0
        match &self.0 {
302
0
            ParseErrorKind::InvalidNamedFlag { got } => {
303
0
                let _got = got;
304
0
305
0
                write!(f, "unrecognized named flag")?;
306
307
                #[cfg(feature = "std")]
308
                {
309
0
                    write!(f, " `{}`", _got)?;
310
                }
311
            }
312
0
            ParseErrorKind::InvalidHexFlag { got } => {
313
0
                let _got = got;
314
0
315
0
                write!(f, "invalid hex flag")?;
316
317
                #[cfg(feature = "std")]
318
                {
319
0
                    write!(f, " `{}`", _got)?;
320
                }
321
            }
322
            ParseErrorKind::EmptyFlag => {
323
0
                write!(f, "encountered empty flag")?;
324
            }
325
        }
326
327
0
        Ok(())
328
0
    }
329
}
330
331
#[cfg(feature = "std")]
332
impl std::error::Error for ParseError {}