<commit_before>//! Array type node attribute.

use std::fmt;
use std::marker::PhantomData;
use std::io;
use std::io::Read;
#[cfg(feature = "flate2")]
use flate2::read::ZlibDecoder;
#[cfg(feature = "libflate")]
use libflate::zlib;

use parser::binary::BinaryParser;
use parser::binary::error::{Result, Error, Warning};
use parser::binary::reader::{ParserSource, ReadLittleEndian};

/// Read array type attribute from the given parser.
pub fn read_array_attribute<R: ParserSource>(
    parser: &mut BinaryParser<R>,
    type_code: u8
) -> Result<(ArrayAttribute<R>, u64)> {
    let header = try!(ArrayAttributeHeader::read_from_parser(parser));
    let current_pos = parser.source.position();
    let BinaryParser { ref mut source, ref mut warnings, .. } = *parser;
    let reader = try!(ArrayDecoder::new(source, &header));

    let value = match type_code {
        b'b' => ArrayAttribute::Bool(ArrayAttributeReader::new(&header, reader, warnings)),
        b'i' => ArrayAttribute::I32(ArrayAttributeReader::new(&header, reader, warnings)),
        b'l' => ArrayAttribute::I64(ArrayAttributeReader::new(&header, reader, warnings)),
        b'f' => ArrayAttribute::F32(ArrayAttributeReader::new(&header, reader, warnings)),
        b'd' => ArrayAttribute::F64(ArrayAttributeReader::new(&header, reader, warnings)),
        _ => unreachable!(),
    };
    Ok((value, current_pos + header.bytelen_elements as u64))
}

/// Header of an array attribute.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
struct ArrayAttributeHeader {
    /// Number of elements.
    num_elements: u32,
    /// Array encoding, i.e. compression method.
    encoding: u32,
    /// Length of the array (excluding this header) in bytes.
    bytelen_elements: u32,
}

impl ArrayAttributeHeader {
    fn read_from_parser<R: ParserSource>(parser: &mut BinaryParser<R>) -> io::Result<Self> {
        let num_elements = try!(parser.source.read_u32());
        let encoding = try!(parser.source.read_u32());
        let bytelen_elements = try!(parser.source.read_u32());

        Ok(ArrayAttributeHeader {
            num_elements: num_elements,
            encoding: encoding,
            bytelen_elements: bytelen_elements,
        })
    }
}

/// Array type attribute.
#[derive(Debug)]
pub enum ArrayAttribute<'a, R: 'a> {
    /// Array of `bool`.
    Bool(ArrayAttributeReader<'a, R, bool>),
    /// Array of `i32`.
    I32(ArrayAttributeReader<'a, R, i32>),
    /// Array of `i64`.
    I64(ArrayAttributeReader<'a, R, i64>),
    /// Array of `f32`.
    F32(ArrayAttributeReader<'a, R, f32>),
    /// Array of `f64`.
    F64(ArrayAttributeReader<'a, R, f64>),
}

/// Reader of array attribute elements.
#[derive(Debug)]
pub struct ArrayAttributeReader<'a, R: 'a, T> {
    num_elements: u64,
    rest_elements: u64,
    reader: ArrayDecoder<'a, R>,
    warnings: &'a mut Vec<Warning>,
    _value_type: PhantomData<T>,
}

impl<'a, R: 'a + Read, T> ArrayAttributeReader<'a, R, T> {
    fn new<'b>(
        header: &'b ArrayAttributeHeader,
        reader: ArrayDecoder<'a, R>,
        warnings: &'a mut Vec<Warning>
    ) -> Self {
        ArrayAttributeReader {
            num_elements: header.num_elements as u64,
            rest_elements: header.num_elements as u64,
            reader: reader,
            warnings: warnings,
            _value_type: PhantomData,
        }
    }

    /// Returns number of elements.
    pub fn num_elements(&self) -> u64 {
        self.num_elements
    }

    /// Returns number of rest elements.
    pub fn rest_elements(&self) -> u64 {
        self.num_elements
    }
}

impl<'a, R: 'a + Read> Iterator for ArrayAttributeReader<'a, R, bool> {
    type Item = io::Result<bool>;

    fn next(&mut self) -> Option<Self::Item> {
        if self.rest_elements == 0 {
            return None;
        }
        self.rest_elements -= 1;
        let raw = match self.reader.read_u8() {
            Ok(val) => val,
            Err(err) => return Some(Err(err)),
        };
        let val = (raw & 1) == 1;
        Some(Ok(val))
    }
}

macro_rules! impl_attr_array_iter {
    ($ty:ty, $f:ident) => {
        impl<'a, R: 'a + Read> Iterator for ArrayAttributeReader<'a, R, $ty> {
            type Item = io::Result<$ty>;

            fn next(&mut self) -> Option<Self::Item> {
                if self.rest_elements == 0 {
                    return None;
                }
                self.rest_elements -= 1;
                Some(self.reader.$f())
            }
        }
    }
}

impl_attr_array_iter!(i32, read_i32);
impl_attr_array_iter!(i64, read_i64);
impl_attr_array_iter!(f32, read_f32);
impl_attr_array_iter!(f64, read_f64);

/// Attribute array decoder.
enum ArrayDecoder<'a, R: 'a> {
    /// Non-compressed stream.
    ///
    /// `encoding` == 0.
    Raw(io::Take<&'a mut R>),
    /// Zlib-compressed stream.
    ///
    /// `encoding` == 1.
    #[cfg(feature = "flate2")]
    Zlib(ZlibDecoder<io::Take<&'a mut R>>),
    /// Zlib-compressed stream.
    ///
    /// `encoding` == 1.
    #[cfg(feature = "libflate")]
    Zlib(zlib::Decoder<io::Take<&'a mut R>>),
}

impl<'a, R: 'a + Read> ArrayDecoder<'a, R> {
    fn new(reader: &'a mut R, header: &ArrayAttributeHeader) -> Result<Self> {
        match header.encoding {
            0 => Ok(ArrayDecoder::Raw(reader.take(header.bytelen_elements as u64))),
            #[cfg(feature = "flate2")]
            1 => Ok(ArrayDecoder::Zlib(ZlibDecoder::new(reader.take(header.bytelen_elements as u64)))),
            #[cfg(feature = "libflate")]
            1 => Ok(ArrayDecoder::Zlib(try!(zlib::Decoder::new(reader.take(header.bytelen_elements as u64))))),
            _ => Err(Error::UnknownArrayAttributeEncoding(header.encoding)),
        }
    }
}

impl<'a, R: 'a + Read> Read for ArrayDecoder<'a, R> {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        match *self {
            ArrayDecoder::Raw(ref mut reader) => reader.read(buf),
            #[cfg(feature = "flate2")]
            ArrayDecoder::Zlib(ref mut reader) => reader.read(buf),
            #[cfg(feature = "libflate")]
            ArrayDecoder::Zlib(ref mut reader) => reader.read(buf),
        }
    }
}

impl<'a, R: 'a> fmt::Debug for ArrayDecoder<'a, R> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,
               "ArrayDecoder::{}",
               match *self {
                   ArrayDecoder::Raw(_) => "Raw",
                   #[cfg(feature = "flate2")]
                   ArrayDecoder::Zlib(_) => "Zlib",
                   #[cfg(feature = "libflate")]
                   ArrayDecoder::Zlib(_) => "Zlib",
               })
    }
}
<commit_msg>Implement efficient array loader for node attributes<commit_after>//! Array type node attribute.

use std::fmt;
use std::marker::PhantomData;
use std::io;
use std::io::Read;
#[cfg(feature = "flate2")]
use flate2::read::ZlibDecoder;
#[cfg(feature = "libflate")]
use libflate::zlib;

use parser::binary::BinaryParser;
use parser::binary::error::{Result, Error, Warning};
use parser::binary::reader::{ParserSource, ReadLittleEndian};

/// Read array type attribute from the given parser.
pub fn read_array_attribute<R: ParserSource>(
    parser: &mut BinaryParser<R>,
    type_code: u8
) -> Result<(ArrayAttribute<R>, u64)> {
    let header = try!(ArrayAttributeHeader::read_from_parser(parser));
    let current_pos = parser.source.position();
    let BinaryParser { ref mut source, ref mut warnings, .. } = *parser;
    let reader = try!(ArrayDecoder::new(source, &header));

    let value = match type_code {
        b'b' => ArrayAttribute::Bool(ArrayAttributeReader::new(&header, reader, warnings)),
        b'i' => ArrayAttribute::I32(ArrayAttributeReader::new(&header, reader, warnings)),
        b'l' => ArrayAttribute::I64(ArrayAttributeReader::new(&header, reader, warnings)),
        b'f' => ArrayAttribute::F32(ArrayAttributeReader::new(&header, reader, warnings)),
        b'd' => ArrayAttribute::F64(ArrayAttributeReader::new(&header, reader, warnings)),
        _ => unreachable!(),
    };
    Ok((value, current_pos + header.bytelen_elements as u64))
}

/// Header of an array attribute.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
struct ArrayAttributeHeader {
    /// Number of elements.
    num_elements: u32,
    /// Array encoding, i.e. compression method.
    encoding: u32,
    /// Length of the array (excluding this header) in bytes.
    bytelen_elements: u32,
}

impl ArrayAttributeHeader {
    fn read_from_parser<R: ParserSource>(parser: &mut BinaryParser<R>) -> io::Result<Self> {
        let num_elements = try!(parser.source.read_u32());
        let encoding = try!(parser.source.read_u32());
        let bytelen_elements = try!(parser.source.read_u32());

        Ok(ArrayAttributeHeader {
            num_elements: num_elements,
            encoding: encoding,
            bytelen_elements: bytelen_elements,
        })
    }
}

