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}