1 use core::fmt;
2
3 use crate::{
4 Buffer, ParseError,
5 err::{perr, ParseErrorKind::*},
6 escape::unescape,
7 };
8
9
10 /// A (single) byte literal, e.g. `b'k'` or `b'!'`.
11 ///
12 /// See [the reference][ref] for more information.
13 ///
14 /// [ref]: https://doc.rust-lang.org/reference/tokens.html#byte-literals
15 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
16 pub struct ByteLit<B: Buffer> {
17 raw: B,
18 value: u8,
19 }
20
21 impl<B: Buffer> ByteLit<B> {
22 /// Parses the input as a byte literal. Returns an error if the input is
23 /// invalid or represents a different kind of literal.
parse(input: B) -> Result<Self, ParseError>24 pub fn parse(input: B) -> Result<Self, ParseError> {
25 if input.is_empty() {
26 return Err(perr(None, Empty));
27 }
28 if !input.starts_with("b'") {
29 return Err(perr(None, InvalidByteLiteralStart));
30 }
31
32 let value = parse_impl(&input)?;
33 Ok(Self { raw: input, value })
34 }
35
36 /// Returns the byte value that this literal represents.
value(&self) -> u837 pub fn value(&self) -> u8 {
38 self.value
39 }
40
41 /// Returns the raw input that was passed to `parse`.
raw_input(&self) -> &str42 pub fn raw_input(&self) -> &str {
43 &self.raw
44 }
45
46 /// Returns the raw input that was passed to `parse`, potentially owned.
into_raw_input(self) -> B47 pub fn into_raw_input(self) -> B {
48 self.raw
49 }
50
51 }
52
53 impl ByteLit<&str> {
54 /// Makes a copy of the underlying buffer and returns the owned version of
55 /// `Self`.
to_owned(&self) -> ByteLit<String>56 pub fn to_owned(&self) -> ByteLit<String> {
57 ByteLit {
58 raw: self.raw.to_owned(),
59 value: self.value,
60 }
61 }
62 }
63
64 impl<B: Buffer> fmt::Display for ByteLit<B> {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result65 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
66 f.pad(&self.raw)
67 }
68 }
69
70 /// Precondition: must start with `b'`.
71 #[inline(never)]
parse_impl(input: &str) -> Result<u8, ParseError>72 pub(crate) fn parse_impl(input: &str) -> Result<u8, ParseError> {
73 if input.len() == 2 {
74 return Err(perr(None, UnterminatedByteLiteral));
75 }
76 if *input.as_bytes().last().unwrap() != b'\'' {
77 return Err(perr(None, UnterminatedByteLiteral));
78 }
79
80 let inner = &input[2..input.len() - 1];
81 let first = inner.as_bytes().get(0).ok_or(perr(None, EmptyByteLiteral))?;
82 let (c, len) = match first {
83 b'\'' => return Err(perr(2, UnescapedSingleQuote)),
84 b'\n' | b'\t' | b'\r'
85 => return Err(perr(2, UnescapedSpecialWhitespace)),
86
87 b'\\' => unescape::<u8>(inner, 2)?,
88 other if other.is_ascii() => (*other, 1),
89 _ => return Err(perr(2, NonAsciiInByteLiteral)),
90 };
91 let rest = &inner[len..];
92
93 if !rest.is_empty() {
94 return Err(perr(len + 2..input.len() - 1, OverlongByteLiteral));
95 }
96
97 Ok(c)
98 }
99
100 #[cfg(test)]
101 mod tests;
102