1use core::fmt;
2
3use crate::{
4 err::{perr, ParseErrorKind::*},
5 escape::unescape,
6 parse::check_suffix,
7 Buffer, ParseError,
8};
9
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
17pub struct ByteLit<B: Buffer> {
18 raw: B,
19 start_suffix: usize,
21 value: u8,
22}
23
24impl<B: Buffer> ByteLit<B> {
25 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 pub fn value(&self) -> u8 {
41 self.value
42 }
43
44 pub fn suffix(&self) -> &str {
46 &(*self.raw)[self.start_suffix..]
47 }
48
49 pub fn raw_input(&self) -> &str {
51 &self.raw
52 }
53
54 pub fn into_raw_input(self) -> B {
56 self.raw
57 }
58}
59
60impl ByteLit<&str> {
61 pub fn to_owned(&self) -> ByteLit<String> {
64 ByteLit {
65 raw: self.raw.to_owned(),
66 start_suffix: self.start_suffix,
67 value: self.value,
68 }
69 }
70}
71
72impl<B: Buffer> fmt::Display for ByteLit<B> {
73 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
74 f.pad(&self.raw)
75 }
76}
77
78#[inline(never)]
80pub(crate) fn parse_impl(input: &str) -> Result<(u8, usize), ParseError> {
81 let input_bytes = input.as_bytes();
82 let first = input_bytes
83 .get(2)
84 .ok_or(perr(None, UnterminatedByteLiteral))?;
85 let (c, len) = match first {
86 b'\'' if input_bytes.get(3) == Some(&b'\'') => return Err(perr(2, UnescapedSingleQuote)),
87 b'\'' => return Err(perr(None, EmptyByteLiteral)),
88 b'\n' | b'\t' | b'\r' => return Err(perr(2, UnescapedSpecialWhitespace)),
89 b'\\' => {
90 let (v, len) = unescape(&input[2..], false, true, true).map_err(|e| e.offset_span(2))?;
91 (v.unwrap_byte(), len)
92 }
93 other if other.is_ascii() => (*other, 1),
94 _ => return Err(perr(2, NonAsciiInByteLiteral)),
95 };
96
97 match input[2 + len..].find('\'') {
98 Some(0) => {}
99 Some(_) => return Err(perr(None, OverlongByteLiteral)),
100 None => return Err(perr(None, UnterminatedByteLiteral)),
101 }
102
103 let start_suffix = 2 + len + 1;
104 let suffix = &input[start_suffix..];
105 check_suffix(suffix).map_err(|kind| perr(start_suffix, kind))?;
106
107 Ok((c, start_suffix))
108}
109
110#[cfg(test)]
111mod tests;