Documentation
// License: see LICENSE file at root directory of `master` branch

//! # I/O

#[cfg(feature="std")]
use {
    alloc::{
        string::String,
        vec::Vec,
    },
    core::str::FromStr,
    std::{
        io::{Error, ErrorKind, Read},
    },
    crate::{Header, HeaderMap, IoResult, LINE_BREAK},
};

/// # Reads headers
#[cfg(feature="std")]
pub fn read_headers<R>(r: &mut R, line_buffer: &mut [u8], bytes: Option<Vec<u8>>) -> IoResult<(HeaderMap, Option<Vec<u8>>)> where R: Read {
    const MAX_SIZE: usize = 1024 * 1024;

    let lf = LINE_BREAK.as_bytes().last();

    let mut bytes = bytes.unwrap_or_else(|| Vec::with_capacity(1024));
    let mut result = HeaderMap::new();
    let mut total_read: usize = 0;

    loop {
        let line = match bytes.iter().position(|b| lf.map(|lf| lf == b).unwrap_or(false)) {
            Some(end) => match end.checked_sub(LINE_BREAK.len()).map(|start| match &bytes[start + 1 ..= end] == LINE_BREAK.as_bytes() {
                true => Some(end + 1),
                false => None,
            }) {
                Some(Some(idx)) => String::from_utf8(bytes.drain(..idx).collect()).map_err(|_|
                    Error::new(ErrorKind::InvalidData, __!("Invalid header"))
                )?,
                _ => return Err(Error::new(ErrorKind::InvalidData, __!("Invalid header"))),
            },
            None => match r.read(line_buffer)? {
                0 => return Err(Error::new(ErrorKind::UnexpectedEof, __!("Invalid headers"))),
                read => match total_read.checked_add(read) {
                    Some(n) if n <= MAX_SIZE => {
                        total_read = n;
                        bytes.extend(&line_buffer[..read]);
                        continue;
                    },
                    _ => return Err(Error::new(ErrorKind::InvalidData, __!("Headers too large"))),
                },
            },
        };
        let line = line.trim();
        if line.is_empty() == false {
            let mut parts = line.splitn(2, ':');
            match (parts.next(), parts.next(), parts.next()) {
                (Some(header), Some(value), None) => drop(result.insert(
                    Header::from_str(header)?,
                    value.chars().skip_while(|c| c == &' ').collect(),
                )),
                _ => return Err(Error::new(ErrorKind::InvalidData, __!("Invalid header: {:?}", line))),
            };
        }

        // Check for break
        match bytes.starts_with(LINE_BREAK.as_bytes()) {
            true => match result.is_empty() {
                true => return Err(Error::new(ErrorKind::InvalidData, __!("Missing headers"))),
                false => {
                    bytes.drain(..LINE_BREAK.len());
                    return Ok((result, match bytes.is_empty() {
                        true => None,
                        false => Some(bytes),
                    }));
                },
            },
            false => if line.is_empty() {
                return Err(Error::new(ErrorKind::InvalidData, __!("Invalid header")));
            },
        };
    }
}