libsip/
parse.rs

1use nom::{
2    branch::alt,
3    bytes::complete::{take_until, take_while},
4    character::{complete::char as parse_char, *},
5    combinator::map_res,
6    error::{ErrorKind, ParseError},
7    IResult,
8};
9
10use std::{
11    io::{Error as IoError, ErrorKind as IoErrorKind},
12    net::Ipv4Addr,
13};
14
15/// Parse input as a string using `String::from_utf8`.
16pub fn slice_to_string<'a, E: ParseError<&'a [u8]>>(slice: &'a [u8]) -> Result<String, E> {
17    if slice.is_empty() {
18        Err(E::from_error_kind(slice, ErrorKind::Eof))
19    } else {
20        Ok(String::from_utf8(Vec::from(slice))
21            .map_err(|_| E::from_error_kind(slice, ErrorKind::IsNot))?)
22    }
23}
24
25pub fn slice_to_string_nullable(slice: &[u8]) -> Result<String, IoError> {
26    Ok(String::from_utf8(Vec::from(slice))
27        .map_err(|_| IoError::new(IoErrorKind::InvalidInput, "Failed to parse utf8 string"))?)
28}
29
30/// Parse unsigned 16 bit integer using `Parse::parse`.
31pub fn parse_u16<'a, E: ParseError<&'a [u8]>>(slice: &'a [u8]) -> Result<u16, E> {
32    Ok(::std::str::from_utf8(slice)
33        .map_err(|_| E::from_error_kind(slice, ErrorKind::IsNot))?
34        .parse()
35        .map_err(|_| E::from_error_kind(slice, ErrorKind::IsNot))?)
36}
37
38/// Parse unsigned 8 bit integer using `Parse::parse`.
39pub fn parse_u8(slice: &[u8]) -> Result<u8, IoError> {
40    Ok(::std::str::from_utf8(slice)
41        .map_err(|_| {
42            IoError::new(
43                IoErrorKind::InvalidInput,
44                "Failed to parse utf8 u16 integer",
45            )
46        })?
47        .parse()
48        .map_err(|_| IoError::new(IoErrorKind::InvalidInput, "Failed to parse u8 integer"))?)
49}
50
51/// Parse unsigned 32 bit integer using `Parse::parse`.
52pub fn parse_u32(slice: &[u8]) -> Result<u32, IoError> {
53    Ok(::std::str::from_utf8(slice)
54        .map_err(|_| {
55            IoError::new(
56                IoErrorKind::InvalidInput,
57                "Failed to parse utf8 u32 integer",
58            )
59        })?
60        .parse()
61        .map_err(|_| IoError::new(IoErrorKind::InvalidInput, "Failed to parse u32 integer"))?)
62}
63
64/// Parse input as an f32 using `Parse::parse`.
65pub fn parse_f32(slice: &[u8]) -> Result<f32, IoError> {
66    Ok(::std::str::from_utf8(slice)
67        .map_err(|_| IoError::new(IoErrorKind::InvalidInput, "Failed to parse utf8 f32"))?
68        .parse()
69        .map_err(|_| IoError::new(IoErrorKind::InvalidInput, "Failed to parse f32"))?)
70}
71
72/// Parse Input as a vector of bytes.
73pub fn parse_byte_vec<'a, E: ParseError<&'a [u8]>>(
74    input: &'a [u8],
75) -> IResult<&'a [u8], Vec<u8>, E> {
76    Ok((&input[input.len()..], input.to_vec()))
77}
78
79pub fn parse_ip_address<'a, E: ParseError<&'a [u8]>>(
80    input: &'a [u8],
81) -> IResult<&[u8], Ipv4Addr, E> {
82    let (input, byte1) = map_res(take_while(is_digit), parse_u8)(input)?;
83    let (input, _) = parse_char('.')(input)?;
84    let (input, byte2) = map_res(take_while(is_digit), parse_u8)(input)?;
85    let (input, _) = parse_char('.')(input)?;
86    let (input, byte3) = map_res(take_while(is_digit), parse_u8)(input)?;
87    let (input, _) = parse_char('.')(input)?;
88    let (input, byte4) = map_res(take_while(is_digit), parse_u8)(input)?;
89    Ok((input, Ipv4Addr::new(byte1, byte2, byte3, byte4)))
90}
91
92pub fn parse_string<'a, E: ParseError<&'a [u8]>>(input: &'a [u8]) -> IResult<&'a [u8], String, E> {
93    map_res(take_while(is_alphanumeric), slice_to_string::<E>)(input)
94}
95
96pub fn parse_possibly_quoted_string<'a, E: ParseError<&'a [u8]>>(
97    input: &'a [u8],
98) -> IResult<&'a [u8], String, E> {
99    let alts = (parse_string::<E>, parse_quoted_string::<E>);
100    alt::<_, _, E, _>(alts)(input)
101}
102
103pub fn parse_quoted_string<'a, E: ParseError<&'a [u8]>>(
104    input: &'a [u8],
105) -> IResult<&'a [u8], String, E> {
106    let (input, _) = parse_char('"')(input)?;
107    let (input, out) = map_res(take_until("\""), slice_to_string_nullable)(input)?;
108    let (input, _) = parse_char('"')(input)?;
109    Ok((input, out))
110}
111
112/// Checks if a given character is a token ([RFC3261: Page 221, "token"](https://tools.ietf.org/html/rfc3261#page-221))
113/// # Examples
114///
115/// ```
116/// use libsip::parse::is_token;
117/// assert!(is_token('a' as u8));
118/// assert!(is_token('+' as u8));
119/// assert!(!is_token('=' as u8));
120/// ```
121pub fn is_token(chr: u8) -> bool {
122    is_alphanumeric(chr) || "-.!%*_+`'~".contains(char::from(chr))
123}