/// Array type attribute.
#[derive(Debug)]
pub enum ArrayAttribute<'a, R: 'a> {
    /// Array of `bool`.
    Bool(ArrayAttributeReader<'a, R, bool>),
    /// Array of `i32`.
    I32(ArrayAttributeReader<'a, R, i32>),
    /// Array of `i64`.
    I64(ArrayAttributeReader<'a, R, i64>),
    /// Array of `f32`.
    F32(ArrayAttributeReader<'a, R, f32>),
    /// Array of `f64`.
    F64(ArrayAttributeReader<'a, R, f64>),
}

/// Reader of array attribute elements.
#[derive(Debug)]
pub struct ArrayAttributeReader<'a, R: 'a, T> {
    num_elements: u64,
    rest_elements: u64,
    reader: ArrayDecoder<'a, R>,
    warnings: &'a mut Vec<Warning>,
    _value_type: PhantomData<T>,
}

impl<'a, R: 'a + Read, T> ArrayAttributeReader<'a, R, T> {
    fn new<'b>(
        header: &'b ArrayAttributeHeader,
        reader: ArrayDecoder<'a, R>,
        warnings: &'a mut Vec<Warning>
    ) -> Self {
        ArrayAttributeReader {
            num_elements: header.num_elements as u64,
            rest_elements: header.num_elements as u64,
            reader: reader,
            warnings: warnings,
            _value_type: PhantomData,
        }
    }

    /// Returns number of elements.
    pub fn num_elements(&self) -> u64 {
        self.num_elements
    }

    /// Returns number of rest elements.
    pub fn rest_elements(&self) -> u64 {
        self.num_elements
    }
}

impl<'a, R: 'a + Read> ArrayAttributeReader<'a, R, bool> {
    /// Reads elements into the given buffer.
    pub fn read_into_buf(&mut self, buf: &mut [bool]) -> io::Result<usize> {
        let size = ::std::cmp::min(buf.len(), self.rest_elements as usize);
        for elem in &mut buf[0..size] {
            self.rest_elements -= 1;
            *elem = (try!(self.reader.read_u8()) & 1) == 1;
        }
        Ok(size)
    }

    /// Reads all elements into `Vec`.
    pub fn into_vec(mut self) -> io::Result<Vec<bool>> {
        let mut vec = vec![false; self.rest_elements as usize];
        try!(self.read_into_buf(&mut vec[..]));
        Ok(vec)
    }
}

macro_rules! impl_attr_array_read {
    ($ty:ty, $f:ident) => {
        impl<'a, R: 'a + Read> ArrayAttributeReader<'a, R, $ty> {
            /// Reads elements into the given buffer.
            pub fn read_into_buf(&mut self, buf: &mut [$ty]) -> io::Result<usize> {
                let size = ::std::cmp::min(buf.len(), self.rest_elements as usize);
                self.rest_elements -= size as u64;
                try!(self.reader.$f(&mut buf[0..size]));
                Ok(size)
            }

            /// Reads all elements into `Vec`.
            pub fn into_vec(mut self) -> io::Result<Vec<$ty>> {
                let mut vec = vec![0 as $ty; self.rest_elements as usize];
                try!(self.read_into_buf(&mut vec[..]));
                Ok(vec)
            }
        }
    }
}

impl_attr_array_read!(i32, read_i32_arr);
impl_attr_array_read!(i64, read_i64_arr);
impl_attr_array_read!(f32, read_f32_arr);
impl_attr_array_read!(f64, read_f64_arr);

impl<'a, R: 'a + Read> Iterator for ArrayAttributeReader<'a, R, bool> {
    type Item = io::Result<bool>;

    fn next(&mut self) -> Option<Self::Item> {
        if self.rest_elements == 0 {
            return None;
        }
        self.rest_elements -= 1;
        let raw = match self.reader.read_u8() {
            Ok(val) => val,
            Err(err) => return Some(Err(err)),
        };
        let val = (raw & 1) == 1;
        Some(Ok(val))
    }
}

macro_rules! impl_attr_array_iter {
    ($ty:ty, $f:ident) => {
        impl<'a, R: 'a + Read> Iterator for ArrayAttributeReader<'a, R, $ty> {
            type Item = io::Result<$ty>;

            fn next(&mut self) -> Option<Self::Item> {
                if self.rest_elements == 0 {
                    return None;
                }
                self.rest_elements -= 1;
                Some(self.reader.$f())
            }
        }
    }
}

impl_attr_array_iter!(i32, read_i32);
impl_attr_array_iter!(i64, read_i64);
impl_attr_array_iter!(f32, read_f32);
impl_attr_array_iter!(f64, read_f64);

/// Attribute array decoder.
enum ArrayDecoder<'a, R: 'a> {
    /// Non-compressed stream.
    ///
    /// `encoding` == 0.
    Raw(io::Take<&'a mut R>),
    /// Zlib-compressed stream.
    ///
    /// `encoding` == 1.
    #[cfg(feature = "flate2")]
    Zlib(ZlibDecoder<io::Take<&'a mut R>>),
    /// Zlib-compressed stream.
    ///
    /// `encoding` == 1.
    #[cfg(feature = "libflate")]
    Zlib(zlib::Decoder<io::Take<&'a mut R>>),
}

impl<'a, R: 'a + Read> ArrayDecoder<'a, R> {
    fn new(reader: &'a mut R, header: &ArrayAttributeHeader) -> Result<Self> {
        match header.encoding {
            0 => Ok(ArrayDecoder::Raw(reader.take(header.bytelen_elements as u64))),
            #[cfg(feature = "flate2")]
            1 => Ok(ArrayDecoder::Zlib(ZlibDecoder::new(reader.take(header.bytelen_elements as u64)))),
            #[cfg(feature = "libflate")]
            1 => Ok(ArrayDecoder::Zlib(try!(zlib::Decoder::new(reader.take(header.bytelen_elements as u64))))),
            _ => Err(Error::UnknownArrayAttributeEncoding(header.encoding)),
        }
    }
}

impl<'a, R: 'a + Read> Read for ArrayDecoder<'a, R> {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        match *self {
            ArrayDecoder::Raw(ref mut reader) => reader.read(buf),
            #[cfg(feature = "flate2")]
            ArrayDecoder::Zlib(ref mut reader) => reader.read(buf),
            #[cfg(feature = "libflate")]
            ArrayDecoder::Zlib(ref mut reader) => reader.read(buf),
        }
    }
}

impl<'a, R: 'a> fmt::Debug for ArrayDecoder<'a, R> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,
               "ArrayDecoder::{}",
               match *self {
                   ArrayDecoder::Raw(_) => "Raw",
                   #[cfg(feature = "flate2")]
                   ArrayDecoder::Zlib(_) => "Zlib",
                   #[cfg(feature = "libflate")]
                   ArrayDecoder::Zlib(_) => "Zlib",
               })
    }
}
<|endoftext|>


<commit_before>use shell_command::ShellCommand;
use nom::IResult;
use std::process::Command;
use std::env;
use std::env::VarError;

fn token_char(ch: char) -> bool {
    match ch {
        '\x00' ... '\x20' => false,
        '\x7f' | '"' | '\'' | '>' | '<' | '|' | ';' | '{' | '}' | '$' => false,
        _ => true,
    }
}

fn var_char(ch: char) -> bool {
    match ch {
        'a' ... 'z' => true,
        'A' ... 'Z' => true,
        '0' ... '9' => true,
        '_' => true,
        _ => false,
    }
}

enum TokenPart {
    Bare(String),
    Placeholder,
    EnvVariable(String),
}

struct Token(Vec<TokenPart>);

impl Token {
    fn into_string(self, args: &mut Iterator<Item = &str>)
            -> Result<String, VarError> {
        let mut token = String::from("");
        for part in self.0 {
            match part {
                TokenPart::Bare(s) => token += &s,
                TokenPart::Placeholder =>
                    token += args.next().expect("Too many placeholders"),
                TokenPart::EnvVariable(name) => {
                    debug!("Environment variable {}", name);
                    token += &env::var(name)?
                }
            }
        }
        Ok(token)
    }
}

named!(bare_token<&str, TokenPart>,
       map!(take_while1_s!(token_char), |s| TokenPart::Bare(String::from(s))));
named!(quoted_token<&str, TokenPart>,
       map!(delimited!(tag_s!("\""), take_until_s!("\""), tag_s!("\"")),
            |s| TokenPart::Bare(String::from(s))));
named!(place_holder<&str, TokenPart>,
       map!(tag_s!("{}"), |_| TokenPart::Placeholder));
named!(env_var<&str, TokenPart>,
       map!(preceded!(tag!("$"), take_while1_s!(var_char)),
            |name| TokenPart::EnvVariable(String::from(name))));
named!(command_token<&str, Token>,
       map!(many1!(alt!(bare_token | quoted_token | place_holder | env_var)),
            |vec| Token(vec)));

named!(command< &str, Vec<Token> >,
       terminated!(ws!(many1!(command_token)), eof!()));

#[macro_export]
macro_rules! cmd {
    ($format:expr) => ($crate::command::new_command($format, &[]).unwrap());
    ($format:expr, $($arg:expr),+) =>
        ($crate::command::new_command($format, &[$($arg),+]).unwrap());
}

fn parse_cmd<'a>(format: &'a str, args: &'a [&str])
        -> Result<Vec<String>, VarError> {
    let tokens = match command(format) {
        IResult::Done(_, result) => result,
        IResult::Error(error) => panic!("Error {:?}", error),
        IResult::Incomplete(needed) => panic!("Needed {:?}", needed)
    };
    let args = args.iter().map(|a| *a).collect::<Vec<_>>();
    let mut args = args.into_iter();
    tokens.into_iter().map(|token| token.into_string(&mut args))
        .collect::<Result<Vec<_>, _>>()
}

