<commit_before>//! This crate parses and processes `hyper::server::Request` data that contains
//! `multipart/form-data` content.
//!
//! The main entry point is `parse_multipart`

extern crate httparse;
extern crate hyper;
extern crate libc;
#[macro_use]
extern crate mime;
extern crate tempdir;
extern crate textnonce;

pub mod buf;
pub mod error;
mod headers;
#[cfg(test)]
mod mock;

use std::path::PathBuf;
use std::fs::File;
use std::io::{BufRead, BufReader, Read};

use hyper::header::{ContentType, Headers};
use hyper::server::Request as HyperRequest;
use mime::{Attr, Mime, Param, SubLevel, TopLevel, Value};
use tempdir::TempDir;
use textnonce::TextNonce;

use buf::BufReadPlus;
pub use error::Error;
use headers::ContentDisposition;

/// An uploaded file that was received as part of `multipart/form-data` parsing.
///
/// Files are streamed to disk because they may not fit in memory.
#[derive(Clone, Debug, PartialEq)]
pub struct UploadedFile {
    /// The temporary file where the data was saved.
    pub path: PathBuf,
    /// The filename that was specified in the data, unfiltered. It may or may not be legal on the
    /// local filesystem.
    pub filename: Option<String>,
    /// The unvalidated content-type that was specified in the data.
    pub content_type: Mime,
    /// The size of the file.
    pub size: usize,
}

/// The extracted text fields and uploaded files from a `multipart/form-data` request.
///
/// Use `parse_multipart` to devise this object from a request.
pub struct FormData {
    /// Key-value pairs for plain text fields. Technically, these are form data parts with no
    /// filename specified in the part's `Content-Disposition`.
    pub fields: Vec<(String, String)>,
    /// Key-value pairs for temporary files. Technically, these are form data parts with a filename
    /// specified in the part's `Content-Disposition`.
    pub files: Vec<(String, UploadedFile)>,
}

impl FormData {
    pub fn new() -> FormData {
        FormData { fields: vec![], files: vec![] }
    }
}

/// Parses and processes `hyper::server::Request` data that is `multipart/form-data` content.
///
/// The request is streamed, and this function saves embedded uploaded files to disk as they are
/// encountered by the parser.
pub fn parse_multipart(request: &mut Request) -> Result<FormData, Error> {
    let boundary = try!(get_boundary(request));
    let mut reader = BufReader::with_capacity(4096, request);
    let mut form_data = FormData::new();
    try!(run_state_machine(boundary, &mut reader, &mut form_data, MultipartSubLevel::FormData));
    Ok(form_data)
}

// A state in the parsing state machine.
enum State {
    // Discard until after initial boundary and CRLF.
    Discarding,
    // Capture headers to blank line.
    ReadingHeaders,
    // Capture entire `multipart/mixed` body until boundary, then discard past CRLF.
    CapturingMixed(ContentDisposition, ContentType),
    // Capture value until boundary, then discard past CRLF.
    CapturingValue(ContentDisposition),
    // Capture file until boundary, then discard past CRLF.
    CapturingFile(ContentDisposition, Option<ContentType>),
}

// A multipart MIME parsing mode.
#[derive(PartialEq)]
enum MultipartSubLevel {
    // Represents `multipart/form-data`.
    FormData,
    // Represents `multipart/mixed` with a `name` key.
    Mixed(String),
}

// Parse either a `multipart/form-data` or `multipart/mixed` MIME body.
fn run_state_machine<R: BufRead>(boundary: String, reader: &mut R, form_data: &mut FormData,
                                 mode: MultipartSubLevel) -> Result<(), Error> {
    use State::*;
    use MultipartSubLevel::*;

    let boundary = boundary.into_bytes();
    let crlf_boundary = crlf_boundary(&boundary);
    let mut state = Discarding;

    loop {
        let mut buf: Vec<u8> = Vec::new();

        match state {
            Discarding => {
                // Read up to and including the boundary.
                let read = try!(reader.stream_until_token(&boundary, &mut buf));
                if read == 0 {
                    return Err(Error::Eof);
                }

                state = ReadingHeaders;
            },
            ReadingHeaders => {
                {
                    // If the next two lookahead characters are '--', parsing is finished.
                    let peeker = try!(reader.fill_buf());
                    if peeker.len() >= 2 && &peeker[..2] == b"--" {
                        return Ok(());
                    }
                }

                // Read up to and including the CRLF after the boundary.
                let read = try!(reader.stream_until_token(b"\r\n", &mut buf));
                if read == 0 {
                    return Err(Error::Eof);
                }

                buf.truncate(0);
                let read = try!(reader.stream_until_token(b"\r\n\r\n", &mut buf));
                if read == 0 {
                    return Err(Error::Eof);
                }
                // `parse_headers` requires this token at the end:
                buf.extend(b"\r\n\r\n".iter().map(|&i| i));

                // Parse the headers.
                let mut header_memory = [httparse::EMPTY_HEADER; 4];
                match httparse::parse_headers(&buf, &mut header_memory) {
                    Ok(httparse::Status::Complete((_, raw_headers))) => {
                        // Turn raw headers into hyper headers.
                        let headers = try!(Headers::from_raw(raw_headers));

                        let cd: &ContentDisposition = match headers.get() {
                            Some(cd) => cd,
                            None => return Err(Error::MissingDisposition),
                        };

                        let ct: Option<&ContentType> = headers.get();

                        match mode {
                            FormData if cd.disposition == "form-data" => {
                                state = if is_multipart_mixed(ct) {
                                    CapturingMixed(cd.clone(), ct.unwrap().clone())
                                } else if ct.is_some() || cd.filename.is_some() {
                                    CapturingFile(cd.clone(), ct.map(|ct| ct.clone()))
                                } else {
                                    CapturingValue(cd.clone())
                                };
                            },
                            Mixed(_) if cd.disposition == "file" => {
                                state = CapturingFile(cd.clone(), ct.map(|ct| ct.clone()));
                            },
                            _ => return Err(Error::InvalidDisposition),
                        }
                    },
                    Ok(httparse::Status::Partial) => return Err(Error::PartialHeaders),
                    Err(err) => return Err(From::from(err)),
                }
            },
            CapturingMixed(cd, ct) => {
                let boundary = try!(get_boundary_token(&(ct.0).2));
                let mode = Mixed(try!(cd.name.ok_or(Error::NoName)));
                try!(run_state_machine(boundary, reader, form_data, mode));
                state = Discarding;
            },
            CapturingValue(_) if mode != FormData => unreachable!(),
            CapturingValue(cd) => {
                buf.truncate(0);
                let _ = try!(reader.stream_until_token(&crlf_boundary, &mut buf));

                let key = try!(cd.name.ok_or(Error::NoName));
                let val = try!(String::from_utf8(buf));

                form_data.fields.push((key, val));

                state = ReadingHeaders;
            },
            CapturingFile(cd, ct) => {
                // Setup a file to capture the contents.
                let mut path = try!(TempDir::new("formdata")).into_path();
                path.push(TextNonce::sized_urlsafe(32).unwrap().into_string());
                let mut file = try!(File::create(path.clone()));

                // Stream out the file.
                let read = try!(reader.stream_until_token(&crlf_boundary, &mut file));

                // TODO: Handle Content-Transfer-Encoding.

                let key = match mode {
                    Mixed(ref name) => name.clone(),
                    FormData => try!(cd.name.ok_or(Error::NoName)),
                };

                let file = UploadedFile {
                    path: path,
                    filename: cd.filename.clone(),
                    content_type: ct.map_or(mime!(Text/Plain; Charset=Utf8), |ct| ct.0),
                    size: read - crlf_boundary.len()
                };

                form_data.files.push((key, file));
                state = ReadingHeaders;
            },
        }
    }
}

fn is_multipart_mixed(ct: Option<&ContentType>) -> bool {
    let ct = match ct {
        Some(ct) => ct,
        None => return false,
    };

    let ContentType(ref mime) = *ct;
    let Mime(ref top_level, ref sub_level, _) = *mime;

    if *top_level != TopLevel::Multipart {
        return false;
    }

    match *sub_level {
        SubLevel::Ext(ref ext) => ext == "mixed",
        _ => false,
    }
}

fn get_boundary(request: &Request) -> Result<String, Error> {
    // Verify that the request is 'Content-Type: multipart/form-data'.
    let ct: &ContentType = match request.headers().get() {
        Some(ct) => ct,
        None => return Err(Error::NoRequestContentType),
    };
    let ContentType(ref mime) = *ct;
    let Mime(ref top_level, ref sub_level, ref params) = *mime;

    if *top_level != TopLevel::Multipart {
        return Err(Error::NotMultipart);
    }

    if *sub_level != SubLevel::FormData {
        return Err(Error::NotFormData);
    }

    get_boundary_token(params)
}

fn get_boundary_token(params: &[Param]) -> Result<String, Error> {
    for &(ref attr, ref val) in params.iter() {
        if let (&Attr::Boundary, &Value::Ext(ref val)) = (attr, val) {
            return Ok(format!("--{}", val.clone()));
        }
    }

    Err(Error::BoundaryNotSpecified)
}

fn crlf_boundary(boundary: &Vec<u8>) -> Vec<u8> {
    let mut crlf_boundary = Vec::with_capacity(2 + boundary.len());
    crlf_boundary.extend(b"\r\n".iter().map(|&i| i));
    crlf_boundary.extend(boundary.clone());
    crlf_boundary
}

/// A wrapper for request data to provide parsing multipart requests to any front-end that provides
/// a `hyper::header::Headers` and a `std::io::Read` of the request's entire body.
pub trait Request: Read {
    /// Returns a reference to the request's headers.
    fn headers(&self) -> &Headers;
}

