]> git.proxmox.com Git - rustc.git/blob - extra/bitflags/src/parser.rs
New upstream version 1.70.0+dfsg2
[rustc.git] / extra / bitflags / src / parser.rs
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 */
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 pub(crate) struct AsDisplay<'a, B>(pub(crate) &'a B);
81
82 impl<'a, B: Flags> fmt::Display for AsDisplay<'a, B>
83 where
84 B::Bits: WriteHex,
85 {
86 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
87 to_writer(self.0, f)
88 }
89 }
90
91 /**
92 Parse a flags value from text.
93
94 This function will fail on any names that don't correspond to defined flags.
95 Unknown bits will be retained.
96 */
97 pub fn from_str<B: Flags>(input: &str) -> Result<B, ParseError>
98 where
99 B::Bits: ParseHex,
100 {
101 let mut parsed_flags = B::empty();
102
103 // If the input is empty then return an empty set of flags
104 if input.trim().is_empty() {
105 return Ok(parsed_flags);
106 }
107
108 for flag in input.split('|') {
109 let flag = flag.trim();
110
111 // If the flag is empty then we've got missing input
112 if flag.is_empty() {
113 return Err(ParseError::empty_flag());
114 }
115
116 // If the flag starts with `0x` then it's a hex number
117 // Parse it directly to the underlying bits type
118 let parsed_flag = if let Some(flag) = flag.strip_prefix("0x") {
119 let bits =
120 <B::Bits>::parse_hex(flag).map_err(|_| ParseError::invalid_hex_flag(flag))?;
121
122 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
127 else {
128 B::from_name(flag).ok_or_else(|| ParseError::invalid_named_flag(flag))?
129 };
130
131 parsed_flags.insert(parsed_flag);
132 }
133
134 Ok(parsed_flags)
135 }
136
137 /**
138 Encode a value as a hex string.
139
140 Implementors of this trait should not write the `0x` prefix.
141 */
142 pub trait WriteHex {
143 /// Write the value as hex.
144 fn write_hex<W: fmt::Write>(&self, writer: W) -> fmt::Result;
145 }
146
147 /**
148 Parse a value from a hex string.
149 */
150 pub trait ParseHex {
151 /// Parse the value from hex.
152 fn parse_hex(input: &str) -> Result<Self, ParseError>
153 where
154 Self: Sized;
155 }
156
157 /// An error encountered while parsing flags from text.
158 #[derive(Debug)]
159 pub struct ParseError(ParseErrorKind);
160
161 #[derive(Debug)]
162 #[allow(clippy::enum_variant_names)]
163 enum ParseErrorKind {
164 EmptyFlag,
165 InvalidNamedFlag {
166 #[cfg(not(feature = "std"))]
167 got: (),
168 #[cfg(feature = "std")]
169 got: String,
170 },
171 InvalidHexFlag {
172 #[cfg(not(feature = "std"))]
173 got: (),
174 #[cfg(feature = "std")]
175 got: String,
176 },
177 }
178
179 impl ParseError {
180 /// An invalid hex flag was encountered.
181 pub fn invalid_hex_flag(flag: impl fmt::Display) -> Self {
182 let _flag = flag;
183
184 let got = {
185 #[cfg(feature = "std")]
186 {
187 _flag.to_string()
188 }
189 };
190
191 ParseError(ParseErrorKind::InvalidHexFlag { got })
192 }
193
194 /// A named flag that doesn't correspond to any on the flags type was encountered.
195 pub fn invalid_named_flag(flag: impl fmt::Display) -> Self {
196 let _flag = flag;
197
198 let got = {
199 #[cfg(feature = "std")]
200 {
201 _flag.to_string()
202 }
203 };
204
205 ParseError(ParseErrorKind::InvalidNamedFlag { got })
206 }
207
208 /// A hex or named flag wasn't found between separators.
209 pub const fn empty_flag() -> Self {
210 ParseError(ParseErrorKind::EmptyFlag)
211 }
212 }
213
214 impl fmt::Display for ParseError {
215 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
216 match &self.0 {
217 ParseErrorKind::InvalidNamedFlag { got } => {
218 let _got = got;
219
220 write!(f, "unrecognized named flag")?;
221
222 #[cfg(feature = "std")]
223 {
224 write!(f, " `{}`", _got)?;
225 }
226 }
227 ParseErrorKind::InvalidHexFlag { got } => {
228 let _got = got;
229
230 write!(f, "invalid hex flag")?;
231
232 #[cfg(feature = "std")]
233 {
234 write!(f, " `{}`", _got)?;
235 }
236 }
237 ParseErrorKind::EmptyFlag => {
238 write!(f, "encountered empty flag")?;
239 }
240 }
241
242 Ok(())
243 }
244 }
245
246 #[cfg(feature = "std")]
247 impl std::error::Error for ParseError {}