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}
7980pub(crate) struct AsDisplay<'a, B>(pub(crate) &'a B);
8182impl<'a, B: Flags> fmt::Display for AsDisplay<'a, B>
83where
84B::Bits: WriteHex,
85{
86fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
87 to_writer(self.0, f)
88 }
89}
9091/**
92Parse a flags value from text.
9394This function will fail on any names that don't correspond to defined flags.
95Unknown bits will be retained.
96*/
97pub fn from_str<B: Flags>(input: &str) -> Result<B, ParseError>
98where
99B::Bits: ParseHex,
100{
101let mut parsed_flags = B::empty();
102103// If the input is empty then return an empty set of flags
104if input.trim().is_empty() {
105return Ok(parsed_flags);
106 }
107108for flag in input.split('|') {
109let flag = flag.trim();
110111// If the flag is empty then we've got missing input
112if flag.is_empty() {
113return Err(ParseError::empty_flag());
114 }
115116// If the flag starts with `0x` then it's a hex number
117 // Parse it directly to the underlying bits type
118let parsed_flag = if let Some(flag) = flag.strip_prefix("0x") {
119let bits =
120 <B::Bits>::parse_hex(flag).map_err(|_| ParseError::invalid_hex_flag(flag))?;
121122 B::from_bits_retain(bits)
123 }
124// Otherwise the flag is a name
125 // The generated flags type will determine whether
126 // or not it's a valid identifier
127else {
128 B::from_name(flag).ok_or_else(|| ParseError::invalid_named_flag(flag))?
129};
130131 parsed_flags.insert(parsed_flag);
132 }
133134Ok(parsed_flags)
135}
136137/**
138Encode a value as a hex string.
139140Implementors of this trait should not write the `0x` prefix.
141*/
142pub trait WriteHex {
143/// Write the value as hex.
144fn write_hex<W: fmt::Write>(&self, writer: W) -> fmt::Result;
145}
146147/**
148Parse a value from a hex string.
149*/
150pub trait ParseHex {
151/// Parse the value from hex.
152fn parse_hex(input: &str) -> Result<Self, ParseError>
153where
154Self: Sized;
155}
156157/// An error encountered while parsing flags from text.
158#[derive(Debug)]
159pub struct ParseError(ParseErrorKind);
160161#[derive(Debug)]
162#[allow(clippy::enum_variant_names)]
163enum ParseErrorKind {
164 EmptyFlag,
165 InvalidNamedFlag {
166#[cfg(not(feature = "std"))]
167got: (),
168#[cfg(feature = "std")]
169got: String,
170 },
171 InvalidHexFlag {
172#[cfg(not(feature = "std"))]
173got: (),
174#[cfg(feature = "std")]
175got: String,
176 },
177}
178179impl ParseError {
180/// An invalid hex flag was encountered.
181pub fn invalid_hex_flag(flag: impl fmt::Display) -> Self {
182let _flag = flag;
183184let got = {
185#[cfg(feature = "std")]
186{
187 _flag.to_string()
188 }
189 };
190191 ParseError(ParseErrorKind::InvalidHexFlag { got })
192 }
193194/// A named flag that doesn't correspond to any on the flags type was encountered.
195pub fn invalid_named_flag(flag: impl fmt::Display) -> Self {
196let _flag = flag;
197198let got = {
199#[cfg(feature = "std")]
200{
201 _flag.to_string()
202 }
203 };
204205 ParseError(ParseErrorKind::InvalidNamedFlag { got })
206 }
207208/// A hex or named flag wasn't found between separators.
209pub const fn empty_flag() -> Self {
210 ParseError(ParseErrorKind::EmptyFlag)
211 }
212}
213214impl fmt::Display for ParseError {
215fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
216match &self.0 {
217 ParseErrorKind::InvalidNamedFlag { got } => {
218let _got = got;
219220write!(f, "unrecognized named flag")?;
221222#[cfg(feature = "std")]
223{
224write!(f, " `{}`", _got)?;
225 }
226 }
227 ParseErrorKind::InvalidHexFlag { got } => {
228let _got = got;
229230write!(f, "invalid hex flag")?;
231232#[cfg(feature = "std")]
233{
234write!(f, " `{}`", _got)?;
235 }
236 }
237 ParseErrorKind::EmptyFlag => {
238write!(f, "encountered empty flag")?;
239 }
240 }
241242Ok(())
243 }
244}
245246#[cfg(feature = "std")]
247impl std::error::Error for ParseError {}