<commit_before>use crate::ascii::Ascii;
use crate::req_resp::RequestOrResponse;
use crate::solicit::header::{HeaderError, HeaderResult};
use bytes::Bytes;
use bytes::BytesMut;
use std::fmt;

#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
pub enum PseudoHeaderName {
    // 8.1.2.3 Request Pseudo-Header Fields
    Method = 0,
    Scheme = 1,
    Authority = 2,
    Path = 3,

    // 8.1.2.4 Response Pseudo-Header Fields
    Status = 4,
}

impl PseudoHeaderName {
    pub fn name(&self) -> &'static str {
        match *self {
            PseudoHeaderName::Method => ":method",
            PseudoHeaderName::Scheme => ":scheme",
            PseudoHeaderName::Authority => ":authority",
            PseudoHeaderName::Path => ":path",
            PseudoHeaderName::Status => ":status",
        }
    }

    pub fn parse(value: &[u8]) -> HeaderResult<PseudoHeaderName> {
        match value {
            b":method" => Ok(PseudoHeaderName::Method),
            b":scheme" => Ok(PseudoHeaderName::Scheme),
            b":authority" => Ok(PseudoHeaderName::Authority),
            b":path" => Ok(PseudoHeaderName::Path),
            b":status" => Ok(PseudoHeaderName::Status),
            _ => Err(HeaderError::UnknownPseudoHeader),
        }
    }

    pub fn req_or_resp(&self) -> RequestOrResponse {
        match *self {
            PseudoHeaderName::Method => RequestOrResponse::Request,
            PseudoHeaderName::Scheme => RequestOrResponse::Request,
            PseudoHeaderName::Authority => RequestOrResponse::Request,
            PseudoHeaderName::Path => RequestOrResponse::Request,
            PseudoHeaderName::Status => RequestOrResponse::Response,
        }
    }

    pub fn name_bytes(&self) -> Bytes {
        Bytes::from_static(self.name().as_bytes())
    }

    pub fn names(request_or_response: RequestOrResponse) -> &'static [PseudoHeaderName] {
        static REQUEST_HEADERS: &[PseudoHeaderName] = &[
            PseudoHeaderName::Method,
            PseudoHeaderName::Scheme,
            PseudoHeaderName::Authority,
            PseudoHeaderName::Path,
        ];
        static RESPONSE_HEADERS: &[PseudoHeaderName] = &[PseudoHeaderName::Status];
        match request_or_response {
            RequestOrResponse::Request => REQUEST_HEADERS,
            RequestOrResponse::Response => RESPONSE_HEADERS,
        }
    }

    pub fn all_names() -> &'static [PseudoHeaderName] {
        static ALL_HEADERS: &[PseudoHeaderName] = &[
            PseudoHeaderName::Method,
            PseudoHeaderName::Scheme,
            PseudoHeaderName::Authority,
            PseudoHeaderName::Path,
            PseudoHeaderName::Status,
        ];
        ALL_HEADERS
    }
}

impl fmt::Display for PseudoHeaderName {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt::Display::fmt(self.name(), f)
    }
}

#[derive(Default)]
pub(crate) struct PseudoHeaderNameSet {
    headers_mask: u32,
}

impl PseudoHeaderNameSet {
    pub fn new() -> PseudoHeaderNameSet {
        Default::default()
    }

    pub fn insert(&mut self, value: PseudoHeaderName) -> bool {
        let contains = self.contains(value);
        self.headers_mask |= 1 << (value as u32);
        !contains
    }

    pub fn contains(&self, value: PseudoHeaderName) -> bool {
        self.headers_mask & (1 << (value as u32)) != 0
    }
}

#[derive(Debug, Eq, PartialEq, Hash, Clone)]
struct RegularHeaderName(Ascii);

impl RegularHeaderName {
    pub fn from_bytes(bs: Bytes) -> Result<RegularHeaderName, (HeaderError, Bytes)> {
        // token          = 1*<any CHAR except CTLs or separators>
        // separators     = "(" | ")" | "<" | ">" | "@"
        //                | "," | ";" | ":" | "\" | <">
        //                | "/" | "[" | "]" | "?" | "="
        //                | "{" | "}" | SP | HT
        // field-name     = token

        if bs.is_empty() {
            return Err((HeaderError::EmptyName, bs));
        }

        for &b in &bs {
            if !b.is_ascii() {
                return Err((HeaderError::HeaderNameNotAscii, bs));
            }
            if b.is_ascii_control() {
                return Err((HeaderError::IncorrectCharInName, bs));
            }
            if b.is_ascii_uppercase() {
                return Err((HeaderError::IncorrectCharInName, bs));
            }
            let bad_chars = b"()<>@,;:\\\"/[]?={} \t";
            if bad_chars.contains(&b) {
                return Err((HeaderError::IncorrectCharInName, bs));
            }
        }
        unsafe { Ok(RegularHeaderName(Ascii::from_bytes_unchecked(bs))) }
    }

    pub const unsafe fn _from_bytes_unchecked(bs: Bytes) -> RegularHeaderName {
        RegularHeaderName(Ascii::from_bytes_unchecked(bs))
    }
}

impl fmt::Display for RegularHeaderName {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt::Display::fmt(&self.0, f)
    }
}

/// Separate type to hide contents from rustdoc.
#[derive(Eq, PartialEq, Hash, Clone)]
enum HeaderNameEnum {
    Pseudo(PseudoHeaderName),
    Regular(RegularHeaderName),
}

impl fmt::Display for HeaderNameEnum {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            HeaderNameEnum::Pseudo(p) => fmt::Display::fmt(p, f),
            HeaderNameEnum::Regular(r) => fmt::Display::fmt(r, f),
        }
    }
}

/// Representation of header name
///
/// Contained value is guaranteed to contain a valid header name.
///
/// # Examples
///
/// ```
/// # use httpbis::*;
/// assert_eq!("content-type", HeaderName::new("Content-Type").name());
/// assert_eq!(":method", HeaderName::pseudo(PseudoHeaderName::Method).name());
/// ```
#[derive(Eq, PartialEq, Hash, Clone)]
pub struct HeaderName(HeaderNameEnum);

impl From<PseudoHeaderName> for HeaderName {
    fn from(p: PseudoHeaderName) -> Self {
        HeaderName(HeaderNameEnum::Pseudo(p))
    }
}

impl<'a> From<&'a str> for HeaderName {
    fn from(s: &'a str) -> Self {
        HeaderName::new(Bytes::copy_from_slice(s.as_bytes()))
    }
}

impl<'a> From<&'a [u8]> for HeaderName {
    fn from(s: &'a [u8]) -> Self {
        HeaderName::new(Bytes::copy_from_slice(s))
    }
}

impl From<String> for HeaderName {
    fn from(s: String) -> Self {
        HeaderName::new(s)
    }
}

impl From<Vec<u8>> for HeaderName {
    fn from(s: Vec<u8>) -> Self {
        HeaderName::new(s)
    }
}

impl From<Bytes> for HeaderName {
    fn from(s: Bytes) -> Self {
        HeaderName::new(s)
    }
}

impl HeaderName {
    /// Construct a pseudo header name.
    ///
    /// # Example
    ///
    /// ```
    /// # use httpbis::*;
    /// assert_eq!(":method", HeaderName::pseudo(PseudoHeaderName::Method).name());
    /// ```
    pub fn pseudo(name: PseudoHeaderName) -> HeaderName {
        HeaderName(HeaderNameEnum::Pseudo(name))
    }

    /// Construct a header name from string
    ///
    /// # Example
    ///
    /// Default construction
    ///
    /// ```
    /// # use httpbis::*;
    /// assert_eq!("content-type", HeaderName::new("Content-Type").name());
    /// assert_eq!(":method", HeaderName::new(":method").name());
    /// ```
    ///
    /// # Panics
    ///
    /// Panics on incorrect header name.
    ///
    /// ```should_panic
    /// # use httpbis::*;
    /// HeaderName::new("");
    /// ```
    pub fn new(name: impl Into<Bytes>) -> HeaderName {
        let mut name = name.into();
        make_ascii_lowercase(&mut name);
        match HeaderName::new_validate(name) {
            Ok(h) => h,
            Err((e, name)) => panic!("incorrect header name: {:?}: {:?}", name, e),
        }
    }

    /// Construct a header from given sequence of bytes
    ///
    /// Returns error if header name is not valid,
    /// in particular if name is upper case.
    ///
    /// # Examples
    ///
    /// ```
    /// # extern crate httpbis;
    /// # extern crate bytes;
    /// # use httpbis::*;
    /// # use bytes::*;
    /// assert!(HeaderName::new_validate(Bytes::from(":method")).is_ok());
    /// assert!(HeaderName::new_validate(Bytes::from("Content-Type")).is_err());
    /// ```
    pub fn new_validate(name: Bytes) -> Result<HeaderName, (HeaderError, Bytes)> {
        if name.len() == 0 {
            return Err((HeaderError::EmptyName, name));
        }

        Ok(if name[0] == b':' {
            HeaderName(HeaderNameEnum::Pseudo(
                PseudoHeaderName::parse(&name).map_err(|e| (e, name))?,
            ))
        } else {
            // HTTP/2 does not use the Connection header field to indicate
            // connection-specific header fields; in this protocol, connection-
            // specific metadata is conveyed by other means.  An endpoint MUST NOT
            // generate an HTTP/2 message containing connection-specific header
            // fields; any message containing connection-specific header fields MUST
            // be treated as malformed (Section 8.1.2.6).
            let connection_specific_headers = [
                "connection",
                "keep-alive",
                "proxy-connection",
                "transfer-encoding",
                "upgrade",
            ];
            for s in &connection_specific_headers {
                if name == s.as_bytes() {
                    return Err((HeaderError::ConnectionSpecificHeader(s), name));
                }
            }

            for &b in &name {
                // TODO: restrict more
                if b >= b'A' && b <= b'Z' {
                    return Err((HeaderError::IncorrectCharInName, name.clone()));
                }
            }

            HeaderName(HeaderNameEnum::Regular(RegularHeaderName::from_bytes(
                name,
            )?))
        })
    }

    /// Return a header name as a string.
    pub fn name(&self) -> &str {
        match &self.0 {
            HeaderNameEnum::Pseudo(p) => p.name(),
            HeaderNameEnum::Regular(r) => r.0.as_str(),
        }
    }

    /// If header name is pseudo header name
    pub fn is_pseudo(&self) -> bool {
        match self.0 {
            HeaderNameEnum::Pseudo(_) => true,
            HeaderNameEnum::Regular(_) => false,
        }
    }

    /// Obtain pseudo header name from this header name
    pub fn pseudo_header_name(&self) -> Option<PseudoHeaderName> {
        match self.0 {
            HeaderNameEnum::Pseudo(p) => Some(p),
            HeaderNameEnum::Regular(_) => None,
        }
    }
}

impl fmt::Debug for HeaderName {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "HeaderName({:?})", self.name())
    }
}

impl fmt::Display for HeaderName {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt::Display::fmt(&self.0, f)
    }
}

fn make_ascii_lowercase(bytes: &mut Bytes) {
    if bytes.as_ref().iter().all(|c| c.is_ascii_lowercase()) {
        return;
    }
    let mut bytes_mut = BytesMut::from(&bytes[..]);
    bytes_mut.as_mut().make_ascii_lowercase();
    *bytes = bytes_mut.freeze();
}

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

    #[test]
    fn header_name_display() {
        assert_eq!(
            ":method",
            format!(
                "{}",
                HeaderName(HeaderNameEnum::Pseudo(PseudoHeaderName::Method))
            )
        );
        assert_eq!(
            "x-fgfg",
            format!(
                "{}",
                HeaderName(HeaderNameEnum::Regular(
                    RegularHeaderName::from_bytes(Bytes::from("x-fgfg")).unwrap()
                ))
            )
        );
    }

    #[test]
    fn header_name_debug() {
        assert_eq!(
            "HeaderName(\":method\")",
            format!(
                "{:?}",
                HeaderName(HeaderNameEnum::Pseudo(PseudoHeaderName::Method))
            )
        );
        assert_eq!(
            "HeaderName(\"x-fgfg\")",
            format!(
                "{:?}",
                HeaderName(HeaderNameEnum::Regular(
                    RegularHeaderName::from_bytes(Bytes::from("x-fgfg")).unwrap()
                ))
            )
        );
    }
}
<commit_msg>Fix conversion to lower case in HeaderName constructor<commit_after>use crate::ascii::Ascii;
use crate::req_resp::RequestOrResponse;
use crate::solicit::header::{HeaderError, HeaderResult};
use bytes::Bytes;
use bytes::BytesMut;
use std::fmt;

#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
pub enum PseudoHeaderName {
    // 8.1.2.3 Request Pseudo-Header Fields
    Method = 0,
    Scheme = 1,
    Authority = 2,
    Path = 3,

    // 8.1.2.4 Response Pseudo-Header Fields
    Status = 4,
}