impl<'a,'b> Request for HyperRequest<'a,'b> {
    fn headers(&self) -> &Headers {
        &self.headers
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    use std::net::SocketAddr;

    use hyper::buffer::BufReader;
    use hyper::net::NetworkStream;
    use hyper::server::Request as HyperRequest;

    use mock::MockStream;

    #[test]
    fn parser() {
        let input = b"POST / HTTP/1.1\r\n\
                      Host: example.domain\r\n\
                      Content-Type: multipart/form-data; boundary=\"abcdefg\"\r\n\
                      Content-Length: 1000\r\n\
                      \r\n\
                      --abcdefg\r\n\
                      Content-Disposition: form-data; name=\"field1\"\r\n\
                      \r\n\
                      data1\r\n\
                      --abcdefg\r\n\
                      Content-Disposition: form-data; name=\"field2\"; filename=\"image.gif\"\r\n\
                      Content-Type: image/gif\r\n\
                      \r\n\
                      This is a file\r\n\
                      with two lines\r\n\
                      --abcdefg\r\n\
                      Content-Disposition: form-data; name=\"field3\"; filename=\"file.txt\"\r\n\
                      \r\n\
                      This is a file\r\n\
                      --abcdefg--";

        let mut mock = MockStream::with_input(input);

        let mock: &mut NetworkStream = &mut mock;
        let mut stream = BufReader::new(mock);
        let sock: SocketAddr = "127.0.0.1:80".parse().unwrap();
        let mut req = HyperRequest::new(&mut stream, sock).unwrap();

        match parse_multipart(&mut req) {
            Ok(form_data) => {
                assert_eq!(form_data.fields.len(), 1);
                for (key, val) in form_data.fields {
                    if &key == "field1" {
                        assert_eq!(&val, "data1");
                    }
                }

                assert_eq!(form_data.files.len(), 2);
                for (key, file) in form_data.files {
                    if &key == "field2" {
                        assert_eq!(file.size, 30);
                        assert_eq!(&file.filename.unwrap(), "image.gif");
                        assert_eq!(file.content_type, mime!(Image/Gif));
                    } else if &key == "field3" {
                        assert_eq!(file.size, 14);
                        assert_eq!(&file.filename.unwrap(), "file.txt");
                        assert_eq!(file.content_type, mime!(Text/Plain; Charset=Utf8));
                    }
                }
            },
            Err(err) => panic!("{}", err),
        }
    }

    #[test]
    fn mixed_parser() {
        let input = b"POST / HTTP/1.1\r\n\
                      Host: example.domain\r\n\
                      Content-Type: multipart/form-data; boundary=AaB03x\r\n\
                      Content-Length: 1000\r\n\
                      \r\n\
                      --AaB03x\r\n\
                      Content-Disposition: form-data; name=\"submit-name\"\r\n\
                      \r\n\
                      Larry\r\n\
                      --AaB03x\r\n\
                      Content-Disposition: form-data; name=\"files\"\r\n\
                      Content-Type: multipart/mixed; boundary=BbC04y\r\n\
                      \r\n\
                      --BbC04y\r\n\
                      Content-Disposition: file; filename=\"file1.txt\"\r\n\
                      \r\n\
                      ... contents of file1.txt ...\r\n\
                      --BbC04y\r\n\
                      Content-Disposition: file; filename=\"awesome_image.gif\"\r\n\
                      Content-Type: image/gif\r\n\
                      Content-Transfer-Encoding: binary\r\n\
                      \r\n\
                      ... contents of awesome_image.gif ...\r\n\
                      --BbC04y--\r\n\
                      --AaB03x--";

        let mut mock = MockStream::with_input(input);

        let mock: &mut NetworkStream = &mut mock;
        let mut stream = BufReader::new(mock);
        let sock: SocketAddr = "127.0.0.1:80".parse().unwrap();
        let mut req = HyperRequest::new(&mut stream, sock).unwrap();

        match parse_multipart(&mut req) {
            Ok(form_data) => {
                assert_eq!(form_data.fields.len(), 1);
                for (key, val) in form_data.fields {
                    if &key == "submit-name" {
                        assert_eq!(&val, "Larry");
                    }
                }

                assert_eq!(form_data.files.len(), 2);
                for (key, file) in form_data.files {
                    assert_eq!(&key, "files");
                    match &file.filename.unwrap()[..] {
                        "file1.txt" => {
                            assert_eq!(file.size, 29);
                            assert_eq!(file.content_type, mime!(Text/Plain; Charset=Utf8));
                        }
                        "awesome_image.gif" => {
                            assert_eq!(file.size, 37);
                            assert_eq!(file.content_type, mime!(Image/Gif));
                        },
                        _ => unreachable!(),
                    }
                }
            },
            Err(err) => panic!("{}", err),
        }
    }
}
<commit_msg>Improve Request ergonomics<commit_after>//! This crate parses and processes `hyper::server::Request` data that contains
//! `multipart/form-data` content.
//!
//! The main entry point is `parse_multipart`

extern crate httparse;
extern crate hyper;
extern crate libc;
#[macro_use]
extern crate mime;
extern crate tempdir;
extern crate textnonce;

pub mod buf;
pub mod error;
mod headers;
#[cfg(test)]
mod mock;

use std::path::PathBuf;
use std::fs::File;
use std::io::{BufRead, BufReader, Read};

use hyper::header::{ContentType, Headers};
use hyper::server::Request as HyperRequest;
use mime::{Attr, Mime, Param, SubLevel, TopLevel, Value};
use tempdir::TempDir;
use textnonce::TextNonce;

use buf::BufReadPlus;
pub use error::Error;
use headers::ContentDisposition;

/// An uploaded file that was received as part of `multipart/form-data` parsing.
///
/// Files are streamed to disk because they may not fit in memory.
#[derive(Clone, Debug, PartialEq)]
pub struct UploadedFile {
    /// The temporary file where the data was saved.
    pub path: PathBuf,
    /// The filename that was specified in the data, unfiltered. It may or may not be legal on the
    /// local filesystem.
    pub filename: Option<String>,
    /// The unvalidated content-type that was specified in the data.
    pub content_type: Mime,
    /// The size of the file.
    pub size: usize,
}

/// The extracted text fields and uploaded files from a `multipart/form-data` request.
///
/// Use `parse_multipart` to devise this object from a request.
pub struct FormData {
    /// Key-value pairs for plain text fields. Technically, these are form data parts with no
    /// filename specified in the part's `Content-Disposition`.
    pub fields: Vec<(String, String)>,
    /// Key-value pairs for temporary files. Technically, these are form data parts with a filename
    /// specified in the part's `Content-Disposition`.
    pub files: Vec<(String, UploadedFile)>,
}

impl FormData {
    pub fn new() -> FormData {
        FormData { fields: vec![], files: vec![] }
    }
}

/// Parses and processes `hyper::server::Request` data that is `multipart/form-data` content.
///
/// The request is streamed, and this function saves embedded uploaded files to disk as they are
/// encountered by the parser.
pub fn parse_multipart(request: &mut Request) -> Result<FormData, Error> {
    let boundary = try!(get_boundary(request));
    let mut reader = BufReader::with_capacity(4096, request.read_mut());
    let mut form_data = FormData::new();
    try!(run_state_machine(boundary, &mut reader, &mut form_data, MultipartSubLevel::FormData));
    Ok(form_data)
}

// A state in the parsing state machine.
enum State {
    // Discard until after initial boundary and CRLF.
    Discarding,
    // Capture headers to blank line.
    ReadingHeaders,
    // Capture entire `multipart/mixed` body until boundary, then discard past CRLF.
    CapturingMixed(ContentDisposition, ContentType),
    // Capture value until boundary, then discard past CRLF.
    CapturingValue(ContentDisposition),
    // Capture file until boundary, then discard past CRLF.
    CapturingFile(ContentDisposition, Option<ContentType>),
}

// A multipart MIME parsing mode.
#[derive(PartialEq)]
enum MultipartSubLevel {
    // Represents `multipart/form-data`.
    FormData,
    // Represents `multipart/mixed` with a `name` key.
    Mixed(String),
}

// Parse either a `multipart/form-data` or `multipart/mixed` MIME body.
fn run_state_machine<R: BufRead>(boundary: String, reader: &mut R, form_data: &mut FormData,
                                 mode: MultipartSubLevel) -> Result<(), Error> {
    use State::*;
    use MultipartSubLevel::*;

    let boundary = boundary.into_bytes();
    let crlf_boundary = crlf_boundary(&boundary);
    let mut state = Discarding;

    loop {
        let mut buf: Vec<u8> = Vec::new();

        match state {
            Discarding => {
                // Read up to and including the boundary.
                let read = try!(reader.stream_until_token(&boundary, &mut buf));
                if read == 0 {
                    return Err(Error::Eof);
                }

                state = ReadingHeaders;
            },
            ReadingHeaders => {
                {
                    // If the next two lookahead characters are '--', parsing is finished.
                    let peeker = try!(reader.fill_buf());
                    if peeker.len() >= 2 && &peeker[..2] == b"--" {
                        return Ok(());
                    }
                }

                // Read up to and including the CRLF after the boundary.
                let read = try!(reader.stream_until_token(b"\r\n", &mut buf));
                if read == 0 {
                    return Err(Error::Eof);
                }

                buf.truncate(0);
                let read = try!(reader.stream_until_token(b"\r\n\r\n", &mut buf));
                if read == 0 {
                    return Err(Error::Eof);
                }
                // `parse_headers` requires this token at the end:
                buf.extend(b"\r\n\r\n".iter().map(|&i| i));

                // Parse the headers.
                let mut header_memory = [httparse::EMPTY_HEADER; 4];
                match httparse::parse_headers(&buf, &mut header_memory) {
                    Ok(httparse::Status::Complete((_, raw_headers))) => {
                        // Turn raw headers into hyper headers.
                        let headers = try!(Headers::from_raw(raw_headers));

                        let cd: &ContentDisposition = match headers.get() {
                            Some(cd) => cd,
                            None => return Err(Error::MissingDisposition),
                        };

                        let ct: Option<&ContentType> = headers.get();

                        match mode {
                            FormData if cd.disposition == "form-data" => {
                                state = if is_multipart_mixed(ct) {
                                    CapturingMixed(cd.clone(), ct.unwrap().clone())
                                } else if ct.is_some() || cd.filename.is_some() {
                                    CapturingFile(cd.clone(), ct.map(|ct| ct.clone()))
                                } else {
                                    CapturingValue(cd.clone())
                                };
                            },
                            Mixed(_) if cd.disposition == "file" => {
                                state = CapturingFile(cd.clone(), ct.map(|ct| ct.clone()));
                            },
                            _ => return Err(Error::InvalidDisposition),
                        }
                    },
                    Ok(httparse::Status::Partial) => return Err(Error::PartialHeaders),
                    Err(err) => return Err(From::from(err)),
                }
            },
            CapturingMixed(cd, ct) => {
                let boundary = try!(get_boundary_token(&(ct.0).2));
                let mode = Mixed(try!(cd.name.ok_or(Error::NoName)));
                try!(run_state_machine(boundary, reader, form_data, mode));
                state = Discarding;
            },
            CapturingValue(_) if mode != FormData => unreachable!(),
            CapturingValue(cd) => {
                buf.truncate(0);
                let _ = try!(reader.stream_until_token(&crlf_boundary, &mut buf));

                let key = try!(cd.name.ok_or(Error::NoName));
                let val = try!(String::from_utf8(buf));

                form_data.fields.push((key, val));

                state = ReadingHeaders;
            },
            CapturingFile(cd, ct) => {
                // Setup a file to capture the contents.
                let mut path = try!(TempDir::new("formdata")).into_path();
                path.push(TextNonce::sized_urlsafe(32).unwrap().into_string());
                let mut file = try!(File::create(path.clone()));

                // Stream out the file.
                let read = try!(reader.stream_until_token(&crlf_boundary, &mut file));

                // TODO: Handle Content-Transfer-Encoding.

                let key = match mode {
                    Mixed(ref name) => name.clone(),
                    FormData => try!(cd.name.ok_or(Error::NoName)),
                };

                let file = UploadedFile {
                    path: path,
                    filename: cd.filename.clone(),
                    content_type: ct.map_or(mime!(Text/Plain; Charset=Utf8), |ct| ct.0),
                    size: read - crlf_boundary.len()
                };

                form_data.files.push((key, file));
                state = ReadingHeaders;
            },
        }
    }
}

fn is_multipart_mixed(ct: Option<&ContentType>) -> bool {
    let ct = match ct {
        Some(ct) => ct,
        None => return false,
    };

    let ContentType(ref mime) = *ct;
    let Mime(ref top_level, ref sub_level, _) = *mime;

    if *top_level != TopLevel::Multipart {
        return false;
    }

    match *sub_level {
        SubLevel::Ext(ref ext) => ext == "mixed",
        _ => false,
    }
}

fn get_boundary(request: &Request) -> Result<String, Error> {
    // Verify that the request is 'Content-Type: multipart/form-data'.
    let ct: &ContentType = match request.headers().get() {
        Some(ct) => ct,
        None => return Err(Error::NoRequestContentType),
    };
    let ContentType(ref mime) = *ct;
    let Mime(ref top_level, ref sub_level, ref params) = *mime;

    if *top_level != TopLevel::Multipart {
        return Err(Error::NotMultipart);
    }

    if *sub_level != SubLevel::FormData {
        return Err(Error::NotFormData);
    }

    get_boundary_token(params)
}

fn get_boundary_token(params: &[Param]) -> Result<String, Error> {
    for &(ref attr, ref val) in params.iter() {
        if let (&Attr::Boundary, &Value::Ext(ref val)) = (attr, val) {
            return Ok(format!("--{}", val.clone()));
        }
    }

    Err(Error::BoundaryNotSpecified)
}

fn crlf_boundary(boundary: &Vec<u8>) -> Vec<u8> {
    let mut crlf_boundary = Vec::with_capacity(2 + boundary.len());
    crlf_boundary.extend(b"\r\n".iter().map(|&i| i));
    crlf_boundary.extend(boundary.clone());
    crlf_boundary
}

/// A wrapper for request data to provide parsing multipart requests to any front-end that provides
/// a `hyper::header::Headers` and a `std::io::Read` of the request's entire body.
pub trait Request {
    /// Returns a reference to the request's headers.
    fn headers(&self) -> &Headers;

