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