impl PseudoHeaderName {
    pub fn name(&self) -> &'static str {
        match *self {
            PseudoHeaderName::Method => ":method",
            PseudoHeaderName::Scheme => ":scheme",
            PseudoHeaderName::Authority => ":authority",
            PseudoHeaderName::Path => ":path",
            PseudoHeaderName::Status => ":status",
        }
    }

    pub fn parse(value: &[u8]) -> HeaderResult<PseudoHeaderName> {
        match value {
            b":method" => Ok(PseudoHeaderName::Method),
            b":scheme" => Ok(PseudoHeaderName::Scheme),
            b":authority" => Ok(PseudoHeaderName::Authority),
            b":path" => Ok(PseudoHeaderName::Path),
            b":status" => Ok(PseudoHeaderName::Status),
            _ => Err(HeaderError::UnknownPseudoHeader),
        }
    }

    pub fn req_or_resp(&self) -> RequestOrResponse {
        match *self {
            PseudoHeaderName::Method => RequestOrResponse::Request,
            PseudoHeaderName::Scheme => RequestOrResponse::Request,
            PseudoHeaderName::Authority => RequestOrResponse::Request,
            PseudoHeaderName::Path => RequestOrResponse::Request,
            PseudoHeaderName::Status => RequestOrResponse::Response,
        }
    }

    pub fn name_bytes(&self) -> Bytes {
        Bytes::from_static(self.name().as_bytes())
    }

    pub fn names(request_or_response: RequestOrResponse) -> &'static [PseudoHeaderName] {
        static REQUEST_HEADERS: &[PseudoHeaderName] = &[
            PseudoHeaderName::Method,
            PseudoHeaderName::Scheme,
            PseudoHeaderName::Authority,
            PseudoHeaderName::Path,
        ];
        static RESPONSE_HEADERS: &[PseudoHeaderName] = &[PseudoHeaderName::Status];
        match request_or_response {
            RequestOrResponse::Request => REQUEST_HEADERS,
            RequestOrResponse::Response => RESPONSE_HEADERS,
        }
    }

    pub fn all_names() -> &'static [PseudoHeaderName] {
        static ALL_HEADERS: &[PseudoHeaderName] = &[
            PseudoHeaderName::Method,
            PseudoHeaderName::Scheme,
            PseudoHeaderName::Authority,
            PseudoHeaderName::Path,
            PseudoHeaderName::Status,
        ];
        ALL_HEADERS
    }
}

impl fmt::Display for PseudoHeaderName {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt::Display::fmt(self.name(), f)
    }
}

#[derive(Default)]
pub(crate) struct PseudoHeaderNameSet {
    headers_mask: u32,
}

impl PseudoHeaderNameSet {
    pub fn new() -> PseudoHeaderNameSet {
        Default::default()
    }

    pub fn insert(&mut self, value: PseudoHeaderName) -> bool {
        let contains = self.contains(value);
        self.headers_mask |= 1 << (value as u32);
        !contains
    }

    pub fn contains(&self, value: PseudoHeaderName) -> bool {
        self.headers_mask & (1 << (value as u32)) != 0
    }
}

#[derive(Debug, Eq, PartialEq, Hash, Clone)]
struct RegularHeaderName(Ascii);

impl RegularHeaderName {
    pub fn from_bytes(bs: Bytes) -> Result<RegularHeaderName, (HeaderError, Bytes)> {
        // token          = 1*<any CHAR except CTLs or separators>
        // separators     = "(" | ")" | "<" | ">" | "@"
        //                | "," | ";" | ":" | "\" | <">
        //                | "/" | "[" | "]" | "?" | "="
        //                | "{" | "}" | SP | HT
        // field-name     = token

        if bs.is_empty() {
            return Err((HeaderError::EmptyName, bs));
        }

        for &b in &bs {
            if !b.is_ascii() {
                return Err((HeaderError::HeaderNameNotAscii, bs));
            }
            if b.is_ascii_control() {
                return Err((HeaderError::IncorrectCharInName, bs));
            }
            if b.is_ascii_uppercase() {
                return Err((HeaderError::IncorrectCharInName, bs));
            }
            let bad_chars = b"()<>@,;:\\\"/[]?={} \t";
            if bad_chars.contains(&b) {
                return Err((HeaderError::IncorrectCharInName, bs));
            }
        }
        unsafe { Ok(RegularHeaderName(Ascii::from_bytes_unchecked(bs))) }
    }

    pub const unsafe fn _from_bytes_unchecked(bs: Bytes) -> RegularHeaderName {
        RegularHeaderName(Ascii::from_bytes_unchecked(bs))
    }
}

impl fmt::Display for RegularHeaderName {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt::Display::fmt(&self.0, f)
    }
}

/// Separate type to hide contents from rustdoc.
#[derive(Eq, PartialEq, Hash, Clone)]
enum HeaderNameEnum {
    Pseudo(PseudoHeaderName),
    Regular(RegularHeaderName),
}

impl fmt::Display for HeaderNameEnum {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            HeaderNameEnum::Pseudo(p) => fmt::Display::fmt(p, f),
            HeaderNameEnum::Regular(r) => fmt::Display::fmt(r, f),
        }
    }
}

/// Representation of header name
///
/// Contained value is guaranteed to contain a valid header name.
///
/// # Examples
///
/// ```
/// # use httpbis::*;
/// assert_eq!("content-type", HeaderName::new("Content-Type").name());
/// assert_eq!(":method", HeaderName::pseudo(PseudoHeaderName::Method).name());
/// ```
#[derive(Eq, PartialEq, Hash, Clone)]
pub struct HeaderName(HeaderNameEnum);

impl From<PseudoHeaderName> for HeaderName {
    fn from(p: PseudoHeaderName) -> Self {
        HeaderName(HeaderNameEnum::Pseudo(p))
    }
}

impl<'a> From<&'a str> for HeaderName {
    fn from(s: &'a str) -> Self {
        HeaderName::new(Bytes::copy_from_slice(s.as_bytes()))
    }
}

impl<'a> From<&'a [u8]> for HeaderName {
    fn from(s: &'a [u8]) -> Self {
        HeaderName::new(Bytes::copy_from_slice(s))
    }
}

impl From<String> for HeaderName {
    fn from(s: String) -> Self {
        HeaderName::new(s)
    }
}

impl From<Vec<u8>> for HeaderName {
    fn from(s: Vec<u8>) -> Self {
        HeaderName::new(s)
    }
}

impl From<Bytes> for HeaderName {
    fn from(s: Bytes) -> Self {
        HeaderName::new(s)
    }
}

impl HeaderName {
    /// Construct a pseudo header name.
    ///
    /// # Example
    ///
    /// ```
    /// # use httpbis::*;
    /// assert_eq!(":method", HeaderName::pseudo(PseudoHeaderName::Method).name());
    /// ```
    pub fn pseudo(name: PseudoHeaderName) -> HeaderName {
        HeaderName(HeaderNameEnum::Pseudo(name))
    }

    /// Construct a header name from string
    ///
    /// # Example
    ///
    /// Default construction
    ///
    /// ```
    /// # use httpbis::*;
    /// assert_eq!("content-type", HeaderName::new("Content-Type").name());
    /// assert_eq!(":method", HeaderName::new(":method").name());
    /// ```
    ///
    /// # Panics
    ///
    /// Panics on incorrect header name.
    ///
    /// ```should_panic
    /// # use httpbis::*;
    /// HeaderName::new("");
    /// ```
    pub fn new(name: impl Into<Bytes>) -> HeaderName {
        let mut name = name.into();
        make_ascii_lowercase(&mut name);
        match HeaderName::new_validate(name) {
            Ok(h) => h,
            Err((e, name)) => panic!("incorrect header name: {:?}: {:?}", name, e),
        }
    }

    /// Construct a header from given sequence of bytes
    ///
    /// Returns error if header name is not valid,
    /// in particular if name is upper case.
    ///
    /// # Examples
    ///
    /// ```
    /// # extern crate httpbis;
    /// # extern crate bytes;
    /// # use httpbis::*;
    /// # use bytes::*;
    /// assert!(HeaderName::new_validate(Bytes::from(":method")).is_ok());
    /// assert!(HeaderName::new_validate(Bytes::from("Content-Type")).is_err());
    /// ```
    pub fn new_validate(name: Bytes) -> Result<HeaderName, (HeaderError, Bytes)> {
        if name.len() == 0 {
            return Err((HeaderError::EmptyName, name));
        }

        Ok(if name[0] == b':' {
            HeaderName(HeaderNameEnum::Pseudo(
                PseudoHeaderName::parse(&name).map_err(|e| (e, name))?,
            ))
        } else {
            // HTTP/2 does not use the Connection header field to indicate
            // connection-specific header fields; in this protocol, connection-
            // specific metadata is conveyed by other means.  An endpoint MUST NOT
            // generate an HTTP/2 message containing connection-specific header
            // fields; any message containing connection-specific header fields MUST
            // be treated as malformed (Section 8.1.2.6).
            let connection_specific_headers = [
                "connection",
                "keep-alive",
                "proxy-connection",
                "transfer-encoding",
                "upgrade",
            ];
            for s in &connection_specific_headers {
                if name == s.as_bytes() {
                    return Err((HeaderError::ConnectionSpecificHeader(s), name));
                }
            }

            for &b in &name {
                // TODO: restrict more
                if b >= b'A' && b <= b'Z' {
                    return Err((HeaderError::IncorrectCharInName, name.clone()));
                }
            }

            HeaderName(HeaderNameEnum::Regular(RegularHeaderName::from_bytes(
                name,
            )?))
        })
    }

    /// Return a header name as a string.
    pub fn name(&self) -> &str {
        match &self.0 {
            HeaderNameEnum::Pseudo(p) => p.name(),
            HeaderNameEnum::Regular(r) => r.0.as_str(),
        }
    }

    /// If header name is pseudo header name
    pub fn is_pseudo(&self) -> bool {
        match self.0 {
            HeaderNameEnum::Pseudo(_) => true,
            HeaderNameEnum::Regular(_) => false,
        }
    }

    /// Obtain pseudo header name from this header name
    pub fn pseudo_header_name(&self) -> Option<PseudoHeaderName> {
        match self.0 {
            HeaderNameEnum::Pseudo(p) => Some(p),
            HeaderNameEnum::Regular(_) => None,
        }
    }
}

impl fmt::Debug for HeaderName {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "HeaderName({:?})", self.name())
    }
}

impl fmt::Display for HeaderName {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt::Display::fmt(&self.0, f)
    }
}

fn make_ascii_lowercase(bytes: &mut Bytes) {
    if bytes.as_ref().iter().all(|c| !c.is_ascii_uppercase()) {
        return;
    }
    let mut bytes_mut = BytesMut::from(&bytes[..]);
    bytes_mut.as_mut().make_ascii_lowercase();
    *bytes = bytes_mut.freeze();
}

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

    #[test]
    fn header_name_new_to_lower() {
        assert_eq!("content-type", HeaderName::new("Content-Type").name());
    }

    #[test]
    fn header_name_display() {
        assert_eq!(
            ":method",
            format!(
                "{}",
                HeaderName(HeaderNameEnum::Pseudo(PseudoHeaderName::Method))
            )
        );
        assert_eq!(
            "x-fgfg",
            format!(
                "{}",
                HeaderName(HeaderNameEnum::Regular(
                    RegularHeaderName::from_bytes(Bytes::from("x-fgfg")).unwrap()
                ))
            )
        );
    }

    #[test]
    fn header_name_debug() {
        assert_eq!(
            "HeaderName(\":method\")",
            format!(
                "{:?}",
                HeaderName(HeaderNameEnum::Pseudo(PseudoHeaderName::Method))
            )
        );
        assert_eq!(
            "HeaderName(\"x-fgfg\")",
            format!(
                "{:?}",
                HeaderName(HeaderNameEnum::Regular(
                    RegularHeaderName::from_bytes(Bytes::from("x-fgfg")).unwrap()
                ))
            )
        );
    }
}
<|endoftext|>


<commit_before>use std::cast;
use rect::Rect;
use get_error;
use std::ptr;
use libc::c_int;
use pixels;
use rwops;

#[allow(non_camel_case_types)]
pub mod ll {
    use pixels::ll::SDL_PixelFormat;
    use pixels::ll::SDL_Palette;
    use render::ll::SDL_BlendMode;
    use rwops::ll::SDL_RWops;
    use rect::Rect;
    use libc::{c_int, c_void, uint32_t, uint8_t};
    pub type SDL_Rect = Rect;
    pub type SDL_bool = c_int;

    pub type SDL_SurfaceFlag = c_int;

    pub static SDL_SWSURFACE: SDL_SurfaceFlag = 0;
    pub static SDL_PREALLOC: SDL_SurfaceFlag = 0x00000001;
    pub static SDL_RLEACCEL: SDL_SurfaceFlag = 0x00000002;
    pub static SDL_DONTFREE: SDL_SurfaceFlag = 0x00000004;

    //SDL_surface.h
    pub struct SDL_BlitMap;