    /// Returns a mutable reference to the request's body reader.
    fn read_mut(&mut self) -> &mut Read;
}

impl<'a,'b> Request for HyperRequest<'a,'b> {
    fn headers(&self) -> &Headers {
        &self.headers
    }

    fn read_mut(&mut self) -> &mut Read {
        self
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    use std::net::SocketAddr;

    use hyper::buffer::BufReader;
    use hyper::net::NetworkStream;
    use hyper::server::Request as HyperRequest;

    use mock::MockStream;

    #[test]
    fn parser() {
        let input = b"POST / HTTP/1.1\r\n\
                      Host: example.domain\r\n\
                      Content-Type: multipart/form-data; boundary=\"abcdefg\"\r\n\
                      Content-Length: 1000\r\n\
                      \r\n\
                      --abcdefg\r\n\
                      Content-Disposition: form-data; name=\"field1\"\r\n\
                      \r\n\
                      data1\r\n\
                      --abcdefg\r\n\
                      Content-Disposition: form-data; name=\"field2\"; filename=\"image.gif\"\r\n\
                      Content-Type: image/gif\r\n\
                      \r\n\
                      This is a file\r\n\
                      with two lines\r\n\
                      --abcdefg\r\n\
                      Content-Disposition: form-data; name=\"field3\"; filename=\"file.txt\"\r\n\
                      \r\n\
                      This is a file\r\n\
                      --abcdefg--";

        let mut mock = MockStream::with_input(input);

        let mock: &mut NetworkStream = &mut mock;
        let mut stream = BufReader::new(mock);
        let sock: SocketAddr = "127.0.0.1:80".parse().unwrap();
        let mut req = HyperRequest::new(&mut stream, sock).unwrap();

        match parse_multipart(&mut req) {
            Ok(form_data) => {
                assert_eq!(form_data.fields.len(), 1);
                for (key, val) in form_data.fields {
                    if &key == "field1" {
                        assert_eq!(&val, "data1");
                    }
                }

                assert_eq!(form_data.files.len(), 2);
                for (key, file) in form_data.files {
                    if &key == "field2" {
                        assert_eq!(file.size, 30);
                        assert_eq!(&file.filename.unwrap(), "image.gif");
                        assert_eq!(file.content_type, mime!(Image/Gif));
                    } else if &key == "field3" {
                        assert_eq!(file.size, 14);
                        assert_eq!(&file.filename.unwrap(), "file.txt");
                        assert_eq!(file.content_type, mime!(Text/Plain; Charset=Utf8));
                    }
                }
            },
            Err(err) => panic!("{}", err),
        }
    }

    #[test]
    fn mixed_parser() {
        let input = b"POST / HTTP/1.1\r\n\
                      Host: example.domain\r\n\
                      Content-Type: multipart/form-data; boundary=AaB03x\r\n\
                      Content-Length: 1000\r\n\
                      \r\n\
                      --AaB03x\r\n\
                      Content-Disposition: form-data; name=\"submit-name\"\r\n\
                      \r\n\
                      Larry\r\n\
                      --AaB03x\r\n\
                      Content-Disposition: form-data; name=\"files\"\r\n\
                      Content-Type: multipart/mixed; boundary=BbC04y\r\n\
                      \r\n\
                      --BbC04y\r\n\
                      Content-Disposition: file; filename=\"file1.txt\"\r\n\
                      \r\n\
                      ... contents of file1.txt ...\r\n\
                      --BbC04y\r\n\
                      Content-Disposition: file; filename=\"awesome_image.gif\"\r\n\
                      Content-Type: image/gif\r\n\
                      Content-Transfer-Encoding: binary\r\n\
                      \r\n\
                      ... contents of awesome_image.gif ...\r\n\
                      --BbC04y--\r\n\
                      --AaB03x--";

        let mut mock = MockStream::with_input(input);

        let mock: &mut NetworkStream = &mut mock;
        let mut stream = BufReader::new(mock);
        let sock: SocketAddr = "127.0.0.1:80".parse().unwrap();
        let mut req = HyperRequest::new(&mut stream, sock).unwrap();

        match parse_multipart(&mut req) {
            Ok(form_data) => {
                assert_eq!(form_data.fields.len(), 1);
                for (key, val) in form_data.fields {
                    if &key == "submit-name" {
                        assert_eq!(&val, "Larry");
                    }
                }

                assert_eq!(form_data.files.len(), 2);
                for (key, file) in form_data.files {
                    assert_eq!(&key, "files");
                    match &file.filename.unwrap()[..] {
                        "file1.txt" => {
                            assert_eq!(file.size, 29);
                            assert_eq!(file.content_type, mime!(Text/Plain; Charset=Utf8));
                        }
                        "awesome_image.gif" => {
                            assert_eq!(file.size, 37);
                            assert_eq!(file.content_type, mime!(Image/Gif));
                        },
                        _ => unreachable!(),
                    }
                }
            },
            Err(err) => panic!("{}", err),
        }
    }
}
<|endoftext|>


<commit_before>mod interval;
mod adjacent_bound;

pub use adjacent_bound::AdjacentBound;

use interval::Interval;
use std::borrow::Borrow;
use std::ops::Range;
use std::collections::VecDeque;
use std::iter::FromIterator;

#[derive(Debug)]
struct DietNode<T> {
    interval: Interval<T>,
    left: Option<Box<DietNode<T>>>,
    right: Option<Box<DietNode<T>>>,
}

#[derive(Debug)]
pub struct Diet<T> {
    root: Option<Box<DietNode<T>>>,
}

#[derive(Debug)]
struct DietNodeIterator<T> {
    pending: VecDeque<T>
}

trait DietNodeRelationshipOps {
    type Bound : AdjacentBound;

    fn max_parent_mut(&mut self) -> &mut Self;

    fn min_parent_mut(&mut self) -> &mut Self;

    fn traverse_to_node_containing(&self, value: &Self::Bound) -> &Self;

    fn traverse_to_node_containing_mut(&mut self, value: &Self::Bound) -> &mut Self;

    fn insert(&mut self, value: Self::Bound) -> bool;

    fn remove(&mut self, value: &Self::Bound) -> bool;
}

impl<T : AdjacentBound> DietNodeRelationshipOps for Option<Box<DietNode<T>>> {
    type Bound = T;

    fn max_parent_mut(&mut self) -> &mut Self {
        let mut parent = self;

        loop {
            let move_next = parent.as_ref().map_or(false, |c|c.right.is_some());
            if move_next {
                parent = &mut {parent}.as_mut().unwrap().right
            } else {
                return parent;
            }
        }
    }

    fn min_parent_mut(&mut self) ->  &mut Self {
        let mut parent = self;

        loop {
            let move_next = parent.as_ref().map_or(false, |c|c.left.is_some());
            if move_next {
                parent = &mut {parent}.as_mut().unwrap().left
            } else {
                return parent;
            }
        }
    }

    fn traverse_to_node_containing(&self, value: &Self::Bound) -> &Self {
        let mut current = self;

        while let Some(ref node) = *current {
            if node.interval.contains(value) {
                break;
            }

            if value < node.interval.inclusive_start() {
                current = &node.left
            } else {
                debug_assert!(value >= node.interval.exclusive_end());
                current = &node.right
            }
        }

        current
    }

    fn traverse_to_node_containing_mut(&mut self, value: &Self::Bound) -> &mut Self {
        let mut current = self;

        loop {
            let local_current = current;
            if let Some(node) = local_current.take() {
                if node.interval.contains(value) {
                    *local_current = Some(node);
                    return local_current;
                }

                if node.is_adjacent(value) {
                    *local_current = Some(node);
                    return local_current;
                }

                if value < node.interval.inclusive_start() {
                    *local_current = Some(node);
                    current = &mut local_current.as_mut().unwrap().left
                } else {
                    debug_assert!(value >= node.interval.exclusive_end());
                    *local_current = Some(node);
                    current = &mut local_current.as_mut().unwrap().right
                }
            } else {
                return local_current;
            }
        }
    }

    fn insert(&mut self, value: Self::Bound) -> bool {
        let mut current = self;
        loop {
            let local_current = current;
            if let Some(ref mut node) = *local_current {
                if value.comes_before(node.interval.inclusive_start()) {

                    node.interval.set_inclusive_start(value);
                    node.join_left();

                    return true;

                } else if &value == node.interval.exclusive_end() {

                    node.interval.set_exclusive_end(value.increment());
                    node.join_right();

                    return true;

                } if &value < node.interval.inclusive_start() {
                    current = &mut node.left;
                } else if &value > node.interval.exclusive_end() {
                    current = &mut node.right;
                }  else {
                    debug_assert!(node.interval.contains(&value), "the value should already be contained within the interval");
                    return false;
                }
            } else {
                let new_exclusive_end = value.increment();

                *local_current = Some(Box::new(DietNode {
                    interval: Interval::from(value..new_exclusive_end),
                    left: None,
                    right: None
                }));

                return true;
            }
        }
    }

    fn remove(&mut self, value: &Self::Bound) -> bool {
        let containing_node = self.traverse_to_node_containing_mut(value);

        let mut delete_node = false;

        let removed = if let Some(ref mut node) = *containing_node {
            if value == node.interval.inclusive_start() {
                if value.comes_before(node.interval.exclusive_end()) {
                    // The entire node needs to be removed but any children should be moved up
                    match (node.left.take(), node.right.take()) {
                        (None, None) => {
                            delete_node = true
                        }
                        (Some(child), None) | (None, Some(child)) => {
                            *node = child;
                        }
                        (mut left @ Some(_), right @ Some(_)) => {
                            let max = *(left.max_parent_mut().take().unwrap());

                            node.interval = max.interval;
                            node.left = left;
                            node.right = right;
                        }
                    }
                } else {
                    node.interval.inclusive_start_mut().increment_ref();
                }
            } else if value.comes_before(node.interval.exclusive_end()) {
                node.interval.exclusive_end_mut().decrement_ref();
            } else {
                node.split_on_value(value);
            };
            true
        } else {
            false
        };

        if delete_node {
            *containing_node = None;
        }

        removed
    }
}

impl<'a, T> Iterator for DietNodeIterator<&'a DietNode<T>> {
    type Item = &'a DietNode<T>;