pub fn new_command(format: &str, args: &[&str])
        -> Result<ShellCommand, VarError> {
    let vec = parse_cmd(format, args)?;
    let mut command = Command::new(&vec[0]);
    if vec.len() > 1 {
        command.args(&vec[1..]);
    }
    let line = vec.join(" ");
    Ok(ShellCommand::new(line, command))
}

#[test]
fn test_parse_cmd() {
    let tokens = parse_cmd(r#"cmd 1 2
                              3 "
  4" {}"#, &["5"]).unwrap();
    assert_eq!("cmd", tokens[0]);
    assert_eq!("1", tokens[1]);
    assert_eq!("2", tokens[2]);
    assert_eq!("3", tokens[3]);
    assert_eq!("\n  4", tokens[4]);
    assert_eq!("5", tokens[5]);
}

#[test]
fn test_parse_cmd_env() {
    use env_logger;
    env_logger::init().unwrap();
    env::set_var("MY_VAR", "VALUE");
    let tokens = parse_cmd("echo $MY_VAR/dir", &[]).unwrap();
    assert_eq!("VALUE/dir", tokens[1]);
}
<commit_msg>Bare token should not contain non-ASCII characters<commit_after>use shell_command::ShellCommand;
use nom::IResult;
use std::process::Command;
use std::env;
use std::env::VarError;

fn token_char(ch: char) -> bool {
    if ch.len_utf8() > 1 {
        return false;
    }
    match ch {
        '\x00' ... '\x20' => false,
        '\x7f' | '"' | '\'' | '>' | '<' | '|' | ';' | '{' | '}' | '$' => false,
        _ => true,
    }
}

fn var_char(ch: char) -> bool {
    match ch {
        'a' ... 'z' => true,
        'A' ... 'Z' => true,
        '0' ... '9' => true,
        '_' => true,
        _ => false,
    }
}

enum TokenPart {
    Bare(String),
    Placeholder,
    EnvVariable(String),
}

struct Token(Vec<TokenPart>);

impl Token {
    fn into_string(self, args: &mut Iterator<Item = &str>)
            -> Result<String, VarError> {
        let mut token = String::from("");
        for part in self.0 {
            match part {
                TokenPart::Bare(s) => token += &s,
                TokenPart::Placeholder =>
                    token += args.next().expect("Too many placeholders"),
                TokenPart::EnvVariable(name) => {
                    debug!("Environment variable {}", name);
                    token += &env::var(name)?
                }
            }
        }
        Ok(token)
    }
}

named!(bare_token<&str, TokenPart>,
       map!(take_while1_s!(token_char), |s| TokenPart::Bare(String::from(s))));
named!(quoted_token<&str, TokenPart>,
       map!(delimited!(tag_s!("\""), take_until_s!("\""), tag_s!("\"")),
            |s| TokenPart::Bare(String::from(s))));
named!(place_holder<&str, TokenPart>,
       map!(tag_s!("{}"), |_| TokenPart::Placeholder));
named!(env_var<&str, TokenPart>,
       map!(preceded!(tag!("$"), take_while1_s!(var_char)),
            |name| TokenPart::EnvVariable(String::from(name))));
named!(command_token<&str, Token>,
       map!(many1!(alt!(bare_token | quoted_token | place_holder | env_var)),
            |vec| Token(vec)));

named!(command< &str, Vec<Token> >,
       terminated!(ws!(many1!(command_token)), eof!()));

#[macro_export]
macro_rules! cmd {
    ($format:expr) => ($crate::command::new_command($format, &[]).unwrap());
    ($format:expr, $($arg:expr),+) =>
        ($crate::command::new_command($format, &[$($arg),+]).unwrap());
}

fn parse_cmd<'a>(format: &'a str, args: &'a [&str])
        -> Result<Vec<String>, VarError> {
    let tokens = match command(format) {
        IResult::Done(_, result) => result,
        IResult::Error(error) => panic!("Error {:?}", error),
        IResult::Incomplete(needed) => panic!("Needed {:?}", needed)
    };
    let args = args.iter().map(|a| *a).collect::<Vec<_>>();
    let mut args = args.into_iter();
    tokens.into_iter().map(|token| token.into_string(&mut args))
        .collect::<Result<Vec<_>, _>>()
}

pub fn new_command(format: &str, args: &[&str])
        -> Result<ShellCommand, VarError> {
    let vec = parse_cmd(format, args)?;
    let mut command = Command::new(&vec[0]);
    if vec.len() > 1 {
        command.args(&vec[1..]);
    }
    let line = vec.join(" ");
    Ok(ShellCommand::new(line, command))
}

#[test]
fn test_parse_cmd() {
    let tokens = parse_cmd(r#"cmd 1 2
                              3 "
  4" {}"#, &["5"]).unwrap();
    assert_eq!("cmd", tokens[0]);
    assert_eq!("1", tokens[1]);
    assert_eq!("2", tokens[2]);
    assert_eq!("3", tokens[3]);
    assert_eq!("\n  4", tokens[4]);
    assert_eq!("5", tokens[5]);
}

#[test]
fn test_parse_cmd_env() {
    use env_logger;
    env_logger::init().unwrap();
    env::set_var("MY_VAR", "VALUE");
    let tokens = parse_cmd("echo $MY_VAR/dir", &[]).unwrap();
    assert_eq!("VALUE/dir", tokens[1]);
}
<|endoftext|>


<commit_before>extern crate semver;

use std::collections::HashMap;
use std::io::net::ip::IpAddr;

pub enum Scheme {
    Http,
    Https
}

pub enum Host<'a> {
    HostName(&'a str),
    HostIp(IpAddr)
}

pub enum Method<'a> {
    Get,
    Post,
    Put,
    Patch,
    Delete,
    Other(&'a str)
}

pub trait Request {
    /// The version of HTTP being used
    fn http_version(&self) -> semver::Version;

    /// The version of the conduit spec being used
    fn conduit_version(&self) -> semver::Version;

    /// The request method, such as GET, POST, PUT, DELETE or PATCH
    fn method<'a>(&'a self) -> Method<'a>;

    /// The scheme part of the request URL
    fn scheme(&self) -> Scheme;

    /// The host part of the requested URL
    fn host<'a>(&'a self) -> Host<'a>;

    /// The initial part of the request URL's path that corresponds
    /// to a virtual root. This allows an application to have a
    /// virtual location that consumes part of the path.
    fn virtual_root<'a>(&'a self) -> Option<&'a str>;

    /// The remainder of the path.
    fn path<'a>(&'a self) -> &'a str;

    /// The portion of the request URL that follows the "?"
    fn query_string<'a>(&'a self) -> Option<&'a str>;

    /// The remote IP address of the client or the last proxy that
    /// sent the request.
    fn remote_ip(&self) -> IpAddr;

    /// The byte-size of the body, if any
    fn content_length(&self) -> Option<uint>;

    /// A Reader for the body of the request
    fn body<'a>(&'a mut self) -> &'a mut Reader;
}

pub struct Response {
    /// The status code as a tuple of the return code and status string
    pub status: (uint, &'static str),

    /// A Map of the headers
    pub headers: HashMap<String, String>,

    /// A Writer for body of the response
    pub body: Box<Reader + Send>
}

pub type Handler = fn(&mut Request) -> Response;
<commit_msg>Derive some common traits<commit_after>extern crate semver;

use std::collections::HashMap;
use std::io::net::ip::IpAddr;

#[deriving(PartialEq, Show, Clone)]
pub enum Scheme {
    Http,
    Https
}

#[deriving(PartialEq, Show, Clone)]
pub enum Host<'a> {
    HostName(&'a str),
    HostIp(IpAddr)
}

#[deriving(PartialEq, Show, Clone)]
pub enum Method<'a> {
    Get,
    Post,
    Put,
    Patch,
    Delete,
    Other(&'a str)
}

pub trait Request {
    /// The version of HTTP being used
    fn http_version(&self) -> semver::Version;

    /// The version of the conduit spec being used
    fn conduit_version(&self) -> semver::Version;

    /// The request method, such as GET, POST, PUT, DELETE or PATCH
    fn method<'a>(&'a self) -> Method<'a>;

    /// The scheme part of the request URL
    fn scheme(&self) -> Scheme;

    /// The host part of the requested URL
    fn host<'a>(&'a self) -> Host<'a>;

    /// The initial part of the request URL's path that corresponds
    /// to a virtual root. This allows an application to have a
    /// virtual location that consumes part of the path.
    fn virtual_root<'a>(&'a self) -> Option<&'a str>;

    /// The remainder of the path.
    fn path<'a>(&'a self) -> &'a str;

    /// The portion of the request URL that follows the "?"
    fn query_string<'a>(&'a self) -> Option<&'a str>;

    /// The remote IP address of the client or the last proxy that
    /// sent the request.
    fn remote_ip(&self) -> IpAddr;

    /// The byte-size of the body, if any
    fn content_length(&self) -> Option<uint>;

    /// A Reader for the body of the request
    fn body<'a>(&'a mut self) -> &'a mut Reader;
}

pub struct Response {
    /// The status code as a tuple of the return code and status string
    pub status: (uint, &'static str),

    /// A Map of the headers
    pub headers: HashMap<String, String>,

    /// A Writer for body of the response
    pub body: Box<Reader + Send>
}

pub type Handler = fn(&mut Request) -> Response;
<|endoftext|>


<commit_before>// Copyright © 2017 Cormac O'Brien
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software
// and associated documentation files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
// BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

// TODO: implement proper Error types

use std::collections::VecDeque;
use std::collections::HashMap;
use std::iter::FromIterator;

use glutin::VirtualKeyCode as Key;

/// Stores console commands.
pub struct CmdRegistry<'a> {
    cmds: HashMap<String, Box<Fn(Vec<&str>) + 'a>>,
}