    pub struct SDL_Surface {
        pub flags: uint32_t,
        pub format: *SDL_PixelFormat,
        pub w: c_int,
        pub h: c_int,
        pub pitch: c_int,
        pub pixels: *c_void,
        pub userdata: *c_void,
        pub locked: c_int,
        pub lock_data: *c_void,
        pub clip_rect: SDL_Rect,
        pub map: *SDL_BlitMap,
        pub refcount: c_int
    }

    extern "C" {
        pub fn SDL_CreateRGBSurface(flags: uint32_t, width: c_int, height: c_int, depth: c_int, Rmask: uint32_t, Gmask: uint32_t, Bmask: uint32_t, Amask: uint32_t) ->  *SDL_Surface;
        pub fn SDL_CreateRGBSurfaceFrom(pixels: *c_void, width: c_int, height: c_int, depth: c_int, pitch: c_int, Rmask: uint32_t, Gmask: uint32_t, Bmask: uint32_t, Amask: uint32_t) ->  *SDL_Surface;
        pub fn SDL_FreeSurface(surface: *SDL_Surface);
        pub fn SDL_SetSurfacePalette(surface: *SDL_Surface, palette: *SDL_Palette) -> c_int;
        pub fn SDL_LockSurface(surface: *SDL_Surface) -> c_int;
        pub fn SDL_UnlockSurface(surface: *SDL_Surface);
        pub fn SDL_LoadBMP_RW(src: *SDL_RWops, freesrc: c_int) ->  *SDL_Surface;
        pub fn SDL_SaveBMP_RW(surface: *SDL_Surface, dst: *SDL_RWops, freedst: c_int) -> c_int;
        pub fn SDL_SetSurfaceRLE(surface: *SDL_Surface, flag: c_int) -> c_int;
        pub fn SDL_SetColorKey(surface: *SDL_Surface, flag: c_int, key: uint32_t) -> c_int;
        pub fn SDL_GetColorKey(surface: *SDL_Surface, key: *uint32_t) -> c_int;
        pub fn SDL_SetSurfaceColorMod(surface: *SDL_Surface, r: uint8_t, g: uint8_t, b: uint8_t) -> c_int;
        pub fn SDL_GetSurfaceColorMod(surface: *SDL_Surface, r: *uint8_t, g: *uint8_t, b: *uint8_t ) -> c_int;
        pub fn SDL_SetSurfaceAlphaMod(surface: *SDL_Surface, alpha: uint8_t) -> c_int;
        pub fn SDL_GetSurfaceAlphaMod(surface: *SDL_Surface, alpha: *uint8_t ) -> c_int;
        pub fn SDL_SetSurfaceBlendMode(surface: *SDL_Surface, blendMode: SDL_BlendMode) -> c_int;
        pub fn SDL_GetSurfaceBlendMode(surface: *SDL_Surface, blendMode: *SDL_BlendMode) -> c_int;
        pub fn SDL_SetClipRect(surface: *SDL_Surface, rect: *SDL_Rect) ->  SDL_bool;
        pub fn SDL_GetClipRect(surface: *SDL_Surface, rect: *SDL_Rect);
        pub fn SDL_ConvertSurface(src: *SDL_Surface, fmt: *SDL_PixelFormat, flags: uint32_t) ->  *SDL_Surface;
        pub fn SDL_ConvertSurfaceFormat(src: *SDL_Surface, pixel_format: uint32_t, flags: uint32_t) ->  *SDL_Surface;
        pub fn SDL_ConvertPixels(width: c_int, height: c_int, src_format: uint32_t, src: *c_void, src_pitch: c_int, dst_format: uint32_t, dst: *c_void, dst_pitch: c_int) -> c_int;
        pub fn SDL_FillRect(dst: *SDL_Surface, rect: *SDL_Rect, color: uint32_t) -> c_int;
        pub fn SDL_FillRects(dst: *SDL_Surface, rects: *SDL_Rect, count: c_int, color: uint32_t) -> c_int;
        pub fn SDL_UpperBlit(src: *SDL_Surface, srcrect: *SDL_Rect, dst: *SDL_Surface, dstrect: *SDL_Rect) -> c_int;
        pub fn SDL_LowerBlit(src: *SDL_Surface, srcrect: *SDL_Rect, dst: *SDL_Surface, dstrect: *SDL_Rect) -> c_int;
        pub fn SDL_SoftStretch(src: *SDL_Surface, srcrect: *SDL_Rect, dst: *SDL_Surface, dstrect: *SDL_Rect) -> c_int;
        pub fn SDL_UpperBlitScaled(src: *SDL_Surface, srcrect: *SDL_Rect, dst: *SDL_Surface, dstrect: *SDL_Rect) -> c_int;
        pub fn SDL_LowerBlitScaled(src: *SDL_Surface, srcrect: *SDL_Rect, dst: *SDL_Surface, dstrect: *SDL_Rect) -> c_int;
    }
}

#[deriving(Eq)]
pub enum SurfaceFlag {
    SWSurface = ll::SDL_SWSURFACE as int,
    PreAlloc = ll::SDL_PREALLOC as int,
    RLEAccel = ll::SDL_RLEACCEL as int,
    DontFree = ll::SDL_DONTFREE as int
}

#[deriving(Eq)] #[allow(raw_pointer_deriving)]
pub struct Surface {
    pub raw: *ll::SDL_Surface,
    pub owned: bool
}

impl Drop for Surface {
    fn drop(&mut self) {
        if self.owned {
            unsafe {
                ll::SDL_FreeSurface(self.raw);
            }
        }
    }
}

impl Surface {
    pub fn new(surface_flags: &[SurfaceFlag], width: int, height: int, bpp: int,
               rmask: u32, gmask: u32, bmask: u32, amask: u32) -> Result<~Surface, ~str> {
        let flags = surface_flags.iter().fold(0u32, |flags, flag| { flags | *flag as u32 });

        unsafe {
            let raw = ll::SDL_CreateRGBSurface(flags, width as c_int, height as c_int, bpp as c_int,
                                               rmask, gmask, bmask, amask);

            if raw == ptr::null() {
                Err(get_error())
            } else {
                Ok(~Surface { raw: raw, owned: true })
            }
        }
    }

    //TODO: From Data
    pub fn get_width(&self) -> u16 {
        unsafe { (*self.raw).w as u16 }
    }

    pub fn get_height(&self) -> u16 {
        unsafe { (*self.raw).h as u16 }
    }

    pub fn get_size(&self) -> (u16, u16) {
        (self.get_width(), self.get_height())
    }

    pub fn get_rect(&self) -> Rect {
        Rect {
            x: 0,
            y: 0,
            w: self.get_width() as i32,
            h: self.get_height() as i32
        }
    }

    pub fn get_pixel_format(&self) -> pixels::PixelFormat {
        pixels::PixelFormat {
            raw: unsafe { (*self.raw).format }
        }
    }

    pub fn lock(&self) -> bool {
        unsafe { ll::SDL_LockSurface(self.raw) == 0 }
    }

    /// Locks a surface so that the pixels can be directly accessed safely.
    pub fn with_lock<R>(&self, f: |pixels: &mut [u8]| -> R) -> R {
        unsafe {
            if ll::SDL_LockSurface(self.raw) != 0 { fail!(~"could not lock surface"); }
            let len = (*self.raw).pitch as uint * ((*self.raw).h as uint);
            let pixels: &mut [u8] = cast::transmute(((*self.raw).pixels, len));
            let rv = f(pixels);
            ll::SDL_UnlockSurface(self.raw);
            rv
        }
    }

    pub fn unlock(&self) {
        unsafe { ll::SDL_UnlockSurface(self.raw); }
    }

    pub fn from_bmp(path: &Path) -> Result<~Surface, ~str> {
        let raw = unsafe {
            ll::SDL_LoadBMP_RW(rwops::RWops::from_file(path, "rb").unwrap().raw, 0)
        };

        if raw.is_null() { Err(get_error()) }
        else { Ok(~Surface{raw: raw, owned: true}) }
    }

    pub fn save_bmp(&self, path: &Path) -> bool {
	unsafe {
            ll::SDL_SaveBMP_RW(self.raw, rwops::RWops::from_file(path, "rb").unwrap().raw, 0) == 0
	}
    }

    pub fn set_palette(&self, palette: &pixels::Palette) -> bool {
        unsafe {
            ll::SDL_SetSurfacePalette(self.raw, palette.raw) == 0
        }
    }

    pub fn enable_RLE(&self) -> bool {
        unsafe {
            ll::SDL_SetSurfaceRLE(self.raw, 1) == 0
        }
    }

    pub fn disable_RLE(&self) -> bool {
        unsafe {
            ll::SDL_SetSurfaceRLE(self.raw, 0) == 0
        }
    }

    pub fn set_color_key(&self, enable: bool, color: pixels::Color) -> Result<(), ~str> {
        let key = color.to_u32(&self.get_pixel_format());
        let result = unsafe {
            ll::SDL_SetColorKey(self.raw, ::std::bool::to_bit(enable), key)
        };
        if result == 0 {
            Ok(())
        } else {
            Err(get_error())
        }
    }

    pub fn get_color_key(&self) -> Result<pixels::Color, ~str> {
        let key: u32 = 0;
        let result = unsafe {
            ll::SDL_GetColorKey(self.raw, &key)
        };

        if result == 0 {
            Ok(pixels::Color::from_u32(&self.get_pixel_format(), key))
        } else {
            Err(get_error())
        }
    }

    pub fn set_color_mod(&self, color: pixels::Color) -> bool {
        let (r, g, b) = match color {
            pixels::RGB(r, g, b) => (r, g, b),
            pixels::RGBA(r, g, b, _) => (r, g, b)
        };

        unsafe {
            ll::SDL_SetSurfaceColorMod(self.raw, r, g, b) == 0
        }
    }

    pub fn get_color_mod(&self) -> Result<pixels::Color, ~str> {
        let r: u8 = 0;
        let g: u8 = 0;
        let b: u8 = 0;

        let result = unsafe {
            ll::SDL_GetSurfaceColorMod(self.raw, &r, &g, &b) == 0
        };

        if result {
            Ok(pixels::RGB(r,g,b))
        } else {
            Err(get_error())
        }
    }
    /*
    pub fn SDL_SetSurfaceAlphaMod(surface: *SDL_Surface, alpha: uint8_t) -> c_int;
    pub fn SDL_GetSurfaceAlphaMod(surface: *SDL_Surface, alpha: *uint8_t ) -> c_int;
    pub fn SDL_SetSurfaceBlendMode(surface: *SDL_Surface, blendMode: SDL_BlendMode) -> c_int;
    pub fn SDL_GetSurfaceBlendMode(surface: *SDL_Surface, blendMode: *SDL_BlendMode) -> c_int;
    pub fn SDL_SetClipRect(surface: *SDL_Surface, rect: *SDL_Rect) ->  SDL_bool;
    pub fn SDL_GetClipRect(surface: *SDL_Surface, rect: *SDL_Rect);
    pub fn SDL_ConvertSurface(src: *SDL_Surface, fmt: *SDL_PixelFormat, flags: uint32_t) ->  *SDL_Surface;
    pub fn SDL_ConvertSurfaceFormat(src: *SDL_Surface, pixel_format: uint32_t, flags: uint32_t) ->  *SDL_Surface;
    pub fn SDL_ConvertPixels(width: c_int, height: c_int, src_format: uint32_t, src: *c_void, src_pitch: c_int, dst_format: uint32_t, dst: *c_void, dst_pitch: c_int) -> c_int;
    pub fn SDL_FillRect(dst: *SDL_Surface, rect: *SDL_Rect, color: uint32_t) -> c_int;
    pub fn SDL_FillRects(dst: *SDL_Surface, rects: *SDL_Rect, count: c_int, color: uint32_t) -> c_int;
    pub fn SDL_UpperBlit(src: *SDL_Surface, srcrect: *SDL_Rect, dst: *SDL_Surface, dstrect: *SDL_Rect) -> c_int;
    pub fn SDL_LowerBlit(src: *SDL_Surface, srcrect: *SDL_Rect, dst: *SDL_Surface, dstrect: *SDL_Rect) -> c_int;
    pub fn SDL_SoftStretch(src: *SDL_Surface, srcrect: *SDL_Rect, dst: *SDL_Surface, dstrect: *SDL_Rect) -> c_int;
    pub fn SDL_UpperBlitScaled(src: *SDL_Surface, srcrect: *SDL_Rect, dst: *SDL_Surface, dstrect: *SDL_Rect) -> c_int;
    pub fn SDL_LowerBlitScaled(src: *SDL_Surface, srcrect: *SDL_Rect, dst: *SDL_Surface, dstrect: *SDL_Rect) -> c_int)*/
}
<commit_msg>fix unhandled error in calling RWops::from_file<commit_after>use std::cast;
use rect::Rect;
use get_error;
use std::ptr;
use libc::c_int;
use pixels;
use rwops;