    fn next(&mut self) -> Option<&'a DietNode<T>> {
        self.pending.pop_front()
            .map(|current| {
                let children = [&current.left, &current.right];
                for child in children.into_iter().flat_map(|x| x.as_ref()) {
                    self.pending.push_back(child);

                }
                current
            })
    }
}

impl<T> DietNode<T> {
    fn min_inclusive_start(&self) -> &T {
        let mut current = self;

        loop {
            if let Some(child) = current.left.as_ref() {
                current = child;
            } else {
                return current.interval.inclusive_start();
            }
        }
    }

    fn max_exclusive_end(&self) -> &T {
        let mut current = self;

        loop {
            if let Some(child) = current.right.as_ref() {
                current = child;
            } else {
                return current.interval.exclusive_end();
            }
        }
    }

    pub fn nodes(&self) -> DietNodeIterator<&DietNode<T>> {
        let mut pending = VecDeque::new();
        pending.push_back(self);

        DietNodeIterator {
            pending: pending
        }
    }

    pub fn len(&self) -> usize {
        self.nodes().count()
    }

    pub fn is_adjacent<Q: ?Sized>(&self, value: &Q) -> bool where T : Borrow<Q>, Q : AdjacentBound {
        value.comes_before(self.interval.inclusive_start().borrow()) ||
                    value == self.interval.exclusive_end().borrow()
    }
}

impl<T: AdjacentBound> DietNode<T> {

    pub fn split_on_value(&mut self, value: &T) {
        debug_assert!(self.interval.contains(value), "the value should be contained within the interval");

        let old_exclusive_end = self.interval.set_exclusive_end(value.decrement());

        let new_right_interval = Interval::from(value.increment()..old_exclusive_end);

        let right = self.right.take();
        self.right = Some(Box::new(DietNode {
            interval: new_right_interval,
            left: None,
            right: right
        }));
    }

    fn join_left(&mut self) {
        let max_parent_mut = self.left.max_parent_mut();

        let interval = &mut self.interval;

        let should_merge = max_parent_mut.as_ref().map_or(false, |c| c.interval.exclusive_end() == interval.inclusive_start());

        if should_merge {
            let max = *max_parent_mut.take().unwrap();
            *max_parent_mut = max.left;
            interval.set_inclusive_start(max.interval.take_inclusive_start());
        }
    }

    fn join_right(&mut self) {
        let min_parent_mut = self.right.min_parent_mut();

        let interval = &mut self.interval;

        let should_merge = min_parent_mut.as_ref().map_or(false, |c| c.interval.inclusive_start() == interval.exclusive_end());

        if should_merge {
            let min = *min_parent_mut.take().unwrap();
            *min_parent_mut = min.right;
            interval.set_exclusive_end(min.interval.take_exclusive_end());
        }
    }
}

impl<T> Diet<T> {
    pub fn len(&self) -> usize {
        self.root.as_ref().map_or(0, |n| n.len())
    }
}

impl<T: AdjacentBound> Diet<T> {

    pub fn insert(&mut self, value: T) -> bool {
        self.root.insert(value)
    }

    pub fn remove(&mut self, value: &T) -> bool {
        self.root.remove(value)
    }

    pub fn contains(&self, value: &T) -> bool {
        self.root.traverse_to_node_containing(value).is_some()
    }
}

// TODO LH Implement iterators which traverse the intervals in the tree

impl<T> Default for Diet<T>{
    fn default() -> Self {
        Self {
            root: None
        }
    }
}

impl<A : AdjacentBound> FromIterator<A> for Diet<A> {
    fn from_iter<T>(iter: T) -> Self where T: IntoIterator<Item = A> {
        let mut diet = Diet::default();

        for value in iter {
            diet.insert(value);
        }

        diet
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn contains_returns_false_for_default(){
        let diet = Diet::<u32>::default();

        assert!(!diet.contains(&5));
    }

    #[test]
    fn contains_returns_true_for_existing_value(){
        let mut diet = Diet::from_iter([3, 1, 5].iter().cloned());

        assert!(diet.contains(&5));
    }

    #[test]
    fn len_returns_zero_for_default() {
        let diet = Diet::<u32>::default();

        assert_eq!(diet.len(), 0);
    }

    #[test]
    fn insert_returns_true_for_new_value() {
        let mut diet = Diet::default();

        assert!(diet.insert(1));
    }

    #[test]
    fn insert_returns_false_for_existing_value() {
        let mut diet = Diet::default();

        diet.insert(1);
        assert!(!diet.insert(1));
    }

    #[test]
    fn insert_extends_existing_ranges() {
        let mut diet = Diet::from_iter([1, 5].iter().cloned());

        diet.insert(2);
        diet.insert(4);

        assert_eq!(diet.len(), 2);
    }

    #[test]
    fn insert_combines_ranges() {
        let mut diet = Diet::from_iter([3, 1, 5, 8].iter().cloned());

        diet.insert(2);
        diet.insert(4);
        diet.insert(6);
        diet.insert(7);

        assert_eq!(diet.len(), 1);
    }

    #[test]
    fn remove_returns_false_for_default(){
        let mut diet = Diet::<u32>::default();

        assert!(!diet.remove(&5));
    }

    #[test]
    fn remove_returns_false_for_non_existant_value(){
        let mut diet = Diet::from_iter([1, 2, 3, 6].iter().cloned());

        assert!(!diet.remove(&10));
    }

    #[test]
    fn remove_removes_values(){
        let mut diet = Diet::from_iter([5, 6, 1, 2, 8, 9].iter().cloned());

        assert!(diet.remove(&5));
        assert!(diet.remove(&6));

        assert_eq!(diet.len(), 2);
    }

     #[test]
    fn test_delete_split_left() {
        let mut d = Diet::default();
        d.insert(8);
        d.insert(9);
        d.insert(10);

        d.insert(2);
        d.insert(4);
        d.insert(6);
        d.insert(14);
        d.insert(12);
        d.insert(16);

        d.remove(&8);
        d.remove(&9);
        d.remove(&10);

        assert!(d.contains(&2));
        assert!(d.contains(&4));
        assert!(d.contains(&6));
        assert!(d.contains(&14));
        assert!(d.contains(&12));
        assert!(d.contains(&16));
    }

    #[test]
    fn test_delete_simple() {
        let mut d = Diet::default();
        d.remove(&5);
        d.insert(5);
        assert!(d.contains(&5));
        d.remove(&5);
        assert!(!d.contains(&5));
    }

    #[test]
    fn test_delete_branch() {
        let mut d = Diet::default();
        d.insert(5);
        d.insert(3);
        d.insert(7);

        d.remove(&3);
        assert!(!d.contains(&3));
        assert!(d.contains(&5));
        assert!(d.contains(&7));

        d.remove(&7);
        assert!(d.contains(&5));
    }

    #[test]
    fn test_delete_replace_merge() {
        let mut d = Diet::default();
        d.insert(5);
        d.insert(3);

        d.remove(&5);
        assert!(!d.contains(&5));
        assert!(d.contains(&3));

        d.insert(7);
        d.remove(&3);
        assert!(!d.contains(&3));
        assert!(d.contains(&7));
    }

    #[test]
    fn test_delete_lower_upper() {
        let mut d = Diet::default();
        d.insert(5);
        d.insert(6);
        d.insert(7);

        d.remove(&5);
        assert!(d.contains(&6));
        assert!(d.contains(&7));

        d.remove(&7);
        assert!(d.contains(&6));
    }

}
<commit_msg>traverse_to_node_containing_mut no longer takes and restores the node<commit_after>mod interval;
mod adjacent_bound;

pub use adjacent_bound::AdjacentBound;

use interval::Interval;
use std::borrow::Borrow;
use std::ops::Range;
use std::collections::VecDeque;
use std::iter::FromIterator;

#[derive(Debug)]
struct DietNode<T> {
    interval: Interval<T>,
    left: Option<Box<DietNode<T>>>,
    right: Option<Box<DietNode<T>>>,
}

#[derive(Debug)]
pub struct Diet<T> {
    root: Option<Box<DietNode<T>>>,
}

#[derive(Debug)]
struct DietNodeIterator<T> {
    pending: VecDeque<T>
}

trait DietNodeRelationshipOps {
    type Bound : AdjacentBound;

    fn max_parent_mut(&mut self) -> &mut Self;

    fn min_parent_mut(&mut self) -> &mut Self;

    fn traverse_to_node_containing(&self, value: &Self::Bound) -> &Self;

    fn traverse_to_node_containing_mut(&mut self, value: &Self::Bound) -> &mut Self;

    fn insert(&mut self, value: Self::Bound) -> bool;

    fn remove(&mut self, value: &Self::Bound) -> bool;
}

impl<T : AdjacentBound> DietNodeRelationshipOps for Option<Box<DietNode<T>>> {
    type Bound = T;

    fn max_parent_mut(&mut self) -> &mut Self {
        let mut parent = self;

        loop {
            let move_next = parent.as_ref().map_or(false, |c|c.right.is_some());
            if move_next {
                parent = &mut {parent}.as_mut().unwrap().right
            } else {
                return parent;
            }
        }
    }

    fn min_parent_mut(&mut self) ->  &mut Self {
        let mut parent = self;

        loop {
            let move_next = parent.as_ref().map_or(false, |c|c.left.is_some());
            if move_next {
                parent = &mut {parent}.as_mut().unwrap().left
            } else {
                return parent;
            }
        }
    }

    fn traverse_to_node_containing(&self, value: &Self::Bound) -> &Self {
        let mut current = self;

        while let Some(ref node) = *current {
            if node.interval.contains(value) {
                break;
            }

            if value < node.interval.inclusive_start() {
                current = &node.left
            } else {
                debug_assert!(value >= node.interval.exclusive_end());
                current = &node.right
            }
        }

        current
    }

    fn traverse_to_node_containing_mut(&mut self, value: &Self::Bound) -> &mut Self {
        let mut current = self;

        loop {

            match current.as_ref().map(|n| n.interval.contains(value)) {
                None | Some(true) => {
                    // If there is no current or current contains the value then return it
                    return current;
                }
                _ => { }
            }

            let node = {current}.as_mut().unwrap();
            if value < node.interval.inclusive_start() {
                current = &mut node.left;
            } else {
                debug_assert!(value >= node.interval.exclusive_end());

                current = &mut node.right;
            }
        }
    }

    fn insert(&mut self, value: Self::Bound) -> bool {
        let mut current = self;
        loop {
            let local_current = current;
            if let Some(ref mut node) = *local_current {
                if value.comes_before(node.interval.inclusive_start()) {

                    node.interval.set_inclusive_start(value);
                    node.join_left();

                    return true;

                } else if &value == node.interval.exclusive_end() {

                    node.interval.set_exclusive_end(value.increment());
                    node.join_right();

                    return true;

                } if &value < node.interval.inclusive_start() {
                    current = &mut node.left;
                } else if &value > node.interval.exclusive_end() {
                    current = &mut node.right;
                }  else {
                    debug_assert!(node.interval.contains(&value), "the value should already be contained within the interval");
                    return false;
                }
            } else {
                let new_exclusive_end = value.increment();

                *local_current = Some(Box::new(DietNode {
                    interval: Interval::from(value..new_exclusive_end),
                    left: None,
                    right: None
                }));

                return true;
            }
        }
    }

