1/*!
2Parsing flags from text.
34Format and parse a flags value as text using the following grammar:
56- _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)*
1112As an example, this is how `Flags::A | Flags::B | 0x0c` can be represented as text:
1314```text
15A | B | 0x0c
16```
1718Alternatively, it could be represented without whitespace:
1920```text
21A|B|0x0C
22```
2324Note that identifiers are *case-sensitive*, so the following is *not equivalent*:
2526```text
27a|b|0x0C
28```
29*/
3031#![allow(clippy::let_unit_value)]
3233use core::fmt::{self, Write};
3435use crate::{Bits, Flags};
3637/**
38Write a flags value as text.
3940Any bits that aren't part of a contained flag will be formatted as a hex number.
41*/
42pub fn to_writer<B: Flags>(flags: &B, mut writer: impl Write) -> Result<(), fmt::Error>
43where
44B::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.
5354 // Iterate over known flag values
55let mut first = true;
56let mut iter = flags.iter_names();
57for (name, _) in &mut iter {
58if !first {
59 writer.write_str(" | ")?;
60 }
6162 first = false;
63 writer.write_str(name)?;
64 }
6566// Append any extra bits that correspond to flags to the end of the format
67let remaining = iter.remaining().bits();
68if remaining != B::Bits::EMPTY {
69if !first {
70 writer.write_str(" | ")?;
71 }
7273 writer.write_str("0x")?;
74 remaining.write_hex(writer)?;
75 }
7677 fmt::Result::Ok(())
78}
7980#[cfg(feature = "serde")]
81pub(crate) struct AsDisplay<'a, B>(pub(crate) &'a B);
8283#[cfg(feature = "serde")]
84impl<'a, B: Flags> fmt::Display for AsDisplay<'a, B>
85where
86B::Bits: WriteHex,
87{
88fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
89 to_writer(self.0, f)
90 }
91}
9293/**
94Parse a flags value from text.
9596This function will fail on any names that don't correspond to defined flags.
97Unknown bits will be retained.
98*/
99pub fn from_str<B: Flags>(input: &str) -> Result<B, ParseError>
100where
101B::Bits: ParseHex,
102{
103let mut parsed_flags = B::empty();
104105// If the input is empty then return an empty set of flags
106if input.trim().is_empty() {
107return Ok(parsed_flags);
108 }
109110for flag in input.split('|') {
111let flag = flag.trim();
112113// If the flag is empty then we've got missing input
114if flag.is_empty() {
115return Err(ParseError::empty_flag());
116 }
117118// If the flag starts with `0x` then it's a hex number
119 // Parse it directly to the underlying bits type
120let parsed_flag = if let Some(flag) = flag.strip_prefix("0x") {
121let bits =
122 <B::Bits>::parse_hex(flag).map_err(|_| ParseError::invalid_hex_flag(flag))?;
123124 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
129else {
130 B::from_name(flag).ok_or_else(|| ParseError::invalid_named_flag(flag))?
131};
132133 parsed_flags.insert(parsed_flag);
134 }
135136Ok(parsed_flags)
137}
138139/**
140Write a flags value as text, ignoring any unknown bits.
141*/
142pub fn to_writer_truncate<B: Flags>(flags: &B, writer: impl Write) -> Result<(), fmt::Error>
143where
144B::Bits: WriteHex,
145{
146 to_writer(&B::from_bits_truncate(flags.bits()), writer)
147}
148149/**
150Parse a flags value from text.
151152This function will fail on any names that don't correspond to defined flags.
153Unknown bits will be ignored.
154*/
155pub fn from_str_truncate<B: Flags>(input: &str) -> Result<B, ParseError>
156where
157B::Bits: ParseHex,
158{
159Ok(B::from_bits_truncate(from_str::<B>(input)?.bits()))
160}
161162/**
163Write only the contained, defined, named flags in a flags value as text.
164*/
165pub 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
168169let mut first = true;
170let mut iter = flags.iter_names();
171for (name, _) in &mut iter {
172if !first {
173 writer.write_str(" | ")?;
174 }
175176 first = false;
177 writer.write_str(name)?;
178 }
179180 fmt::Result::Ok(())
181}
182183/**
184Parse a flags value from text.
185186This function will fail on any names that don't correspond to defined flags.
187This function will fail to parse hex values.
188*/
189pub 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
192193let mut parsed_flags = B::empty();
194195// If the input is empty then return an empty set of flags
196if input.trim().is_empty() {
197return Ok(parsed_flags);
198 }
199200for flag in input.split('|') {
201let flag = flag.trim();
202203// If the flag is empty then we've got missing input
204if flag.is_empty() {
205return Err(ParseError::empty_flag());
206 }
207208// If the flag starts with `0x` then it's a hex number
209 // These aren't supported in the strict parser
210if flag.starts_with("0x") {
211return Err(ParseError::invalid_hex_flag("unsupported hex flag value"));
212 }
213214let parsed_flag = B::from_name(flag).ok_or_else(|| ParseError::invalid_named_flag(flag))?;
215216 parsed_flags.insert(parsed_flag);
217 }
218219Ok(parsed_flags)
220}
221222/**
223Encode a value as a hex string.
224225Implementors of this trait should not write the `0x` prefix.
226*/
227pub trait WriteHex {
228/// Write the value as hex.
229fn write_hex<W: fmt::Write>(&self, writer: W) -> fmt::Result;
230}
231232/**
233Parse a value from a hex string.
234*/
235pub trait ParseHex {
236/// Parse the value from hex.
237fn parse_hex(input: &str) -> Result<Self, ParseError>
238where
239Self: Sized;
240}
241242/// An error encountered while parsing flags from text.
243#[derive(Debug)]
244pub struct ParseError(ParseErrorKind);
245246#[derive(Debug)]
247#[allow(clippy::enum_variant_names)]
248enum ParseErrorKind {
249 EmptyFlag,
250 InvalidNamedFlag {
251#[cfg(not(feature = "std"))]
252got: (),
253#[cfg(feature = "std")]
254got: String,
255 },
256 InvalidHexFlag {
257#[cfg(not(feature = "std"))]
258got: (),
259#[cfg(feature = "std")]
260got: String,
261 },
262}
263264impl ParseError {
265/// An invalid hex flag was encountered.
266pub fn invalid_hex_flag(flag: impl fmt::Display) -> Self {
267let _flag = flag;
268269let got = {
270#[cfg(feature = "std")]
271{
272 _flag.to_string()
273 }
274 };
275276 ParseError(ParseErrorKind::InvalidHexFlag { got })
277 }
278279/// A named flag that doesn't correspond to any on the flags type was encountered.
280pub fn invalid_named_flag(flag: impl fmt::Display) -> Self {
281let _flag = flag;
282283let got = {
284#[cfg(feature = "std")]
285{
286 _flag.to_string()
287 }
288 };
289290 ParseError(ParseErrorKind::InvalidNamedFlag { got })
291 }
292293/// A hex or named flag wasn't found between separators.
294pub const fn empty_flag() -> Self {
295 ParseError(ParseErrorKind::EmptyFlag)
296 }
297}
298299impl fmt::Display for ParseError {
300fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
301match &self.0 {
302 ParseErrorKind::InvalidNamedFlag { got } => {
303let _got = got;
304305write!(f, "unrecognized named flag")?;
306307#[cfg(feature = "std")]
308{
309write!(f, " `{}`", _got)?;
310 }
311 }
312 ParseErrorKind::InvalidHexFlag { got } => {
313let _got = got;
314315write!(f, "invalid hex flag")?;
316317#[cfg(feature = "std")]
318{
319write!(f, " `{}`", _got)?;
320 }
321 }
322 ParseErrorKind::EmptyFlag => {
323write!(f, "encountered empty flag")?;
324 }
325 }
326327Ok(())
328 }
329}
330331#[cfg(feature = "std")]
332impl std::error::Error for ParseError {}