use std::{
any::TypeId,
hash::{DefaultHasher, Hash, Hasher},
};
use crate::{
cache::ParsingCache,
parser::{Parsable, Parser, Source},
result::{Error, ParseResult},
};
#[derive(Clone, PartialEq, Eq)]
pub struct Literal {
expected: LiteralData,
}
#[derive(Clone, PartialEq, Eq)]
enum LiteralData {
Owned(Box<[u8]>),
Static(&'static [u8]),
}
impl Literal {
pub fn from_bytes(bytes: &[u8]) -> Self {
Self {
expected: LiteralData::Owned(bytes.into()),
}
}
#[allow(clippy::should_implement_trait)]
pub fn from_str<S: AsRef<str>>(s: S) -> Self {
Self {
expected: LiteralData::Owned(s.as_ref().as_bytes().into()),
}
}
pub const fn from_str_const(s: &'static str) -> Self {
Self {
expected: LiteralData::Static(s.as_bytes()),
}
}
pub const fn from_bytes_const(bytes: &'static [u8]) -> Self {
Self {
expected: LiteralData::Static(bytes),
}
}
pub fn bytes(&self) -> &[u8] {
match &self.expected {
LiteralData::Owned(bytes) => bytes,
LiteralData::Static(bytes) => bytes,
}
}
pub fn len(&self) -> usize {
self.bytes().len()
}
pub fn is_empty(&self) -> bool {
self.bytes().is_empty()
}
}
impl<Ctx> Parser<Ctx> for Literal {
type Output = Box<[u8]>;
fn id(&self) -> u64 {
let mut hasher = DefaultHasher::new();
TypeId::of::<Self>().hash(&mut hasher);
self.bytes().hash(&mut hasher);
hasher.finish()
}
fn read<S>(
&self,
source: &mut Source<S>,
_cache: &mut impl ParsingCache,
_context: &mut Ctx,
) -> ParseResult<Self::Output>
where
S: Parsable,
{
if self.is_empty() {
return Ok(Box::new([]));
}
let expected_bytes = self.bytes();
match source.peek(expected_bytes.len()) {
Ok(bytes) => {
if bytes == expected_bytes {
source.advance(expected_bytes.len());
Ok(expected_bytes.into())
} else {
Err(Error::NoMatch)
}
}
Err(Error::NoMatch) => {
Err(Error::NoMatch)
}
Err(err) => Err(err),
}
}
}
impl From<&[u8]> for Literal {
fn from(bytes: &[u8]) -> Self {
Self::from_bytes(bytes)
}
}
impl From<&str> for Literal {
fn from(s: &str) -> Self {
Self::from_str(s)
}
}
impl From<String> for Literal {
fn from(s: String) -> Self {
Self::from_str(s)
}
}
impl From<Vec<u8>> for Literal {
fn from(bytes: Vec<u8>) -> Self {
Self {
expected: LiteralData::Owned(bytes.into_boxed_slice()),
}
}
}
impl From<Box<[u8]>> for Literal {
fn from(bytes: Box<[u8]>) -> Self {
Self {
expected: LiteralData::Owned(bytes),
}
}
}
impl std::fmt::Debug for Literal {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let bytes = self.bytes();
match std::str::from_utf8(bytes) {
Ok(s) => write!(f, "Literal::from_str({s:?})"),
Err(_) => write!(f, "Literal::from_bytes({bytes:?})"),
}
}
}
impl std::fmt::Display for Literal {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let bytes = self.bytes();
match std::str::from_utf8(bytes) {
Ok(s) => write!(f, "\"{s}\""),
Err(_) => write!(f, "{bytes:?}"),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_literal_from_str() {
let literal = Literal::from_str("hello");
assert_eq!(literal.bytes(), b"hello");
assert_eq!(literal.len(), 5);
assert!(!literal.is_empty());
}
#[test]
fn test_literal_from_bytes() {
let literal = Literal::from_bytes(b"\x89PNG");
assert_eq!(literal.bytes(), &[0x89, 0x50, 0x4E, 0x47]);
assert_eq!(literal.len(), 4);
}
#[test]
fn test_empty_literal() {
let literal = Literal::from_str("");
assert!(literal.is_empty());
assert_eq!(literal.len(), 0);
}
#[test]
fn test_literal_conversion_traits() {
let _from_str: Literal = "test".into();
let _from_string: Literal = String::from("test").into();
let _from_bytes: Literal = b"test".as_slice().into();
let _from_vec: Literal = vec![116, 101, 115, 116].into();
}
#[test]
fn test_literal_parsing_success() {
use crate::parser::parse;
use std::io::Cursor;
let literal = Literal::from_str("hello");
let mut input = Cursor::new(b"hello world");
let mut source = crate::parser::Source::new(&mut input);
let result = parse(literal, &mut source).unwrap();
assert_eq!(result, b"hello".as_slice().into());
}
#[test]
fn test_literal_parsing_failure() {
use crate::parser::parse;
use std::io::Cursor;
let literal = Literal::from_str("hello");
let mut input = Cursor::new(b"world");
let mut source = crate::parser::Source::new(&mut input);
let result = parse(literal, &mut source);
assert!(matches!(result, Err(Error::NoMatch)));
}
#[test]
fn test_literal_parsing_partial_match() {
use crate::parser::parse;
use std::io::Cursor;
let literal = Literal::from_str("hello");
let mut input = Cursor::new(b"hell");
let mut source = crate::parser::Source::new(&mut input);
let result = parse(literal, &mut source);
assert!(matches!(result, Err(Error::NoMatch)));
}
#[test]
fn test_literal_parsing_prefix_match() {
use crate::parser::parse;
use std::io::Cursor;
let literal = Literal::from_str("he");
let mut input = Cursor::new(b"hello");
let mut source = crate::parser::Source::new(&mut input);
let result = parse(literal, &mut source).unwrap();
assert_eq!(result, b"he".as_slice().into());
}
#[test]
fn test_literal_parsing_empty() {
use crate::parser::parse;
use std::io::Cursor;
let literal = Literal::from_str("");
let mut input = Cursor::new(b"anything");
let mut source = crate::parser::Source::new(&mut input);
let result = parse(literal, &mut source).unwrap();
assert_eq!(result, b"".as_slice().into());
}
#[test]
fn test_literal_parsing_empty_input() {
use crate::parser::parse;
use std::io::Cursor;
let literal = Literal::from_str("hello");
let mut input = Cursor::new(b"");
let mut source = crate::parser::Source::new(&mut input);
let result = parse(literal, &mut source);
assert!(matches!(result, Err(Error::NoMatch)));
}
#[test]
fn test_literal_parsing_empty_on_empty() {
use crate::parser::parse;
use std::io::Cursor;
let literal = Literal::from_str("");
let mut input = Cursor::new(b"");
let mut source = crate::parser::Source::new(&mut input);
let result = parse(literal, &mut source).unwrap();
assert_eq!(result, b"".as_slice().into());
}
#[test]
fn test_literal_parsing_binary_data() {
use crate::parser::parse;
use std::io::Cursor;
let png_header = Literal::from_bytes(&[0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]);
let mut input = Cursor::new(&[0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00]);
let mut source = crate::parser::Source::new(&mut input);
let result = parse(png_header, &mut source).unwrap();
assert_eq!(
result,
[0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]
.as_slice()
.into()
);
}
#[test]
fn test_literal_parsing_unicode() {
use crate::parser::parse;
use std::io::Cursor;
let literal = Literal::from_str("héllo"); let input_bytes = "héllo world".as_bytes();
let mut input = Cursor::new(input_bytes);
let mut source = crate::parser::Source::new(&mut input);
let result = parse(literal, &mut source).unwrap();
assert_eq!(result, "héllo".as_bytes().into());
}
#[test]
fn test_literal_clone() {
let literal1 = Literal::from_str("test");
let literal2 = literal1.clone();
assert_eq!(literal1.bytes(), literal2.bytes());
assert_eq!(literal1.len(), literal2.len());
}
#[test]
fn test_literal_equality() {
let literal1 = Literal::from_str("test");
let literal2 = Literal::from_str("test");
let literal3 = Literal::from_str("different");
let literal4 = Literal::from_bytes(b"test");
assert_eq!(literal1, literal2);
assert_eq!(literal1, literal4); assert_ne!(literal1, literal3);
}
#[test]
fn test_literal_debug_format() {
let string_literal = Literal::from_str("hello");
let debug_str = format!("{string_literal:?}");
assert_eq!(debug_str, "Literal::from_str(\"hello\")");
let binary_literal = Literal::from_bytes(&[0x89, 0x50]);
let debug_str = format!("{binary_literal:?}");
assert_eq!(debug_str, "Literal::from_bytes([137, 80])");
}
#[test]
fn test_literal_display_format() {
let string_literal = Literal::from_str("hello");
let display_str = format!("{string_literal}");
assert_eq!(display_str, "\"hello\"");
let binary_literal = Literal::from_bytes(&[0x89, 0x50]);
let display_str = format!("{binary_literal}");
assert_eq!(display_str, "[137, 80]");
}
#[test]
fn test_literal_large_input() {
use crate::parser::parse;
use std::io::Cursor;
let large_string = "x".repeat(1000);
let literal = Literal::from_str(&large_string);
let input_data = format!("{large_string}more");
let mut input = Cursor::new(input_data.as_bytes());
let mut source = crate::parser::Source::new(&mut input);
let result = parse(literal, &mut source).unwrap();
assert_eq!(result, large_string.as_bytes().into());
}
#[test]
fn test_literal_from_box() {
let boxed_bytes: Box<[u8]> = Box::new([1, 2, 3, 4]);
let literal = Literal::from(boxed_bytes);
assert_eq!(literal.bytes(), &[1, 2, 3, 4]);
}
#[test]
fn test_literal_single_byte() {
use crate::parser::parse;
use std::io::Cursor;
let literal = Literal::from_bytes(b"A");
let mut input = Cursor::new(b"ABCD");
let mut source = crate::parser::Source::new(&mut input);
let result = parse(literal, &mut source).unwrap();
assert_eq!(result, b"A".as_slice().into());
}
#[test]
fn test_literal_case_sensitive() {
use crate::parser::parse;
use std::io::Cursor;
let literal = Literal::from_str("Hello");
let mut input = Cursor::new(b"hello");
let mut source = crate::parser::Source::new(&mut input);
let result = parse(literal, &mut source);
assert!(matches!(result, Err(Error::NoMatch)));
}
#[test]
fn test_literal_newlines_and_whitespace() {
use crate::parser::parse;
use std::io::Cursor;
let literal = Literal::from_str("hello\nworld\t!");
let mut input = Cursor::new(b"hello\nworld\t!more");
let mut source = crate::parser::Source::new(&mut input);
let result = parse(literal, &mut source).unwrap();
assert_eq!(result, b"hello\nworld\t!".as_slice().into());
}
#[test]
fn test_literal_null_bytes() {
use crate::parser::parse;
use std::io::Cursor;
let literal = Literal::from_bytes(&[b'a', 0, b'b']);
let mut input = Cursor::new(&[b'a', 0, b'b', b'c']);
let mut source = crate::parser::Source::new(&mut input);
let result = parse(literal, &mut source).unwrap();
assert_eq!(result, [b'a', 0, b'b'].as_slice().into());
}
#[test]
fn test_literal_max_length() {
let max_bytes = vec![255u8; 10000];
let literal = Literal::from_bytes(&max_bytes);
assert_eq!(literal.len(), 10000);
assert_eq!(literal.bytes(), &max_bytes);
}
}