    fn remove(&mut self, value: &Self::Bound) -> bool {
        let containing_node = self.traverse_to_node_containing_mut(value);

        let mut delete_node = false;

        let removed = if let Some(ref mut node) = *containing_node {
            if value == node.interval.inclusive_start() {
                if value.comes_before(node.interval.exclusive_end()) {
                    // The entire node needs to be removed but any children should be moved up
                    match (node.left.take(), node.right.take()) {
                        (None, None) => {
                            delete_node = true
                        }
                        (Some(child), None) | (None, Some(child)) => {
                            *node = child;
                        }
                        (mut left @ Some(_), right @ Some(_)) => {
                            let max = *(left.max_parent_mut().take().unwrap());

                            node.interval = max.interval;
                            node.left = left;
                            node.right = right;
                        }
                    }
                } else {
                    node.interval.inclusive_start_mut().increment_ref();
                }
            } else if value.comes_before(node.interval.exclusive_end()) {
                node.interval.exclusive_end_mut().decrement_ref();
            } else {
                node.split_on_value(value);
            };
            true
        } else {
            false
        };

        if delete_node {
            *containing_node = None;
        }

        removed
    }
}

impl<'a, T> Iterator for DietNodeIterator<&'a DietNode<T>> {
    type Item = &'a DietNode<T>;

    fn next(&mut self) -> Option<&'a DietNode<T>> {
        self.pending.pop_front()
            .map(|current| {
                let children = [&current.left, &current.right];
                for child in children.into_iter().flat_map(|x| x.as_ref()) {
                    self.pending.push_back(child);

                }
                current
            })
    }
}

impl<T> DietNode<T> {
    fn min_inclusive_start(&self) -> &T {
        let mut current = self;

        loop {
            if let Some(child) = current.left.as_ref() {
                current = child;
            } else {
                return current.interval.inclusive_start();
            }
        }
    }

    fn max_exclusive_end(&self) -> &T {
        let mut current = self;

        loop {
            if let Some(child) = current.right.as_ref() {
                current = child;
            } else {
                return current.interval.exclusive_end();
            }
        }
    }

    pub fn nodes(&self) -> DietNodeIterator<&DietNode<T>> {
        let mut pending = VecDeque::new();
        pending.push_back(self);

        DietNodeIterator {
            pending: pending
        }
    }

    pub fn len(&self) -> usize {
        self.nodes().count()
    }

    pub fn is_adjacent<Q: ?Sized>(&self, value: &Q) -> bool where T : Borrow<Q>, Q : AdjacentBound {
        value.comes_before(self.interval.inclusive_start().borrow()) ||
                    value == self.interval.exclusive_end().borrow()
    }
}

impl<T: AdjacentBound> DietNode<T> {

    pub fn split_on_value(&mut self, value: &T) {
        debug_assert!(self.interval.contains(value), "the value should be contained within the interval");

        let old_exclusive_end = self.interval.set_exclusive_end(value.decrement());

        let new_right_interval = Interval::from(value.increment()..old_exclusive_end);

        let right = self.right.take();
        self.right = Some(Box::new(DietNode {
            interval: new_right_interval,
            left: None,
            right: right
        }));
    }

    fn join_left(&mut self) {
        let max_parent_mut = self.left.max_parent_mut();

        let interval = &mut self.interval;

        let should_merge = max_parent_mut.as_ref().map_or(false, |c| c.interval.exclusive_end() == interval.inclusive_start());

        if should_merge {
            let max = *max_parent_mut.take().unwrap();
            *max_parent_mut = max.left;
            interval.set_inclusive_start(max.interval.take_inclusive_start());
        }
    }

    fn join_right(&mut self) {
        let min_parent_mut = self.right.min_parent_mut();

        let interval = &mut self.interval;

        let should_merge = min_parent_mut.as_ref().map_or(false, |c| c.interval.inclusive_start() == interval.exclusive_end());

        if should_merge {
            let min = *min_parent_mut.take().unwrap();
            *min_parent_mut = min.right;
            interval.set_exclusive_end(min.interval.take_exclusive_end());
        }
    }
}

impl<T> Diet<T> {
    pub fn len(&self) -> usize {
        self.root.as_ref().map_or(0, |n| n.len())
    }
}

impl<T: AdjacentBound> Diet<T> {

    pub fn insert(&mut self, value: T) -> bool {
        self.root.insert(value)
    }

    pub fn remove(&mut self, value: &T) -> bool {
        self.root.remove(value)
    }

