Commit 0bc5bd26 authored by Nugine's avatar Nugine
Browse files

s3s: refactor parsers

parent 9859f559
Loading
Loading
Loading
Loading
+13 −21
Original line number Diff line number Diff line
@@ -75,27 +75,19 @@ struct ChunkMeta<'a> {

/// nom parser
fn parse_chunk_meta(mut input: &[u8]) -> nom::IResult<&[u8], ChunkMeta<'_>> {
    use nom::{
        bytes::complete::{tag, take, take_till1},
        combinator::{all_consuming, map_res},
        number::complete::hex_u32,
        sequence::tuple,
    };
    use crate::utils::parser::consume;

    let mut parser = all_consuming(tuple((
        take_till1(|c| c == b';'),
        tag(b";chunk-signature="),
        take(64_usize),
        tag(b"\r\n"),
    )));

    let (size_str, signature) = {
        let (remain, (size_str, _, signature, _)) = parser(input)?;
        input = remain;
        (size_str, signature)
    };
    use nom::bytes::complete::{tag, take, take_till1};
    use nom::combinator::{all_consuming, map_res};
    use nom::number::complete::hex_u32;
    use nom::sequence::delimited;

    let s = &mut input;

    let size = consume(s, take_till1(|c| c == b';'))?;
    let (_, size) = map_res(hex_u32, TryInto::try_into)(size)?;

    let (_, size) = map_res(hex_u32, TryInto::try_into)(size_str)?;
    let signature = consume(s, all_consuming(delimited(tag(b";chunk-signature="), take(64_usize), tag(b"\r\n"))))?;

    Ok((input, ChunkMeta { size, signature }))
}
+11 −11
Original line number Diff line number Diff line
@@ -427,26 +427,26 @@ struct ContentDisposition<'a> {

