1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
use nom::{
    branch::alt,
    bytes::complete::{take_until, take_while},
    character::{complete::char as parse_char, *},
    combinator::map_res,
    error::{ErrorKind, ParseError},
    IResult,
};

use std::{
    io::{Error as IoError, ErrorKind as IoErrorKind},
    net::Ipv4Addr,
};

/// Parse input as a string using `String::from_utf8`.
pub fn slice_to_string<'a, E: ParseError<&'a [u8]>>(slice: &'a [u8]) -> Result<String, E> {
    if slice.is_empty() {
        Err(E::from_error_kind(slice, ErrorKind::Eof))
    } else {
        Ok(String::from_utf8(Vec::from(slice))
            .map_err(|_| E::from_error_kind(slice, ErrorKind::IsNot))?)
    }
}

pub fn slice_to_string_nullable(slice: &[u8]) -> Result<String, IoError> {
    Ok(String::from_utf8(Vec::from(slice))
        .map_err(|_| IoError::new(IoErrorKind::InvalidInput, "Failed to parse utf8 string"))?)
}

/// Parse unsigned 16 bit integer using `Parse::parse`.
pub fn parse_u16<'a, E: ParseError<&'a [u8]>>(slice: &'a [u8]) -> Result<u16, E> {
    Ok(::std::str::from_utf8(slice)
        .map_err(|_| E::from_error_kind(slice, ErrorKind::IsNot))?
        .parse()
        .map_err(|_| E::from_error_kind(slice, ErrorKind::IsNot))?)
}

/// Parse unsigned 8 bit integer using `Parse::parse`.
pub fn parse_u8(slice: &[u8]) -> Result<u8, IoError> {
    Ok(::std::str::from_utf8(slice)
        .map_err(|_| {
            IoError::new(
                IoErrorKind::InvalidInput,
                "Failed to parse utf8 u16 integer",
            )
        })?
        .parse()
        .map_err(|_| IoError::new(IoErrorKind::InvalidInput, "Failed to parse u8 integer"))?)
}

/// Parse unsigned 32 bit integer using `Parse::parse`.
pub fn parse_u32(slice: &[u8]) -> Result<u32, IoError> {
    Ok(::std::str::from_utf8(slice)
        .map_err(|_| {
            IoError::new(
                IoErrorKind::InvalidInput,
                "Failed to parse utf8 u32 integer",
            )
        })?
        .parse()
        .map_err(|_| IoError::new(IoErrorKind::InvalidInput, "Failed to parse u32 integer"))?)
}

/// Parse input as an f32 using `Parse::parse`.
pub fn parse_f32(slice: &[u8]) -> Result<f32, IoError> {
    Ok(::std::str::from_utf8(slice)
        .map_err(|_| IoError::new(IoErrorKind::InvalidInput, "Failed to parse utf8 f32"))?
        .parse()
        .map_err(|_| IoError::new(IoErrorKind::InvalidInput, "Failed to parse f32"))?)
}

/// Parse Input as a vector of bytes.
pub fn parse_byte_vec<'a, E: ParseError<&'a [u8]>>(
    input: &'a [u8],
) -> IResult<&'a [u8], Vec<u8>, E> {
    Ok((&input[input.len()..], input.to_vec()))
}

pub fn parse_ip_address<'a, E: ParseError<&'a [u8]>>(
    input: &'a [u8],
) -> IResult<&[u8], Ipv4Addr, E> {
    let (input, byte1) = map_res(take_while(is_digit), parse_u8)(input)?;
    let (input, _) = parse_char('.')(input)?;
    let (input, byte2) = map_res(take_while(is_digit), parse_u8)(input)?;
    let (input, _) = parse_char('.')(input)?;
    let (input, byte3) = map_res(take_while(is_digit), parse_u8)(input)?;
    let (input, _) = parse_char('.')(input)?;
    let (input, byte4) = map_res(take_while(is_digit), parse_u8)(input)?;
    Ok((input, Ipv4Addr::new(byte1, byte2, byte3, byte4)))
}

pub fn parse_string<'a, E: ParseError<&'a [u8]>>(input: &'a [u8]) -> IResult<&'a [u8], String, E> {
    map_res(take_while(is_alphanumeric), slice_to_string::<E>)(input)
}

pub fn parse_possibly_quoted_string<'a, E: ParseError<&'a [u8]>>(
    input: &'a [u8],
) -> IResult<&'a [u8], String, E> {
    let alts = (parse_string::<E>, parse_quoted_string::<E>);
    alt::<_, _, E, _>(alts)(input)
}

pub fn parse_quoted_string<'a, E: ParseError<&'a [u8]>>(
    input: &'a [u8],
) -> IResult<&'a [u8], String, E> {
    let (input, _) = parse_char('"')(input)?;
    let (input, out) = map_res(take_until("\""), slice_to_string_nullable)(input)?;
    let (input, _) = parse_char('"')(input)?;
    Ok((input, out))
}

/// Checks if a given character is a token ([RFC3261: Page 221, "token"](https://tools.ietf.org/html/rfc3261#page-221))
/// # Examples
///
/// ```
/// use libsip::parse::is_token;
/// assert!(is_token('a' as u8));
/// assert!(is_token('+' as u8));
/// assert!(!is_token('=' as u8));
/// ```
pub fn is_token(chr: u8) -> bool {
    is_alphanumeric(chr) || "-.!%*_+`'~".contains(char::from(chr))
}