#[allow(non_camel_case_types)]
pub mod ll {
    use pixels::ll::SDL_PixelFormat;
    use pixels::ll::SDL_Palette;
    use render::ll::SDL_BlendMode;
    use rwops::ll::SDL_RWops;
    use rect::Rect;
    use libc::{c_int, c_void, uint32_t, uint8_t};
    pub type SDL_Rect = Rect;
    pub type SDL_bool = c_int;

    pub type SDL_SurfaceFlag = c_int;

    pub static SDL_SWSURFACE: SDL_SurfaceFlag = 0;
    pub static SDL_PREALLOC: SDL_SurfaceFlag = 0x00000001;
    pub static SDL_RLEACCEL: SDL_SurfaceFlag = 0x00000002;
    pub static SDL_DONTFREE: SDL_SurfaceFlag = 0x00000004;

    //SDL_surface.h
    pub struct SDL_BlitMap;

    pub struct SDL_Surface {
        pub flags: uint32_t,
        pub format: *SDL_PixelFormat,
        pub w: c_int,
        pub h: c_int,
        pub pitch: c_int,
        pub pixels: *c_void,
        pub userdata: *c_void,
        pub locked: c_int,
        pub lock_data: *c_void,
        pub clip_rect: SDL_Rect,
        pub map: *SDL_BlitMap,
        pub refcount: c_int
    }

    extern "C" {
        pub fn SDL_CreateRGBSurface(flags: uint32_t, width: c_int, height: c_int, depth: c_int, Rmask: uint32_t, Gmask: uint32_t, Bmask: uint32_t, Amask: uint32_t) ->  *SDL_Surface;
        pub fn SDL_CreateRGBSurfaceFrom(pixels: *c_void, width: c_int, height: c_int, depth: c_int, pitch: c_int, Rmask: uint32_t, Gmask: uint32_t, Bmask: uint32_t, Amask: uint32_t) ->  *SDL_Surface;
        pub fn SDL_FreeSurface(surface: *SDL_Surface);
        pub fn SDL_SetSurfacePalette(surface: *SDL_Surface, palette: *SDL_Palette) -> c_int;
        pub fn SDL_LockSurface(surface: *SDL_Surface) -> c_int;
        pub fn SDL_UnlockSurface(surface: *SDL_Surface);
        pub fn SDL_LoadBMP_RW(src: *SDL_RWops, freesrc: c_int) ->  *SDL_Surface;
        pub fn SDL_SaveBMP_RW(surface: *SDL_Surface, dst: *SDL_RWops, freedst: c_int) -> c_int;
        pub fn SDL_SetSurfaceRLE(surface: *SDL_Surface, flag: c_int) -> c_int;
        pub fn SDL_SetColorKey(surface: *SDL_Surface, flag: c_int, key: uint32_t) -> c_int;
        pub fn SDL_GetColorKey(surface: *SDL_Surface, key: *uint32_t) -> c_int;
        pub fn SDL_SetSurfaceColorMod(surface: *SDL_Surface, r: uint8_t, g: uint8_t, b: uint8_t) -> c_int;
        pub fn SDL_GetSurfaceColorMod(surface: *SDL_Surface, r: *uint8_t, g: *uint8_t, b: *uint8_t ) -> c_int;
        pub fn SDL_SetSurfaceAlphaMod(surface: *SDL_Surface, alpha: uint8_t) -> c_int;
        pub fn SDL_GetSurfaceAlphaMod(surface: *SDL_Surface, alpha: *uint8_t ) -> c_int;
        pub fn SDL_SetSurfaceBlendMode(surface: *SDL_Surface, blendMode: SDL_BlendMode) -> c_int;
        pub fn SDL_GetSurfaceBlendMode(surface: *SDL_Surface, blendMode: *SDL_BlendMode) -> c_int;
        pub fn SDL_SetClipRect(surface: *SDL_Surface, rect: *SDL_Rect) ->  SDL_bool;
        pub fn SDL_GetClipRect(surface: *SDL_Surface, rect: *SDL_Rect);
        pub fn SDL_ConvertSurface(src: *SDL_Surface, fmt: *SDL_PixelFormat, flags: uint32_t) ->  *SDL_Surface;
        pub fn SDL_ConvertSurfaceFormat(src: *SDL_Surface, pixel_format: uint32_t, flags: uint32_t) ->  *SDL_Surface;
        pub fn SDL_ConvertPixels(width: c_int, height: c_int, src_format: uint32_t, src: *c_void, src_pitch: c_int, dst_format: uint32_t, dst: *c_void, dst_pitch: c_int) -> c_int;
        pub fn SDL_FillRect(dst: *SDL_Surface, rect: *SDL_Rect, color: uint32_t) -> c_int;
        pub fn SDL_FillRects(dst: *SDL_Surface, rects: *SDL_Rect, count: c_int, color: uint32_t) -> c_int;
        pub fn SDL_UpperBlit(src: *SDL_Surface, srcrect: *SDL_Rect, dst: *SDL_Surface, dstrect: *SDL_Rect) -> c_int;
        pub fn SDL_LowerBlit(src: *SDL_Surface, srcrect: *SDL_Rect, dst: *SDL_Surface, dstrect: *SDL_Rect) -> c_int;
        pub fn SDL_SoftStretch(src: *SDL_Surface, srcrect: *SDL_Rect, dst: *SDL_Surface, dstrect: *SDL_Rect) -> c_int;
        pub fn SDL_UpperBlitScaled(src: *SDL_Surface, srcrect: *SDL_Rect, dst: *SDL_Surface, dstrect: *SDL_Rect) -> c_int;
        pub fn SDL_LowerBlitScaled(src: *SDL_Surface, srcrect: *SDL_Rect, dst: *SDL_Surface, dstrect: *SDL_Rect) -> c_int;
    }
}

#[deriving(Eq)]
pub enum SurfaceFlag {
    SWSurface = ll::SDL_SWSURFACE as int,
    PreAlloc = ll::SDL_PREALLOC as int,
    RLEAccel = ll::SDL_RLEACCEL as int,
    DontFree = ll::SDL_DONTFREE as int
}

#[deriving(Eq)] #[allow(raw_pointer_deriving)]
pub struct Surface {
    pub raw: *ll::SDL_Surface,
    pub owned: bool
}

impl Drop for Surface {
    fn drop(&mut self) {
        if self.owned {
            unsafe {
                ll::SDL_FreeSurface(self.raw);
            }
        }
    }
}

impl Surface {
    pub fn new(surface_flags: &[SurfaceFlag], width: int, height: int, bpp: int,
               rmask: u32, gmask: u32, bmask: u32, amask: u32) -> Result<~Surface, ~str> {
        let flags = surface_flags.iter().fold(0u32, |flags, flag| { flags | *flag as u32 });

        unsafe {
            let raw = ll::SDL_CreateRGBSurface(flags, width as c_int, height as c_int, bpp as c_int,
                                               rmask, gmask, bmask, amask);

            if raw == ptr::null() {
                Err(get_error())
            } else {
                Ok(~Surface { raw: raw, owned: true })
            }
        }
    }

    //TODO: From Data
    pub fn get_width(&self) -> u16 {
        unsafe { (*self.raw).w as u16 }
    }

    pub fn get_height(&self) -> u16 {
        unsafe { (*self.raw).h as u16 }
    }

    pub fn get_size(&self) -> (u16, u16) {
        (self.get_width(), self.get_height())
    }

    pub fn get_rect(&self) -> Rect {
        Rect {
            x: 0,
            y: 0,
            w: self.get_width() as i32,
            h: self.get_height() as i32
        }
    }

    pub fn get_pixel_format(&self) -> pixels::PixelFormat {
        pixels::PixelFormat {
            raw: unsafe { (*self.raw).format }
        }
    }

    pub fn lock(&self) -> bool {
        unsafe { ll::SDL_LockSurface(self.raw) == 0 }
    }

    /// Locks a surface so that the pixels can be directly accessed safely.
    pub fn with_lock<R>(&self, f: |pixels: &mut [u8]| -> R) -> R {
        unsafe {
            if ll::SDL_LockSurface(self.raw) != 0 { fail!(~"could not lock surface"); }
            let len = (*self.raw).pitch as uint * ((*self.raw).h as uint);
            let pixels: &mut [u8] = cast::transmute(((*self.raw).pixels, len));
            let rv = f(pixels);
            ll::SDL_UnlockSurface(self.raw);
            rv
        }
    }

    pub fn unlock(&self) {
        unsafe { ll::SDL_UnlockSurface(self.raw); }
    }

    pub fn from_bmp(path: &Path) -> Result<~Surface, ~str> {
        let raw = unsafe {
            ll::SDL_LoadBMP_RW(try!(rwops::RWops::from_file(path, "rb")).raw, 0)
        };

        if raw.is_null() { Err(get_error()) }
        else { Ok(~Surface{raw: raw, owned: true}) }
    }

    pub fn save_bmp(&self, path: &Path) -> bool {
	unsafe {
            ll::SDL_SaveBMP_RW(self.raw, try!(rwops::RWops::from_file(path, "rb")).raw, 0) == 0
	}
    }

    pub fn set_palette(&self, palette: &pixels::Palette) -> bool {
        unsafe {
            ll::SDL_SetSurfacePalette(self.raw, palette.raw) == 0
        }
    }

    pub fn enable_RLE(&self) -> bool {
        unsafe {
            ll::SDL_SetSurfaceRLE(self.raw, 1) == 0
        }
    }

    pub fn disable_RLE(&self) -> bool {
        unsafe {
            ll::SDL_SetSurfaceRLE(self.raw, 0) == 0
        }
    }

    pub fn set_color_key(&self, enable: bool, color: pixels::Color) -> Result<(), ~str> {
        let key = color.to_u32(&self.get_pixel_format());
        let result = unsafe {
            ll::SDL_SetColorKey(self.raw, ::std::bool::to_bit(enable), key)
        };
        if result == 0 {
            Ok(())
        } else {
            Err(get_error())
        }
    }

    pub fn get_color_key(&self) -> Result<pixels::Color, ~str> {
        let key: u32 = 0;
        let result = unsafe {
            ll::SDL_GetColorKey(self.raw, &key)
        };

        if result == 0 {
            Ok(pixels::Color::from_u32(&self.get_pixel_format(), key))
        } else {
            Err(get_error())
        }
    }

    pub fn set_color_mod(&self, color: pixels::Color) -> bool {
        let (r, g, b) = match color {
            pixels::RGB(r, g, b) => (r, g, b),
            pixels::RGBA(r, g, b, _) => (r, g, b)
        };

        unsafe {
            ll::SDL_SetSurfaceColorMod(self.raw, r, g, b) == 0
        }
    }

    pub fn get_color_mod(&self) -> Result<pixels::Color, ~str> {
        let r: u8 = 0;
        let g: u8 = 0;
        let b: u8 = 0;

        let result = unsafe {
            ll::SDL_GetSurfaceColorMod(self.raw, &r, &g, &b) == 0
        };

        if result {
            Ok(pixels::RGB(r,g,b))
        } else {
            Err(get_error())
        }
    }
    /*
    pub fn SDL_SetSurfaceAlphaMod(surface: *SDL_Surface, alpha: uint8_t) -> c_int;
    pub fn SDL_GetSurfaceAlphaMod(surface: *SDL_Surface, alpha: *uint8_t ) -> c_int;
    pub fn SDL_SetSurfaceBlendMode(surface: *SDL_Surface, blendMode: SDL_BlendMode) -> c_int;
    pub fn SDL_GetSurfaceBlendMode(surface: *SDL_Surface, blendMode: *SDL_BlendMode) -> c_int;
    pub fn SDL_SetClipRect(surface: *SDL_Surface, rect: *SDL_Rect) ->  SDL_bool;
    pub fn SDL_GetClipRect(surface: *SDL_Surface, rect: *SDL_Rect);
    pub fn SDL_ConvertSurface(src: *SDL_Surface, fmt: *SDL_PixelFormat, flags: uint32_t) ->  *SDL_Surface;
    pub fn SDL_ConvertSurfaceFormat(src: *SDL_Surface, pixel_format: uint32_t, flags: uint32_t) ->  *SDL_Surface;
    pub fn SDL_ConvertPixels(width: c_int, height: c_int, src_format: uint32_t, src: *c_void, src_pitch: c_int, dst_format: uint32_t, dst: *c_void, dst_pitch: c_int) -> c_int;
    pub fn SDL_FillRect(dst: *SDL_Surface, rect: *SDL_Rect, color: uint32_t) -> c_int;
    pub fn SDL_FillRects(dst: *SDL_Surface, rects: *SDL_Rect, count: c_int, color: uint32_t) -> c_int;
    pub fn SDL_UpperBlit(src: *SDL_Surface, srcrect: *SDL_Rect, dst: *SDL_Surface, dstrect: *SDL_Rect) -> c_int;
    pub fn SDL_LowerBlit(src: *SDL_Surface, srcrect: *SDL_Rect, dst: *SDL_Surface, dstrect: *SDL_Rect) -> c_int;
    pub fn SDL_SoftStretch(src: *SDL_Surface, srcrect: *SDL_Rect, dst: *SDL_Surface, dstrect: *SDL_Rect) -> c_int;
    pub fn SDL_UpperBlitScaled(src: *SDL_Surface, srcrect: *SDL_Rect, dst: *SDL_Surface, dstrect: *SDL_Rect) -> c_int;
    pub fn SDL_LowerBlitScaled(src: *SDL_Surface, srcrect: *SDL_Rect, dst: *SDL_Surface, dstrect: *SDL_Rect) -> c_int)*/
}
<|endoftext|>