impl<'a> CmdRegistry<'a> {
    pub fn new() -> CmdRegistry<'a> {
        CmdRegistry {
            cmds: HashMap::new(),
        }
    }

    /// Registers a new command.
    ///
    /// Returns an error if a command with the specified name already exists.
    pub fn add_cmd<S>(&mut self, name: S, cmd: Box<Fn(Vec<&str>) + 'a>) -> Result<(), ()>
    where
        S: AsRef<str>,
    {
        let name = name.as_ref().to_owned();

        match self.cmds.get(&name) {
            Some(_) => {
                error!("Command \"{}\" already registered.", name);
                return Err(());
            }
            None => {
                self.cmds.insert(name, cmd);
            }
        }

        Ok(())
    }

    /// Executes a command.
    ///
    /// Returns an error if no command with the specified name exists.
    pub fn exec_cmd<S>(&mut self, name: S, args: Vec<&str>) -> Result<(), ()>
    where
        S: AsRef<str>,
    {
        let name = name.as_ref().to_owned();

        match self.cmds.get(&name) {
            Some(cmd) => cmd(args),
            None => return Err(()),
        }

        Ok(())
    }
}

/// A configuration variable.
///
/// Cvars are the primary method of configuring the game.
struct Cvar {
    // Value of this variable
    val: String,

    // If true, this variable should be archived in vars.rc
    archive: bool,

    // If true, updating this variable must also update serverinfo/userinfo
    info: bool,

    // The default value of this variable
    default: String,
}

pub struct CvarRegistry {
    cvars: HashMap<String, Cvar>
}

impl CvarRegistry {
    /// Construct a new empty `CvarRegistry`.
    pub fn new() -> CvarRegistry {
        CvarRegistry {
            cvars: HashMap::new(),
        }
    }

    /// Register a new `Cvar` with the given name.
    pub fn register<S>(&mut self, name: S, default: S) -> Result<(), ()> where S: AsRef<str> {
        let name = name.as_ref();
        let default = default.as_ref();

        match self.cvars.get(name) {
            Some(_) => return Err(()),
            None => {
                self.cvars.insert(name.to_owned(), Cvar {
                    val: default.to_owned(),
                    archive: false,
                    info: false,
                    default: default.to_owned(),
                });
            }
        }

        Ok(())
    }

    /// Register a new archived `Cvar` with the given name.
    ///
    /// The value of this `Cvar` should be written to `vars.rc` whenever the game is closed or
    /// `host_writeconfig` is issued.
    pub fn register_archive<S>(&mut self, name: S, default: S) -> Result<(), ()> where S: AsRef<str> {
        let name = name.as_ref();
        let default = default.as_ref();

        match self.cvars.get(name) {
            Some(_) => return Err(()),
            None => {
                self.cvars.insert(name.to_owned(), Cvar {
                    val: default.to_owned(),
                    archive: true,
                    info: false,
                    default: default.to_owned(),
                });
            }
        }

        Ok(())
    }

    /// Register a new info `Cvar` with the given name.
    ///
    /// When this `Cvar` is set, the serverinfo or userinfo string should be update to reflect its
    /// new value.
    pub fn register_updateinfo<S>(&mut self, name: S, default: S) -> Result<(), ()> where S: AsRef<str> {
        let name = name.as_ref();
        let default = default.as_ref();

        match self.cvars.get(name) {
            Some(_) => return Err(()),
            None => {
                self.cvars.insert(name.to_owned(), Cvar {
                    val: default.to_owned(),
                    archive: false,
                    info: true,
                    default: default.to_owned(),
                });
            }
        }

        Ok(())
    }

    pub fn register_archive_updateinfo<S>(&mut self, name: S, default: S) -> Result<(), ()> where S: AsRef<str> {
        let name = name.as_ref();
        let default = default.as_ref();

        match self.cvars.get(name) {
            Some(_) => return Err(()),
            None => {
                self.cvars.insert(name.to_owned(), Cvar {
                    val: default.to_owned(),
                    archive: true,
                    info: true,
                    default: default.to_owned(),
                });
            }
        }

        Ok(())
    }
}

/// The line of text currently being edited in the console.
pub struct ConsoleInput {
    text: Vec<char>,
    curs: usize,
}

impl ConsoleInput {
    /// Constructs a new `ConsoleInput`.
    ///
    /// Initializes the text content to be empty and places the cursor at position 0.
    pub fn new() -> ConsoleInput {
        ConsoleInput {
            text: Vec::new(),
            curs: 0,
        }
    }

    /// Returns the current content of the `ConsoleInput`.
    pub fn get_text(&self) -> Vec<char> {
        self.text.to_owned()
    }

    /// Sets the content of the `ConsoleInput` to `Text`.
    ///
    /// This also moves the cursor to the end of the line.
    pub fn set_text(&mut self, text: &Vec<char>) {
        self.text = text.clone();
        self.curs = self.text.len();
    }

    /// Inserts the specified character at the position of the cursor.
    ///
    /// The cursor is moved one character to the right.
    pub fn insert(&mut self, c: char) {
        self.text.insert(self.curs, c);
        self.cursor_right();
    }

    /// Moves the cursor to the right.
    ///
    /// If the cursor is at the end of the current text, no change is made.
    pub fn cursor_right(&mut self) {
        if self.curs < self.text.len() {
            self.curs += 1;
        }
    }

    /// Moves the cursor to the left.
    ///
    /// If the cursor is at the beginning of the current text, no change is made.
    pub fn cursor_left(&mut self) {
        if self.curs > 0 {
            self.curs -= 1;
        }
    }

    /// Deletes the character to the right of the cursor.
    ///
    /// If the cursor is at the end of the current text, no character is deleted.
    pub fn delete(&mut self) {
        if self.curs < self.text.len() {
            self.text.remove(self.curs);
        }
    }

    /// Deletes the character to the left of the cursor.
    ///
    /// If the cursor is at the beginning of the current text, no character is deleted.
    pub fn backspace(&mut self) {
        if self.curs > 0 {
            self.text.remove(self.curs - 1);
            self.curs -= 1;
        }
    }

    /// Clears the contents of the `ConsoleInput`.
    ///
    /// Also moves the cursor to position 0.
    pub fn clear(&mut self) {
        self.text.clear();
        self.curs = 0;
    }

    pub fn debug_string(&self) -> String {
        format!(
            "{}_{}",
            String::from_iter(self.text[..self.curs].to_owned().into_iter()),
            String::from_iter(self.text[self.curs..].to_owned().into_iter())
        )
    }
}

pub struct History {
    lines: VecDeque<Vec<char>>,
    curs: usize,
}

impl History {
    pub fn new() -> History {
        History {
            lines: VecDeque::new(),
            curs: 0,
        }
    }

    pub fn add_line(&mut self, line: Vec<char>) {
        self.lines.push_front(line);
        self.curs = 0;
    }

    // TODO: handle case where history is empty
    pub fn line_up(&mut self) -> Option<Vec<char>> {
        if self.lines.len() == 0 || self.curs >= self.lines.len() {
            None
        } else {
            self.curs += 1;
            Some(self.lines[self.curs - 1].clone())
        }
    }

    pub fn line_down(&mut self) -> Option<Vec<char>> {
        if self.curs > 0 {
            self.curs -= 1;
        }

        if self.curs > 0 {
            Some(self.lines[self.curs - 1].clone())
        } else {
            Some(Vec::new().clone())
        }
    }
}

pub struct ConsoleOutput {
    lines: Vec<Vec<char>>,
}

impl ConsoleOutput {
    pub fn println<S>(&mut self, msg: S)
    where
        S: AsRef<str>,
    {
        println!("{}", msg.as_ref());
    }
}

pub struct Console {
    input: ConsoleInput,
    hist: History,
}

impl Console {
    pub fn new() -> Console {
        Console {
            input: ConsoleInput::new(),
            hist: History::new(),
        }
    }

    pub fn send_char(&mut self, c: char) -> Result<(), ()> {
        match c {
            '\r' => {
                let entered = self.get_string();
                let mut parts = entered.split_whitespace();

                let cmd_name = match parts.next() {
                    Some(c) => c,
                    None => return Ok(()),
                };

                let args: Vec<&str> = parts.collect();

                self.hist.add_line(self.input.get_text());
                self.input.clear();
            }

            // backspace
            '\x08' => self.input.backspace(),

            // delete
            '\x7f' => self.input.delete(),

            '\t' => (), // TODO: tab completion

            c => self.input.insert(c),
        }

        println!("{}", self.debug_string());

        Ok(())
    }

    pub fn send_key(&mut self, key: Key) {
        match key {
            Key::Up => if let Some(line) = self.hist.line_up() {
                self.input.set_text(&line);
            }

            Key::Down => if let Some(line) = self.hist.line_down() {
                self.input.set_text(&line);
            }

            Key::Right => self.input.cursor_right(),
            Key::Left => self.input.cursor_left(),
            _ => return,
        }

        println!("{}", self.debug_string());
    }

    fn get_string(&self) -> String {
        String::from_iter(self.input.text.clone().into_iter())
    }

    fn debug_string(&self) -> String {
        format!(
            "{}_{}",
            String::from_iter(self.input.text[..self.input.curs].to_owned().into_iter()),
            String::from_iter(self.input.text[self.input.curs..].to_owned().into_iter())
        )
    }
}

struct Tokenizer<'a> {
    input: &'a str,
    byte_offset: usize,
}