/// parse content disposition value
fn parse_content_disposition(input: &[u8]) -> nom::IResult<&[u8], ContentDisposition<'_>> {
    use nom::{
        bytes::complete::{tag, take, take_till1},
        combinator::{all_consuming, map_res, opt},
        sequence::{delimited, preceded, tuple},
    };
    use nom::bytes::complete::{tag, take, take_till1};
    use nom::combinator::{all_consuming, map_res, opt};
    use nom::sequence::{delimited, preceded, tuple};

    // TODO: escape?

    let name_parser = delimited(tag(b"name=\""), map_res(take_till1(|c| c == b'"'), std::str::from_utf8), take(1_usize));
    let parse_name = delimited(tag(b"name=\""), map_res(take_till1(|c| c == b'"'), std::str::from_utf8), take(1_usize));

    let filename_parser = delimited(
    let parse_filename = delimited(
        tag(b"filename=\""),
        map_res(take_till1(|c| c == b'"'), std::str::from_utf8),
        take(1_usize),
    );

    let mut parser = all_consuming(tuple((
        preceded(tag(b"form-data; "), name_parser),
        opt(preceded(tag(b"; "), filename_parser)),
    let mut parse = all_consuming(tuple((
        preceded(tag(b"form-data; "), parse_name),
        opt(preceded(tag(b"; "), parse_filename)),
    )));

    let (remaining, (name, filename)) = parser(input)?;
    let (remaining, (name, filename)) = parse(input)?;

    Ok((remaining, ContentDisposition { name, filename }))
}
+1 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@
    clippy::single_match_else,
    clippy::wildcard_imports,
    clippy::let_underscore_untyped,
    clippy::inline_always,
)]

#[macro_use]
+26 −15
Original line number Diff line number Diff line
@@ -3,12 +3,6 @@
//! <https://docs.aws.amazon.com/AmazonS3/latest/userguide/RESTAuthentication.html#ConstructingTheAuthenticationHeader>
//!

use crate::utils::parser;

use nom::bytes::complete::{tag, take, take_till};
use nom::combinator::{all_consuming, rest};
use nom::sequence::terminated;

pub struct AuthorizationV2<'a> {
    pub access_key: &'a str,
    pub signature: &'a str,
@@ -24,18 +18,35 @@ pub struct ParseAuthorizationV2Error {

impl<'a> AuthorizationV2<'a> {
    pub fn parse(input: &'a str) -> Result<Self, ParseAuthorizationV2Error> {
        let error = |_| ParseAuthorizationV2Error { _priv: () };
        let colon_tail0 = terminated(take_till(|c| c == ':'), take(1_usize));
        match parser::parse_authorization(input) {
            Ok(("", ans)) => Ok(ans),
            Ok(_) | Err(_) => Err(ParseAuthorizationV2Error { _priv: () }),
        }
    }
}

        parser::parse(input, |p| {
            p.nom(tag("AWS "))?;
mod parser {
    use super::AuthorizationV2;

            let access_key = p.nom(colon_tail0)?;
            let signature = p.nom(all_consuming(rest))?;
    use crate::utils::parser::consume;

    use nom::bytes::complete::{tag, take, take_till};
    use nom::combinator::rest;
    use nom::sequence::terminated;
    use nom::IResult;

    pub fn parse_authorization(mut input: &str) -> IResult<&str, AuthorizationV2<'_>> {
        let s = &mut input;

        consume(s, tag("AWS "))?;
        let access_key = consume(s, until_colon0)?;
        let signature = consume(s, rest)?;

        Ok((input, AuthorizationV2 { access_key, signature }))
    }

            Ok(Self { access_key, signature })
        })
        .map_err(error)
    fn until_colon0(input: &str) -> IResult<&str, &str> {
        terminated(take_till(|c| c == ':'), take(1_usize))(input)
    }
}

+40 −48
Original line number Diff line number Diff line
//! x-amz-date

use std::fmt::Write as _;
use std::str::FromStr;

use arrayvec::ArrayString;

@@ -25,59 +24,14 @@ pub struct AmzDate {
/// [`AmzDate`]
#[derive(Debug, thiserror::Error)]
#[error("ParseAmzDateError")]
pub struct ParseAmzDateError {
    /// private place holder
    _priv: (),
}
pub struct ParseAmzDateError(());

impl AmzDate {
    /// Parses `AmzDate` from header
    /// # Errors
    /// Returns an error if the header is invalid
    pub fn parse(header: &str) -> Result<Self, ParseAmzDateError> {
        /// nom parser
        fn nom_parse(input: &str) -> nom::IResult<&str, [&str; 6]> {
            use nom::{
                bytes::complete::{tag, take},
                combinator::all_consuming,
                sequence::tuple,
            };

            let mut parser = all_consuming(tuple((
                take(4_usize),
                take(2_usize),
                take(2_usize),
                tag("T"),
                take(2_usize),
                take(2_usize),
                take(2_usize),
                tag("Z"),
            )));

            let (_, (year_str, month_str, day_str, _, hour_str, minute_str, second_str, _)) = parser(input)?;

            Ok((input, [year_str, month_str, day_str, hour_str, minute_str, second_str]))
        }

        /// parse number
        fn to_num<T: FromStr>(input: &str) -> Result<T, ParseAmzDateError> {
            match input.parse::<T>() {
                Ok(x) => Ok(x),
                Err(_) => Err(ParseAmzDateError { _priv: () }),
            }
        }

        match nom_parse(header) {
            Err(_) => Err(ParseAmzDateError { _priv: () }),
            Ok((_, [year_str, month_str, day_str, hour_str, minute_str, second_str])) => Ok(Self {
                year: to_num(year_str)?,
                month: to_num(month_str)?,
                day: to_num(day_str)?,
                hour: to_num(hour_str)?,
                minute: to_num(minute_str)?,
                second: to_num(second_str)?,
            }),
        }
        self::parser::parse(header).map_err(|_| ParseAmzDateError(()))
    }

    /// `{YYYY}{MM}{DD}T{HH}{MM}{SS}Z`
@@ -107,3 +61,41 @@ impl AmzDate {
        Some(t.assume_utc())
    }
}

mod parser {
    use super::*;

    use crate::utils::parser::{digit2, digit4, Error};

    macro_rules! ensure {
        ($cond:expr) => {
            if !$cond {
                return Err(Error);
            }
        };
    }

    pub fn parse(input: &str) -> Result<AmzDate, Error> {
        let x = input.as_bytes();
        ensure!(x.len() == 16);

        let year = digit4([x[0], x[1], x[2], x[3]])?;
        let month = digit2([x[4], x[5]])?;
        let day = digit2([x[6], x[7]])?;
        ensure!(x[8] == b'T');

        let hour = digit2([x[9], x[10]])?;
        let minute = digit2([x[11], x[12]])?;
        let second = digit2([x[13], x[14]])?;
        ensure!(x[15] == b'Z');

        Ok(AmzDate {
            year,
            month,
            day,
            hour,
            minute,
            second,
        })
    }
}
Loading