<commit_before>//! Program x86 segmentation hardware.

use core::fmt;

/// Specifies which element to load into a segment from
/// descriptor tables (i.e., is a index to LDT or GDT table
/// with some additional flags).
bitflags! {
    flags SegmentSelector: u16 {
        /// Requestor Privilege Level
        const RPL_0 = 0b00,
        const RPL_1 = 0b01,
        const RPL_2 = 0b10,
        const RPL_3 = 0b11,

        /// Table Indicator (TI) 0 means GDT is used.
        const TI_GDT = 0 << 3,
        /// Table Indicator (TI) 1 means LDT is used.
        const TI_LDT = 1 << 3,
    }
}

impl SegmentSelector {
    /// Create a new SegmentSelector
    ///
    /// # Arguments
    ///  * `index` index in GDT or LDT array.
    ///
    pub fn new(index: u16) -> SegmentSelector {
        SegmentSelector { bits: index << 3 }
    }

    pub fn from_raw(bits: u16) -> SegmentSelector {
        SegmentSelector { bits: bits }
    }
}

impl fmt::Debug for SegmentSelector {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let r0 = match self.contains(RPL_0) {
            false => "",
            true => "Ring 0 segment selector.",
        };
        let r1 = match self.contains(RPL_1) {
            false => "",
            true => "Ring 1 segment selector.",
        };
        let r2 = match self.contains(RPL_2) {
            false => "",
            true => "Ring 2 segment selector.",
        };
        let r3 = match self.contains(RPL_3) {
            false => "",
            true => "Ring 3 segment selector.",
        };
        let tbl = match self.contains(TI_LDT) {
            false => "GDT Table",
            true => "LDT Table",
        };

        write!(f,
               "Index {} in {}, {}{}{}{}",
               self.bits >> 3,
               tbl,
               r0,
               r1,
               r2,
               r3)
        // write!(f, "Index")
    }
}

/// Entry for GDT or LDT. Provides size and location of a segment.
bitflags! {
    flags SegmentDescriptor: u64 {
        /// Descriptor type (0 = system; 1 = code or data).
        const DESC_S    = 1 << (32+12),
        /// Descriptor privilege level 0.
        const DESC_DPL0 = 0b00 << (32+13),
        /// Descriptor privilege level 1.
        const DESC_DPL1 = 0b01 << (32+13),
        /// Descriptor privilege level 2.
        const DESC_DPL2 = 0b10 << (32+13),
        /// Descriptor privilege level 3.
        const DESC_DPL3 = 0b11 << (32+13),
        /// Descriptor is Present.
        const DESC_P = 1 << (32+15),
        /// Available for use by system software.
        const DESC_AVL  = 1 << (32+20),
        /// 64-bit code segment (IA-32e mode only).
        const DESC_L    = 1 << (32+21),
        /// Default operation size (0 = 16-bit segment, 1 = 32-bit segment)
        const DESC_DB   = 1 << (32+22),
        ///  Granularity.
        const DESC_G    = 1 << (32+23),

        // System-Segment and Gate-Descriptor Types for IA32e mode.
        // When the S (descriptor type) flag in a segment descriptor is clear,
        // the descriptor type is a system descriptor.

        const TYPE_SYS_LDT = 0b0010 << (32+8),
        const TYPE_SYS_TSS_AVAILABLE = 0b1001 << (32+8),
        const TYPE_SYS_TSS_BUSY = 0b1011 << (32+8),
        const TYPE_SYS_CALL_GATE = 0b1100 << (32+8),
        const TYPE_SYS_INTERRUPT_GATE = 0b1110 << (32+8),
        const TYPE_SYS_TRAP_GATE = 0b1111 << (32+8),

        // Code- and Data-Segment Descriptor Types.
        // When the S (descriptor type) flag in a segment descriptor is set,
        // the descriptor is for either a code or a data segment.

        /// Data Read-Only
        const TYPE_D_RO = 0b0000 << (32+8),
        /// Data Read-Only, accessed
        const TYPE_D_ROA = 0b0001 << (32+8),
        /// Data Read/Write
        const TYPE_D_RW = 0b0010 << (32+8),
        /// Data Read/Write, accessed
        const TYPE_D_RWA = 0b0011 << (32+8),
        /// Data Read-Only, expand-down
        const TYPE_D_ROEXD = 0b0100 << (32+8),
        /// Data Read-Only, expand-down, accessed
        const TYPE_D_ROEXDA = 0b0101 << (32+8),
        /// Data Read/Write, expand-down
        const TYPE_D_RWEXD = 0b0110 << (32+8),
        /// Data Read/Write, expand-down, accessed
        const TYPE_D_RWEXDA = 0b0111 << (32+8),

        /// Code Execute-Only
        const TYPE_C_EO = 0b1000 << (32+8),
        /// Code Execute-Only, accessed
        const TYPE_C_EOA = 0b1001 << (32+8),
        /// Code Execute/Read
        const TYPE_C_ER = 0b1010 << (32+8),
        /// Code Execute/Read, accessed
        const TYPE_C_ERA = 0b1011 << (32+8),
        /// Code Execute-Only, conforming
        const TYPE_C_EOC = 0b1100 << (32+8),
        /// Code Execute-Only, conforming, accessed
        const TYPE_C_EOCA = 0b1101 << (32+8),
        /// Code Execute/Read, conforming
        const TYPE_C_ERC = 0b1110 << (32+8),
        /// Code Execute/Read, conforming, accessed
        const TYPE_C_ERCA = 0b1111 << (32+8),
    }
}

/// This is data-structure is a ugly mess thing so we provide some
/// convenience function to program it.
impl SegmentDescriptor {
    pub fn new(base: u32, limit: u32) -> SegmentDescriptor {
        let base_low: u64 = base as u64 & 0xffffff;
        let base_high: u64 = (base as u64 >> 24) & 0xff;

        let limit_low: u64 = limit as u64 & 0xffff;
        let limit_high: u64 = (limit as u64 & (0b1111 << 16)) >> 16;

        SegmentDescriptor {
            bits: limit_low | base_low << 16 | limit_high << (32 + 16) | base_high << (32 + 24),
        }
    }
}

impl fmt::Debug for SegmentDescriptor {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "SD: 0x{:x}", self.bits)
    }
}

/// Reload stack segment register.
pub unsafe fn load_ss(sel: SegmentSelector) {
    asm!("movw $0, %ss " :: "r" (sel) : "memory");
}

/// Reload data segment register.
pub unsafe fn load_ds(sel: SegmentSelector) {
    asm!("movw $0, %ds " :: "r" (sel) : "memory");
}

/// Reload fs segment register.
pub unsafe fn load_es(sel: SegmentSelector) {
    asm!("movw $0, %es " :: "r" (sel) : "memory");
}

/// Reload fs segment register.
pub unsafe fn load_fs(sel: SegmentSelector) {
    asm!("movw $0, %fs " :: "r" (sel) : "memory");
}

/// Reload gs segment register.
pub unsafe fn load_gs(sel: SegmentSelector) {
    asm!("movw $0, %gs " :: "r" (sel) : "memory");
}

/// Reload code segment register.
/// Note this is special since we can not directly move
/// to %cs. Instead we push the new segment selector
/// and return value on the stack and use lretq
/// to reload cs and continue at 1:.
pub unsafe fn load_cs(sel: SegmentSelector) {
    asm!("pushq $0
         lea 1f(%rip), %rax
         pushq %rax
         lretq
         1:" :: "r" (sel.bits() as u64) : "{rax}" "memory");
}
<commit_msg>Small doc comment fix<commit_after>//! Program x86 segmentation hardware.

use core::fmt;

/// Specifies which element to load into a segment from
/// descriptor tables (i.e., is a index to LDT or GDT table
/// with some additional flags).
bitflags! {
    flags SegmentSelector: u16 {
        /// Requestor Privilege Level
        const RPL_0 = 0b00,
        const RPL_1 = 0b01,
        const RPL_2 = 0b10,
        const RPL_3 = 0b11,

        /// Table Indicator (TI) 0 means GDT is used.
        const TI_GDT = 0 << 3,
        /// Table Indicator (TI) 1 means LDT is used.
        const TI_LDT = 1 << 3,
    }
}

impl SegmentSelector {
    /// Create a new SegmentSelector
    ///
    /// # Arguments
    ///  * `index` index in GDT or LDT array.
    ///
    pub fn new(index: u16) -> SegmentSelector {
        SegmentSelector { bits: index << 3 }
    }

    pub fn from_raw(bits: u16) -> SegmentSelector {
        SegmentSelector { bits: bits }
    }
}

impl fmt::Debug for SegmentSelector {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let r0 = match self.contains(RPL_0) {
            false => "",
            true => "Ring 0 segment selector.",
        };
        let r1 = match self.contains(RPL_1) {
            false => "",
            true => "Ring 1 segment selector.",
        };
        let r2 = match self.contains(RPL_2) {
            false => "",
            true => "Ring 2 segment selector.",
        };
        let r3 = match self.contains(RPL_3) {
            false => "",
            true => "Ring 3 segment selector.",
        };
        let tbl = match self.contains(TI_LDT) {
            false => "GDT Table",
            true => "LDT Table",
        };

        write!(f,
               "Index {} in {}, {}{}{}{}",
               self.bits >> 3,
               tbl,
               r0,
               r1,
               r2,
               r3)
        // write!(f, "Index")
    }
}

/// Entry for GDT or LDT. Provides size and location of a segment.
bitflags! {
    flags SegmentDescriptor: u64 {
        /// Descriptor type (0 = system; 1 = code or data).
        const DESC_S    = 1 << (32+12),
        /// Descriptor privilege level 0.
        const DESC_DPL0 = 0b00 << (32+13),
        /// Descriptor privilege level 1.
        const DESC_DPL1 = 0b01 << (32+13),
        /// Descriptor privilege level 2.
        const DESC_DPL2 = 0b10 << (32+13),
        /// Descriptor privilege level 3.
        const DESC_DPL3 = 0b11 << (32+13),
        /// Descriptor is Present.
        const DESC_P = 1 << (32+15),
        /// Available for use by system software.
        const DESC_AVL  = 1 << (32+20),
        /// 64-bit code segment (IA-32e mode only).
        const DESC_L    = 1 << (32+21),
        /// Default operation size (0 = 16-bit segment, 1 = 32-bit segment)
        const DESC_DB   = 1 << (32+22),
        ///  Granularity.
        const DESC_G    = 1 << (32+23),

        // System-Segment and Gate-Descriptor Types for IA32e mode.
        // When the S (descriptor type) flag in a segment descriptor is clear,
        // the descriptor type is a system descriptor.

        const TYPE_SYS_LDT = 0b0010 << (32+8),
        const TYPE_SYS_TSS_AVAILABLE = 0b1001 << (32+8),
        const TYPE_SYS_TSS_BUSY = 0b1011 << (32+8),
        const TYPE_SYS_CALL_GATE = 0b1100 << (32+8),
        const TYPE_SYS_INTERRUPT_GATE = 0b1110 << (32+8),
        const TYPE_SYS_TRAP_GATE = 0b1111 << (32+8),

        // Code- and Data-Segment Descriptor Types.
        // When the S (descriptor type) flag in a segment descriptor is set,
        // the descriptor is for either a code or a data segment.

        /// Data Read-Only
        const TYPE_D_RO = 0b0000 << (32+8),
        /// Data Read-Only, accessed
        const TYPE_D_ROA = 0b0001 << (32+8),
        /// Data Read/Write
        const TYPE_D_RW = 0b0010 << (32+8),
        /// Data Read/Write, accessed
        const TYPE_D_RWA = 0b0011 << (32+8),
        /// Data Read-Only, expand-down
        const TYPE_D_ROEXD = 0b0100 << (32+8),
        /// Data Read-Only, expand-down, accessed
        const TYPE_D_ROEXDA = 0b0101 << (32+8),
        /// Data Read/Write, expand-down
        const TYPE_D_RWEXD = 0b0110 << (32+8),
        /// Data Read/Write, expand-down, accessed
        const TYPE_D_RWEXDA = 0b0111 << (32+8),

        /// Code Execute-Only
        const TYPE_C_EO = 0b1000 << (32+8),
        /// Code Execute-Only, accessed
        const TYPE_C_EOA = 0b1001 << (32+8),
        /// Code Execute/Read
        const TYPE_C_ER = 0b1010 << (32+8),
        /// Code Execute/Read, accessed
        const TYPE_C_ERA = 0b1011 << (32+8),
        /// Code Execute-Only, conforming
        const TYPE_C_EOC = 0b1100 << (32+8),
        /// Code Execute-Only, conforming, accessed
        const TYPE_C_EOCA = 0b1101 << (32+8),
        /// Code Execute/Read, conforming
        const TYPE_C_ERC = 0b1110 << (32+8),
        /// Code Execute/Read, conforming, accessed
        const TYPE_C_ERCA = 0b1111 << (32+8),
    }
}