    pub fn contains(&self, value: &T) -> bool {
        self.root.traverse_to_node_containing(value).is_some()
    }
}

// TODO LH Implement iterators which traverse the intervals in the tree

impl<T> Default for Diet<T>{
    fn default() -> Self {
        Self {
            root: None
        }
    }
}

impl<A : AdjacentBound> FromIterator<A> for Diet<A> {
    fn from_iter<T>(iter: T) -> Self where T: IntoIterator<Item = A> {
        let mut diet = Diet::default();

        for value in iter {
            diet.insert(value);
        }

        diet
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn contains_returns_false_for_default(){
        let diet = Diet::<u32>::default();

        assert!(!diet.contains(&5));
    }

    #[test]
    fn contains_returns_true_for_existing_value(){
        let mut diet = Diet::from_iter([3, 1, 5].iter().cloned());

        assert!(diet.contains(&5));
    }

    #[test]
    fn len_returns_zero_for_default() {
        let diet = Diet::<u32>::default();

        assert_eq!(diet.len(), 0);
    }

    #[test]
    fn insert_returns_true_for_new_value() {
        let mut diet = Diet::default();

        assert!(diet.insert(1));
    }

    #[test]
    fn insert_returns_false_for_existing_value() {
        let mut diet = Diet::default();

        diet.insert(1);
        assert!(!diet.insert(1));
    }

    #[test]
    fn insert_extends_existing_ranges() {
        let mut diet = Diet::from_iter([1, 5].iter().cloned());

        diet.insert(2);
        diet.insert(4);

        assert_eq!(diet.len(), 2);
    }

    #[test]
    fn insert_combines_ranges() {
        let mut diet = Diet::from_iter([3, 1, 5, 8].iter().cloned());

        diet.insert(2);
        diet.insert(4);
        diet.insert(6);
        diet.insert(7);

        assert_eq!(diet.len(), 1);
    }

    #[test]
    fn remove_returns_false_for_default(){
        let mut diet = Diet::<u32>::default();

        assert!(!diet.remove(&5));
    }

    #[test]
    fn remove_returns_false_for_non_existant_value(){
        let mut diet = Diet::from_iter([1, 2, 3, 6].iter().cloned());

        assert!(!diet.remove(&10));
    }

    #[test]
    fn remove_removes_values(){
        let mut diet = Diet::from_iter([5, 6, 1, 2, 8, 9].iter().cloned());

        assert!(diet.remove(&5));
        assert!(diet.remove(&6));

        assert_eq!(diet.len(), 2);
    }

    #[test]
    fn remove_of_adjacent_returns_false() {
        let mut diet = Diet::from_iter([4, 5, 6].iter().cloned());

        assert!(!diet.remove(&3));
    }

     #[test]
    fn test_delete_split_left() {
        let mut d = Diet::default();
        d.insert(8);
        d.insert(9);
        d.insert(10);

        d.insert(2);
        d.insert(4);
        d.insert(6);
        d.insert(14);
        d.insert(12);
        d.insert(16);

        d.remove(&8);
        d.remove(&9);
        d.remove(&10);

        assert!(d.contains(&2));
        assert!(d.contains(&4));
        assert!(d.contains(&6));
        assert!(d.contains(&14));
        assert!(d.contains(&12));
        assert!(d.contains(&16));
    }

    #[test]
    fn test_delete_simple() {
        let mut d = Diet::default();
        d.remove(&5);
        d.insert(5);
        assert!(d.contains(&5));
        d.remove(&5);
        assert!(!d.contains(&5));
    }

    #[test]
    fn test_delete_branch() {
        let mut d = Diet::default();
        d.insert(5);
        d.insert(3);
        d.insert(7);

        d.remove(&3);
        assert!(!d.contains(&3));
        assert!(d.contains(&5));
        assert!(d.contains(&7));

        d.remove(&7);
        assert!(d.contains(&5));
    }

    #[test]
    fn test_delete_replace_merge() {
        let mut d = Diet::default();
        d.insert(5);
        d.insert(3);

        d.remove(&5);
        assert!(!d.contains(&5));
        assert!(d.contains(&3));

        d.insert(7);
        d.remove(&3);
        assert!(!d.contains(&3));
        assert!(d.contains(&7));
    }

    #[test]
    fn test_delete_lower_upper() {
        let mut d = Diet::default();
        d.insert(5);
        d.insert(6);
        d.insert(7);

        d.remove(&5);
        assert!(d.contains(&6));
        assert!(d.contains(&7));

        d.remove(&7);
        assert!(d.contains(&6));
    }

}
<|endoftext|>


<commit_before>//! [yEnc](http://www.yenc.org) is an encoding scheme to include binary files in Usenet messages.
mod crc32;

use std::fs::OpenOptions;
use std::io;
use std::io::{Read, Write, BufReader, BufRead};
use std::path::PathBuf;
use std::convert::From;

const NUL: u8 = 0;
const TAB: u8 = b'\t';
const LF: u8 = b'\n';
const CR: u8 = b'\r';
const SPACE: u8 = b' ';
const ESCAPE: u8 = b'=';
const LINE_SIZE: u16 = 128;

#[derive(Debug)]
pub enum DecodeError {
    IncompleteData(usize, usize),
    InvalidHeader(String, usize),
    InvalidChecksum,
    IoError(io::Error),
}

impl From<io::Error> for DecodeError {
    fn from(error: io::Error) -> DecodeError {
        DecodeError::IoError(error)
    }
}

#[derive(Default,Debug)]
struct MetaData {
    name: Option<String>,
    line: Option<u16>,
    size: Option<usize>,
    crc32: Option<u32>,
}

/// Encodes the input file in a new output file.
/// # Example
/// ```
/// yenc::yencode_file("test1.bin", "test1.bin.yenc");
/// ```
/// # Errors
/// - when the output file already exists
///
pub fn yencode_file(input_filename: &str, output_filename: &str) -> Result<(), io::Error> {
    let mut input_file = OpenOptions::new().read(true).open(input_filename)?;
    let mut output_file = OpenOptions::new().write(true).create_new(true).open(output_filename)?;
    let mut checksum: crc32::Crc32 = Default::default();
    let mut buffer = [0u8; 65536];
    let mut col = 0;
    output_file.write(format!("=ybegin line={} size={} name={}\r\n",
                       LINE_SIZE,
                       input_file.metadata()?.len(),
                       input_filename)
            .as_bytes())?;
    loop {
        let bytes_read = input_file.read(&mut buffer)?;
        match bytes_read {
            0 => break,
            _ => {
                checksum.update_with_slice(&buffer[0..bytes_read]);
                output_file.write(yencode_buffer(&buffer[0..bytes_read], &mut col, LINE_SIZE).as_slice())?;
            }
        };
    }

    output_file.write(format!("\r\n=yend size={} crc32={:08X}\r\n",
                       checksum.num_bytes,
                       checksum.crc)
            .as_bytes())?;
    Ok(())
}

/// Encode the byte slice into a vector of yEncoded bytes, with the maximum of `line_length` characters per line.
pub fn yencode_buffer(input: &[u8], col: &mut u16, line_length: u16) -> Vec<u8> {
    let mut output = Vec::<u8>::with_capacity(input.len());
    for &b in input {
        let encoded = yencode_byte(b);
        output.extend_from_slice(&encoded);
        *col += encoded.len() as u16;
        if *col >= line_length {
            output.push(CR);
            output.push(LF);
            *col = 0;
        }
    }
    output
}

#[inline]
fn yencode_byte(input_byte: u8) -> Vec<u8> {
    let mut output = Vec::<u8>::with_capacity(2);
    let mut output_byte = input_byte.overflowing_add(42).0;
    match output_byte {
        NUL | CR | LF | ESCAPE | TAB | SPACE => {
            output.push(ESCAPE);
            output_byte = output_byte.overflowing_add(64).0;
        }
        _ => {}
    };
    output.push(output_byte);
    output
}

/// Decodes the input file in a new output file.
///
/// If ok, returns the path of the decoded file.
///
/// # Example
/// ```
/// yenc::ydecode_file("test2.bin.yenc", "test2.bin");
/// ```
/// # Errors
/// - when the output file already exists
///
pub fn ydecode_file(input_filename: &str, output_path: &str) -> Result<String, DecodeError> {
    let input_file = OpenOptions::new().read(true).open(input_filename)?;
    let mut output_pathbuf = PathBuf::new();
    output_pathbuf.push(output_path);
    let mut rdr = BufReader::new(input_file);
    let mut line_buf = Vec::<u8>::with_capacity(2 * LINE_SIZE as usize);
    let mut checksum: crc32::Crc32 = Default::default();
    let mut yenc_block_found = false;

    while !yenc_block_found {
        line_buf.clear();
        let length = rdr.read_until(LF, &mut line_buf)?;
        if length == 0 {
            break;
        }
        if line_buf.starts_with(b"=ybegin ") {
            yenc_block_found = true;
        }
    }

    if yenc_block_found {
        // parse header line and determine output filename
        let metadata = parse_header_line(&line_buf, 8)?;
        output_pathbuf.push(metadata.name.unwrap().to_string().trim());
        let mut output_file =
            OpenOptions::new().write(true).create_new(true).open(output_pathbuf.as_path())?;

        let mut footer_found = false;
        while !footer_found {
            line_buf.clear();
            let length = rdr.read_until(LF, &mut line_buf)?;
            if length == 0 {
                break;
            }
            if line_buf.starts_with(b"=yend ") {
                footer_found = true;
            } else {
                let decoded = ydecode_buffer(&line_buf[0..length]);
                checksum.update_with_slice(decoded.as_slice());
                output_file.write(decoded.as_slice())?;
            }
        }
        if let Some(expected_size) = metadata.size {
            if expected_size != checksum.num_bytes {
                return Err(DecodeError::IncompleteData(expected_size, checksum.num_bytes));
            }
        }
        if footer_found {
            let metadata = parse_header_line(&line_buf, 6)?;
            if let Some(expected_size) = metadata.size {
                if expected_size != checksum.num_bytes {
                    return Err(DecodeError::IncompleteData(expected_size, checksum.num_bytes));
                }
            }
            if let Some(expected_crc) = metadata.crc32 {
                if expected_crc != checksum.crc {
                    return Err(DecodeError::InvalidChecksum);
                }
            }
        }
    }
    Ok(output_pathbuf.to_str().unwrap().to_string())
}

/// Decode the yEncoded byte slice into a vector of bytes.
/// Carriage Return (CR) and Line Feed (LF) are ignored.
pub fn ydecode_buffer(input: &[u8]) -> Vec<u8> {
    let mut output = Vec::<u8>::with_capacity((input.len() as f64 * 1.02) as usize);
    let mut i = 0;
    while i < input.len() {
        let mut byte = input[i];
        i += 1;
        match byte {
            CR | LF => continue,
            ESCAPE => {
                byte = input[i].overflowing_sub(64).0;
                i += 1;
            }
            _ => {}
        };
        output.push(byte.overflowing_sub(42).0);
    }
    output
}

fn parse_header_line(line_buf: &[u8], offset: usize) -> Result<MetaData, DecodeError> {
    #[derive(Debug)]
    enum State {
        Keyword,
        Value,
        End,
    };

    let mut metadata: MetaData = Default::default();
    let mut state = State::Keyword;
    let mut keyword = Vec::<u8>::with_capacity(4);
    let mut value = Vec::<u8>::with_capacity(96);

    let header_line = String::from_utf8_lossy(line_buf).to_string();
    for (i, &c) in line_buf[offset..].iter().enumerate() {
        let position = i + offset;
        match state {
            State::End => unreachable!(),
            State::Keyword => {
                match c {
                    b'a'...b'z' | b'0'...b'9' => keyword.push(c),
                    b'=' => {
                        if keyword.is_empty() ||
                           !(keyword.as_slice() == b"name" || keyword.as_slice() == b"line" ||
                             keyword.as_slice() == b"size" ||
                             keyword.as_slice() == b"crc32") {
                            return Err(DecodeError::InvalidHeader(header_line, position));
                        } else {
                            state = State::Value;
                        }
                    }
                    _ => {
                        return Err(DecodeError::InvalidHeader(header_line, position));
                    }
                }
            }
            State::Value => {
                match keyword.as_slice() {
                    b"name" => {
                        match c {
                            b'\r' => {}
                            b'\n' => {
                                state = State::End;
                                metadata.name = Some(String::from_utf8_lossy(&value).to_string());
                            }
                            _ => value.push(c),
                        }
                    }
                    b"size" => {
                        match c {
                            b'0'...b'9' => value.push(c),
                            b' ' => {
                                metadata.size =
                                    Some(usize::from_str_radix(&String::from_utf8_lossy(&value),
                                                               10)
                                        .unwrap());
                                state = State::Keyword;
                                keyword.clear();
                                value.clear();
                            }
                            _ => {
                                return Err(DecodeError::InvalidHeader(header_line, position));
                            }
                        }
                    }
                    b"line" => {
                        match c {
                            b'0'...b'9' => value.push(c),
                            b' ' => {
                                metadata.line =
                                    Some(u16::from_str_radix(&String::from_utf8_lossy(&value), 10)
                                        .unwrap());
                                state = State::Keyword;
                                keyword.clear();
                                value.clear();
                            }
                            _ => {
                                return Err(DecodeError::InvalidHeader(header_line, position));
                            }
                        }
                    }
                    b"crc32" => {
                        match c {
                            b'0'...b'9' | b'A'...b'F' | b'a'...b'f' => value.push(c),
                            b' ' => {
                                state = State::Keyword;
                                metadata.crc32 =
                                    Some(u32::from_str_radix(&String::from_utf8_lossy(&value), 16)
                                        .unwrap());
                                keyword.clear();
                                value.clear();
                            }
                            b'\n' => {
                                state = State::End;
                                metadata.crc32 =
                                    Some(u32::from_str_radix(&String::from_utf8_lossy(&value), 16)
                                        .unwrap());
                                keyword.clear();
                                value.clear();
                            }
                            b'\r' => {}
                            _ => {
                                return Err(DecodeError::InvalidHeader(header_line, position));
                            }
                        }
                    }
                    _ => unreachable!(),
                }
            }
        };
    }
    Ok(metadata)
}

#[cfg(test)]
mod tests {
    use super::{ESCAPE, TAB, LF, CR, SPACE, yencode_buffer, ydecode_buffer, yencode_byte};

    #[test]
    fn escape_null() {
        assert_eq!(vec![ESCAPE, 0x40], yencode_byte(214));
    }

    #[test]
    fn escape_tab() {
        assert_eq!(vec![ESCAPE, 0x49], yencode_byte(214 + TAB));
    }

    #[test]
    fn escape_lf() {
        assert_eq!(vec![ESCAPE, 0x4A], yencode_byte(214 + LF));
    }

    #[test]
    fn escape_cr() {
        assert_eq!(vec![ESCAPE, 0x4D], yencode_byte(214 + CR));
    }

    #[test]
    fn escape_space() {
        assert_eq!(vec![ESCAPE, 0x60], yencode_byte(214 + SPACE));
    }

    #[test]
    fn escape_equal_sign() {
        assert_eq!(vec![ESCAPE, 0x7D], yencode_byte(ESCAPE - 42));
    }

