kalosm_sample/structured_parser/
literal.rs1use std::borrow::Cow;
2
3use crate::bail;
4
5use crate::{CreateParserState, ParseStatus, Parser};
6
7#[derive(Debug, PartialEq, Eq, Clone)]
9pub struct LiteralParser {
10 literal: Cow<'static, str>,
11}
12
13impl CreateParserState for LiteralParser {
14 fn create_parser_state(&self) -> <Self as Parser>::PartialState {
15 LiteralParserOffset::default()
16 }
17}
18
19impl<S: Into<Cow<'static, str>>> From<S> for LiteralParser {
20 fn from(literal: S) -> Self {
21 Self {
22 literal: literal.into(),
23 }
24 }
25}
26
27impl LiteralParser {
28 pub fn new<S: Into<Cow<'static, str>>>(literal: S) -> Self {
30 Self {
31 literal: literal.into(),
32 }
33 }
34}
35
36#[derive(Default, Debug, PartialEq, Eq, Copy, Clone)]
38pub struct LiteralParserOffset {
39 pub(crate) offset: usize,
40}
41
42impl LiteralParserOffset {
43 pub fn new(offset: usize) -> Self {
45 Self { offset }
46 }
47}
48
49#[derive(Debug, PartialEq, Eq, Copy, Clone)]
51pub struct LiteralMismatchError;
52
53impl std::fmt::Display for LiteralMismatchError {
54 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
55 write!(f, "Literal mismatch")
56 }
57}
58
59impl std::error::Error for LiteralMismatchError {}
60
61impl Parser for LiteralParser {
62 type Output = ();
63 type PartialState = LiteralParserOffset;
64
65 fn parse<'a>(
66 &self,
67 state: &LiteralParserOffset,
68 input: &'a [u8],
69 ) -> crate::ParseResult<ParseStatus<'a, Self::PartialState, Self::Output>> {
70 let mut bytes_consumed = 0;
71
72 for (input_byte, literal_byte) in input
73 .iter()
74 .zip(self.literal.as_bytes()[state.offset..].iter())
75 {
76 if input_byte != literal_byte {
77 bail!(LiteralMismatchError);
78 }
79 bytes_consumed += 1;
80 }
81
82 if state.offset + bytes_consumed == self.literal.len() {
83 Ok(ParseStatus::Finished {
84 result: (),
85 remaining: &input[bytes_consumed..],
86 })
87 } else {
88 Ok(ParseStatus::Incomplete {
89 new_state: LiteralParserOffset {
90 offset: state.offset + bytes_consumed,
91 },
92 required_next: {
93 match &self.literal {
94 Cow::Borrowed(cow) => {
95 Cow::Borrowed(cow.split_at(state.offset + bytes_consumed).1)
96 }
97 Cow::Owned(cow) => {
98 Cow::Owned(cow.split_at(state.offset + bytes_consumed).1.to_string())
99 }
100 }
101 },
102 })
103 }
104 }
105}
106
107#[test]
108fn literal_parser() {
109 let parser = LiteralParser::new("Hello, world!");
110 let state = LiteralParserOffset { offset: 0 };
111 assert_eq!(
112 parser.parse(&state, b"Hello, world!").unwrap(),
113 ParseStatus::Finished {
114 result: (),
115 remaining: &[]
116 }
117 );
118 assert_eq!(
119 parser.parse(&state, b"Hello, ").unwrap(),
120 ParseStatus::Incomplete {
121 new_state: LiteralParserOffset { offset: 7 },
122 required_next: "world!".into()
123 }
124 );
125 assert_eq!(
126 parser
127 .parse(
128 &parser
129 .parse(&state, b"Hello, ")
130 .unwrap()
131 .unwrap_incomplete()
132 .0,
133 b"world!"
134 )
135 .unwrap(),
136 ParseStatus::Finished {
137 result: (),
138 remaining: &[]
139 }
140 );
141 assert!(parser.parse(&state, b"Goodbye, world!").is_err(),);
142}