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