litrs/byte/
mod.rs

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 = input.as_bytes();
83    let first = input_bytes.get(2).ok_or(perr(None, UnterminatedByteLiteral))?;
84    let (c, len) = match first {
85        b'\'' if input_bytes.get(3) == Some(&b'\'') => return Err(perr(2, UnescapedSingleQuote)),
86        b'\'' => return Err(perr(None, EmptyByteLiteral)),
87        b'\n' | b'\t' | b'\r' => return Err(perr(2, UnescapedSpecialWhitespace)),
88        b'\\' => unescape::<u8>(&input[2..], 2)?,
89        other if other.is_ascii() => (*other, 1),
90        _ => return Err(perr(2, NonAsciiInByteLiteral)),
91    };
92
93    match input[2 + len..].find('\'') {
94        Some(0) => {}
95        Some(_) => return Err(perr(None, OverlongByteLiteral)),
96        None => return Err(perr(None, UnterminatedByteLiteral)),
97    }
98
99    let start_suffix = 2 + len + 1;
100    let suffix = &input[start_suffix..];
101    check_suffix(suffix).map_err(|kind| perr(start_suffix, kind))?;
102
103    Ok((c, start_suffix))
104}
105
106#[cfg(test)]
107mod tests;