impl<'a> Tokenizer<'a> {
    fn new(input: &'a str) -> Tokenizer<'a> {
        Tokenizer {
            input: input,
            byte_offset: 0,
        }
    }

    fn get_remaining_input(&self) -> &'a str {
        &self.input[self.byte_offset..]
    }

    fn skip_spaces(&mut self) {
        let iter = self.get_remaining_input().char_indices();
        match iter.skip_while(|&(_, c)| c.is_whitespace()).next() {
            Some((i, _)) => self.byte_offset += i,
            None => self.byte_offset = self.input.len(),
        }
    }

    fn try_skip_line_comment(&mut self) -> bool {
        if self.get_remaining_input().starts_with("//") {
            match self.get_remaining_input()
                .char_indices()
                .skip_while(|&(_, c)| c != '\n')
                .next()
            {
                Some((i, _)) => self.byte_offset += i,
                None => self.byte_offset = self.input.len(),
            }

            return true;
        }

        false
    }

    fn next(&mut self) -> Option<&'a str> {
        loop {
            self.skip_spaces();

            if !self.try_skip_line_comment() {
                break;
            }
        }

        let mut char_indices = self.get_remaining_input().char_indices();
        match char_indices.next() {
            Some((start_i, '"')) => {
                let offset = self.byte_offset + start_i;
                match char_indices.skip_while(|&(_, c)| c != '"').next() {
                    Some((end_i, '"')) => {
                        let len = end_i + 1 - start_i;
                        self.byte_offset += len;
                        Some(&self.input[offset..offset + len])
                    }
                    Some(_) => panic!("wtf hapen"),
                    None => None
                }
            }
            Some((start_i, _)) => {
                let offset = self.byte_offset + start_i;

                match char_indices.take_while(|&(_, c)| !c.is_whitespace()).last() {
                    Some((end_i, _)) => {
                        let len = end_i + 1 - start_i;
                        self.byte_offset += len;
                        Some(&self.input[offset..offset + len])
                    }
                    None => None,
                }
            }
            None => None,
        }
    }
}

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

    #[test]
    fn test_tokenizer_empty() {
        let mut tokenizer = Tokenizer::new("");
        assert_eq!(tokenizer.next(), None);
    }
}
<commit_msg>Add docs for console::Tokenizer<commit_after>// Copyright © 2017 Cormac O'Brien
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software
// and associated documentation files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
// BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

// TODO: implement proper Error types

use std::collections::VecDeque;
use std::collections::HashMap;
use std::iter::FromIterator;

use glutin::VirtualKeyCode as Key;

/// Stores console commands.
pub struct CmdRegistry<'a> {
    cmds: HashMap<String, Box<Fn(Vec<&str>) + 'a>>,
}

impl<'a> CmdRegistry<'a> {
    pub fn new() -> CmdRegistry<'a> {
        CmdRegistry {
            cmds: HashMap::new(),
        }
    }

    /// Registers a new command.
    ///
    /// Returns an error if a command with the specified name already exists.
    pub fn add_cmd<S>(&mut self, name: S, cmd: Box<Fn(Vec<&str>) + 'a>) -> Result<(), ()>
    where
        S: AsRef<str>,
    {
        let name = name.as_ref().to_owned();

        match self.cmds.get(&name) {
            Some(_) => {
                error!("Command \"{}\" already registered.", name);
                return Err(());
            }
            None => {
                self.cmds.insert(name, cmd);
            }
        }

        Ok(())
    }

    /// Executes a command.
    ///
    /// Returns an error if no command with the specified name exists.
    pub fn exec_cmd<S>(&mut self, name: S, args: Vec<&str>) -> Result<(), ()>
    where
        S: AsRef<str>,
    {
        let name = name.as_ref().to_owned();

        match self.cmds.get(&name) {
            Some(cmd) => cmd(args),
            None => return Err(()),
        }

        Ok(())
    }
}

/// A configuration variable.
///
/// Cvars are the primary method of configuring the game.
struct Cvar {
    // Value of this variable
    val: String,

    // If true, this variable should be archived in vars.rc
    archive: bool,

    // If true, updating this variable must also update serverinfo/userinfo
    info: bool,

    // The default value of this variable
    default: String,
}

pub struct CvarRegistry {
    cvars: HashMap<String, Cvar>
}

impl CvarRegistry {
    /// Construct a new empty `CvarRegistry`.
    pub fn new() -> CvarRegistry {
        CvarRegistry {
            cvars: HashMap::new(),
        }
    }

    /// Register a new `Cvar` with the given name.
    pub fn register<S>(&mut self, name: S, default: S) -> Result<(), ()> where S: AsRef<str> {
        let name = name.as_ref();
        let default = default.as_ref();

        match self.cvars.get(name) {
            Some(_) => return Err(()),
            None => {
                self.cvars.insert(name.to_owned(), Cvar {
                    val: default.to_owned(),
                    archive: false,
                    info: false,
                    default: default.to_owned(),
                });
            }
        }

        Ok(())
    }

    /// Register a new archived `Cvar` with the given name.
    ///
    /// The value of this `Cvar` should be written to `vars.rc` whenever the game is closed or
    /// `host_writeconfig` is issued.
    pub fn register_archive<S>(&mut self, name: S, default: S) -> Result<(), ()> where S: AsRef<str> {
        let name = name.as_ref();
        let default = default.as_ref();

        match self.cvars.get(name) {
            Some(_) => return Err(()),
            None => {
                self.cvars.insert(name.to_owned(), Cvar {
                    val: default.to_owned(),
                    archive: true,
                    info: false,
                    default: default.to_owned(),
                });
            }
        }

        Ok(())
    }

    /// Register a new info `Cvar` with the given name.
    ///
    /// When this `Cvar` is set, the serverinfo or userinfo string should be update to reflect its
    /// new value.
    pub fn register_updateinfo<S>(&mut self, name: S, default: S) -> Result<(), ()> where S: AsRef<str> {
        let name = name.as_ref();
        let default = default.as_ref();

        match self.cvars.get(name) {
            Some(_) => return Err(()),
            None => {
                self.cvars.insert(name.to_owned(), Cvar {
                    val: default.to_owned(),
                    archive: false,
                    info: true,
                    default: default.to_owned(),
                });
            }
        }

        Ok(())
    }

    pub fn register_archive_updateinfo<S>(&mut self, name: S, default: S) -> Result<(), ()> where S: AsRef<str> {
        let name = name.as_ref();
        let default = default.as_ref();

        match self.cvars.get(name) {
            Some(_) => return Err(()),
            None => {
                self.cvars.insert(name.to_owned(), Cvar {
                    val: default.to_owned(),
                    archive: true,
                    info: true,
                    default: default.to_owned(),
                });
            }
        }

        Ok(())
    }
}

/// The line of text currently being edited in the console.
pub struct ConsoleInput {
    text: Vec<char>,
    curs: usize,
}

impl ConsoleInput {
    /// Constructs a new `ConsoleInput`.
    ///
    /// Initializes the text content to be empty and places the cursor at position 0.
    pub fn new() -> ConsoleInput {
        ConsoleInput {
            text: Vec::new(),
            curs: 0,
        }
    }

    /// Returns the current content of the `ConsoleInput`.
    pub fn get_text(&self) -> Vec<char> {
        self.text.to_owned()
    }

    /// Sets the content of the `ConsoleInput` to `Text`.
    ///
    /// This also moves the cursor to the end of the line.
    pub fn set_text(&mut self, text: &Vec<char>) {
        self.text = text.clone();
        self.curs = self.text.len();
    }

    /// Inserts the specified character at the position of the cursor.
    ///
    /// The cursor is moved one character to the right.
    pub fn insert(&mut self, c: char) {
        self.text.insert(self.curs, c);
        self.cursor_right();
    }

    /// Moves the cursor to the right.
    ///
    /// If the cursor is at the end of the current text, no change is made.
    pub fn cursor_right(&mut self) {
        if self.curs < self.text.len() {
            self.curs += 1;
        }
    }

    /// Moves the cursor to the left.
    ///
    /// If the cursor is at the beginning of the current text, no change is made.
    pub fn cursor_left(&mut self) {
        if self.curs > 0 {
            self.curs -= 1;
        }
    }

    /// Deletes the character to the right of the cursor.
    ///
    /// If the cursor is at the end of the current text, no character is deleted.
    pub fn delete(&mut self) {
        if self.curs < self.text.len() {
            self.text.remove(self.curs);
        }
    }

    /// Deletes the character to the left of the cursor.
    ///
    /// If the cursor is at the beginning of the current text, no character is deleted.
    pub fn backspace(&mut self) {
        if self.curs > 0 {
            self.text.remove(self.curs - 1);
            self.curs -= 1;
        }
    }

    /// Clears the contents of the `ConsoleInput`.
    ///
    /// Also moves the cursor to position 0.
    pub fn clear(&mut self) {
        self.text.clear();
        self.curs = 0;
    }

    pub fn debug_string(&self) -> String {
        format!(
            "{}_{}",
            String::from_iter(self.text[..self.curs].to_owned().into_iter()),
            String::from_iter(self.text[self.curs..].to_owned().into_iter())
        )
    }
}

pub struct History {
    lines: VecDeque<Vec<char>>,
    curs: usize,
}

impl History {
    pub fn new() -> History {
        History {
            lines: VecDeque::new(),
            curs: 0,
        }
    }

    pub fn add_line(&mut self, line: Vec<char>) {
        self.lines.push_front(line);
        self.curs = 0;
    }

    // TODO: handle case where history is empty
    pub fn line_up(&mut self) -> Option<Vec<char>> {
        if self.lines.len() == 0 || self.curs >= self.lines.len() {
            None
        } else {
            self.curs += 1;
            Some(self.lines[self.curs - 1].clone())
        }
    }