    #[test]
    fn equality() {
        let b = (0..256).map(|c| c as u8).collect::<Vec<u8>>();
        let mut col = 0;
        assert_eq!(b,
                   ydecode_buffer(&yencode_buffer(&b, &mut col, 128)).as_slice());
    }
}
<commit_msg>Handle space as last char in =yend line<commit_after>//! [yEnc](http://www.yenc.org) is an encoding scheme to include binary files in Usenet messages.
mod crc32;

use std::fs::OpenOptions;
use std::io;
use std::io::{Read, Write, BufReader, BufRead};
use std::path::PathBuf;
use std::convert::From;

const NUL: u8 = 0;
const TAB: u8 = b'\t';
const LF: u8 = b'\n';
const CR: u8 = b'\r';
const SPACE: u8 = b' ';
const ESCAPE: u8 = b'=';
const LINE_SIZE: u16 = 128;

#[derive(Debug)]
pub enum DecodeError {
    IncompleteData(usize, usize),
    InvalidHeader(String, usize),
    InvalidChecksum,
    IoError(io::Error),
}

impl From<io::Error> for DecodeError {
    fn from(error: io::Error) -> DecodeError {
        DecodeError::IoError(error)
    }
}

#[derive(Default,Debug)]
struct MetaData {
    name: Option<String>,
    line: Option<u16>,
    size: Option<usize>,
    crc32: Option<u32>,
}

/// Encodes the input file in a new output file.
/// # Example
/// ```
/// yenc::yencode_file("test1.bin", "test1.bin.yenc");
/// ```
/// # Errors
/// - when the output file already exists
///
pub fn yencode_file(input_filename: &str, output_filename: &str) -> Result<(), io::Error> {
    let mut input_file = OpenOptions::new().read(true).open(input_filename)?;
    let mut output_file = OpenOptions::new().write(true).create_new(true).open(output_filename)?;
    let mut checksum: crc32::Crc32 = Default::default();
    let mut buffer = [0u8; 65536];
    let mut col = 0;
    output_file.write(format!("=ybegin line={} size={} name={}\r\n",
                       LINE_SIZE,
                       input_file.metadata()?.len(),
                       input_filename)
            .as_bytes())?;
    loop {
        let bytes_read = input_file.read(&mut buffer)?;
        match bytes_read {
            0 => break,
            _ => {
                checksum.update_with_slice(&buffer[0..bytes_read]);
                output_file.write(yencode_buffer(&buffer[0..bytes_read], &mut col, LINE_SIZE).as_slice())?;
            }
        };
    }

    output_file.write(format!("\r\n=yend size={} crc32={:08X}\r\n",
                       checksum.num_bytes,
                       checksum.crc)
            .as_bytes())?;
    Ok(())
}

/// Encode the byte slice into a vector of yEncoded bytes, with the maximum of `line_length` characters per line.
pub fn yencode_buffer(input: &[u8], col: &mut u16, line_length: u16) -> Vec<u8> {
    let mut output = Vec::<u8>::with_capacity(input.len());
    for &b in input {
        let encoded = yencode_byte(b);
        output.extend_from_slice(&encoded);
        *col += encoded.len() as u16;
        if *col >= line_length {
            output.push(CR);
            output.push(LF);
            *col = 0;
        }
    }
    output
}

#[inline]
fn yencode_byte(input_byte: u8) -> Vec<u8> {
    let mut output = Vec::<u8>::with_capacity(2);
    let mut output_byte = input_byte.overflowing_add(42).0;
    match output_byte {
        NUL | CR | LF | ESCAPE | TAB | SPACE => {
            output.push(ESCAPE);
            output_byte = output_byte.overflowing_add(64).0;
        }
        _ => {}
    };
    output.push(output_byte);
    output
}

/// Decodes the input file in a new output file.
///
/// If ok, returns the path of the decoded file.
///
/// # Example
/// ```
/// yenc::ydecode_file("test2.bin.yenc", "test2.bin");
/// ```
/// # Errors
/// - when the output file already exists
///
pub fn ydecode_file(input_filename: &str, output_path: &str) -> Result<String, DecodeError> {
    let input_file = OpenOptions::new().read(true).open(input_filename)?;
    let mut output_pathbuf = PathBuf::new();
    output_pathbuf.push(output_path);
    let mut rdr = BufReader::new(input_file);
    let mut line_buf = Vec::<u8>::with_capacity(2 * LINE_SIZE as usize);
    let mut checksum: crc32::Crc32 = Default::default();
    let mut yenc_block_found = false;

    while !yenc_block_found {
        line_buf.clear();
        let length = rdr.read_until(LF, &mut line_buf)?;
        if length == 0 {
            break;
        }
        if line_buf.starts_with(b"=ybegin ") {
            yenc_block_found = true;
        }
    }

    if yenc_block_found {
        // parse header line and determine output filename
        let metadata = parse_header_line(&line_buf, 8)?;
        output_pathbuf.push(metadata.name.unwrap().to_string().trim());
        let mut output_file =
            OpenOptions::new().write(true).create_new(true).open(output_pathbuf.as_path())?;

        let mut footer_found = false;
        while !footer_found {
            line_buf.clear();
            let length = rdr.read_until(LF, &mut line_buf)?;
            if length == 0 {
                break;
            }
            if line_buf.starts_with(b"=yend ") {
                footer_found = true;
            } else {
                let decoded = ydecode_buffer(&line_buf[0..length]);
                checksum.update_with_slice(decoded.as_slice());
                output_file.write(decoded.as_slice())?;
            }
        }
        if let Some(expected_size) = metadata.size {
            if expected_size != checksum.num_bytes {
                return Err(DecodeError::IncompleteData(expected_size, checksum.num_bytes));
            }
        }
        if footer_found {
            let metadata = parse_header_line(&line_buf, 6)?;
            if let Some(expected_size) = metadata.size {
                if expected_size != checksum.num_bytes {
                    return Err(DecodeError::IncompleteData(expected_size, checksum.num_bytes));
                }
            }
            if let Some(expected_crc) = metadata.crc32 {
                if expected_crc != checksum.crc {
                    return Err(DecodeError::InvalidChecksum);
                }
            }
        }
    }
    Ok(output_pathbuf.to_str().unwrap().to_string())
}

/// Decode the yEncoded byte slice into a vector of bytes.
/// Carriage Return (CR) and Line Feed (LF) are ignored.
pub fn ydecode_buffer(input: &[u8]) -> Vec<u8> {
    let mut output = Vec::<u8>::with_capacity((input.len() as f64 * 1.02) as usize);
    let mut i = 0;
    while i < input.len() {
        let mut byte = input[i];
        i += 1;
        match byte {
            CR | LF => continue,
            ESCAPE => {
                byte = input[i].overflowing_sub(64).0;
                i += 1;
            }
            _ => {}
        };
        output.push(byte.overflowing_sub(42).0);
    }
    output
}

fn parse_header_line(line_buf: &[u8], offset: usize) -> Result<MetaData, DecodeError> {
    #[derive(Debug)]
    enum State {
        Keyword,
        Value,
        End,
    };

    let mut metadata: MetaData = Default::default();
    let mut state = State::Keyword;
    let mut keyword = Vec::<u8>::with_capacity(4);
    let mut value = Vec::<u8>::with_capacity(96);

    let header_line = String::from_utf8_lossy(line_buf).to_string();
    for (i, &c) in line_buf[offset..].iter().enumerate() {
        let position = i + offset;
        match state {
            State::End => unreachable!(),
            State::Keyword => {
                match c {
                    b'a'...b'z' | b'0'...b'9' => keyword.push(c),
                    b'=' => {
                        if keyword.is_empty() ||
                           !(keyword.as_slice() == b"name" || keyword.as_slice() == b"line" ||
                             keyword.as_slice() == b"size" ||
                             keyword.as_slice() == b"crc32") {
                            return Err(DecodeError::InvalidHeader(header_line, position));
                        } else {
                            state = State::Value;
                        }
                    }
                    b'\r' | b'\n' => {}
                    _ => {
                        return Err(DecodeError::InvalidHeader(header_line, position));
                    }
                }
            }
            State::Value => {
                match keyword.as_slice() {
                    b"name" => {
                        match c {
                            b'\r' => {}
                            b'\n' => {
                                state = State::End;
                                metadata.name = Some(String::from_utf8_lossy(&value).to_string());
                            }
                            _ => value.push(c),
                        }
                    }
                    b"size" => {
                        match c {
                            b'0'...b'9' => value.push(c),
                            b' ' => {
                                metadata.size =
                                    Some(usize::from_str_radix(&String::from_utf8_lossy(&value),
                                                               10)
                                        .unwrap());
                                state = State::Keyword;
                                keyword.clear();
                                value.clear();
                            }
                            _ => {
                                return Err(DecodeError::InvalidHeader(header_line, position));
                            }
                        }
                    }
                    b"line" => {
                        match c {
                            b'0'...b'9' => value.push(c),
                            b' ' => {
                                metadata.line =
                                    Some(u16::from_str_radix(&String::from_utf8_lossy(&value), 10)
                                        .unwrap());
                                state = State::Keyword;
                                keyword.clear();
                                value.clear();
                            }
                            _ => {
                                return Err(DecodeError::InvalidHeader(header_line, position));
                            }
                        }
                    }
                    b"crc32" => {
                        match c {
                            b'0'...b'9' | b'A'...b'F' | b'a'...b'f' => value.push(c),
                            b' ' => {
                                state = State::Keyword;
                                metadata.crc32 =
                                    Some(u32::from_str_radix(&String::from_utf8_lossy(&value), 16)
                                        .unwrap());
                                keyword.clear();
                                value.clear();
                            }
                            b'\n' => {
                                state = State::End;
                                metadata.crc32 =
                                    Some(u32::from_str_radix(&String::from_utf8_lossy(&value), 16)
                                        .unwrap());
                                keyword.clear();
                                value.clear();
                            }
                            b'\r' => {}
                            _ => {
                                return Err(DecodeError::InvalidHeader(header_line, position));
                            }
                        }
                    }
                    _ => unreachable!(),
                }
            }
        };
    }
    Ok(metadata)
}

#[cfg(test)]
mod tests {
    use super::{ESCAPE, TAB, LF, CR, SPACE, yencode_buffer, ydecode_buffer, yencode_byte};

    #[test]
    fn escape_null() {
        assert_eq!(vec![ESCAPE, 0x40], yencode_byte(214));
    }

    #[test]
    fn escape_tab() {
        assert_eq!(vec![ESCAPE, 0x49], yencode_byte(214 + TAB));
    }

    #[test]
    fn escape_lf() {
        assert_eq!(vec![ESCAPE, 0x4A], yencode_byte(214 + LF));
    }

    #[test]
    fn escape_cr() {
        assert_eq!(vec![ESCAPE, 0x4D], yencode_byte(214 + CR));
    }

    #[test]
    fn escape_space() {
        assert_eq!(vec![ESCAPE, 0x60], yencode_byte(214 + SPACE));
    }

    #[test]
    fn escape_equal_sign() {
        assert_eq!(vec![ESCAPE, 0x7D], yencode_byte(ESCAPE - 42));
    }