/// This is data-structure is a ugly mess thing so we provide some
/// convenience function to program it.
impl SegmentDescriptor {
    pub fn new(base: u32, limit: u32) -> SegmentDescriptor {
        let base_low: u64 = base as u64 & 0xffffff;
        let base_high: u64 = (base as u64 >> 24) & 0xff;

        let limit_low: u64 = limit as u64 & 0xffff;
        let limit_high: u64 = (limit as u64 & (0b1111 << 16)) >> 16;

        SegmentDescriptor {
            bits: limit_low | base_low << 16 | limit_high << (32 + 16) | base_high << (32 + 24),
        }
    }
}

impl fmt::Debug for SegmentDescriptor {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "SD: 0x{:x}", self.bits)
    }
}

/// Reload stack segment register.
pub unsafe fn load_ss(sel: SegmentSelector) {
    asm!("movw $0, %ss " :: "r" (sel) : "memory");
}

/// Reload data segment register.
pub unsafe fn load_ds(sel: SegmentSelector) {
    asm!("movw $0, %ds " :: "r" (sel) : "memory");
}

/// Reload es segment register.
pub unsafe fn load_es(sel: SegmentSelector) {
    asm!("movw $0, %es " :: "r" (sel) : "memory");
}

/// Reload fs segment register.
pub unsafe fn load_fs(sel: SegmentSelector) {
    asm!("movw $0, %fs " :: "r" (sel) : "memory");
}

/// Reload gs segment register.
pub unsafe fn load_gs(sel: SegmentSelector) {
    asm!("movw $0, %gs " :: "r" (sel) : "memory");
}

/// Reload code segment register.
/// Note this is special since we can not directly move
/// to %cs. Instead we push the new segment selector
/// and return value on the stack and use lretq
/// to reload cs and continue at 1:.
pub unsafe fn load_cs(sel: SegmentSelector) {
    asm!("pushq $0
         lea 1f(%rip), %rax
         pushq %rax
         lretq
         1:" :: "r" (sel.bits() as u64) : "{rax}" "memory");
}
<|endoftext|>


<commit_before>// Copyright 2017 `multipart-async` Crate Developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.
use futures::{Future, Stream, Async, Poll};
use futures::Async::*;

use mime::Mime;

use std::{io, mem, str};

use server::boundary::BoundaryFinder;
use server::{BodyChunk, StreamError, Multipart, httparse, twoway};

use self::httparse::EMPTY_HEADER;

use helpers::*;

const MAX_BUF_LEN: usize = 1024;
const MAX_HEADERS: usize = 4;

pub struct NextField<S: Stream> {
    stream: SomeCell<BoundaryFinder<S>>,
    accumulator: Vec<u8>,
    headers: Option<Headers>,
}

pub fn next_field<S: Stream>(stream: BoundaryFinder<S>) -> NextField<S> {
    NextField {
        stream: SomeCell::new(stream, "NextField value already taken"),
        accumulator: Vec::new(),
        headers: None,
    }
}

impl<S: Stream> NextField<S> where S::Item: BodyChunk, S::Error: StreamError {
    fn read_headers(&mut self) -> Poll<Headers, S::Error> {
        if let Some(headers) = self.headers.take() {
            return ready(headers);
        }

        loop {
            let chunk = try_ready!(self.stream.poll())
                .ok_or_else(|| io_error("unexpected end of stream"))?;

            // End of the headers section is signalled by a double-CRLF
            if let Some(header_end) = twoway::find_bytes(chunk.as_slice(), b"\r\n\r\n") {
                // Split after the double-CRLF because we don't want to yield it and httparse expects it
                let (headers, rem) = chunk.split_at(header_end + 4);
                self.stream.try_as_mut()?.push_chunk(rem);

                if !self.accumulator.is_empty() {
                    self.accumulator.extend_from_slice(headers.as_slice());
                    let headers = parse_headers(&self.accumulator)?;
                    self.accumulator.clear();

                    return ready(headers);
                } else {
                    return ready(parse_headers(headers.as_slice())?);
                }
            } else if let Some(split_idx) = header_end_split(&self.accumulator, chunk.as_slice()) {
                let (head, tail) = chunk.split_at(split_idx);
                self.accumulator.extend_from_slice(head.as_slice());
                continue;
            }

            // TODO: edge case where double-CRLF falls on chunk boundaries

            if self.accumulator.len().saturating_add(chunk.len()) > MAX_BUF_LEN {
                return error("headers section too long or trailing double-CRLF missing");
            }

            self.accumulator.extend_from_slice(chunk.as_slice());
        }
    }

    fn read_to_string(&mut self) -> Poll<String, S::Error> {
        loop {
            if self.accumulator.is_empty() {
                self.accumulator = try_ready!(self.stream.poll())
                    .map_or(Vec::new(), BodyChunk::into_vec);
            }

            match try_ready!(self.stream.poll()) {
                Some(chunk) => self.accumulator.extend_from_slice(chunk.as_slice()),
                None => return ready(String::from_utf8(self.take_accumulator())?),
            }
         }
    }

    fn take_accumulator(&mut self) -> Vec<u8> {
        mem::replace(&mut self.accumulator, Vec::new())
    }
}
impl<S: Stream> Future for NextField<S> where S::Item: BodyChunk, S::Error: StreamError {
    type Item = Field<S>;
    type Error = S::Error;

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        let headers = try_ready!(self.read_headers());

        let data = if let Some(cont_type) = headers.cont_type {
            FieldData::File(FileData {
                content_type: cont_type,
                filename: headers.filename,
                multipart: Multipart { stream: self.stream.try_take()? },
                _priv: (),
            })
        } else if let Ready(text) = self.read_to_string()? {
            FieldData::Text(
                TextData {
                    text: text,
                    multipart: Multipart { stream: self.stream.try_take()? },
                    _priv: (),
                }
            )
        } else {
            self.headers = Some(headers);
            return not_ready();
        };

        ready(Field {
            name: headers.name,
            data: data,
            _priv: (),
        })
    }
}

const CRLF2: &[u8] = b"\r\n\r\n";

/// Check if the double-CRLF falls between chunk boundaries, and if so, the split index of
/// the second boundary
fn header_end_split(first: &[u8], second: &[u8]) -> Option<usize> {
    fn split_subcheck(start: usize, first: &[u8], second: &[u8]) -> bool {
        first.len() >= start && first[first.len() - start ..].iter().chain(second).take(4).eq(CRLF2)
    }

    let len = first.len();

    if split_subcheck(3, first, second) {
        Some(1)
    } else if split_subcheck(2, first, second) {
        Some(2)
    } else if split_subcheck(1, first, second) {
        Some(3)
    } else {
        None
    }
}

fn parse_headers(bytes: &[u8]) -> io::Result<Headers> {
    debug_assert!(bytes.ends_with(b"\r\n\r\n"),
                  "header byte sequence does not end with `\\r\\n\\r\\n`: {}",
                  show_bytes(bytes));

    let mut header_buf = [EMPTY_HEADER; MAX_HEADERS];

    let (_, headers) = httparse::parse_headers(bytes, &mut header_buf).map_err(io_error)?;

    let mut out_headers = Headers::default();

    for header in headers {
        let str_val = str::from_utf8(header.value)
            .map_err(|_| io_error("multipart field headers must be UTF-8 encoded"))?
            .trim();

        match header.name {
            "Content-Disposition" => parse_cont_disp_val(str_val, &mut out_headers)?,
            "Content-Type" => out_headers.cont_type = Some(str_val.parse::<Mime>().map_err(io_error)),
        }
    }

    Ok(out_headers)
}

fn parse_cont_disp_val(val: &str, out: &mut Headers) -> io::Result<()> {
    // Only take the first section, the rest can be in quoted strings that we want to handle
    let mut sections = val.splitn(';', 1).map(str::trim);

    match sections.next() {
        Some("form-data") => (),
        Some(other) => error(format!("unexpected multipart field Content-Disposition: {}", other))?,
        None => error("each multipart field requires a Content-Disposition: form-data header")?,
    }

    let mut rem = sections.next().unwrap_or("");

    while let Some((key, val, rest)) = parse_keyval(rem) {
        rem = rest;

        match key {
            "name" => out.name = val.to_string(),
            "filename" => out.filename = Some(val.to_string()),
            other => debug!("unknown key-value pair in Content-Disposition: {:?} = {:?}", key, val),
        }
    }

    if out.name.is_empty() {
        return error(format!("expected 'name' attribute in Content-Disposition: {}", val));
    }

    Ok(())
}

fn parse_keyval(input: &str) -> Option<(&str, &str, &str)> {
    let (name, rest) = try_opt!(param_name(input));
    let (val, rest) = try_opt!(param_val(rest));

    Some((name, val, rest))
}

fn param_name(input: &str) -> Option<(&str, &str)> {
    let mut splits = input.trim_left_matches(&[' ', ';']).splitn('=', 1);

    let name = try_opt!(splits.next()).trim();
    let rem = splits.next().unwrap_or("");

    Some((name, rem))
}

fn param_val(input: &str) -> Option<(&str, &str)> {
    let mut splits = input.splitn(&['"'], 2);

    let token = try_opt!(splits.next()).trim();

    // the value doesn't have to be in quotes if it doesn't contain forbidden chars like `;`
    if !token.is_empty() {
        let mut splits = token.splitn(';', 1);
        let token = try_opt!(splits.next()).trim();
        let rem = splits.next().unwrap_or("");

        return Some((token, rem));
    }

    let qstr = try_opt!(splits.next()).trim();
    let rem = splits.next().unwrap_or_else(|| { warn!("unterminated quote: {:?}", qstr); "" });

    Some((qstr, rem))
}

#[derive(Default)]
struct Headers {
    name: String,
    cont_type: Option<Mime>,
    filename: Option<String>,
}

pub struct Field<S: Stream> {
    pub name: String,
    pub data: FieldData<S>,
    _priv: (),
}

pub enum FieldData<S: Stream> {
    Text(TextData<S>),
    File(FileData<S>),
}

pub struct TextData<S: Stream> {
    pub text: String,
    pub multipart: Multipart<S>,
    _priv: (),
}

pub struct FileData<S: Stream> {
    pub filename: Option<String>,
    pub content_type: Mime,
    pub multipart: Multipart<S>,
    _priv: (),
}

const CONT_DISP: &str = "Content-Disposition";

#[test]
fn test_header_end_split() {
    assert_eq!(header_end_split(b"\r\n\r", b"\n"), Some(1));
    assert_eq!(header_end_split(b"\r\n", b"\r\n"), Some(2));
    assert_eq!(header_end_split(b"\r", b"\n\r\n"), Some(3));
    assert_eq!(header_end_split(b"\r\n\r\n", b"FOOBAR"), None);
    assert_eq!(header_end_split(b"FOOBAR", b"\r\n\r\n"), None);
}
<commit_msg>This edge case is covered<commit_after>// Copyright 2017 `multipart-async` Crate Developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.
use futures::{Future, Stream, Async, Poll};
use futures::Async::*;

use mime::Mime;

use std::{io, mem, str};

use server::boundary::BoundaryFinder;
use server::{BodyChunk, StreamError, Multipart, httparse, twoway};

use self::httparse::EMPTY_HEADER;

use helpers::*;

const MAX_BUF_LEN: usize = 1024;
const MAX_HEADERS: usize = 4;

pub struct NextField<S: Stream> {
    stream: SomeCell<BoundaryFinder<S>>,
    accumulator: Vec<u8>,
    headers: Option<Headers>,
}

pub fn next_field<S: Stream>(stream: BoundaryFinder<S>) -> NextField<S> {
    NextField {
        stream: SomeCell::new(stream, "NextField value already taken"),
        accumulator: Vec::new(),
        headers: None,
    }
}