    pub fn line_down(&mut self) -> Option<Vec<char>> {
        if self.curs > 0 {
            self.curs -= 1;
        }

        if self.curs > 0 {
            Some(self.lines[self.curs - 1].clone())
        } else {
            Some(Vec::new().clone())
        }
    }
}

pub struct ConsoleOutput {
    lines: Vec<Vec<char>>,
}

impl ConsoleOutput {
    pub fn println<S>(&mut self, msg: S)
    where
        S: AsRef<str>,
    {
        println!("{}", msg.as_ref());
    }
}

pub struct Console {
    input: ConsoleInput,
    hist: History,
}

impl Console {
    pub fn new() -> Console {
        Console {
            input: ConsoleInput::new(),
            hist: History::new(),
        }
    }

    pub fn send_char(&mut self, c: char) -> Result<(), ()> {
        match c {
            '\r' => {
                let entered = self.get_string();
                let mut parts = entered.split_whitespace();

                let cmd_name = match parts.next() {
                    Some(c) => c,
                    None => return Ok(()),
                };

                let args: Vec<&str> = parts.collect();

                self.hist.add_line(self.input.get_text());
                self.input.clear();
            }

            // backspace
            '\x08' => self.input.backspace(),

            // delete
            '\x7f' => self.input.delete(),

            '\t' => (), // TODO: tab completion

            c => self.input.insert(c),
        }

        println!("{}", self.debug_string());

        Ok(())
    }

    pub fn send_key(&mut self, key: Key) {
        match key {
            Key::Up => if let Some(line) = self.hist.line_up() {
                self.input.set_text(&line);
            }

            Key::Down => if let Some(line) = self.hist.line_down() {
                self.input.set_text(&line);
            }

            Key::Right => self.input.cursor_right(),
            Key::Left => self.input.cursor_left(),
            _ => return,
        }

        println!("{}", self.debug_string());
    }

    fn get_string(&self) -> String {
        String::from_iter(self.input.text.clone().into_iter())
    }

    fn debug_string(&self) -> String {
        format!(
            "{}_{}",
            String::from_iter(self.input.text[..self.input.curs].to_owned().into_iter()),
            String::from_iter(self.input.text[self.input.curs..].to_owned().into_iter())
        )
    }
}

pub struct Tokenizer<'a> {
    input: &'a str,
    byte_offset: usize,
}

impl<'a> Tokenizer<'a> {
    /// Constructs a command tokenizer with the specified input stream.
    ///
    /// # Examples
    ///
    /// ```
    /// # extern crate richter;
    /// use richter::console::Tokenizer;
    ///
    /// # fn main() {
    /// let tokenizer = Tokenizer::new("map e1m1");
    /// # }
    /// ```
    pub fn new(input: &'a str) -> Tokenizer<'a> {
        Tokenizer {
            input: input,
            byte_offset: 0,
        }
    }

    fn get_remaining_input(&self) -> &'a str {
        &self.input[self.byte_offset..]
    }

    fn skip_spaces(&mut self) {
        let iter = self.get_remaining_input().char_indices();
        match iter.skip_while(|&(_, c)| c.is_whitespace()).next() {
            Some((i, _)) => self.byte_offset += i,
            None => self.byte_offset = self.input.len(),
        }
    }

    fn try_skip_line_comment(&mut self) -> bool {
        if self.get_remaining_input().starts_with("//") {
            match self.get_remaining_input()
                .char_indices()
                .skip_while(|&(_, c)| c != '\n')
                .next()
            {
                Some((i, _)) => self.byte_offset += i,
                None => self.byte_offset = self.input.len(),
            }

            return true;
        }

        false
    }

    /// Returns the next token in the input stream.
    ///
    /// This will skip any leading any leading whitespace characters as recognized by the
    /// `.is_whitespace()` function of `std::char`. Note that the original Quake engine only
    /// expects ASCII input and recognizes as whitespace any character with a code point less than
    /// or equal to `U+0020`, including control characters. It will also skip single-line comments
    /// beginning with `//` and ending with a newline (`U+000A LINE FEED`).
    ///
    /// This function *does not* process semicolons in order to split commands.
    ///
    /// # Examples
    ///
    /// ```
    /// # extern crate richter;
    /// use richter::console::Tokenizer;
    ///
    /// # fn main() {
    /// let mut tokenizer = Tokenizer::new("map e1m1");
    /// assert_eq!(tokenizer.next(), Some("map"));
    /// assert_eq!(tokenizer.next(), Some("e1m1"));
    /// assert_eq!(tokenizer.next(), None);
    /// # }
    /// ```
    ///
    /// # Panics
    ///
    /// The function panics if the end of input is reached and there is an unmatched double-quote.
    /// This is not permanent behavior.
    pub fn next(&mut self) -> Option<&'a str> {
        loop {
            // Skip leading whitespace
            self.skip_spaces();

            // If this line is a comment, move on to the next line
            if !self.try_skip_line_comment() {
                break;
            }
        }

        let mut char_indices = self.get_remaining_input().char_indices();
        match char_indices.next() {
            // On encountering an opening double-quote, find the closing double-quote
            Some((start_i, '"')) => {
                let offset = self.byte_offset + start_i;
                match char_indices.skip_while(|&(_, c)| c != '"').next() {
                    Some((end_i, '"')) => {
                        let len = end_i + 1 - start_i;
                        self.byte_offset += len;
                        Some(&self.input[offset..offset + len])
                    }

                    // This case should not be possible
                    Some(_) => None,

                    // This means an unmatched quote.
                    // TODO: this should not panic, make it fail gracefully and update the docs
                    None => panic!("Unmatched quote in Tokenizer::next()"),
                }
            }

            // Any other token ends on the next whitespace character
            Some((start_i, _)) => {
                let offset = self.byte_offset + start_i;

                match char_indices.take_while(|&(_, c)| !c.is_whitespace()).last() {
                    Some((end_i, _)) => {
                        let len = end_i + 1 - start_i;
                        self.byte_offset += len;
                        Some(&self.input[offset..offset + len])
                    }
                    None => None,
                }
            }

            // If there are no characters left, tokenizer is at the end of the input stream
            None => None,
        }
    }
}

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

    #[test]
    fn test_tokenizer_empty() {
        let mut tokenizer = Tokenizer::new("");
        assert_eq!(tokenizer.next(), None);
    }

    #[test]
    fn test_tokenizer_whitespace_only() {
        let mut tokenizer = Tokenizer::new(" \t\n\r");
        assert_eq!(tokenizer.next(), None);
    }

    #[test]
    fn test_tokenizer_comment_only() {
        let mut tokenizer = Tokenizer::new("// this is a comment");
        assert_eq!(tokenizer.next(), None);
    }
}
<|endoftext|>


<commit_before>use std::io::{self, Write};

use Style;
use color::Color;

/// Extension to the `Write` trait.
///
/// This extension to the `Write` trait is capable of producing the correct ANSI escape sequences
/// for given commands, effectively controlling the terminal.
pub trait TermWrite {

    /// Print the CSI (control sequence introducer) followed by a byte string.
    #[inline]
    fn csi(&mut self, b: &[u8]) -> io::Result<usize>;
    /// Print OSC (operating system command) followed by a byte string.
    #[inline]
    fn osc(&mut self, b: &[u8]) -> io::Result<usize>;
    /// Print DSC (device control string) followed by a byte string.
    #[inline]
    fn dsc(&mut self, b: &[u8]) -> io::Result<usize>;

    /// Clear the entire screen.
    #[inline]
    fn clear(&mut self) -> io::Result<usize> {
        self.csi(b"2J")
    }

    /// Clear everything _after_ the cursor.
    #[inline]
    fn clear_after(&mut self) -> io::Result<usize> {
        self.csi(b"J")
    }

    /// Clear everything _before_ the cursor.
    #[inline]
    fn clear_before(&mut self) -> io::Result<usize> {
        self.csi(b"1J")
    }

    /// Clear the current line.
    #[inline]
    fn clear_line(&mut self) -> io::Result<usize> {
        self.csi(b"2K")
    }

    /// Clear from the cursor until newline.
    #[inline]
    fn clear_until_newline(&mut self) -> io::Result<usize> {
        self.csi(b"K")
    }

    /// Show the cursor.
    #[inline]
    fn show_cursor(&mut self) -> io::Result<usize> {
        self.csi(b"?25h")
    }

    /// Hide the cursor.
    #[inline]
    fn hide_cursor(&mut self) -> io::Result<usize> {
        self.csi(b"?25l")
    }

    /// Move the cursor `num` spaces to the left.
    #[inline]
    fn move_cursor_left(&mut self, num: u32) -> io::Result<usize> {
        if num > 0 {
            self.csi(&[b'0' + (num / 10000) as u8,
                       b'0' + (num / 1000) as u8 % 10,
                       b'0' + (num / 100) as u8 % 10,
                       b'0' + (num / 10) as u8 % 10,
                       b'0' + num as u8 % 10,
                       b'D'])
        } else {
            Ok(0)
        }
    }
    /// Move the cursor `num` spaces to the right.
    #[inline]
    fn move_cursor_right(&mut self, num: u32) -> io::Result<usize> {
        if num > 0 {
            self.csi(&[b'0' + (num / 10000) as u8,
                       b'0' + (num / 1000) as u8 % 10,
                       b'0' + (num / 100) as u8 % 10,
                       b'0' + (num / 10) as u8 % 10,
                       b'0' + num as u8 % 10,
                       b'C'])
        } else {
            Ok(0)
        }
    }

