#
bitflags/
parser.rs

    1/*!
    2Parsing flags from text.
    3
    4Format 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
    12As an example, this is how `Flags::A | Flags::B | 0x0c` can be represented as text:
    13
    14```text
    15A | B | 0x0c
    16```
    17
    18Alternatively, it could be represented without whitespace:
    19
    20```text
    21A|B|0x0C
    22```
    23
    24Note that identifiers are *case-sensitive*, so the following is *not equivalent*:
    25
    26```text
    27a|b|0x0C
    28```
    29*/
    30
    31#![allow(clippy::let_unit_value)]
    32
    33use core::fmt::{self, Write};
    34
    35use crate::{Bits, Flags};
    36
    37/**
    38Write a flags value as text.
    39
    40Any 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
    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")]
    81pub(crate) struct AsDisplay<'a, B>(pub(crate) &'a B);
    82
    83#[cfg(feature = "serde")]
    84impl<'a, B: Flags> fmt::Display for AsDisplay<'a, B>
    85where
    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/**
    94Parse a flags value from text.
    95
    96This 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
    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/**
    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
    144    B::Bits: WriteHex,
    145{
    146    to_writer(&B::from_bits_truncate(flags.bits()), writer)
    147}
    148
    149/**
    150Parse a flags value from text.
    151
    152This 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
    157    B::Bits: ParseHex,
    158{
    159    Ok(B::from_bits_truncate(from_str::<B>(input)?.bits()))
    160}
    161
    162/**
    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
    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/**
    184Parse a flags value from text.
    185
    186This 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
    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/**
    223Encode a value as a hex string.
    224
    225Implementors of this trait should not write the `0x` prefix.
    226*/
    227pub trait WriteHex {
    228    /// Write the value as hex.
    229    fn write_hex<W: fmt::Write>(&self, writer: W) -> fmt::Result;
    230}
    231
    232/**
    233Parse a value from a hex string.
    234*/
    235pub 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)]
    244pub struct ParseError(ParseErrorKind);
    245
    246#[derive(Debug)]
    247#[allow(clippy::enum_variant_names)]
    248enum 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
    264impl ParseError {
    265    /// An invalid hex flag was encountered.
    266    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.
    280    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.
    294    pub const fn empty_flag() -> Self {
    295        ParseError(ParseErrorKind::EmptyFlag)
    296    }
    297}
    298
    299impl fmt::Display for ParseError {
    300    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")]
    332impl std::error::Error for ParseError {}