    #[test]
    fn equality() {
        let b = (0..256).map(|c| c as u8).collect::<Vec<u8>>();
        let mut col = 0;
        assert_eq!(b,
                   ydecode_buffer(&yencode_buffer(&b, &mut col, 128)).as_slice());
    }
}
<|endoftext|>


<commit_before>//! [![github]](https://github.com/dtolnay/serde-yaml) [![crates-io]](https://crates.io/crates/serde-yaml) [![docs-rs]](https://docs.rs/serde-yaml)
//!
//! [github]: https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github
//! [crates-io]: https://img.shields.io/badge/crates.io-fc8d62?style=for-the-badge&labelColor=555555&logo=rust
//! [docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs
//!
//!

//!
//! This crate is a Rust library for using the [Serde] serialization framework
//! with data in [YAML] file format.
//!
//! [Serde]: https://github.com/serde-rs/serde
//! [YAML]: https://yaml.org/
//!
//! # Examples
//!
//! ```
//! use std::collections::BTreeMap;
//!
//! fn main() -> Result<(), serde_yaml::Error> {
//!     // You have some type.
//!     let mut map = BTreeMap::new();
//!     map.insert("x".to_string(), 1.0);
//!     map.insert("y".to_string(), 2.0);
//!
//!     // Serialize it to a YAML string.
//!     let yaml = serde_yaml::to_string(&map)?;
//!     assert_eq!(yaml, "x: 1.0\ny: 2.0\n");
//!
//!     // Deserialize it back to a Rust type.
//!     let deserialized_map: BTreeMap<String, f64> = serde_yaml::from_str(&yaml)?;
//!     assert_eq!(map, deserialized_map);
//!     Ok(())
//! }
//! ```
//!
//! ## Using Serde derive
//!
//! It can also be used with Serde's derive macros to handle structs and enums
//! defined in your program.
//!
//! Structs serialize in the obvious way:
//!
//! ```
//! # use serde_derive::{Serialize, Deserialize};
//! use serde::{Serialize, Deserialize};
//!
//! #[derive(Serialize, Deserialize, PartialEq, Debug)]
//! struct Point {
//!     x: f64,
//!     y: f64,
//! }
//!
//! fn main() -> Result<(), serde_yaml::Error> {
//!     let point = Point { x: 1.0, y: 2.0 };
//!
//!     let yaml = serde_yaml::to_string(&point)?;
//!     assert_eq!(yaml, "x: 1.0\ny: 2.0\n");
//!
//!     let deserialized_point: Point = serde_yaml::from_str(&yaml)?;
//!     assert_eq!(point, deserialized_point);
//!     Ok(())
//! }
//! ```
//!
//! Enums serialize using YAML's `!tag` syntax to identify the variant name.
//!
//! ```
//! # use serde_derive::{Serialize, Deserialize};
//! use serde::{Serialize, Deserialize};
//!
//! #[derive(Serialize, Deserialize, PartialEq, Debug)]
//! enum Enum {
//!     Unit,
//!     Newtype(usize),
//!     Tuple(usize, usize, usize),
//!     Struct { x: f64, y: f64 },
//! }
//!
//! fn main() -> Result<(), serde_yaml::Error> {
//!     let yaml = "
//!         - !Newtype 1
//!         - !Tuple [0, 0, 0]
//!         - !Struct {x: 1.0, y: 2.0}
//!     ";
//!     let values: Vec<Enum> = serde_yaml::from_str(yaml).unwrap();
//!     assert_eq!(values[0], Enum::Newtype(1));
//!     assert_eq!(values[1], Enum::Tuple(0, 0, 0));
//!     assert_eq!(values[2], Enum::Struct { x: 1.0, y: 2.0 });
//!
//!     // The last two in YAML's block style instead:
//!     let yaml = "
//!         - !Tuple
//!           - 0
//!           - 0
//!           - 0
//!         - !Struct
//!           x: 1.0
//!           y: 2.0
//!     ";
//!     let values: Vec<Enum> = serde_yaml::from_str(yaml).unwrap();
//!     assert_eq!(values[0], Enum::Tuple(0, 0, 0));
//!     assert_eq!(values[1], Enum::Struct { x: 1.0, y: 2.0 });
//!
//!     // Variants with no data can be written using !Tag or just the string name.
//!     let yaml = "
//!         - Unit  # serialization produces this one
//!         - !Unit
//!     ";
//!     let values: Vec<Enum> = serde_yaml::from_str(yaml).unwrap();
//!     assert_eq!(values[0], Enum::Unit);
//!     assert_eq!(values[1], Enum::Unit);
//!
//!     Ok(())
//! }
//! ```

#![doc(html_root_url = "https://docs.rs/serde_yaml/0.9.14")]
#![deny(missing_docs)]
// Suppressed clippy_pedantic lints
#![allow(
    // buggy
    clippy::iter_not_returning_iterator, // https://github.com/rust-lang/rust-clippy/issues/8285
    clippy::ptr_arg, // https://github.com/rust-lang/rust-clippy/issues/9218
    clippy::question_mark, // https://github.com/rust-lang/rust-clippy/issues/7859
    // private Deserializer::next
    clippy::should_implement_trait,
    // things are often more readable this way
    clippy::cast_lossless,
    clippy::checked_conversions,
    clippy::if_not_else,
    clippy::manual_assert,
    clippy::match_like_matches_macro,
    clippy::match_same_arms,
    clippy::module_name_repetitions,
    clippy::needless_pass_by_value,
    clippy::option_if_let_else,
    clippy::redundant_else,
    clippy::single_match_else,
    // code is acceptable
    clippy::blocks_in_if_conditions,
    clippy::cast_possible_truncation,
    clippy::cast_possible_wrap,
    clippy::cast_precision_loss,
    clippy::cast_sign_loss,
    clippy::derive_hash_xor_eq,
    clippy::derive_partial_eq_without_eq,
    clippy::doc_markdown,
    clippy::items_after_statements,
    clippy::let_underscore_drop,
    clippy::manual_map,
    clippy::missing_panics_doc,
    clippy::never_loop,
    clippy::return_self_not_must_use,
    clippy::too_many_lines,
    clippy::uninlined_format_args,
    clippy::unsafe_removed_from_name,
    clippy::wildcard_in_or_patterns,
    // noisy
    clippy::missing_errors_doc,
    clippy::must_use_candidate,
)]

pub use crate::de::{from_reader, from_slice, from_str, Deserializer};
pub use crate::error::{Error, Location, Result};
pub use crate::ser::{to_string, to_writer, Serializer};
#[doc(inline)]
pub use crate::value::{from_value, to_value, Index, Number, Sequence, Value};

#[doc(inline)]
pub use crate::mapping::Mapping;

mod de;
mod error;
mod libyaml;
mod loader;
pub mod mapping;
mod number;
mod path;
mod ser;
pub mod value;
pub mod with;

// Prevent downstream code from implementing the Index trait.
mod private {
    pub trait Sealed {}
    impl Sealed for usize {}
    impl Sealed for str {}
    impl Sealed for String {}
    impl Sealed for crate::Value {}
    impl<'a, T> Sealed for &'a T where T: ?Sized + Sealed {}
}
<commit_msg>Fix renamed let_underscore_drop lint<commit_after>//! [![github]](https://github.com/dtolnay/serde-yaml) [![crates-io]](https://crates.io/crates/serde-yaml) [![docs-rs]](https://docs.rs/serde-yaml)
//!
//! [github]: https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github
//! [crates-io]: https://img.shields.io/badge/crates.io-fc8d62?style=for-the-badge&labelColor=555555&logo=rust
//! [docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs
//!
//!

//!
//! This crate is a Rust library for using the [Serde] serialization framework
//! with data in [YAML] file format.
//!
//! [Serde]: https://github.com/serde-rs/serde
//! [YAML]: https://yaml.org/
//!
//! # Examples
//!
//! ```
//! use std::collections::BTreeMap;
//!
//! fn main() -> Result<(), serde_yaml::Error> {
//!     // You have some type.
//!     let mut map = BTreeMap::new();
//!     map.insert("x".to_string(), 1.0);
//!     map.insert("y".to_string(), 2.0);
//!
//!     // Serialize it to a YAML string.
//!     let yaml = serde_yaml::to_string(&map)?;
//!     assert_eq!(yaml, "x: 1.0\ny: 2.0\n");
//!
//!     // Deserialize it back to a Rust type.
//!     let deserialized_map: BTreeMap<String, f64> = serde_yaml::from_str(&yaml)?;
//!     assert_eq!(map, deserialized_map);
//!     Ok(())
//! }
//! ```
//!
//! ## Using Serde derive
//!
//! It can also be used with Serde's derive macros to handle structs and enums
//! defined in your program.
//!
//! Structs serialize in the obvious way:
//!
//! ```
//! # use serde_derive::{Serialize, Deserialize};
//! use serde::{Serialize, Deserialize};
//!
//! #[derive(Serialize, Deserialize, PartialEq, Debug)]
//! struct Point {
//!     x: f64,
//!     y: f64,
//! }
//!
//! fn main() -> Result<(), serde_yaml::Error> {
//!     let point = Point { x: 1.0, y: 2.0 };
//!
//!     let yaml = serde_yaml::to_string(&point)?;
//!     assert_eq!(yaml, "x: 1.0\ny: 2.0\n");
//!
//!     let deserialized_point: Point = serde_yaml::from_str(&yaml)?;
//!     assert_eq!(point, deserialized_point);
//!     Ok(())
//! }
//! ```
//!
//! Enums serialize using YAML's `!tag` syntax to identify the variant name.
//!
//! ```
//! # use serde_derive::{Serialize, Deserialize};
//! use serde::{Serialize, Deserialize};
//!
//! #[derive(Serialize, Deserialize, PartialEq, Debug)]
//! enum Enum {
//!     Unit,
//!     Newtype(usize),
//!     Tuple(usize, usize, usize),
//!     Struct { x: f64, y: f64 },
//! }
//!
//! fn main() -> Result<(), serde_yaml::Error> {
//!     let yaml = "
//!         - !Newtype 1
//!         - !Tuple [0, 0, 0]
//!         - !Struct {x: 1.0, y: 2.0}
//!     ";
//!     let values: Vec<Enum> = serde_yaml::from_str(yaml).unwrap();
//!     assert_eq!(values[0], Enum::Newtype(1));
//!     assert_eq!(values[1], Enum::Tuple(0, 0, 0));
//!     assert_eq!(values[2], Enum::Struct { x: 1.0, y: 2.0 });
//!
//!     // The last two in YAML's block style instead:
//!     let yaml = "
//!         - !Tuple
//!           - 0
//!           - 0
//!           - 0
//!         - !Struct
//!           x: 1.0
//!           y: 2.0
//!     ";
//!     let values: Vec<Enum> = serde_yaml::from_str(yaml).unwrap();
//!     assert_eq!(values[0], Enum::Tuple(0, 0, 0));
//!     assert_eq!(values[1], Enum::Struct { x: 1.0, y: 2.0 });
//!
//!     // Variants with no data can be written using !Tag or just the string name.
//!     let yaml = "
//!         - Unit  # serialization produces this one
//!         - !Unit
//!     ";
//!     let values: Vec<Enum> = serde_yaml::from_str(yaml).unwrap();
//!     assert_eq!(values[0], Enum::Unit);
//!     assert_eq!(values[1], Enum::Unit);
//!
//!     Ok(())
//! }
//! ```

#![doc(html_root_url = "https://docs.rs/serde_yaml/0.9.14")]
#![deny(missing_docs)]
// Suppressed clippy_pedantic lints
#![allow(
    // buggy
    clippy::iter_not_returning_iterator, // https://github.com/rust-lang/rust-clippy/issues/8285
    clippy::ptr_arg, // https://github.com/rust-lang/rust-clippy/issues/9218
    clippy::question_mark, // https://github.com/rust-lang/rust-clippy/issues/7859
    // private Deserializer::next
    clippy::should_implement_trait,
    // things are often more readable this way
    clippy::cast_lossless,
    clippy::checked_conversions,
    clippy::if_not_else,
    clippy::manual_assert,
    clippy::match_like_matches_macro,
    clippy::match_same_arms,
    clippy::module_name_repetitions,
    clippy::needless_pass_by_value,
    clippy::option_if_let_else,
    clippy::redundant_else,
    clippy::single_match_else,
    // code is acceptable
    clippy::blocks_in_if_conditions,
    clippy::cast_possible_truncation,
    clippy::cast_possible_wrap,
    clippy::cast_precision_loss,
    clippy::cast_sign_loss,
    clippy::derive_hash_xor_eq,
    clippy::derive_partial_eq_without_eq,
    clippy::doc_markdown,
    clippy::items_after_statements,
    clippy::manual_map,
    clippy::missing_panics_doc,
    clippy::never_loop,
    clippy::return_self_not_must_use,
    clippy::too_many_lines,
    clippy::uninlined_format_args,
    clippy::unsafe_removed_from_name,
    clippy::wildcard_in_or_patterns,
    // noisy
    clippy::missing_errors_doc,
    clippy::must_use_candidate,
)]

pub use crate::de::{from_reader, from_slice, from_str, Deserializer};
pub use crate::error::{Error, Location, Result};
pub use crate::ser::{to_string, to_writer, Serializer};
#[doc(inline)]
pub use crate::value::{from_value, to_value, Index, Number, Sequence, Value};

#[doc(inline)]
pub use crate::mapping::Mapping;

mod de;
mod error;
mod libyaml;
mod loader;
pub mod mapping;
mod number;
mod path;
mod ser;
pub mod value;
pub mod with;

// Prevent downstream code from implementing the Index trait.
mod private {
    pub trait Sealed {}
    impl Sealed for usize {}
    impl Sealed for str {}
    impl Sealed for String {}
    impl Sealed for crate::Value {}
    impl<'a, T> Sealed for &'a T where T: ?Sized + Sealed {}
}
<|endoftext|>