    /// Move the cursor `num` spaces up.
    #[inline]
    fn move_cursor_up(&mut self, num: u32) -> io::Result<usize> {
        if num > 0 {
            self.csi(&[b'0' + (num / 10000) as u8,
                       b'0' + (num / 1000) as u8 % 10,
                       b'0' + (num / 100) as u8 % 10,
                       b'0' + (num / 10) as u8 % 10,
                       b'0' + num as u8 % 10,
                       b'A'])
        } else {
            Ok(0)
        }
    }

    /// Move the cursor `num` spaces down.
    #[inline]
    fn move_cursor_down(&mut self, num: u32) -> io::Result<usize> {
        if num > 0 {
            self.csi(&[b'0' + (num / 10000) as u8,
                       b'0' + (num / 1000) as u8 % 10,
                       b'0' + (num / 100) as u8 % 10,
                       b'0' + (num / 10) as u8 % 10,
                       b'0' + num as u8 % 10,
                       b'B'])
        } else {
            Ok(0)
        }
    }

    /// Scroll the window up.
    #[inline]
    fn scroll_up(&mut self, num: u32) -> io::Result<usize> {
        if num > 0 {
            self.csi(&[b'0' + (num / 10000) as u8,
                       b'0' + (num / 1000) as u8 % 10,
                       b'0' + (num / 100) as u8 % 10,
                       b'0' + (num / 10) as u8 % 10,
                       b'0' + num as u8 % 10,
                       b'S'])
        } else {
            Ok(0)
        }
    }

    /// Scroll the window down.
    #[inline]
    fn scroll_down(&mut self, num: u32) -> io::Result<usize> {
        if num > 0 {
            self.csi(&[b'0' + (num / 10000) as u8,
                       b'0' + (num / 1000) as u8 % 10,
                       b'0' + (num / 100) as u8 % 10,
                       b'0' + (num / 10) as u8 % 10,
                       b'0' + num as u8 % 10,
                       b'T'])
        } else {
            Ok(0)
        }
    }

    /// Reset the rendition mode.
    ///
    /// This will reset both the current style and color.
    #[inline]
    fn reset(&mut self) -> io::Result<usize> {
        self.csi(b"m")
    }

    /// Restore the defaults.
    ///
    /// This will reset color, position, cursor state, and so on. It is recommended that you use
    /// this before you exit your program, to avoid messing up the user's terminal.
    #[inline]
    fn restore(&mut self) -> io::Result<usize> {
        Ok(try!(self.reset()) + try!(self.clear()) + try!(self.goto(0, 0)) + try!(self.show_cursor()))
    }

    /// Go to a given position.
    ///
    /// The position is 0-based.
    #[inline]
    fn goto(&mut self, mut x: u16, mut y: u16) -> io::Result<usize> {
        x += 1;
        y += 1;

        self.csi(&[
            b'0' + (y / 10000) as u8,
            b'0' + (y / 1000) as u8 % 10,
            b'0' + (y / 100) as u8 % 10,
            b'0' + (y / 10) as u8 % 10,
            b'0' + y as u8 % 10,
            b';',
            b'0' + (x / 10000) as u8,
            b'0' + (x / 1000) as u8 % 10,
            b'0' + (x / 100) as u8 % 10,
            b'0' + (x / 10) as u8 % 10,
            b'0' + x as u8 % 10,
            b'H',
        ])
    }

    /// Set graphic rendition.
    #[inline]
    fn rendition(&mut self, r: u8) -> io::Result<usize> {
        self.csi(&[
            b'0' + r / 100,
            b'0' + r / 10 % 10,
            b'0' + r % 10,
            b'm',
        ])
    }

    /// Set foreground color.
    #[inline]
    fn color<C: Color>(&mut self, color: C) -> io::Result<usize> {
        let ansi = color.to_ansi_val();
        self.csi(&[
            b'3',
            b'8',
            b';',
            b'5',
            b';',
            b'0' + ansi / 100,
            b'0' + ansi / 10 % 10,
            b'0' + ansi % 10,
            b'm',
        ])
    }

    /// Set background color.
    #[inline]
    fn bg_color<C: Color>(&mut self, color: C) -> io::Result<usize> {
        let ansi = color.to_ansi_val();
        self.csi(&[
            b'4',
            b'8',
            b';',
            b'5',
            b';',
            b'0' + ansi / 100,
            b'0' + ansi / 10 % 10,
            b'0' + ansi % 10,
            b'm',
        ])
    }

    /// Set rendition mode (SGR).
    #[inline]
    fn style(&mut self, mode: Style) -> io::Result<usize> {
        self.rendition(mode as u8)
    }
}

impl<W: Write> TermWrite for W {
    #[inline]
    fn csi(&mut self, b: &[u8]) -> io::Result<usize> {
        Ok(try!(self.write(b"\x1B[")) + try!(self.write(b)))
    }

    #[inline]
    fn osc(&mut self, b: &[u8]) -> io::Result<usize> {
        Ok(try!(self.write(b"\x1B]")) + try!(self.write(b)))
    }

    #[inline]
    fn dsc(&mut self, b: &[u8]) -> io::Result<usize> {
        Ok(try!(self.write(b"\x1BP")) + try!(self.write(b)))
    }
}

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

    #[test]
    fn test_csi() {
        let mut buf = Cursor::new(Vec::new());
        buf.csi(b"bluh").unwrap();

        assert_eq!(buf.get_ref(), b"\x1B[bluh");

        buf.csi(b"blah").unwrap();
        assert_eq!(buf.get_ref(), b"\x1B[bluh\x1B[blah");
    }

    #[test]
    fn test_csi_partial() {
        let mut buf = [0; 3];
        let mut buf = &mut buf[..];
        assert_eq!(buf.csi(b"blu").unwrap(), 3);
        assert_eq!(buf.csi(b"").unwrap(), 0);
        assert_eq!(buf.csi(b"nooooo").unwrap(), 0);
    }

    #[test]
    fn test_osc() {
        let mut buf = Cursor::new(Vec::new());
        buf.osc(b"bluh").unwrap();

        assert_eq!(buf.get_ref(), b"\x1B]bluh");

        buf.osc(b"blah").unwrap();
        assert_eq!(buf.get_ref(), b"\x1B]bluh\x1B]blah");
    }

    #[test]
    fn test_osc_partial() {
        let mut buf = [0; 3];
        let mut buf = &mut buf[..];
        assert_eq!(buf.osc(b"blu").unwrap(), 3);
        assert_eq!(buf.osc(b"").unwrap(), 0);
        assert_eq!(buf.osc(b"nooooo").unwrap(), 0);
    }

    #[test]
    fn test_dsc() {
        let mut buf = Cursor::new(Vec::new());
        buf.dsc(b"bluh").unwrap();

        assert_eq!(buf.get_ref(), b"\x1BPbluh");

        buf.dsc(b"blah").unwrap();
        assert_eq!(buf.get_ref(), b"\x1BPbluh\x1BPblah");
    }

    #[test]
    fn test_dsc_partial() {
        let mut buf = [0; 3];
        let mut buf = &mut buf[..];
        assert_eq!(buf.dsc(b"blu").unwrap(), 3);
        assert_eq!(buf.dsc(b"").unwrap(), 0);
        assert_eq!(buf.dsc(b"nooooo").unwrap(), 0);
    }

    #[test]
    fn test_clear() {
        let mut buf = Cursor::new(Vec::new());
        buf.clear().unwrap();
        assert_eq!(buf.get_ref(), b"\x1B[2J");
        buf.clear().unwrap();
        assert_eq!(buf.get_ref(), b"\x1B[2J\x1B[2J");
    }

    #[test]
    fn test_goto() {
        let mut buf = Cursor::new(Vec::new());
        buf.goto(34, 43).unwrap();
        assert_eq!(buf.get_ref(), b"\x1B[00044;00035H");
        buf.goto(24, 45).unwrap();
        assert_eq!(buf.get_ref(), b"\x1B[00044;00035H\x1B[00046;00025H");
    }

    #[test]
    fn test_style() {
        use Style;

        let mut buf = Cursor::new(Vec::new());
        buf.style(Style::Bold).unwrap();
        assert_eq!(buf.get_ref(), b"\x1B[001m");
        buf.style(Style::Italic).unwrap();
        assert_eq!(buf.get_ref(), b"\x1B[001m\x1B[003m");
    }
}
<commit_msg>Improve function documentation<commit_after>use std::io::{self, Write};

use Style;
use color::Color;

/// Extension to the `Write` trait.
///
/// This extension to the `Write` trait is capable of producing the correct ANSI escape sequences
/// for given commands, effectively controlling the terminal.
pub trait TermWrite {

    /// Print the CSI (control sequence introducer) followed by a byte string.
    #[inline]
    fn csi(&mut self, b: &[u8]) -> io::Result<usize>;
    /// Print OSC (operating system command) followed by a byte string.
    #[inline]
    fn osc(&mut self, b: &[u8]) -> io::Result<usize>;
    /// Print DSC (device control string) followed by a byte string.
    #[inline]
    fn dsc(&mut self, b: &[u8]) -> io::Result<usize>;

    /// Clear the entire screen.
    #[inline]
    fn clear(&mut self) -> io::Result<usize> {
        self.csi(b"2J")
    }

    /// Clear everything _after_ the cursor.
    #[inline]
    fn clear_after(&mut self) -> io::Result<usize> {
        self.csi(b"J")
    }

    /// Clear everything _before_ the cursor.
    #[inline]
    fn clear_before(&mut self) -> io::Result<usize> {
        self.csi(b"1J")
    }

    /// Clear the current line.
    #[inline]
    fn clear_line(&mut self) -> io::Result<usize> {
        self.csi(b"2K")
    }

    /// Clear from the cursor until newline.
    #[inline]
    fn clear_until_newline(&mut self) -> io::Result<usize> {
        self.csi(b"K")
    }

