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 */
to_writer<B: Flags>(flags: &B, mut writer: impl Write) -> Result<(), fmt::Error> where B::Bits: WriteHex,42 pub fn to_writer<B: Flags>(flags: &B, mut writer: impl Write) -> Result<(), fmt::Error>
43 where
44 B::Bits: WriteHex,
45 {
46 // A formatter for bitflags that produces text output like:
47 //
48 // A | B | 0xf6
49 //
50 // The names of set flags are written in a bar-separated-format,
51 // followed by a hex number of any remaining bits that are set
52 // but don't correspond to any flags.
53
54 // Iterate over known flag values
55 let mut first = true;
56 let mut iter = flags.iter_names();
57 for (name, _) in &mut iter {
58 if !first {
59 writer.write_str(" | ")?;
60 }
61
62 first = false;
63 writer.write_str(name)?;
64 }
65
66 // Append any extra bits that correspond to flags to the end of the format
67 let remaining = iter.remaining().bits();
68 if remaining != B::Bits::EMPTY {
69 if !first {
70 writer.write_str(" | ")?;
71 }
72
73 writer.write_str("0x")?;
74 remaining.write_hex(writer)?;
75 }
76
77 fmt::Result::Ok(())
78 }
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 {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result88 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 */
from_str<B: Flags>(input: &str) -> Result<B, ParseError> where B::Bits: ParseHex,99 pub fn from_str<B: Flags>(input: &str) -> Result<B, ParseError>
100 where
101 B::Bits: ParseHex,
102 {
103 let mut parsed_flags = B::empty();
104
105 // If the input is empty then return an empty set of flags
106 if input.trim().is_empty() {
107 return Ok(parsed_flags);
108 }
109
110 for flag in input.split('|') {
111 let flag = flag.trim();
112
113 // If the flag is empty then we've got missing input
114 if flag.is_empty() {
115 return Err(ParseError::empty_flag());
116 }
117
118 // If the flag starts with `0x` then it's a hex number
119 // Parse it directly to the underlying bits type
120 let parsed_flag = if let Some(flag) = flag.strip_prefix("0x") {
121 let bits =
122 <B::Bits>::parse_hex(flag).map_err(|_| ParseError::invalid_hex_flag(flag))?;
123
124 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 B::from_name(flag).ok_or_else(|| ParseError::invalid_named_flag(flag))?
131 };
132
133 parsed_flags.insert(parsed_flag);
134 }
135
136 Ok(parsed_flags)
137 }
138
139 /**
140 Write a flags value as text, ignoring any unknown bits.
141 */
to_writer_truncate<B: Flags>(flags: &B, writer: impl Write) -> Result<(), fmt::Error> where B::Bits: WriteHex,142 pub fn to_writer_truncate<B: Flags>(flags: &B, writer: impl Write) -> Result<(), fmt::Error>
143 where
144 B::Bits: WriteHex,
145 {
146 to_writer(&B::from_bits_truncate(flags.bits()), writer)
147 }
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 */
from_str_truncate<B: Flags>(input: &str) -> Result<B, ParseError> where B::Bits: ParseHex,155 pub fn from_str_truncate<B: Flags>(input: &str) -> Result<B, ParseError>
156 where
157 B::Bits: ParseHex,
158 {
159 Ok(B::from_bits_truncate(from_str::<B>(input)?.bits()))
160 }
161
162 /**
163 Write only the contained, defined, named flags in a flags value as text.
164 */
to_writer_strict<B: Flags>(flags: &B, mut writer: impl Write) -> Result<(), fmt::Error>165 pub fn to_writer_strict<B: Flags>(flags: &B, mut writer: impl Write) -> Result<(), fmt::Error> {
166 // This is a simplified version of `to_writer` that ignores
167 // any bits not corresponding to a named flag
168
169 let mut first = true;
170 let mut iter = flags.iter_names();
171 for (name, _) in &mut iter {
172 if !first {
173 writer.write_str(" | ")?;
174 }
175
176 first = false;
177 writer.write_str(name)?;
178 }
179
180 fmt::Result::Ok(())
181 }
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 */
from_str_strict<B: Flags>(input: &str) -> Result<B, ParseError>189 pub fn from_str_strict<B: Flags>(input: &str) -> Result<B, ParseError> {
190 // This is a simplified version of `from_str` that ignores
191 // any bits not corresponding to a named flag
192
193 let mut parsed_flags = B::empty();
194
195 // If the input is empty then return an empty set of flags
196 if input.trim().is_empty() {
197 return Ok(parsed_flags);
198 }
199
200 for flag in input.split('|') {
201 let flag = flag.trim();
202
203 // If the flag is empty then we've got missing input
204 if flag.is_empty() {
205 return Err(ParseError::empty_flag());
206 }
207
208 // If the flag starts with `0x` then it's a hex number
209 // These aren't supported in the strict parser
210 if flag.starts_with("0x") {
211 return Err(ParseError::invalid_hex_flag("unsupported hex flag value"));
212 }
213
214 let parsed_flag = B::from_name(flag).ok_or_else(|| ParseError::invalid_named_flag(flag))?;
215
216 parsed_flags.insert(parsed_flag);
217 }
218
219 Ok(parsed_flags)
220 }
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.
write_hex<W: fmt::Write>(&self, writer: W) -> fmt::Result229 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.
parse_hex(input: &str) -> Result<Self, ParseError> where Self: Sized237 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.
invalid_hex_flag(flag: impl fmt::Display) -> Self266 pub fn invalid_hex_flag(flag: impl fmt::Display) -> Self {
267 let _flag = flag;
268
269 let got = {
270 #[cfg(feature = "std")]
271 {
272 _flag.to_string()
273 }
274 };
275
276 ParseError(ParseErrorKind::InvalidHexFlag { got })
277 }
278
279 /// A named flag that doesn't correspond to any on the flags type was encountered.
invalid_named_flag(flag: impl fmt::Display) -> Self280 pub fn invalid_named_flag(flag: impl fmt::Display) -> Self {
281 let _flag = flag;
282
283 let got = {
284 #[cfg(feature = "std")]
285 {
286 _flag.to_string()
287 }
288 };
289
290 ParseError(ParseErrorKind::InvalidNamedFlag { got })
291 }
292
293 /// A hex or named flag wasn't found between separators.
empty_flag() -> Self294 pub const fn empty_flag() -> Self {
295 ParseError(ParseErrorKind::EmptyFlag)
296 }
297 }
298
299 impl fmt::Display for ParseError {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result300 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
301 match &self.0 {
302 ParseErrorKind::InvalidNamedFlag { got } => {
303 let _got = got;
304
305 write!(f, "unrecognized named flag")?;
306
307 #[cfg(feature = "std")]
308 {
309 write!(f, " `{}`", _got)?;
310 }
311 }
312 ParseErrorKind::InvalidHexFlag { got } => {
313 let _got = got;
314
315 write!(f, "invalid hex flag")?;
316
317 #[cfg(feature = "std")]
318 {
319 write!(f, " `{}`", _got)?;
320 }
321 }
322 ParseErrorKind::EmptyFlag => {
323 write!(f, "encountered empty flag")?;
324 }
325 }
326
327 Ok(())
328 }
329 }
330
331 #[cfg(feature = "std")]
332 impl std::error::Error for ParseError {}
333