http_box/fsm.rs
1// +-----------------------------------------------------------------------------------------------+
2// | Copyright 2016 Sean Kerr |
3// | |
4// | Licensed under the Apache License, Version 2.0 (the "License"); |
5// | you may not use this file except in compliance with the License. |
6// | You may obtain a copy of the License at |
7// | |
8// | http://www.apache.org/licenses/LICENSE-2.0 |
9// | |
10// | Unless required by applicable law or agreed to in writing, software |
11// | distributed under the License is distributed on an "AS IS" BASIS, |
12// | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13// | See the License for the specific language governing permissions and |
14// | limitations under the License. |
15// +-----------------------------------------------------------------------------------------------+
16
17//! Finite state machine macros and types.
18
19use std::fmt;
20
21/// Execute callback `$callback`. If it returns `true`, execute `$exec`. Otherwise exit with
22/// `Success::Callback`.
23macro_rules! callback {
24 ($parser:expr, $handler:expr, $context:expr, $callback:ident, $data:expr, $exec:expr) => ({
25 if $handler.$callback($data) {
26 $exec
27 }
28
29 exit_callback!($parser, $context);
30 });
31
32 ($parser:expr, $handler:expr, $context:expr, $callback:ident, $exec:expr) => ({
33 if bs_slice_length!($context) > 0 {
34 if $handler.$callback(bs_slice!($context)) {
35 $exec
36 }
37
38 exit_callback!($parser, $context);
39 }
40
41 $exec
42 });
43}
44
45/// Reusable callback EOS expression that executes `$callback`.
46macro_rules! callback_eos_expr {
47 ($parser:expr, $handler:expr, $context:expr, $callback:ident) => ({
48 callback!($parser, $handler, $context, $callback, {
49 exit_eos!($parser, $context);
50 });
51 });
52}
53
54/// Execute callback `$callback` ignoring the last collected byte. If it returns `true`, transition
55/// to `$state`. Otherwise exit with `Success::Callback`.
56macro_rules! callback_ignore_transition {
57 ($parser:expr, $handler:expr, $context:expr, $callback:ident, $state:ident,
58 $state_function:ident) => ({
59 set_state!($parser, $state, $state_function);
60
61 // compare against 1 instead of 0 because we're ignoring the last slice byte
62 if bs_slice_length!($context) > 1 {
63 if $handler.$callback(bs_slice_ignore!($context)) {
64 transition!($parser, $context);
65 }
66
67 exit_callback!($parser, $context);
68 }
69
70 transition!($parser, $context);
71 });
72}
73
74/// Execute callback `$callback`. If it returns `true`, transition to `$state`. Otherwise exit
75/// with `Success::Callback`.
76///
77/// This macro exists to enforce the design decision that after each callback, state must either
78/// change, or the parser must exit with `Success::Callback`.
79macro_rules! callback_transition {
80 ($parser:expr, $handler:expr, $context:expr, $callback:ident, $data:expr, $state:ident,
81 $state_function:ident) => ({
82 set_state!($parser, $state, $state_function);
83 callback!($parser, $handler, $context, $callback, $data, {
84 transition!($parser, $context);
85 });
86 });
87
88 ($parser:expr, $handler:expr, $context:expr, $callback:ident, $state:ident,
89 $state_function:ident) => ({
90 set_state!($parser, $state, $state_function);
91 callback!($parser, $handler, $context, $callback, {
92 transition!($parser, $context);
93 });
94 });
95}
96
97/// Exit parser with `Success::Callback`.
98macro_rules! exit_callback {
99 ($parser:expr, $context:expr, $state:ident, $state_function:ident) => ({
100 set_state!($parser, $state, $state_function);
101
102 return Ok(ParserValue::Exit(Success::Callback($context.stream_index)));
103 });
104
105 ($parser:expr, $context:expr) => ({
106 return Ok(ParserValue::Exit(Success::Callback($context.stream_index)));
107 });
108}
109
110/// Exit parser with `Success::Eos`.
111macro_rules! exit_eos {
112 ($parser:expr, $context:expr) => ({
113 return Ok(ParserValue::Exit(Success::Eos($context.stream_index)));
114 });
115}
116
117/// Exit parser with `ParserError`.
118macro_rules! exit_error {
119 ($error:ident, $byte:expr) => ({
120 return Err(ParserError::$error($byte));
121 });
122
123 ($error:ident) => ({
124 return Err(ParserError::$error);
125 });
126}
127
128/// Exit parser with `Success::Finished`.
129macro_rules! exit_finished {
130 ($parser:expr, $context:expr) => ({
131 return Ok(ParserValue::Exit(Success::Finished($context.stream_index)));
132 });
133}
134
135/// If the stream is EOS, exit with `Success::Eos`. Otherwise do nothing.
136macro_rules! exit_if_eos {
137 ($parser:expr, $context:expr) => ({
138 bs_available!($context) > 0 || exit_eos!($parser, $context);
139 });
140}
141
142/// Retrieve the state.
143macro_rules! get_state {
144 ($parser:expr) => ({
145 $parser.state
146 })
147}
148
149/// Set state and state function.
150macro_rules! set_state {
151 ($parser:expr, $state:ident, $state_function:ident) => ({
152 $parser.state = ParserState::$state;
153 $parser.state_function = Parser::$state_function;
154 });
155}
156
157/// Transition to `$state`.
158macro_rules! transition {
159 ($parser:expr, $context:expr, $state:ident, $state_function:ident) => ({
160 set_state!($parser, $state, $state_function);
161
162 bs_mark!($context, $context.stream_index);
163
164 return Ok(ParserValue::Continue);
165 });
166
167 ($parser:expr, $context:expr) => ({
168 bs_mark!($context, $context.stream_index);
169
170 return Ok(ParserValue::Continue);
171 });
172}
173
174/// Transition to `$state`.
175///
176/// This will not readjust the mark index.
177macro_rules! transition_no_remark {
178 ($parser:expr, $context:expr, $state:ident, $state_function:ident) => ({
179 set_state!($parser, $state, $state_function);
180
181 return Ok(ParserValue::Continue);
182 });
183}
184
185// -------------------------------------------------------------------------------------------------
186
187/// Parsing function return values.
188pub enum ParserValue {
189 /// Continue the parser loop.
190 Continue,
191
192 /// Exit the parser loop.
193 Exit(Success)
194}
195
196// -------------------------------------------------------------------------------------------------
197
198/// Parsing function success return values.
199#[derive(Clone,Copy,PartialEq)]
200pub enum Success {
201 /// A callback returned `false` and the parser function exited prematurely. This can be
202 /// treated the same as `Success::Finished`.
203 ///
204 /// # Arguments
205 ///
206 /// **(1)**: The amount of `stream` bytes that were processed before the callback was executed.
207 /// In most cases this will not match `stream.len()`.
208 Callback(usize),
209
210 /// Additional `stream` data is expected. Continue executing the parser function until
211 /// `Success::Finished` is returned.
212 ///
213 /// # Arguments
214 ///
215 /// **(1)**: The amount of `stream` bytes that were processed. This value will always match
216 /// `stream.len()`.
217 Eos(usize),
218
219 /// The parser function finished successfully.
220 ///
221 /// # Arguments
222 ///
223 /// **(1)**: The amount of `stream` bytes that were processed. Under some circumstances this
224 /// will be less than `stream.len()`. This indicates that there must be a transition
225 /// between the current parser function and the next one. For example, a typical HTTP
226 /// request would consist of a call to
227 /// [Parser::parse_head()](../http1/struct.Parser.html#method.parse_head), and
228 /// depending on the content type you may need to transition to
229 /// [Parser::parse_chunked()](../http1/struct.Parser.html#method.parse_chunked),
230 /// [Parser::parse_multipart()](../http1/struct.Parser.html#method.parse_multipart), or
231 /// [Parser::parse_url_encoded()](../http1/struct.Parser.html#method.parse_url_encoded).
232 Finished(usize)
233}
234
235impl fmt::Debug for Success {
236 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
237 match *self {
238 Success::Callback(length) => {
239 write!(formatter, "Success::Callback({})", length)
240 },
241 Success::Eos(length) => {
242 write!(formatter, "Success::Eos({})", length)
243 },
244 Success::Finished(length) => {
245 write!(formatter, "Success::Finished({})", length)
246 }
247 }
248 }
249}
250
251impl fmt::Display for Success {
252 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
253 match *self {
254 Success::Callback(length) => {
255 write!(formatter, "{}", length)
256 },
257 Success::Eos(length) => {
258 write!(formatter, "{}", length)
259 },
260 Success::Finished(length) => {
261 write!(formatter, "{}", length)
262 }
263 }
264 }
265}