    /// Show the cursor.
    #[inline]
    fn show_cursor(&mut self) -> io::Result<usize> {
        self.csi(b"?25h")
    }

    /// Hide the cursor.
    #[inline]
    fn hide_cursor(&mut self) -> io::Result<usize> {
        self.csi(b"?25l")
    }

    /// Move the cursor `num` spaces to the left.
    #[inline]
    fn move_cursor_left(&mut self, num: u32) -> io::Result<usize> {
        if num > 0 {
            self.csi(&[b'0' + (num / 10000) as u8,
                       b'0' + (num / 1000) as u8 % 10,
                       b'0' + (num / 100) as u8 % 10,
                       b'0' + (num / 10) as u8 % 10,
                       b'0' + num as u8 % 10,
                       b'D'])
        } else {
            Ok(0)
        }
    }
    /// Move the cursor `num` spaces to the right.
    #[inline]
    fn move_cursor_right(&mut self, num: u32) -> io::Result<usize> {
        if num > 0 {
            self.csi(&[b'0' + (num / 10000) as u8,
                       b'0' + (num / 1000) as u8 % 10,
                       b'0' + (num / 100) as u8 % 10,
                       b'0' + (num / 10) as u8 % 10,
                       b'0' + num as u8 % 10,
                       b'C'])
        } else {
            Ok(0)
        }
    }

    /// Move the cursor `num` spaces up.
    #[inline]
    fn move_cursor_up(&mut self, num: u32) -> io::Result<usize> {
        if num > 0 {
            self.csi(&[b'0' + (num / 10000) as u8,
                       b'0' + (num / 1000) as u8 % 10,
                       b'0' + (num / 100) as u8 % 10,
                       b'0' + (num / 10) as u8 % 10,
                       b'0' + num as u8 % 10,
                       b'A'])
        } else {
            Ok(0)
        }
    }

    /// Move the cursor `num` spaces down.
    #[inline]
    fn move_cursor_down(&mut self, num: u32) -> io::Result<usize> {
        if num > 0 {
            self.csi(&[b'0' + (num / 10000) as u8,
                       b'0' + (num / 1000) as u8 % 10,
                       b'0' + (num / 100) as u8 % 10,
                       b'0' + (num / 10) as u8 % 10,
                       b'0' + num as u8 % 10,
                       b'B'])
        } else {
            Ok(0)
        }
    }

    /// Scroll the window `num` spaces up.
    #[inline]
    fn scroll_up(&mut self, num: u32) -> io::Result<usize> {
        if num > 0 {
            self.csi(&[b'0' + (num / 10000) as u8,
                       b'0' + (num / 1000) as u8 % 10,
                       b'0' + (num / 100) as u8 % 10,
                       b'0' + (num / 10) as u8 % 10,
                       b'0' + num as u8 % 10,
                       b'S'])
        } else {
            Ok(0)
        }
    }

    /// Scroll the window `num` spaces down.
    #[inline]
    fn scroll_down(&mut self, num: u32) -> io::Result<usize> {
        if num > 0 {
            self.csi(&[b'0' + (num / 10000) as u8,
                       b'0' + (num / 1000) as u8 % 10,
                       b'0' + (num / 100) as u8 % 10,
                       b'0' + (num / 10) as u8 % 10,
                       b'0' + num as u8 % 10,
                       b'T'])
        } else {
            Ok(0)
        }
    }

    /// Reset the rendition mode.
    ///
    /// This will reset both the current style and color.
    #[inline]
    fn reset(&mut self) -> io::Result<usize> {
        self.csi(b"m")
    }

    /// Restore the defaults.
    ///
    /// This will reset color, position, cursor state, and so on. It is recommended that you use
    /// this before you exit your program, to avoid messing up the user's terminal.
    #[inline]
    fn restore(&mut self) -> io::Result<usize> {
        Ok(try!(self.reset()) + try!(self.clear()) + try!(self.goto(0, 0)) + try!(self.show_cursor()))
    }

    /// Go to a given position.
    ///
    /// The position is 0-based.
    #[inline]
    fn goto(&mut self, mut x: u16, mut y: u16) -> io::Result<usize> {
        x += 1;
        y += 1;

        self.csi(&[
            b'0' + (y / 10000) as u8,
            b'0' + (y / 1000) as u8 % 10,
            b'0' + (y / 100) as u8 % 10,
            b'0' + (y / 10) as u8 % 10,
            b'0' + y as u8 % 10,
            b';',
            b'0' + (x / 10000) as u8,
            b'0' + (x / 1000) as u8 % 10,
            b'0' + (x / 100) as u8 % 10,
            b'0' + (x / 10) as u8 % 10,
            b'0' + x as u8 % 10,
            b'H',
        ])
    }

    /// Set graphic rendition.
    #[inline]
    fn rendition(&mut self, r: u8) -> io::Result<usize> {
        self.csi(&[
            b'0' + r / 100,
            b'0' + r / 10 % 10,
            b'0' + r % 10,
            b'm',
        ])
    }

    /// Set foreground color.
    #[inline]
    fn color<C: Color>(&mut self, color: C) -> io::Result<usize> {
        let ansi = color.to_ansi_val();
        self.csi(&[
            b'3',
            b'8',
            b';',
            b'5',
            b';',
            b'0' + ansi / 100,
            b'0' + ansi / 10 % 10,
            b'0' + ansi % 10,
            b'm',
        ])
    }

    /// Set background color.
    #[inline]
    fn bg_color<C: Color>(&mut self, color: C) -> io::Result<usize> {
        let ansi = color.to_ansi_val();
        self.csi(&[
            b'4',
            b'8',
            b';',
            b'5',
            b';',
            b'0' + ansi / 100,
            b'0' + ansi / 10 % 10,
            b'0' + ansi % 10,
            b'm',
        ])
    }

    /// Set rendition mode (SGR).
    #[inline]
    fn style(&mut self, mode: Style) -> io::Result<usize> {
        self.rendition(mode as u8)
    }
}

impl<W: Write> TermWrite for W {
    #[inline]
    fn csi(&mut self, b: &[u8]) -> io::Result<usize> {
        Ok(try!(self.write(b"\x1B[")) + try!(self.write(b)))
    }

    #[inline]
    fn osc(&mut self, b: &[u8]) -> io::Result<usize> {
        Ok(try!(self.write(b"\x1B]")) + try!(self.write(b)))
    }

    #[inline]
    fn dsc(&mut self, b: &[u8]) -> io::Result<usize> {
        Ok(try!(self.write(b"\x1BP")) + try!(self.write(b)))
    }
}

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

    #[test]
    fn test_csi() {
        let mut buf = Cursor::new(Vec::new());
        buf.csi(b"bluh").unwrap();

        assert_eq!(buf.get_ref(), b"\x1B[bluh");

        buf.csi(b"blah").unwrap();
        assert_eq!(buf.get_ref(), b"\x1B[bluh\x1B[blah");
    }

    #[test]
    fn test_csi_partial() {
        let mut buf = [0; 3];
        let mut buf = &mut buf[..];
        assert_eq!(buf.csi(b"blu").unwrap(), 3);
        assert_eq!(buf.csi(b"").unwrap(), 0);
        assert_eq!(buf.csi(b"nooooo").unwrap(), 0);
    }

    #[test]
    fn test_osc() {
        let mut buf = Cursor::new(Vec::new());
        buf.osc(b"bluh").unwrap();

        assert_eq!(buf.get_ref(), b"\x1B]bluh");

        buf.osc(b"blah").unwrap();
        assert_eq!(buf.get_ref(), b"\x1B]bluh\x1B]blah");
    }

    #[test]
    fn test_osc_partial() {
        let mut buf = [0; 3];
        let mut buf = &mut buf[..];
        assert_eq!(buf.osc(b"blu").unwrap(), 3);
        assert_eq!(buf.osc(b"").unwrap(), 0);
        assert_eq!(buf.osc(b"nooooo").unwrap(), 0);
    }

    #[test]
    fn test_dsc() {
        let mut buf = Cursor::new(Vec::new());
        buf.dsc(b"bluh").unwrap();

        assert_eq!(buf.get_ref(), b"\x1BPbluh");

        buf.dsc(b"blah").unwrap();
        assert_eq!(buf.get_ref(), b"\x1BPbluh\x1BPblah");
    }

    #[test]
    fn test_dsc_partial() {
        let mut buf = [0; 3];
        let mut buf = &mut buf[..];
        assert_eq!(buf.dsc(b"blu").unwrap(), 3);
        assert_eq!(buf.dsc(b"").unwrap(), 0);
        assert_eq!(buf.dsc(b"nooooo").unwrap(), 0);
    }

    #[test]
    fn test_clear() {
        let mut buf = Cursor::new(Vec::new());
        buf.clear().unwrap();
        assert_eq!(buf.get_ref(), b"\x1B[2J");
        buf.clear().unwrap();
        assert_eq!(buf.get_ref(), b"\x1B[2J\x1B[2J");
    }

    #[test]
    fn test_goto() {
        let mut buf = Cursor::new(Vec::new());
        buf.goto(34, 43).unwrap();
        assert_eq!(buf.get_ref(), b"\x1B[00044;00035H");
        buf.goto(24, 45).unwrap();
        assert_eq!(buf.get_ref(), b"\x1B[00044;00035H\x1B[00046;00025H");
    }

    #[test]
    fn test_style() {
        use Style;

        let mut buf = Cursor::new(Vec::new());
        buf.style(Style::Bold).unwrap();
        assert_eq!(buf.get_ref(), b"\x1B[001m");
        buf.style(Style::Italic).unwrap();
        assert_eq!(buf.get_ref(), b"\x1B[001m\x1B[003m");
    }
}
<|endoftext|>