diff options
Diffstat (limited to 'vendor/bitflags/src/parser.rs')
-rw-r--r-- | vendor/bitflags/src/parser.rs | 247 |
1 files changed, 247 insertions, 0 deletions
diff --git a/vendor/bitflags/src/parser.rs b/vendor/bitflags/src/parser.rs new file mode 100644 index 0000000..130dc2e --- /dev/null +++ b/vendor/bitflags/src/parser.rs @@ -0,0 +1,247 @@ +/*! +Parsing flags from text. + +Format and parse a flags value as text using the following grammar: + +- _Flags:_ (_Whitespace_ _Flag_ _Whitespace_)`|`* +- _Flag:_ _Name_ | _Hex Number_ +- _Name:_ The name of any defined flag +- _Hex Number_: `0x`([0-9a-fA-F])* +- _Whitespace_: (\s)* + +As an example, this is how `Flags::A | Flags::B | 0x0c` can be represented as text: + +```text +A | B | 0x0c +``` + +Alternatively, it could be represented without whitespace: + +```text +A|B|0x0C +``` + +Note that identifiers are *case-sensitive*, so the following is *not equivalent*: + +```text +a|b|0x0C +``` +*/ + +#![allow(clippy::let_unit_value)] + +use core::fmt::{self, Write}; + +use crate::{Bits, Flags}; + +/** +Write a flags value as text. + +Any bits that aren't part of a contained flag will be formatted as a hex number. +*/ +pub fn to_writer<B: Flags>(flags: &B, mut writer: impl Write) -> Result<(), fmt::Error> +where + B::Bits: WriteHex, +{ + // A formatter for bitflags that produces text output like: + // + // A | B | 0xf6 + // + // The names of set flags are written in a bar-separated-format, + // followed by a hex number of any remaining bits that are set + // but don't correspond to any flags. + + // Iterate over known flag values + let mut first = true; + let mut iter = flags.iter_names(); + for (name, _) in &mut iter { + if !first { + writer.write_str(" | ")?; + } + + first = false; + writer.write_str(name)?; + } + + // Append any extra bits that correspond to flags to the end of the format + let remaining = iter.remaining().bits(); + if remaining != B::Bits::EMPTY { + if !first { + writer.write_str(" | ")?; + } + + writer.write_str("0x")?; + remaining.write_hex(writer)?; + } + + fmt::Result::Ok(()) +} + +pub(crate) struct AsDisplay<'a, B>(pub(crate) &'a B); + +impl<'a, B: Flags> fmt::Display for AsDisplay<'a, B> +where + B::Bits: WriteHex, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + to_writer(self.0, f) + } +} + +/** +Parse a flags value from text. + +This function will fail on any names that don't correspond to defined flags. +Unknown bits will be retained. +*/ +pub fn from_str<B: Flags>(input: &str) -> Result<B, ParseError> +where + B::Bits: ParseHex, +{ + let mut parsed_flags = B::empty(); + + // If the input is empty then return an empty set of flags + if input.trim().is_empty() { + return Ok(parsed_flags); + } + + for flag in input.split('|') { + let flag = flag.trim(); + + // If the flag is empty then we've got missing input + if flag.is_empty() { + return Err(ParseError::empty_flag()); + } + + // If the flag starts with `0x` then it's a hex number + // Parse it directly to the underlying bits type + let parsed_flag = if let Some(flag) = flag.strip_prefix("0x") { + let bits = + <B::Bits>::parse_hex(flag).map_err(|_| ParseError::invalid_hex_flag(flag))?; + + B::from_bits_retain(bits) + } + // Otherwise the flag is a name + // The generated flags type will determine whether + // or not it's a valid identifier + else { + B::from_name(flag).ok_or_else(|| ParseError::invalid_named_flag(flag))? + }; + + parsed_flags.insert(parsed_flag); + } + + Ok(parsed_flags) +} + +/** +Encode a value as a hex string. + +Implementors of this trait should not write the `0x` prefix. +*/ +pub trait WriteHex { + /// Write the value as hex. + fn write_hex<W: fmt::Write>(&self, writer: W) -> fmt::Result; +} + +/** +Parse a value from a hex string. +*/ +pub trait ParseHex { + /// Parse the value from hex. + fn parse_hex(input: &str) -> Result<Self, ParseError> + where + Self: Sized; +} + +/// An error encountered while parsing flags from text. +#[derive(Debug)] +pub struct ParseError(ParseErrorKind); + +#[derive(Debug)] +#[allow(clippy::enum_variant_names)] +enum ParseErrorKind { + EmptyFlag, + InvalidNamedFlag { + #[cfg(not(feature = "std"))] + got: (), + #[cfg(feature = "std")] + got: String, + }, + InvalidHexFlag { + #[cfg(not(feature = "std"))] + got: (), + #[cfg(feature = "std")] + got: String, + }, +} + +impl ParseError { + /// An invalid hex flag was encountered. + pub fn invalid_hex_flag(flag: impl fmt::Display) -> Self { + let _flag = flag; + + let got = { + #[cfg(feature = "std")] + { + _flag.to_string() + } + }; + + ParseError(ParseErrorKind::InvalidHexFlag { got }) + } + + /// A named flag that doesn't correspond to any on the flags type was encountered. + pub fn invalid_named_flag(flag: impl fmt::Display) -> Self { + let _flag = flag; + + let got = { + #[cfg(feature = "std")] + { + _flag.to_string() + } + }; + + ParseError(ParseErrorKind::InvalidNamedFlag { got }) + } + + /// A hex or named flag wasn't found between separators. + pub const fn empty_flag() -> Self { + ParseError(ParseErrorKind::EmptyFlag) + } +} + +impl fmt::Display for ParseError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match &self.0 { + ParseErrorKind::InvalidNamedFlag { got } => { + let _got = got; + + write!(f, "unrecognized named flag")?; + + #[cfg(feature = "std")] + { + write!(f, " `{}`", _got)?; + } + } + ParseErrorKind::InvalidHexFlag { got } => { + let _got = got; + + write!(f, "invalid hex flag")?; + + #[cfg(feature = "std")] + { + write!(f, " `{}`", _got)?; + } + } + ParseErrorKind::EmptyFlag => { + write!(f, "encountered empty flag")?; + } + } + + Ok(()) + } +} + +#[cfg(feature = "std")] +impl std::error::Error for ParseError {} |