flask/combinators/
mod.rs

1use nom::branch::alt;
2use nom::bytes::complete::{is_a, is_not, take, take_until1};
3use nom::bytes::streaming::{tag, tag_no_case, take_while};
4use nom::character::is_alphanumeric;
5// use std::str::{self, from_utf8};
6use nom::IResult;
7
8// ***************************************************************************
9// scalar combinators
10// ***************************************************************************
11
12pub fn crlf(i: &str) -> IResult<&str, &str> {
13  tag("\r\n")(i)
14}
15
16pub fn colon(i: &str) -> IResult<&str, &str> {
17  tag(":")(i)
18}
19
20pub fn space(i: &str) -> IResult<&str, &str> {
21  tag(" ")(i)
22}
23
24// ***************************************************************************
25// repeated combinators
26// ***************************************************************************
27pub fn spaces(i: &str) -> IResult<&str, &str> {
28  is_a(" ")(i)
29}
30
31// pub fn digits(i: &str) -> IResult<&str, &str> {
32//   is_a("0123456789")(i)
33// }
34
35pub fn to_space(s: &str) -> IResult<&str, &str> {
36    is_not(" ")(s)
37}
38
39pub fn take_until_carriage_return(s: &str) -> IResult<&str, &str> {
40  take_until1("\r")(s)
41}
42
43// ***************************************************************************
44// classifiers
45// ***************************************************************************
46pub fn is_digit_char(ch: char) -> bool {
47  let ch_u8 = ch as u8;
48  b"0123456789".contains(&ch_u8)
49}
50
51pub fn number(s: &str) -> IResult<&str, &str> {
52  take_while(is_digit_char)(s)
53}
54
55// ***************************************************************************
56// http related combinators
57// ***************************************************************************
58// pub fn http(i: &str) -> IResult<&str, &str> {
59//   tag_no_case("HTTP")(i)
60// }
61
62pub fn http_method(s: &str) -> IResult<&str, &str> {
63  alt((
64    tag("CONNECT"),
65    tag("DELETE"),
66    tag("GET"),
67    tag("HEAD"),
68    tag("OPTIONS"),
69    tag("PATCH"),
70    tag("POST"),
71    tag("PUT"),
72    tag("TRACE") 
73  ))(s)
74}
75
76pub fn http_slash(s: &str) ->  IResult<&str, &str> {
77  tag_no_case("HTTP/")(s)
78}
79// take_until1("\r")(s)
80pub fn http_version(s: &str) -> IResult<&str, &str> {
81  let result = http_slash(s);
82  match result {
83    Ok((s2, _)) => take(3usize)(s2),
84    Err(_) => result
85  }
86}
87
88fn is_http_header_name_char(ch: char) -> bool {
89  let ch_u8 = ch as u8;
90  is_alphanumeric(ch_u8) ||
91  b"!#$%&'*+-.^_`|~".contains(&ch_u8)
92}
93
94pub fn http_header_name(s: &str) -> IResult<&str, &str> {
95  take_while(is_http_header_name_char)(s)
96}
97
98// allows ISO-8859-1 characters in header values
99// this is allowed in RFC 2616 but not in rfc7230
100#[cfg(  feature = "tolerant-http1-parser")]
101pub fn is_header_value_char(ch: char) -> bool {
102  let ch_u8 = ch as u8;
103  ch_u8 == 9 || (ch_u8 >= 32 && ch_u8 <= 126) || i >= 160
104}
105
106#[cfg(not(feature = "tolerant-http1-parser"))]
107pub fn is_header_value_char(ch: char) -> bool {
108  let ch_u8 = ch as u8;
109  ch_u8 == 9 || (ch_u8 >= 32 && ch_u8 <= 126)
110}
111
112pub fn header_value(s: &str) -> IResult<&str, &str> {
113  take_while(is_header_value_char)(s)
114}
115
116
117//#################################################################################################################
118// test cases go below here
119//#################################################################################################################
120#[cfg(test)]
121mod tests {
122  extern crate rand;
123
124  use super::*;
125  use nom::error::ErrorKind;
126  use nom::Err;
127  // use nom::Err::Incomplete;
128  use nom::error::Error;
129
130
131  #[test]
132  fn test_crlf() {
133    assert_eq!(crlf("\r\n"), Ok(("", "\r\n")));
134    assert_eq!(crlf("\r\nWorld!"), Ok(("World!", "\r\n")));
135    assert_eq!(crlf("\r\nHello\r\nWorld!"), Ok(("Hello\r\nWorld!", "\r\n")));
136    assert_eq!(crlf("\r\n     "), Ok(("     ", "\r\n")));
137
138    let resp = crlf("Something");
139    let err = Err(Err::Error(Error::new("Something", ErrorKind::Tag)));
140    assert_eq!(resp, err);
141
142    let resp2 = crlf("Foo\r\nBar");
143    let err2 = Err(Err::Error(Error::new("Foo\r\nBar", ErrorKind::Tag)));
144    assert_eq!(resp2, err2); 
145  }
146
147  #[test]
148  fn test_space() {
149    assert_eq!(space(" "), Ok(("", " ")));
150    assert_eq!(space(" Hello"), Ok(("Hello", " ")));
151    assert_eq!(space("      "), Ok(("     ", " ")));
152
153    let resp = space("Rust World");
154    let err = Err(Err::Error(Error::new("Rust World", ErrorKind::Tag)));
155    assert_eq!(resp, err);
156  }
157
158  #[test]
159  fn test_spaces() {
160    assert_eq!(spaces(" "), Ok(("", " ")));
161    assert_eq!(spaces("   "), Ok(("", "   ")));
162    assert_eq!(spaces(" cat"), Ok(("cat", " ")));
163    assert_eq!(spaces("   Hello"), Ok(("Hello", "   ")));
164    assert_eq!(spaces("  "), Ok(("", "  ")));
165    assert_eq!(spaces("  TREE  "), Ok(("TREE  ", "  ")));
166
167    let resp = spaces("none");
168    let err = Err(Err::Error(Error::new("none", ErrorKind::IsA)));
169    assert_eq!(resp, err); 
170  }
171
172  // #[test]
173  // fn test_digits() {
174  //   assert_eq!(digits("7"), Ok(("", "7")));
175  //   assert_eq!(digits("777"), Ok(("", "777")));
176  //   assert_eq!(digits("123xxx456"), Ok(("xxx456", "123")));
177
178  //   let resp = digits("car 5");
179  //   let err = Err(Err::Error(Error::new("car 5", ErrorKind::IsA)));
180  //   assert_eq!(resp, err);
181  // }
182
183  #[test]
184  fn test_http_method() {
185    assert_eq!(http_method("POST foo bar baz"), Ok((" foo bar baz", "POST")));
186    assert_eq!(http_method("HEAD foo bar baz"), Ok((" foo bar baz", "HEAD")));
187    assert_eq!(http_method("GET foo bar baz"), Ok((" foo bar baz", "GET")));
188  }
189}
190