impl<S: Stream> NextField<S> where S::Item: BodyChunk, S::Error: StreamError {
    fn read_headers(&mut self) -> Poll<Headers, S::Error> {
        if let Some(headers) = self.headers.take() {
            return ready(headers);
        }

        loop {
            let chunk = try_ready!(self.stream.poll())
                .ok_or_else(|| io_error("unexpected end of stream"))?;

            // End of the headers section is signalled by a double-CRLF
            if let Some(header_end) = twoway::find_bytes(chunk.as_slice(), b"\r\n\r\n") {
                // Split after the double-CRLF because we don't want to yield it and httparse expects it
                let (headers, rem) = chunk.split_at(header_end + 4);
                self.stream.try_as_mut()?.push_chunk(rem);

                if !self.accumulator.is_empty() {
                    self.accumulator.extend_from_slice(headers.as_slice());
                    let headers = parse_headers(&self.accumulator)?;
                    self.accumulator.clear();

                    return ready(headers);
                } else {
                    return ready(parse_headers(headers.as_slice())?);
                }
            } else if let Some(split_idx) = header_end_split(&self.accumulator, chunk.as_slice()) {
                let (head, tail) = chunk.split_at(split_idx);
                self.accumulator.extend_from_slice(head.as_slice());
                self.stream.push_chunk(tail);
                continue;
            }

            if self.accumulator.len().saturating_add(chunk.len()) > MAX_BUF_LEN {
                return error("headers section too long or trailing double-CRLF missing");
            }

            self.accumulator.extend_from_slice(chunk.as_slice());
        }
    }

    fn read_to_string(&mut self) -> Poll<String, S::Error> {
        loop {
            if self.accumulator.is_empty() {
                self.accumulator = try_ready!(self.stream.poll())
                    .map_or(Vec::new(), BodyChunk::into_vec);
            }

            match try_ready!(self.stream.poll()) {
                Some(chunk) => self.accumulator.extend_from_slice(chunk.as_slice()),
                None => return ready(String::from_utf8(self.take_accumulator())?),
            }
         }
    }

    fn take_accumulator(&mut self) -> Vec<u8> {
        mem::replace(&mut self.accumulator, Vec::new())
    }
}
impl<S: Stream> Future for NextField<S> where S::Item: BodyChunk, S::Error: StreamError {
    type Item = Field<S>;
    type Error = S::Error;

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        let headers = try_ready!(self.read_headers());

        let data = if let Some(cont_type) = headers.cont_type {
            FieldData::File(FileData {
                content_type: cont_type,
                filename: headers.filename,
                multipart: Multipart { stream: self.stream.try_take()? },
                _priv: (),
            })
        } else if let Ready(text) = self.read_to_string()? {
            FieldData::Text(
                TextData {
                    text: text,
                    multipart: Multipart { stream: self.stream.try_take()? },
                    _priv: (),
                }
            )
        } else {
            self.headers = Some(headers);
            return not_ready();
        };

        ready(Field {
            name: headers.name,
            data: data,
            _priv: (),
        })
    }
}

const CRLF2: &[u8] = b"\r\n\r\n";

/// Check if the double-CRLF falls between chunk boundaries, and if so, the split index of
/// the second boundary
fn header_end_split(first: &[u8], second: &[u8]) -> Option<usize> {
    fn split_subcheck(start: usize, first: &[u8], second: &[u8]) -> bool {
        first.len() >= start && first[first.len() - start ..].iter().chain(second).take(4).eq(CRLF2)
    }

    let len = first.len();

    if split_subcheck(3, first, second) {
        Some(1)
    } else if split_subcheck(2, first, second) {
        Some(2)
    } else if split_subcheck(1, first, second) {
        Some(3)
    } else {
        None
    }
}

fn parse_headers(bytes: &[u8]) -> io::Result<Headers> {
    debug_assert!(bytes.ends_with(b"\r\n\r\n"),
                  "header byte sequence does not end with `\\r\\n\\r\\n`: {}",
                  show_bytes(bytes));

    let mut header_buf = [EMPTY_HEADER; MAX_HEADERS];

    let (_, headers) = httparse::parse_headers(bytes, &mut header_buf).map_err(io_error)?;

    let mut out_headers = Headers::default();

    for header in headers {
        let str_val = str::from_utf8(header.value)
            .map_err(|_| io_error("multipart field headers must be UTF-8 encoded"))?
            .trim();

        match header.name {
            "Content-Disposition" => parse_cont_disp_val(str_val, &mut out_headers)?,
            "Content-Type" => out_headers.cont_type = Some(str_val.parse::<Mime>().map_err(io_error)),
        }
    }

    Ok(out_headers)
}

fn parse_cont_disp_val(val: &str, out: &mut Headers) -> io::Result<()> {
    // Only take the first section, the rest can be in quoted strings that we want to handle
    let mut sections = val.splitn(';', 1).map(str::trim);

    match sections.next() {
        Some("form-data") => (),
        Some(other) => error(format!("unexpected multipart field Content-Disposition: {}", other))?,
        None => error("each multipart field requires a Content-Disposition: form-data header")?,
    }

    let mut rem = sections.next().unwrap_or("");

    while let Some((key, val, rest)) = parse_keyval(rem) {
        rem = rest;

        match key {
            "name" => out.name = val.to_string(),
            "filename" => out.filename = Some(val.to_string()),
            other => debug!("unknown key-value pair in Content-Disposition: {:?} = {:?}", key, val),
        }
    }

    if out.name.is_empty() {
        return error(format!("expected 'name' attribute in Content-Disposition: {}", val));
    }

    Ok(())
}

fn parse_keyval(input: &str) -> Option<(&str, &str, &str)> {
    let (name, rest) = try_opt!(param_name(input));
    let (val, rest) = try_opt!(param_val(rest));

    Some((name, val, rest))
}

fn param_name(input: &str) -> Option<(&str, &str)> {
    let mut splits = input.trim_left_matches(&[' ', ';']).splitn('=', 1);

    let name = try_opt!(splits.next()).trim();
    let rem = splits.next().unwrap_or("");

    Some((name, rem))
}

fn param_val(input: &str) -> Option<(&str, &str)> {
    let mut splits = input.splitn(&['"'], 2);

    let token = try_opt!(splits.next()).trim();

    // the value doesn't have to be in quotes if it doesn't contain forbidden chars like `;`
    if !token.is_empty() {
        let mut splits = token.splitn(';', 1);
        let token = try_opt!(splits.next()).trim();
        let rem = splits.next().unwrap_or("");

        return Some((token, rem));
    }

    let qstr = try_opt!(splits.next()).trim();
    let rem = splits.next().unwrap_or_else(|| { warn!("unterminated quote: {:?}", qstr); "" });

    Some((qstr, rem))
}

#[derive(Default)]
struct Headers {
    name: String,
    cont_type: Option<Mime>,
    filename: Option<String>,
}

pub struct Field<S: Stream> {
    pub name: String,
    pub data: FieldData<S>,
    _priv: (),
}

pub enum FieldData<S: Stream> {
    Text(TextData<S>),
    File(FileData<S>),
}

pub struct TextData<S: Stream> {
    pub text: String,
    pub multipart: Multipart<S>,
    _priv: (),
}

pub struct FileData<S: Stream> {
    pub filename: Option<String>,
    pub content_type: Mime,
    pub multipart: Multipart<S>,
    _priv: (),
}

const CONT_DISP: &str = "Content-Disposition";

#[test]
fn test_header_end_split() {
    assert_eq!(header_end_split(b"\r\n\r", b"\n"), Some(1));
    assert_eq!(header_end_split(b"\r\n", b"\r\n"), Some(2));
    assert_eq!(header_end_split(b"\r", b"\n\r\n"), Some(3));
    assert_eq!(header_end_split(b"\r\n\r\n", b"FOOBAR"), None);
    assert_eq!(header_end_split(b"FOOBAR", b"\r\n\r\n"), None);
}
<|endoftext|>


<commit_before>//! Helper functions for dealing with shaders.

// External crates.
use gl;
use gl::types::{
    GLboolean,
    GLchar,
    GLenum,
    GLint,
    GLuint,
};

use std::ptr;
use std::mem;

/// Describes a shader attribute.
pub struct DynamicAttribute {
    /// The vertex buffer object.
    vbo: GLuint,
    /// The number of components.
    size: i32,
    /// The location of the attribute in shader.
    location: GLuint,
    /// Whether to normalize when sending to GPU.
    normalize: GLboolean,
    /// The type, for example gl::FLOAT.
    ty: GLenum,
}

impl Drop for DynamicAttribute {
    fn drop(&mut self) {
        unsafe {
            gl::DeleteBuffers(1, &self.vbo)
        }
    }
}

impl DynamicAttribute {
    /// Binds to a vertex array object.
    ///
    /// The vertex array object remembers the format for later.
    pub fn bind_vao(&self, vao: GLuint) {
        gl::BindVertexArray(vao);
        gl::BindBuffer(gl::ARRAY_BUFFER, self.vbo);
        let stride = 0;
        unsafe {
            gl::VertexAttribPointer(
                self.location,
                self.size,
                self.ty,
                self.normalize,
                stride,
                ptr::null()
            );
        }
    }

    fn new(
        program: GLuint,
        name: &str,
        size: i32,
        normalize: GLboolean,
        ty: GLenum
    ) -> Result<DynamicAttribute, String> {
        let location = try!(attribute_location(program, name));
        let mut vbo = 0;
        unsafe {
            gl::GenBuffers(1, &mut vbo);
        }
        Ok(DynamicAttribute {
            vbo: vbo,
            size: size,
            location: location,
            normalize: normalize,
            ty: ty,
        })
    }

    /// Create XYZ vertex attribute.
    pub fn xyz(program: GLuint, name: &str) -> Result<DynamicAttribute, String> {
        DynamicAttribute::new(program, name, 3, gl::FALSE, gl::FLOAT)
    }

    /// Create XY vertex attribute.
    pub fn xy(program: GLuint, name: &str) -> Result<DynamicAttribute, String> {
        DynamicAttribute::new(program, name, 2, gl::FALSE, gl::FLOAT)
    }

    /// Create RGB color attribute.
    pub fn rgb(program: GLuint, name: &str) -> Result<DynamicAttribute, String> {
        DynamicAttribute::new(program, name, 3, gl::FALSE, gl::FLOAT)
    }

    /// Create RGBA color attribute.
    pub fn rgba(program: GLuint, name: &str) -> Result<DynamicAttribute, String> {
        DynamicAttribute::new(program, name, 4, gl::FALSE, gl::FLOAT)
    }

    /// Create texture coordinate attribute.
    pub fn uv(program: GLuint, name: &str) -> Result<DynamicAttribute, String> {
        DynamicAttribute::new(program, name, 2, gl::FALSE, gl::FLOAT)
    }

    /// Sets attribute data.
    pub unsafe fn set<T>(&self, data: &[T]) {
        gl::EnableVertexAttribArray(self.location);
        gl::BindBuffer(gl::ARRAY_BUFFER, self.vbo);
        gl::BufferData(
            gl::ARRAY_BUFFER,
            data.len() as i64 * mem::size_of::<T>() as i64,
            mem::transmute(data.as_ptr()),
            gl::DYNAMIC_DRAW
        );
    }
}

/// Compiles a shader.
///
/// Returns a shader or a message with the error.
pub fn compile_shader(
    shader_type: GLenum,
    source: &str
) -> Result<GLuint, String> {
    let shader = gl::CreateShader(shader_type);
    unsafe {
        source.with_c_str(
            |ptr| gl::ShaderSource(shader, 1, &ptr, ptr::null())
        );
        gl::CompileShader(shader);
        let mut status = gl::FALSE as GLint;
        gl::GetShaderiv(shader, gl::COMPILE_STATUS, &mut status);
        if status == (gl::TRUE as GLint) {
            Ok(shader)
        } else {
            let mut len = 0;
            gl::GetShaderiv(shader, gl::INFO_LOG_LENGTH, &mut len);

            if len == 0 {
                Err("Compilation failed with no log. The OpenGL context might have been created on another thread, or not have been created.".to_string())
            }
            else {
                // Subtract 1 to skip the trailing null character.
                let mut buf = Vec::from_elem(len as uint - 1, 0u8);
                gl::GetShaderInfoLog(
                    shader,
                    len,
                    ptr::mut_null(),
                    buf.as_mut_ptr() as *mut GLchar
                );

                gl::DeleteShader(shader);

                Err(String::from_utf8(buf).ok().expect(
                    "ShaderInfoLog not valid utf8"
                ))
            }
        }
    }
}

/// Creates a vertex buffer for an attribute from a program.
///
/// Returns `None` if there is no attribute with such name.
pub fn attribute_location(program: GLuint, name: &str) -> Result<GLuint, String> {
    unsafe {
        name.with_c_str(|ptr| {
            let id = gl::GetAttribLocation(program, ptr);
            if id < 0 {
                Err(format!("Attribute '{}' does not exists in shader", name))
            } else {
                Ok(id as GLuint)
            }
        })
    }
}

<commit_msg>Casting to GLsizeiptr<commit_after>//! Helper functions for dealing with shaders.

// External crates.
use gl;
use gl::types::{
    GLboolean,
    GLchar,
    GLenum,
    GLint,
    GLsizeiptr,
    GLuint,
};

use std::ptr;
use std::mem;

/// Describes a shader attribute.
pub struct DynamicAttribute {
    /// The vertex buffer object.
    vbo: GLuint,
    /// The number of components.
    size: i32,
    /// The location of the attribute in shader.
    location: GLuint,
    /// Whether to normalize when sending to GPU.
    normalize: GLboolean,
    /// The type, for example gl::FLOAT.
    ty: GLenum,
}

impl Drop for DynamicAttribute {
    fn drop(&mut self) {
        unsafe {
            gl::DeleteBuffers(1, &self.vbo)
        }
    }
}

impl DynamicAttribute {
    /// Binds to a vertex array object.
    ///
    /// The vertex array object remembers the format for later.
    pub fn bind_vao(&self, vao: GLuint) {
        gl::BindVertexArray(vao);
        gl::BindBuffer(gl::ARRAY_BUFFER, self.vbo);
        let stride = 0;
        unsafe {
            gl::VertexAttribPointer(
                self.location,
                self.size,
                self.ty,
                self.normalize,
                stride,
                ptr::null()
            );
        }
    }

    fn new(
        program: GLuint,
        name: &str,
        size: i32,
        normalize: GLboolean,
        ty: GLenum
    ) -> Result<DynamicAttribute, String> {
        let location = try!(attribute_location(program, name));
        let mut vbo = 0;
        unsafe {
            gl::GenBuffers(1, &mut vbo);
        }
        Ok(DynamicAttribute {
            vbo: vbo,
            size: size,
            location: location,
            normalize: normalize,
            ty: ty,
        })
    }

    /// Create XYZ vertex attribute.
    pub fn xyz(program: GLuint, name: &str) -> Result<DynamicAttribute, String> {
        DynamicAttribute::new(program, name, 3, gl::FALSE, gl::FLOAT)
    }

    /// Create XY vertex attribute.
    pub fn xy(program: GLuint, name: &str) -> Result<DynamicAttribute, String> {
        DynamicAttribute::new(program, name, 2, gl::FALSE, gl::FLOAT)
    }

    /// Create RGB color attribute.
    pub fn rgb(program: GLuint, name: &str) -> Result<DynamicAttribute, String> {
        DynamicAttribute::new(program, name, 3, gl::FALSE, gl::FLOAT)
    }

    /// Create RGBA color attribute.
    pub fn rgba(program: GLuint, name: &str) -> Result<DynamicAttribute, String> {
        DynamicAttribute::new(program, name, 4, gl::FALSE, gl::FLOAT)
    }

    /// Create texture coordinate attribute.
    pub fn uv(program: GLuint, name: &str) -> Result<DynamicAttribute, String> {
        DynamicAttribute::new(program, name, 2, gl::FALSE, gl::FLOAT)
    }

    /// Sets attribute data.
    pub unsafe fn set<T>(&self, data: &[T]) {
        gl::EnableVertexAttribArray(self.location);
        gl::BindBuffer(gl::ARRAY_BUFFER, self.vbo);
        gl::BufferData(
            gl::ARRAY_BUFFER,
            data.len() as GLsizeiptr * mem::size_of::<T>() as GLsizeiptr,
            mem::transmute(data.as_ptr()),
            gl::DYNAMIC_DRAW
        );
    }
}

/// Compiles a shader.
///
/// Returns a shader or a message with the error.
pub fn compile_shader(
    shader_type: GLenum,
    source: &str
) -> Result<GLuint, String> {
    let shader = gl::CreateShader(shader_type);
    unsafe {
        source.with_c_str(
            |ptr| gl::ShaderSource(shader, 1, &ptr, ptr::null())
        );
        gl::CompileShader(shader);
        let mut status = gl::FALSE as GLint;
        gl::GetShaderiv(shader, gl::COMPILE_STATUS, &mut status);
        if status == (gl::TRUE as GLint) {
            Ok(shader)
        } else {
            let mut len = 0;
            gl::GetShaderiv(shader, gl::INFO_LOG_LENGTH, &mut len);

            if len == 0 {
                Err("Compilation failed with no log. The OpenGL context might have been created on another thread, or not have been created.".to_string())
            }
            else {
                // Subtract 1 to skip the trailing null character.
                let mut buf = Vec::from_elem(len as uint - 1, 0u8);
                gl::GetShaderInfoLog(
                    shader,
                    len,
                    ptr::mut_null(),
                    buf.as_mut_ptr() as *mut GLchar
                );

                gl::DeleteShader(shader);

                Err(String::from_utf8(buf).ok().expect(
                    "ShaderInfoLog not valid utf8"
                ))
            }
        }
    }
}

/// Creates a vertex buffer for an attribute from a program.
///
/// Returns `None` if there is no attribute with such name.
pub fn attribute_location(program: GLuint, name: &str) -> Result<GLuint, String> {
    unsafe {
        name.with_c_str(|ptr| {
            let id = gl::GetAttribLocation(program, ptr);
            if id < 0 {
                Err(format!("Attribute '{}' does not exists in shader", name))
            } else {
                Ok(id as GLuint)
            }
        })
    }
}

<|endoftext|>


<commit_before>use fnv::FnvHashMap;

lazy_static! {
    static ref ATTRIBUTES: FnvHashMap<&'static str, &'static str> = {
        let mut map = FnvHashMap::default();
        map.insert("bold", "1");
        map.insert("dim", "2");
        map.insert("underlined", "4");
        map.insert("blink", "5");
        map.insert("reverse", "7");
        map.insert("hidden", "8");
        map
    };
}

lazy_static! {
    static ref COLORS: FnvHashMap<&'static str, &'static str> = {
        let mut map = FnvHashMap::default();
        map.insert("black", "30");
        map.insert("red", "31");
        map.insert("green", "32");
        map.insert("yellow", "33");
        map.insert("blue", "34");
        map.insert("magenta", "35");
        map.insert("cyan", "36");
        map.insert("light_gray", "37");
        map.insert("default", "39");
        map.insert("blackbg", "40");
        map.insert("dark_gray", "90");
        map.insert("light_red", "91");
        map.insert("light_green", "92");
        map.insert("light_yellow", "93");
        map.insert("light_blue", "94");
        map.insert("light_magenta", "95");
        map.insert("light_cyan", "96");
        map
    };
}

lazy_static! {
    static ref BG_COLORS: FnvHashMap<&'static str, &'static str> = {
        let mut map = FnvHashMap::default();
        map.insert("redbg", "41");
        map.insert("greenbg", "42");
        map.insert("yellowbg", "43");
        map.insert("bluebg", "44");
        map.insert("magentabg", "45");
        map.insert("cyanbg", "46");
        map.insert("light_graybg", "47");
        map.insert("defaultbg", "49");
        map.insert("dark_graybg", "100");
        map.insert("light_redbg", "101");
        map.insert("light_greenbg", "102");
        map.insert("light_yellowbg",  "103");
        map.insert("light_bluebg", "104");
        map.insert("light_magentabg", "105");
        map.insert("light_cyanbg", "106");
        map.insert("whitebg", "107");
        map
    };
}

pub struct Colors {
    foreground: Option<&'static str>,
    background: Option<&'static str>,
    attribute: Option<Vec<&'static str>>
}

impl Colors {
    pub fn collect(input: &str) -> Colors {
        let mut colors = Colors { foreground: None, background: None, attribute: None };
        for variable in input.split(",") {
            if variable == "reset" {
                return Colors { foreground: None, background: None, attribute: Some(vec!["0"]) };
            } else if let Some(color) = ATTRIBUTES.get(&variable) {
                let vec_exists = match colors.attribute.as_mut() {
                    Some(mut vec) => { vec.push(color); true },
                    None => false
                };

                if vec_exists {
                    colors.attribute = Some(vec![color]);
                }
            } else if let Some(color) = COLORS.get(&variable) {
                colors.foreground = Some(*color)
            } else {
                match BG_COLORS.get(&variable) {
                    Some(color) => colors.background = Some(*color),
                    None => eprintln!("ion: {} is not a valid color", variable),
                }
            }
        }
        colors
    }

    pub fn into_string(self) -> Option<String> {
        let mut output = String::from("\x1b[");
        if let Some(attr) = self.attribute {
            output.push_str(&attr.join(";"));
            match (self.foreground, self.background) {
                (Some(c), None) | (None, Some(c)) => Some([&output, ";", c, "m"].concat()),
                (None, None) => Some([&output, "m"].concat()),
                (Some(fg), Some(bg)) => Some([&output, ";", fg, ";", bg, "m"].concat())
            }
        } else {
            match (self.foreground, self.background) {
                (Some(c), None) | (None, Some(c)) => Some([&output, c, "m"].concat()),
                (None, None) => None,
                (Some(fg), Some(bg)) => Some([&output, fg, ";", bg, "m"].concat())
            }
        }
    }
}
<commit_msg>Implement 256-bit Colors Support<commit_after>use fnv::FnvHashMap;
use std::u16;

lazy_static! {
    static ref ATTRIBUTES: FnvHashMap<&'static str, &'static str> = {
        let mut map = FnvHashMap::default();
        map.insert("bold", "1");
        map.insert("dim", "2");
        map.insert("underlined", "4");
        map.insert("blink", "5");
        map.insert("reverse", "7");
        map.insert("hidden", "8");
        map
    };
}

lazy_static! {
    static ref COLORS: FnvHashMap<&'static str, &'static str> = {
        let mut map = FnvHashMap::default();
        map.insert("black", "30");
        map.insert("red", "31");
        map.insert("green", "32");
        map.insert("yellow", "33");
        map.insert("blue", "34");
        map.insert("magenta", "35");
        map.insert("cyan", "36");
        map.insert("light_gray", "37");
        map.insert("default", "39");
        map.insert("blackbg", "40");
        map.insert("dark_gray", "90");
        map.insert("light_red", "91");
        map.insert("light_green", "92");
        map.insert("light_yellow", "93");
        map.insert("light_blue", "94");
        map.insert("light_magenta", "95");
        map.insert("light_cyan", "96");
        map
    };
}

lazy_static! {
    static ref BG_COLORS: FnvHashMap<&'static str, &'static str> = {
        let mut map = FnvHashMap::default();
        map.insert("redbg", "41");
        map.insert("greenbg", "42");
        map.insert("yellowbg", "43");
        map.insert("bluebg", "44");
        map.insert("magentabg", "45");
        map.insert("cyanbg", "46");
        map.insert("light_graybg", "47");
        map.insert("defaultbg", "49");
        map.insert("dark_graybg", "100");
        map.insert("light_redbg", "101");
        map.insert("light_greenbg", "102");
        map.insert("light_yellowbg",  "103");
        map.insert("light_bluebg", "104");
        map.insert("light_magentabg", "105");
        map.insert("light_cyanbg", "106");
        map.insert("whitebg", "107");
        map
    };
}

enum Mode {
    Name(&'static str),
    bits256(u16),
}

pub struct Colors {
    foreground: Option<Mode>,
    background: Option<Mode>,
    attribute: Option<Vec<&'static str>>
}

impl Colors {
    pub fn collect(input: &str) -> Colors {
        let mut colors = Colors { foreground: None, background: None, attribute: None };
        for variable in input.split(",") {
            if variable == "reset" {
                return Colors { foreground: None, background: None, attribute: Some(vec!["0"]) };
            } else if let Some(color) = ATTRIBUTES.get(&variable) {
                let vec_exists = match colors.attribute.as_mut() {
                    Some(vec) => { vec.push(color); true },
                    None => false
                };

                if !vec_exists {
                    colors.attribute = Some(vec![color]);
                }
            } else if let Some(color) = COLORS.get(&variable) {
                colors.foreground = Some(Mode::Name(*color))
            } else {
                match BG_COLORS.get(&variable) {
                    Some(color) => colors.background = Some(Mode::Name(*color)),
                    None => if let Ok(value) = u16::from_str_radix(&variable[0..2], 16) {
                        if variable.ends_with("bg") {
                            colors.background = Some(Mode::bits256(value));
                        } else {
                            colors.foreground = Some(Mode::bits256(value));
                        }
                    } else {
                        eprintln!("ion: {} is not a valid color", variable)
                    }
                }
            }
        }
        colors
    }

    pub fn into_string(self) -> Option<String> {
        let mut output = String::from("\x1b[");

        let foreground = match self.foreground {
            Some(Mode::Name(string)) => Some(string.to_owned()),
            Some(Mode::bits256(value)) => Some(format!("38;5;{}", value)),
            None => None
        };

        let background = match self.background {
            Some(Mode::Name(string)) => Some(string.to_owned()),
            Some(Mode::bits256(value)) => Some(format!("48;5;{}", value)),
            None => None
        };

        if let Some(attr) = self.attribute {
            output.push_str(&attr.join(";"));
            match (foreground, background) {
                (Some(c), None) | (None, Some(c)) => Some([&output, ";", &c, "m"].concat()),
                (None, None) => Some([&output, "m"].concat()),
                (Some(fg), Some(bg)) => Some([&output, ";", &fg, ";", &bg, "m"].concat())
            }
        } else {
            match (foreground, background) {
                (Some(c), None) | (None, Some(c)) => Some([&output, &c, "m"].concat()),
                (None, None) => None,
                (Some(fg), Some(bg)) => Some([&output, &fg, ";", &bg, "m"].concat())
            }
        }
    }
}
<|endoftext|>