<commit_before>use voodoo::color::ColorValue;
use voodoo::window::{FormattedString, Point, Window};

use program::{Ability, Program};

pub struct InfoView {
    window: Window,
    ability_list: Vec<(String, Ability)>,
    selected_ability: Option<(usize, Ability)>,
}

impl InfoView {
    pub fn new(window: Window) -> InfoView {
        InfoView {
            window: window,
            ability_list: Vec::new(),
            selected_ability: None,
        }
    }

    pub fn from_global_frame(&self, p: Point) -> Option<Point> {
        self.window.position.from_global_frame(p)
    }

    pub fn refresh(&mut self, stdout: &mut ::std::io::Stdout) {
        self.window.refresh(stdout);
    }

    pub fn clear(&mut self) {
        self.ability_list.clear();
        self.selected_ability = None;
        self.window.clear();
        self.window.border();
    }

    pub fn display_abilities(&mut self) {
        let mut y = 5;
        for (ability_number, &(ref name, _)) in self.ability_list.iter().enumerate() {
            let mut f: FormattedString = name.into();
            f.bg = if let Some((offset, _)) = self.selected_ability {
                if offset == ability_number {
                    Some(ColorValue::Red)
                }
                else {
                    Some(ColorValue::Magenta)
                }
            } else { Some(ColorValue::Magenta) };
            self.window.print_at(Point::new(2, y), f);
            y += 1;
        }
    }

    // TODO: take a ProgramRef and store it (maybe a weak reference)
    pub fn display_program(&mut self, program: &Program) {
        self.window.print_at(Point::new(2, 2), &program.name);

        self.window.print_at(Point::new(2, 4), "Abilities:");
        self.ability_list.extend(program.abilities.iter().cloned());

        self.display_abilities();
    }

    // TODO: return the ability range or something? Ability descriptor
    pub fn translate_click(&mut self, click: Point) -> Option<Ability> {
        for (offset, &(_, ability)) in self.ability_list.iter().enumerate() {
            if click.y == 5 + offset as u16 {
                self.selected_ability = Some((offset, ability));
                break;
            }
        }
        self.display_abilities();

        self.selected_ability.map(|(_, ability)| ability)
    }

    pub fn clear_ability(&mut self) {
        self.selected_ability = None;
        self.display_abilities();
    }
}
<commit_msg>feat: show end turn button<commit_after>use voodoo::color::ColorValue;
use voodoo::window::{FormattedString, Point, Window};

use program::{Ability, Program};

pub struct InfoView {
    window: Window,
    ability_list: Vec<(String, Ability)>,
    selected_ability: Option<(usize, Ability)>,
}

impl InfoView {
    pub fn new(mut window: Window) -> InfoView {
        let mut f: FormattedString = "     End Turn     ".into();
        f.bg = Some(ColorValue::Magenta);
        window.print_at(Point::new(2, 23), f);

        InfoView {
            window: window,
            ability_list: Vec::new(),
            selected_ability: None,
        }
    }

    pub fn from_global_frame(&self, p: Point) -> Option<Point> {
        self.window.position.from_global_frame(p)
    }

    pub fn refresh(&mut self, stdout: &mut ::std::io::Stdout) {
        self.window.refresh(stdout);
    }

    pub fn clear(&mut self) {
        self.ability_list.clear();
        self.selected_ability = None;
        self.window.clear();
        self.window.border();
    }

    pub fn display_abilities(&mut self) {
        let mut y = 5;
        for (ability_number, &(ref name, _)) in self.ability_list.iter().enumerate() {
            let mut f: FormattedString = name.into();
            f.bg = if let Some((offset, _)) = self.selected_ability {
                if offset == ability_number {
                    Some(ColorValue::Red)
                }
                else {
                    Some(ColorValue::Magenta)
                }
            } else { Some(ColorValue::Magenta) };
            self.window.print_at(Point::new(2, y), f);
            y += 1;
        }
    }

    // TODO: take a ProgramRef and store it (maybe a weak reference)
    pub fn display_program(&mut self, program: &Program) {
        self.window.print_at(Point::new(2, 2), &program.name);

        self.window.print_at(Point::new(2, 4), "Abilities:");
        self.ability_list.extend(program.abilities.iter().cloned());

        self.display_abilities();
    }

    // TODO: return the ability range or something? Ability descriptor
    pub fn translate_click(&mut self, click: Point) -> Option<Ability> {
        for (offset, &(_, ability)) in self.ability_list.iter().enumerate() {
            if click.y == 5 + offset as u16 {
                self.selected_ability = Some((offset, ability));
                break;
            }
        }
        self.display_abilities();

        self.selected_ability.map(|(_, ability)| ability)
    }

    pub fn clear_ability(&mut self) {
        self.selected_ability = None;
        self.display_abilities();
    }
}
<|endoftext|>


<commit_before>//! decrypt TLS data using rustls on a async-std TcpStream

use std::io::{Error as IoError, ErrorKind as IoErrorKind, Read as SyncRead, Write as SyncWrite};
use std::pin::Pin;
use std::sync::Arc;
use std::vec::Vec;

use async_std::io::{Read, Result as IoResult, Write};
use async_std::net::TcpStream;
use async_std::prelude::*;
use async_std::task::{Context, Poll};
use rustls::{ClientConfig, ClientSession, ProtocolVersion, Session};
use webpki::DNSNameRef;
use webpki_roots;

use super::constants;
use super::errors::CabotError;
use super::results::CabotResult;

fn create_config() -> Arc<ClientConfig> {
    let mut config = ClientConfig::new();
    config
        .root_store
        .add_server_trust_anchors(&webpki_roots::TLS_SERVER_ROOTS);
    let rc_config = Arc::new(config);
    rc_config
}

fn create_client(host: &str) -> CabotResult<ClientSession> {
    let rc_config = create_config();
    let host = DNSNameRef::try_from_ascii_str(host)
        .map_err(|_| CabotError::HostnameParseError(host.to_string()))?;
    let tlsclient = ClientSession::new(&rc_config, host);
    Ok(tlsclient)
}

/// Stream to read from a ciphered TcpStream and cipher data before
/// writing the that TcpStream.
/// Use it like a TcpStream, but call the starttls after calling new.
pub struct TLSStream<'a> {
    tcpstream: &'a mut TcpStream,
    tlsclient: ClientSession,
}

impl<'a> TLSStream<'a> {
    /// Create new TLSStream instance
    pub fn new(tcpstream: &'a mut TcpStream, host: &str) -> CabotResult<Self> {
        Ok(TLSStream {
            tcpstream,
            tlsclient: create_client(host)?,
        })
    }
    /// Call it just after creating the sream
    pub async fn starttls(&mut self) -> CabotResult<()> {
        let mut buf_tlswrite: Vec<u8> = Vec::new();
        let mut read_buf: [u8; constants::BUFFER_PAGE_SIZE] = [0; constants::BUFFER_PAGE_SIZE];
        let mut is_handshaking = true;
        while is_handshaking {
            while self.tlsclient.wants_write() {
                let count = self.write(&mut buf_tlswrite).await?;
                debug!("Write {} TLS bytes during hangshake", count);
            }
            if self.tlsclient.wants_read() {
                let count = self.read(&mut read_buf).await?;
                debug!("Read {} TLS bytes during hangshake", count);
            }
            if is_handshaking && !self.tlsclient.is_handshaking() {
                info!("Handshake complete");
                is_handshaking = false;
                let protocol = self.tlsclient.get_protocol_version();
                match protocol {
                    Some(ProtocolVersion::SSLv2) => {
                        info!("Protocol SSL v2 negociated");
                    }
                    Some(ProtocolVersion::SSLv3) => {
                        info!("Protocol SSL v3 negociated");
                    }
                    Some(ProtocolVersion::TLSv1_0) => {
                        info!("Protocol TLS v1.0 negociated");
                    }
                    Some(ProtocolVersion::TLSv1_1) => {
                        info!("Protocol TLS v1.1 negociated");
                    }
                    Some(ProtocolVersion::TLSv1_2) => {
                        info!("Protocol TLS v1.2 negociated");
                    }
                    Some(ProtocolVersion::TLSv1_3) => {
                        info!("Protocol TLS v1.3 negociated");
                    }
                    Some(ProtocolVersion::Unknown(num)) => {
                        info!("Unknown TLS Protocol negociated: {}", num);
                    }
                    None => {
                        info!("No TLS Protocol negociated");
                    }
                }
            }
        }
        Ok(())
    }
}

impl<'a> Read for TLSStream<'a> {
    fn poll_read(
        self: Pin<&mut Self>,
        cx: &mut Context<'_>,
        buf: &mut [u8],
    ) -> Poll<IoResult<usize>> {
        let self_ = Pin::get_mut(self);

        let cnt = self_.tlsclient.read(&mut buf[..]).unwrap();
        if cnt > 0 {
            debug!("Remaining {} Unencrypted bytes", cnt);
            return Poll::Ready(Ok(cnt));
        }

        let mut tcp_buf: [u8; constants::BUFFER_PAGE_SIZE] = [0; constants::BUFFER_PAGE_SIZE];

        let count =
            futures_core::ready!(Pin::new(&mut self_.tcpstream).poll_read(cx, &mut tcp_buf[..]));

        match count {
            Err(err) => Poll::Ready(Err(err)),
            Ok(n) => {
                if n > 0 {
                    debug!("Read {} TCP bytes", n);
                    let count = self_.tlsclient.read_tls(&mut &tcp_buf[..n])?;
                    debug!("Decode {} TLS bytes", count);

                    let packets = self_.tlsclient.process_new_packets();
                    match packets {
                        Ok(_) => {
                            let cnt = self_.tlsclient.read(&mut buf[..]).unwrap();
                            debug!("Read {} Unencrypted bytes", cnt);
                            Poll::Ready(Ok(cnt))
                        }
                        Err(err) => Poll::Ready(Err(IoError::new(
                            IoErrorKind::InvalidData,
                            format!("{:?}", err),
                        ))),
                    }
                } else {
                    Poll::Pending
                }
            }
        }
    }
}

impl<'a> Write for TLSStream<'a> {
    fn poll_write(self: Pin<&mut Self>, cx: &mut Context, buf: &[u8]) -> Poll<IoResult<usize>> {
        let self_ = Pin::get_mut(self);
        let mut buf_tlswrite: Vec<u8> = Vec::with_capacity(constants::BUFFER_PAGE_SIZE);

        let ret = buf.len();

        self_.tlsclient.write_all(&buf)?;
        debug!("Write {} TLS Clear bytes", ret);
        while self_.tlsclient.wants_write() {
            let count = self_.tlsclient.write_tls(&mut buf_tlswrite)?;
            debug!("Write {} TLS Ciphered bytes", count);
        }
        let stream = Pin::new(&mut *self_.tcpstream);
        let count = futures_core::ready!(stream.poll_write(cx, buf_tlswrite.as_slice()))?;
        buf_tlswrite.clear();
        debug!("Write {} TLS Ciphered bytes", count);
        Poll::Ready(Ok(ret))
    }

    fn poll_flush(self: Pin<&mut Self>, cx: &mut Context) -> Poll<IoResult<()>> {
        let self_ = Pin::get_mut(self);
        Pin::new(&mut *self_.tcpstream).poll_flush(cx)
    }

    fn poll_close(self: Pin<&mut Self>, cx: &mut Context) -> Poll<IoResult<()>> {
        let self_ = Pin::get_mut(self);
        Pin::new(&mut *self_.tcpstream).poll_close(cx)
    }
}
<commit_msg>Do not send Pending when reading 0 bytes<commit_after>//! decrypt TLS data using rustls on a async-std TcpStream

use std::io::{Error as IoError, ErrorKind as IoErrorKind, Read as SyncRead, Write as SyncWrite};
use std::pin::Pin;
use std::sync::Arc;
use std::vec::Vec;

use async_std::io::{Read, Result as IoResult, Write};
use async_std::net::TcpStream;
use async_std::prelude::*;
use async_std::task::{Context, Poll};
use rustls::{ClientConfig, ClientSession, ProtocolVersion, Session};
use webpki::DNSNameRef;
use webpki_roots;

use super::constants;
use super::errors::CabotError;
use super::results::CabotResult;

fn create_config() -> Arc<ClientConfig> {
    let mut config = ClientConfig::new();
    config
        .root_store
        .add_server_trust_anchors(&webpki_roots::TLS_SERVER_ROOTS);
    let rc_config = Arc::new(config);
    rc_config
}

fn create_client(host: &str) -> CabotResult<ClientSession> {
    let rc_config = create_config();
    let host = DNSNameRef::try_from_ascii_str(host)
        .map_err(|_| CabotError::HostnameParseError(host.to_string()))?;
    let tlsclient = ClientSession::new(&rc_config, host);
    Ok(tlsclient)
}

/// Stream to read from a ciphered TcpStream and cipher data before
/// writing the that TcpStream.
/// Use it like a TcpStream, but call the starttls after calling new.
pub struct TLSStream<'a> {
    tcpstream: &'a mut TcpStream,
    tlsclient: ClientSession,
}

impl<'a> TLSStream<'a> {
    /// Create new TLSStream instance
    pub fn new(tcpstream: &'a mut TcpStream, host: &str) -> CabotResult<Self> {
        Ok(TLSStream {
            tcpstream,
            tlsclient: create_client(host)?,
        })
    }
    /// Call it just after creating the sream
    pub async fn starttls(&mut self) -> CabotResult<()> {
        let mut buf_tlswrite: Vec<u8> = Vec::new();
        let mut read_buf: [u8; constants::BUFFER_PAGE_SIZE] = [0; constants::BUFFER_PAGE_SIZE];
        let mut is_handshaking = true;
        while is_handshaking {
            while self.tlsclient.wants_write() {
                let count = self.write(&mut buf_tlswrite).await?;
                debug!("Write {} TLS bytes during hangshake", count);
            }
            if self.tlsclient.wants_read() {
                let count = self.read(&mut read_buf).await?;
                debug!("Read {} TLS bytes during hangshake", count);
            }
            if is_handshaking && !self.tlsclient.is_handshaking() {
                info!("Handshake complete");
                is_handshaking = false;
                let protocol = self.tlsclient.get_protocol_version();
                match protocol {
                    Some(ProtocolVersion::SSLv2) => {
                        info!("Protocol SSL v2 negociated");
                    }
                    Some(ProtocolVersion::SSLv3) => {
                        info!("Protocol SSL v3 negociated");
                    }
                    Some(ProtocolVersion::TLSv1_0) => {
                        info!("Protocol TLS v1.0 negociated");
                    }
                    Some(ProtocolVersion::TLSv1_1) => {
                        info!("Protocol TLS v1.1 negociated");
                    }
                    Some(ProtocolVersion::TLSv1_2) => {
                        info!("Protocol TLS v1.2 negociated");
                    }
                    Some(ProtocolVersion::TLSv1_3) => {
                        info!("Protocol TLS v1.3 negociated");
                    }
                    Some(ProtocolVersion::Unknown(num)) => {
                        info!("Unknown TLS Protocol negociated: {}", num);
                    }
                    None => {
                        info!("No TLS Protocol negociated");
                    }
                }
            }
        }
        Ok(())
    }
}

impl<'a> Read for TLSStream<'a> {
    fn poll_read(
        self: Pin<&mut Self>,
        cx: &mut Context<'_>,
        buf: &mut [u8],
    ) -> Poll<IoResult<usize>> {
        let self_ = Pin::get_mut(self);

        let cnt = self_.tlsclient.read(&mut buf[..]).unwrap();
        if cnt > 0 {
            debug!("Remaining {} Unencrypted bytes", cnt);
            return Poll::Ready(Ok(cnt));
        }

        let mut tcp_buf: [u8; constants::BUFFER_PAGE_SIZE] = [0; constants::BUFFER_PAGE_SIZE];

        let count =
            futures_core::ready!(Pin::new(&mut self_.tcpstream).poll_read(cx, &mut tcp_buf[..]));

        match count {
            Err(err) => {
                error!("Received an error {:?}", err);
                Poll::Ready(Err(err))
            }
            Ok(n) => {
                debug!("Read {} TCP bytes", n);
                let count = self_.tlsclient.read_tls(&mut &tcp_buf[..n])?;
                debug!("Decode {} TLS bytes", count);

                let packets = self_.tlsclient.process_new_packets();
                match packets {
                    Ok(_) => {
                        let cnt = self_.tlsclient.read(&mut buf[..]).unwrap();
                        debug!("Read {} Unencrypted bytes", cnt);
                        Poll::Ready(Ok(cnt))
                    }
                    Err(err) => Poll::Ready(Err(IoError::new(
                        IoErrorKind::InvalidData,
                        format!("{:?}", err),
                    ))),
                }
            }
        }
    }
}

impl<'a> Write for TLSStream<'a> {
    fn poll_write(self: Pin<&mut Self>, cx: &mut Context, buf: &[u8]) -> Poll<IoResult<usize>> {
        let self_ = Pin::get_mut(self);
        let mut buf_tlswrite: Vec<u8> = Vec::with_capacity(constants::BUFFER_PAGE_SIZE);

        let ret = buf.len();

        self_.tlsclient.write_all(&buf)?;
        debug!("Write {} TLS Clear bytes", ret);
        while self_.tlsclient.wants_write() {
            let count = self_.tlsclient.write_tls(&mut buf_tlswrite)?;
            debug!("Write {} TLS Ciphered bytes", count);
        }
        let stream = Pin::new(&mut *self_.tcpstream);
        let count = futures_core::ready!(stream.poll_write(cx, buf_tlswrite.as_slice()))?;
        buf_tlswrite.clear();
        debug!("Write {} TLS Ciphered bytes", count);
        Poll::Ready(Ok(ret))
    }

    fn poll_flush(self: Pin<&mut Self>, cx: &mut Context) -> Poll<IoResult<()>> {
        let self_ = Pin::get_mut(self);
        Pin::new(&mut *self_.tcpstream).poll_flush(cx)
    }

    fn poll_close(self: Pin<&mut Self>, cx: &mut Context) -> Poll<IoResult<()>> {
        let self_ = Pin::get_mut(self);
        Pin::new(&mut *self_.tcpstream).poll_close(cx)
    }
}
<|endoftext|>


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

use num::bigint;

pub const DEFAULT_INDENT_WIDTH: usize = 2;
pub const DEFAULT_MAX_TERMS_PER_LINE: usize = 4;

#[derive(Debug, PartialEq)]
pub enum BertTerm {
    Nil,
    Int(i32),
    BigInt(bigint::BigInt),
    Float(f64),
    Atom(String),
    Tuple(Vec<BertTerm>),
    List(Vec<BertTerm>),
    Map(Vec<BertTerm>, Vec<BertTerm>),
    String(Vec<u8>),
    Binary(Vec<u8>)
}

impl BertTerm {
    fn is_basic(&self) -> bool {
        match *self {
            BertTerm::Int(_)
            | BertTerm::BigInt(_)
            | BertTerm::Float(_)
            | BertTerm::Atom(_)
            | BertTerm::String(_)
            | BertTerm::Binary(_)
            | BertTerm::Nil => true,
            BertTerm::List(_)
            | BertTerm::Tuple(_)
            | BertTerm::Map(_, _) => false
        }
    }

    fn is_proplist(&self) -> bool {
        match *self {
            BertTerm::List(ref elems) => elems.iter().all(|e| e.is_proplist_entry()),
            _ => false
        }
    }

    fn is_proplist_entry(&self) -> bool {
        match *self {
            BertTerm::Tuple(ref elems) =>
                elems.len() == 2 && elems[0].is_basic(),
            _ => false
        }
    }
}

impl fmt::Display for BertTerm {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let pp = PrettyPrinter::new(self,
                                    DEFAULT_INDENT_WIDTH,
                                    DEFAULT_MAX_TERMS_PER_LINE);
        write!(f, "{}", pp)
    }
}

/// Outputs a vector of BertTerms to stdout.
pub fn pp_bert(terms: Vec<BertTerm>, indent_width: usize, terms_per_line: usize) {
    for t in terms {
        let pp = PrettyPrinter::new(&t, indent_width, terms_per_line);
        println!("{}", pp);
    }
}

/// Outputs a BertTerm as JSON to stdout.
pub fn pp_json(terms: Vec<BertTerm>, _: usize, _: usize) {
    for t in terms {
        let pp = JsonPrettyPrinter { term: &t, transform_proplists: false };
        println!("{}", pp);
    }
}

/// Outputs a BertTerm as JSON to stdout;
/// Erlang proplists are converted to JSON objects.
pub fn pp_json_proplist(terms: Vec<BertTerm>, _: usize, _: usize) {
    for t in terms {
        let pp = JsonPrettyPrinter { term: &t, transform_proplists: true };
        println!("{}", pp);
    }
}

pub struct PrettyPrinter<'a> {
    term: &'a BertTerm,
    indent_width: usize,
    max_terms_per_line: usize
}

impl <'a> fmt::Display for PrettyPrinter<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.write_term(self.term, f, 0)
    }
}

impl <'a> PrettyPrinter<'a> {
    /// Creates a pretty printer for `term` where sub-terms
    /// are indented with a width of `indent_width` and a
    /// maximum of `max_terms_per_line` basic terms (i.e.,
    /// integers, floats, strings) can be printed per line.
    pub fn new(term: &'a BertTerm,
               indent_width: usize,
               max_terms_per_line: usize) -> Self {
        PrettyPrinter { term, indent_width, max_terms_per_line }
    }

    fn write_term(&self, term: &BertTerm, f: &mut fmt::Formatter, depth: usize) -> fmt::Result {
        match *term {
            BertTerm::Nil => f.write_str("[]"),
            BertTerm::Int(n) => write!(f, "{}", n),
            BertTerm::BigInt(ref n) => write!(f, "{}", n),
            BertTerm::Float(x) => write!(f, "{}", x),
            BertTerm::Atom(ref s) => f.write_str(s),
            BertTerm::String(ref bytes) => self.write_string(bytes, f, "\"", "\""),
            BertTerm::Binary(ref bytes) => self.write_string(bytes, f, "<<\"", "\">>"),
            BertTerm::List(ref terms) => self.write_collection(terms, f, depth, '[', ']'),
            BertTerm::Tuple(ref terms) => self.write_collection(terms, f, depth, '{', '}'),
            BertTerm::Map(ref keys, ref vals) => self.write_map(keys, vals, f, depth)
        }
    }

    fn write_string(&self,
                    bytes: &[u8],
                    f: &mut fmt::Formatter,
                    open: &str,
                    close: &str) -> fmt::Result {
        f.write_str(open)?;
        for &b in bytes {
            if is_printable(b) {
                f.write_char(b as char)?;
            } else {
                write!(f, "\\x{:02x}", b)?;
            }
        }
        f.write_str(close)
    }

    fn write_collection(&self,
                        terms: &[BertTerm],
                        f: &mut fmt::Formatter,
                        depth: usize,
                        open: char,
                        close: char) -> fmt::Result {
        let multi_line = !self.is_small_collection(terms);

        // Every element will have the same indentation,
        // so pre-compute it once.
        let prefix =
            if multi_line {
                self.indentation(depth+1)
            } else {
                String::new()
            };

        f.write_char(open)?;
        let mut comma = "";
        for t in terms {
            f.write_str(comma)?;
            f.write_str(&prefix)?;
            self.write_term(t, f, depth + 1)?;
            comma = ", ";
        }

        if multi_line {
            f.write_str(&self.indentation(depth))?;
        }

        f.write_char(close)
    }

    fn write_map(&self,
                 keys: &[BertTerm],
                 vals: &[BertTerm],
                 f: &mut fmt::Formatter,
                 depth: usize) -> fmt::Result {
        let multi_line =
            !self.is_small_collection(keys) || !self.is_small_collection(vals);
        let prefix =
            if multi_line {
                self.indentation(depth+1)
            } else {
                String::new()
            };

        f.write_str("#{")?;
        let mut comma = "";
        for i in 0 .. keys.len() {
            f.write_str(comma)?;
            f.write_str(&prefix)?;
            self.write_term(&keys[i], f, depth + 1)?;
            f.write_str(" => ")?;
            self.write_term(&vals[i], f, depth + 1)?;
            comma = ", ";
        }

        if multi_line {
            f.write_str(&self.indentation(depth))?;
        }
        f.write_str("}")
    }

    fn is_small_collection(&self, terms: &[BertTerm]) -> bool {
        terms.len() <= self.max_terms_per_line &&
            terms.iter().all(BertTerm::is_basic)
    }

    fn indentation(&self, depth: usize) -> String {
        ::std::iter::once('\n')
            .chain((0 .. depth * self.indent_width).map(|_| ' '))
            .collect()
    }
}

pub struct JsonPrettyPrinter<'a> {
    term: &'a BertTerm,
    transform_proplists: bool
}

impl <'a> fmt::Display for JsonPrettyPrinter<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.term.write_json(f, self.transform_proplists)
    }
}

impl BertTerm {
    fn write_json(&self, f: &mut fmt::Formatter, transform_proplists: bool) -> fmt::Result {
        use self::BertTerm::*;
        match *self {
            Nil => f.write_str("[]"),
            Int(n) => write!(f, "{}", n),
            BigInt(ref b) => write!(f, "\"{}\"", b),
            Float(x) => write!(f, "{}", x),
            Atom(ref s) => write!(f, "\"{}\"", s),
            List(ref terms) =>
                if transform_proplists && self.is_proplist() {
                    f.write_char('{')?;
                    let mut first = true;
                    for term in terms {
                        if !first { f.write_char(',')?; }
                        first = false;
                        term.write_as_kv_pair(f, transform_proplists)?;
                    }
                    f.write_char('}')
                } else {
                    self.write_list(f, terms, transform_proplists)
                }
            Tuple(ref terms) => self.write_list(f, terms, transform_proplists),
            Binary(ref bytes) | String(ref bytes) => {
                f.write_char('"')?;
                for b in bytes {
                    if must_be_escaped(*b) {
                        write!(f, "\\{}", *b as char)?;
                    } else if is_printable(*b) {
                        f.write_char(*b as char)?;
                    } else {
                        write!(f, "\\u{:04x}", *b)?;
                    }
                }
                f.write_char('"')
            }
            Map(ref keys, ref values) => {
                f.write_char('{')?;
                let mut first = true;
                for (key, value) in keys.iter().zip(values) {
                    if !first { f.write_char(',')?; }
                    first = false;
                    key.write_json(f, transform_proplists)?;
                    f.write_char(':')?;
                    value.write_json(f, transform_proplists)?;
                }
                f.write_char('}')
            }
        }
    }

    fn write_as_kv_pair(&self, f: &mut fmt::Formatter, transform_proplists: bool) -> fmt::Result {
        match *self {
            BertTerm::Tuple(ref kv) => {
                assert_eq!(2, kv.len());
                kv[0].write_json(f, transform_proplists)?;
                f.write_char(':')?;
                kv[1].write_json(f, transform_proplists)
            }
            _ => panic!("{} is not a proplist item", self)
        }
    }

    fn write_list(&self, f: &mut fmt::Formatter, terms: &[BertTerm], transform_proplists: bool) -> fmt::Result {
        f.write_char('[')?;
        let mut first = true;
        for term in terms {
            if !first { f.write_char(',')?; }
            first = false;
            term.write_json(f, transform_proplists)?;
        }
        f.write_char(']')
    }
}

fn is_printable(b: u8) -> bool {
    b >= 0x20 && b <= 0x7e
}

fn must_be_escaped(b: u8) -> bool {
    b == b'"' || b == b'\\'
}
<commit_msg>FIX: ensure only strings, binaries, and atoms are proplist keys<commit_after>use std::fmt::{self, Write};

use num::bigint;

pub const DEFAULT_INDENT_WIDTH: usize = 2;
pub const DEFAULT_MAX_TERMS_PER_LINE: usize = 4;

#[derive(Debug, PartialEq)]
pub enum BertTerm {
    Nil,
    Int(i32),
    BigInt(bigint::BigInt),
    Float(f64),
    Atom(String),
    Tuple(Vec<BertTerm>),
    List(Vec<BertTerm>),
    Map(Vec<BertTerm>, Vec<BertTerm>),
    String(Vec<u8>),
    Binary(Vec<u8>)
}

impl BertTerm {
    fn is_basic(&self) -> bool {
        match *self {
            BertTerm::Int(_)
            | BertTerm::BigInt(_)
            | BertTerm::Float(_)
            | BertTerm::Atom(_)
            | BertTerm::String(_)
            | BertTerm::Binary(_)
            | BertTerm::Nil => true,
            BertTerm::List(_)
            | BertTerm::Tuple(_)
            | BertTerm::Map(_, _) => false
        }
    }

    fn is_proplist(&self) -> bool {
        match *self {
            BertTerm::List(ref elems) => elems.iter().all(|e| e.is_proplist_entry()),
            _ => false
        }
    }

    fn is_proplist_entry(&self) -> bool {
        match *self {
            BertTerm::Tuple(ref elems) =>
                elems.len() == 2 && elems[0].is_proplist_key(),
            _ => false
        }
    }

    fn is_proplist_key(&self) -> bool {
        match *self {
            BertTerm::Atom(_) => true,
            BertTerm::String(_) => true,
            BertTerm::Binary(_) => true,
            _ => false
        }
    }
}

impl fmt::Display for BertTerm {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let pp = PrettyPrinter::new(self,
                                    DEFAULT_INDENT_WIDTH,
                                    DEFAULT_MAX_TERMS_PER_LINE);
        write!(f, "{}", pp)
    }
}

/// Outputs a vector of BertTerms to stdout.
pub fn pp_bert(terms: Vec<BertTerm>, indent_width: usize, terms_per_line: usize) {
    for t in terms {
        let pp = PrettyPrinter::new(&t, indent_width, terms_per_line);
        println!("{}", pp);
    }
}

/// Outputs a BertTerm as JSON to stdout.
pub fn pp_json(terms: Vec<BertTerm>, _: usize, _: usize) {
    for t in terms {
        let pp = JsonPrettyPrinter { term: &t, transform_proplists: false };
        println!("{}", pp);
    }
}

/// Outputs a BertTerm as JSON to stdout;
/// Erlang proplists are converted to JSON objects.
pub fn pp_json_proplist(terms: Vec<BertTerm>, _: usize, _: usize) {
    for t in terms {
        let pp = JsonPrettyPrinter { term: &t, transform_proplists: true };
        println!("{}", pp);
    }
}

pub struct PrettyPrinter<'a> {
    term: &'a BertTerm,
    indent_width: usize,
    max_terms_per_line: usize
}

impl <'a> fmt::Display for PrettyPrinter<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.write_term(self.term, f, 0)
    }
}

impl <'a> PrettyPrinter<'a> {
    /// Creates a pretty printer for `term` where sub-terms
    /// are indented with a width of `indent_width` and a
    /// maximum of `max_terms_per_line` basic terms (i.e.,
    /// integers, floats, strings) can be printed per line.
    pub fn new(term: &'a BertTerm,
               indent_width: usize,
               max_terms_per_line: usize) -> Self {
        PrettyPrinter { term, indent_width, max_terms_per_line }
    }

    fn write_term(&self, term: &BertTerm, f: &mut fmt::Formatter, depth: usize) -> fmt::Result {
        match *term {
            BertTerm::Nil => f.write_str("[]"),
            BertTerm::Int(n) => write!(f, "{}", n),
            BertTerm::BigInt(ref n) => write!(f, "{}", n),
            BertTerm::Float(x) => write!(f, "{}", x),
            BertTerm::Atom(ref s) => f.write_str(s),
            BertTerm::String(ref bytes) => self.write_string(bytes, f, "\"", "\""),
            BertTerm::Binary(ref bytes) => self.write_string(bytes, f, "<<\"", "\">>"),
            BertTerm::List(ref terms) => self.write_collection(terms, f, depth, '[', ']'),
            BertTerm::Tuple(ref terms) => self.write_collection(terms, f, depth, '{', '}'),
            BertTerm::Map(ref keys, ref vals) => self.write_map(keys, vals, f, depth)
        }
    }

    fn write_string(&self,
                    bytes: &[u8],
                    f: &mut fmt::Formatter,
                    open: &str,
                    close: &str) -> fmt::Result {
        f.write_str(open)?;
        for &b in bytes {
            if is_printable(b) {
                f.write_char(b as char)?;
            } else {
                write!(f, "\\x{:02x}", b)?;
            }
        }
        f.write_str(close)
    }

    fn write_collection(&self,
                        terms: &[BertTerm],
                        f: &mut fmt::Formatter,
                        depth: usize,
                        open: char,
                        close: char) -> fmt::Result {
        let multi_line = !self.is_small_collection(terms);

        // Every element will have the same indentation,
        // so pre-compute it once.
        let prefix =
            if multi_line {
                self.indentation(depth+1)
            } else {
                String::new()
            };

        f.write_char(open)?;
        let mut comma = "";
        for t in terms {
            f.write_str(comma)?;
            f.write_str(&prefix)?;
            self.write_term(t, f, depth + 1)?;
            comma = ", ";
        }

        if multi_line {
            f.write_str(&self.indentation(depth))?;
        }

        f.write_char(close)
    }

    fn write_map(&self,
                 keys: &[BertTerm],
                 vals: &[BertTerm],
                 f: &mut fmt::Formatter,
                 depth: usize) -> fmt::Result {
        let multi_line =
            !self.is_small_collection(keys) || !self.is_small_collection(vals);
        let prefix =
            if multi_line {
                self.indentation(depth+1)
            } else {
                String::new()
            };

        f.write_str("#{")?;
        let mut comma = "";
        for i in 0 .. keys.len() {
            f.write_str(comma)?;
            f.write_str(&prefix)?;
            self.write_term(&keys[i], f, depth + 1)?;
            f.write_str(" => ")?;
            self.write_term(&vals[i], f, depth + 1)?;
            comma = ", ";
        }

        if multi_line {
            f.write_str(&self.indentation(depth))?;
        }
        f.write_str("}")
    }

    fn is_small_collection(&self, terms: &[BertTerm]) -> bool {
        terms.len() <= self.max_terms_per_line &&
            terms.iter().all(BertTerm::is_basic)
    }

    fn indentation(&self, depth: usize) -> String {
        ::std::iter::once('\n')
            .chain((0 .. depth * self.indent_width).map(|_| ' '))
            .collect()
    }
}

pub struct JsonPrettyPrinter<'a> {
    term: &'a BertTerm,
    transform_proplists: bool
}

impl <'a> fmt::Display for JsonPrettyPrinter<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.term.write_json(f, self.transform_proplists)
    }
}

impl BertTerm {
    fn write_json(&self, f: &mut fmt::Formatter, transform_proplists: bool) -> fmt::Result {
        use self::BertTerm::*;
        match *self {
            Nil => f.write_str("[]"),
            Int(n) => write!(f, "{}", n),
            BigInt(ref b) => write!(f, "\"{}\"", b),
            Float(x) => write!(f, "{}", x),
            Atom(ref s) => write!(f, "\"{}\"", s),
            List(ref terms) =>
                if transform_proplists && self.is_proplist() {
                    f.write_char('{')?;
                    let mut first = true;
                    for term in terms {
                        if !first { f.write_char(',')?; }
                        first = false;
                        term.write_as_kv_pair(f, transform_proplists)?;
                    }
                    f.write_char('}')
                } else {
                    self.write_list(f, terms, transform_proplists)
                }
            Tuple(ref terms) => self.write_list(f, terms, transform_proplists),
            Binary(ref bytes) | String(ref bytes) => {
                f.write_char('"')?;
                for b in bytes {
                    if must_be_escaped(*b) {
                        write!(f, "\\{}", *b as char)?;
                    } else if is_printable(*b) {
                        f.write_char(*b as char)?;
                    } else {
                        write!(f, "\\u{:04x}", *b)?;
                    }
                }
                f.write_char('"')
            }
            Map(ref keys, ref values) => {
                f.write_char('{')?;
                let mut first = true;
                for (key, value) in keys.iter().zip(values) {
                    if !first { f.write_char(',')?; }
                    first = false;
                    key.write_json(f, transform_proplists)?;
                    f.write_char(':')?;
                    value.write_json(f, transform_proplists)?;
                }
                f.write_char('}')
            }
        }
    }

    fn write_as_kv_pair(&self, f: &mut fmt::Formatter, transform_proplists: bool) -> fmt::Result {
        match *self {
            BertTerm::Tuple(ref kv) => {
                assert_eq!(2, kv.len());
                kv[0].write_json(f, transform_proplists)?;
                f.write_char(':')?;
                kv[1].write_json(f, transform_proplists)
            }
            _ => panic!("{} is not a proplist item", self)
        }
    }

    fn write_list(&self, f: &mut fmt::Formatter, terms: &[BertTerm], transform_proplists: bool) -> fmt::Result {
        f.write_char('[')?;
        let mut first = true;
        for term in terms {
            if !first { f.write_char(',')?; }
            first = false;
            term.write_json(f, transform_proplists)?;
        }
        f.write_char(']')
    }
}

fn is_printable(b: u8) -> bool {
    b >= 0x20 && b <= 0x7e
}

fn must_be_escaped(b: u8) -> bool {
    b == b'"' || b == b'\\'
}
<|endoftext|>


<commit_before>extern crate serde;
#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate neon;
extern crate neon_serde;
extern crate fall_tree;
extern crate fall_editor;
extern crate lang_rust;
extern crate lang_fall;

use std::iter;

use neon::vm::{Call, JsResult, Lock};
use neon::mem::Handle;
use neon::scope::Scope;
use neon::js::{JsString, JsInteger, JsNull, JsValue, JsFunction};
use neon::js::class::{Class, JsClass};
use neon::task::Task;

use fall_tree::{TextEditBuilder, Text, TextRange, TextEdit, TextEditOp, tu};
use fall_editor::{EditorSupport, EditorFile};

mod support;

use self::support::{arg1, arg2, ret};
use neon_serde::from_value;

const LANGUAGES: &[EditorSupport] = &[
    lang_fall::FALL_EDITOR_SUPPORT,
    lang_rust::RUST_EDITOR_SUPPORT,
];

declare_types! {
    pub class JsSupport for EditorSupport {
        init(call) {
            let scope = call.scope;
            let idx: usize = arg1(scope, &call.arguments)?;
            Ok(LANGUAGES[idx])
        }

        method parse(call) {
            let scope = call.scope;
            let text = call.arguments.require(scope, 0)?.check::<JsString>()?;
            let support = call.arguments.this(scope);

            let class: Handle<JsClass<JsFile>> = JsFile::class(scope)?;
            let ctor: Handle<JsFunction<JsFile>> = class.constructor(scope)?;
            let ctor_args = iter::once(support.upcast()).chain(iter::once(text.upcast()));
            let file = ctor.construct::<_, JsValue, _>(scope, ctor_args)?;
            Ok(file.upcast())
        }
    }

    pub class JsFile for EditorFile {
        init(call) {
            let scope = call.scope;
            let file = match call.arguments.len() {
                2 => {
                    let mut support = call.arguments.require(scope, 0)?.check::<JsSupport>()?;
                    let text = call.arguments.require(scope, 1)?.check::<JsString>()?.value();
                    support.grab(move |support| support.parse(&text))
                }
                3 => {
                    let mut file: Handle<JsFile> = call.arguments.require(scope, 0)?.check::<JsFile>()?;
                    let edits = call.arguments.require(scope, 1)?;
                    let edits: Vec<VsEdit> = from_value(scope, edits)?;
                    file.grab(|file| {
                        let edits = from_vs_edits(file.file().text(), edits);
                        file.edit(&edits)
                    })
                }
                _ => panic!("Bad ctor invocation")
            };
            Ok(file)
        }

        method edit(call) {
            let scope = call.scope;

            let this = call.arguments.this(scope);
            let edits = call.arguments.require(scope, 0)?;

            let class: Handle<JsClass<JsFile>> = JsFile::class(scope)?;
            let constructor: Handle<JsFunction<JsFile>> = class.constructor(scope)?;
            let args = iter::once(this.upcast()).chain(iter::once(edits)).chain(iter::once(JsNull::new().upcast()));
            let file = constructor.construct(scope, args)?;
            Ok(file.upcast())
        }

        method syntaxTree(call) {
            let scope = call.scope;
            let tree = call.arguments.this(scope).grab(move |file| {
                file.syntax_tree()
            });
            ret(scope, tree)
        }

        method structure(call) {
            let scope = call.scope;
            let structure = call.arguments.this(scope).grab(|file| file.structure());
            ret(scope, structure)
        }

        method highlight(call) {
            let scope = call.scope;
            let highlights = call.arguments.this(scope).grab(move |file| file.highlight());
            ret(scope, highlights)
        }

        method diagnostics(call) {
            let scope = call.scope;
            let diagnostics = call.arguments.this(scope).grab(|file| file.diagnostics());
            ret(scope, diagnostics)
        }

        method extendSelection(call) {
            let scope = call.scope;
            let range: TextRange = arg1(scope, &call.arguments)?;
            let range = call.arguments.this(scope).grab(|file| file.extend_selection(range));
            ret(scope, range)
        }

        method metrics(call) {
            let scope = call.scope;
            let metrics = call.arguments.this(scope).grab(move |file| {
                file.metrics()
            });
            ret(scope, metrics)
        }

        method contextActions(call) {
            let scope = call.scope;
            let range: TextRange = arg1(scope, &call.arguments)?;
            let actions = call.arguments.this(scope).grab(|file| file.context_actions(range));
            ret(scope, actions)
        }

        method applyContextAction(call) {
            let scope = call.scope;
            let (range, id): (TextRange, String) = arg2(scope, &call.arguments)?;
            let edit = call.arguments.this(scope).grab(move |file| file.apply_context_action(range, &id));
            let edit = edit.map(to_vs_edits).unwrap_or_default();
            ret(scope, edit)
        }
    }
}

register_module!(m, {
    m.export("status", status)?;
    m.export("supportForExtension", support_for_extension)?;
    Ok(())
});

fn support_for_extension(call: Call) -> JsResult<JsValue> {
    let scope = call.scope;
    let ext: String = arg1(scope, &call.arguments)?;
    let idx = match LANGUAGES.iter().position(|s| s.extension == ext) {
        None => return Ok(JsNull::new().upcast()),
        Some(idx) => idx,
    };

    let arg: Handle<JsValue> = JsInteger::new(scope, idx as i32).upcast();
    let class: Handle<JsClass<JsSupport>> = JsSupport::class(scope)?;
    let constructor: Handle<JsFunction<JsSupport>> = class.constructor(scope)?;
    let sup = constructor.construct::<_, JsValue, _>(scope, ::std::iter::once(arg))?;
    Ok(sup.upcast())
}

fn status(call: Call) -> JsResult<JsValue> {
    let scope = call.scope;
    ret(scope, "Hello from Rust")
}

#[derive(Serialize, Deserialize)]
struct VsEdit {
    delete: TextRange,
    insert: String,
}

fn from_vs_edits(text: Text, edits: Vec<VsEdit>) -> TextEdit {
    let mut edit = TextEditBuilder::new(text);
    for e in edits {
        edit.replace(e.delete, e.insert)
    }
    edit.build()
}

fn to_vs_edits(edit: TextEdit) -> Vec<VsEdit> {
    let mut result = Vec::new();
    let mut offset = tu(0);
    for op in edit.ops {
        match op {
            TextEditOp::Copy(range) => {
                if range.start() != offset {
                    let range = TextRange::from_to(offset, range.start());
                    result.push(VsEdit { delete: range, insert: String::new() })
                }
                offset = range.end();
            }
            TextEditOp::Insert(text) => {
                let range = TextRange::from_len(offset, tu(0));
                result.push(VsEdit { delete: range, insert: text.to_string() })
            }
        }
    }
    return result;
}
<commit_msg>Dead code<commit_after>extern crate serde;
#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate neon;
extern crate neon_serde;
extern crate fall_tree;
extern crate fall_editor;
extern crate lang_rust;
extern crate lang_fall;

use std::iter;

use neon::vm::{Call, JsResult, Lock};
use neon::mem::Handle;
use neon::scope::Scope;
use neon::js::{JsString, JsInteger, JsNull, JsValue, JsFunction};
use neon::js::class::{Class, JsClass};
use neon::task::Task;

use fall_tree::{TextEditBuilder, Text, TextRange, TextEdit, TextEditOp, tu};
use fall_editor::{EditorSupport, EditorFile};

mod support;

use self::support::{arg1, arg2, ret};
use neon_serde::from_value;

const LANGUAGES: &[EditorSupport] = &[
    lang_fall::FALL_EDITOR_SUPPORT,
    lang_rust::RUST_EDITOR_SUPPORT,
];

declare_types! {
    pub class JsSupport for EditorSupport {
        init(call) {
            let scope = call.scope;
            let idx: usize = arg1(scope, &call.arguments)?;
            Ok(LANGUAGES[idx])
        }

        method parse(call) {
            let scope = call.scope;
            let text = call.arguments.require(scope, 0)?.check::<JsString>()?;
            let support = call.arguments.this(scope);

            let class: Handle<JsClass<JsFile>> = JsFile::class(scope)?;
            let ctor: Handle<JsFunction<JsFile>> = class.constructor(scope)?;
            let ctor_args = iter::once(support.upcast()).chain(iter::once(text.upcast()));
            let file = ctor.construct::<_, JsValue, _>(scope, ctor_args)?;
            Ok(file.upcast())
        }
    }

    pub class JsFile for EditorFile {
        init(call) {
            let scope = call.scope;
            let file = match call.arguments.len() {
                2 => {
                    let mut support = call.arguments.require(scope, 0)?.check::<JsSupport>()?;
                    let text = call.arguments.require(scope, 1)?.check::<JsString>()?.value();
                    support.grab(move |support| support.parse(&text))
                }
                3 => {
                    let mut file: Handle<JsFile> = call.arguments.require(scope, 0)?.check::<JsFile>()?;
                    let edits = call.arguments.require(scope, 1)?;
                    let edits: Vec<VsEdit> = from_value(scope, edits)?;
                    file.grab(|file| {
                        let edits = from_vs_edits(file.file().text(), edits);
                        file.edit(&edits)
                    })
                }
                _ => panic!("Bad ctor invocation")
            };
            Ok(file)
        }

        method edit(call) {
            let scope = call.scope;

            let this = call.arguments.this(scope);
            let edits = call.arguments.require(scope, 0)?;

            let class: Handle<JsClass<JsFile>> = JsFile::class(scope)?;
            let constructor: Handle<JsFunction<JsFile>> = class.constructor(scope)?;
            let args = iter::once(this.upcast()).chain(iter::once(edits)).chain(iter::once(JsNull::new().upcast()));
            let file = constructor.construct(scope, args)?;
            Ok(file.upcast())
        }

        method syntaxTree(call) {
            let scope = call.scope;
            let tree = call.arguments.this(scope).grab(move |file| {
                file.syntax_tree()
            });
            ret(scope, tree)
        }

        method structure(call) {
            let scope = call.scope;
            let structure = call.arguments.this(scope).grab(|file| file.structure());
            ret(scope, structure)
        }

        method highlight(call) {
            let scope = call.scope;
            let highlights = call.arguments.this(scope).grab(move |file| file.highlight());
            ret(scope, highlights)
        }

        method diagnostics(call) {
            let scope = call.scope;
            let diagnostics = call.arguments.this(scope).grab(|file| file.diagnostics());
            ret(scope, diagnostics)
        }

        method extendSelection(call) {
            let scope = call.scope;
            let range: TextRange = arg1(scope, &call.arguments)?;
            let range = call.arguments.this(scope).grab(|file| file.extend_selection(range));
            ret(scope, range)
        }

        method metrics(call) {
            let scope = call.scope;
            let metrics = call.arguments.this(scope).grab(move |file| {
                file.metrics()
            });
            ret(scope, metrics)
        }

        method contextActions(call) {
            let scope = call.scope;
            let range: TextRange = arg1(scope, &call.arguments)?;
            let actions = call.arguments.this(scope).grab(|file| file.context_actions(range));
            ret(scope, actions)
        }

        method applyContextAction(call) {
            let scope = call.scope;
            let (range, id): (TextRange, String) = arg2(scope, &call.arguments)?;
            let edit = call.arguments.this(scope).grab(move |file| file.apply_context_action(range, &id));
            let edit = edit.map(to_vs_edits).unwrap_or_default();
            ret(scope, edit)
        }
    }
}

register_module!(m, {
    m.export("supportForExtension", support_for_extension)?;
    Ok(())
});

fn support_for_extension(call: Call) -> JsResult<JsValue> {
    let scope = call.scope;
    let ext: String = arg1(scope, &call.arguments)?;
    let idx = match LANGUAGES.iter().position(|s| s.extension == ext) {
        None => return Ok(JsNull::new().upcast()),
        Some(idx) => idx,
    };

    let arg: Handle<JsValue> = JsInteger::new(scope, idx as i32).upcast();
    let class: Handle<JsClass<JsSupport>> = JsSupport::class(scope)?;
    let constructor: Handle<JsFunction<JsSupport>> = class.constructor(scope)?;
    let sup = constructor.construct::<_, JsValue, _>(scope, ::std::iter::once(arg))?;
    Ok(sup.upcast())
}

#[derive(Serialize, Deserialize)]
struct VsEdit {
    delete: TextRange,
    insert: String,
}

fn from_vs_edits(text: Text, edits: Vec<VsEdit>) -> TextEdit {
    let mut edit = TextEditBuilder::new(text);
    for e in edits {
        edit.replace(e.delete, e.insert)
    }
    edit.build()
}

fn to_vs_edits(edit: TextEdit) -> Vec<VsEdit> {
    let mut result = Vec::new();
    let mut offset = tu(0);
    for op in edit.ops {
        match op {
            TextEditOp::Copy(range) => {
                if range.start() != offset {
                    let range = TextRange::from_to(offset, range.start());
                    result.push(VsEdit { delete: range, insert: String::new() })
                }
                offset = range.end();
            }
            TextEditOp::Insert(text) => {
                let range = TextRange::from_len(offset, tu(0));
                result.push(VsEdit { delete: range, insert: text.to_string() })
            }
        }
    }
    return result;
}
<|endoftext|>


<commit_before>//
// MSX CAS Packager
// Copyright (c) 2015 Alvaro Polo
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

extern crate byteorder;
extern crate docopt;
extern crate rustc_serialize;

#[cfg(test)]
extern crate quickcheck;

mod args;
mod tape;
mod wav;

use std::fs;
use std::fs::File;
use std::io::Read;
use std::io::Write;

#[allow(dead_code)]
fn main() {
    let cmd = args::parse();
    match cmd {
        args::Command::Version => print_version(),
        args::Command::List(path) => list_files(&path[..]),
        args::Command::Add(path, files) =>  add_files(&path[..], &files[..]),
        args::Command::Extract(path) => extract_all(&path[..]),
        args::Command::Export(path, output) => export(&*path, &*output),
    };
}

fn print_version() {
    println!("MSX CAS Packager (MCP) v0.1.0");
    println!("Copyright (C) 2015 Alvaro Polo");
    println!("");
    println!("This program is subject to the terms of the Mozilla Public License v2.0.");
    println!("");
}

macro_rules! open_file {
    ($path: expr) => ({
        match File::open($path) {
            Ok(f) => f,
            Err(e) => {
                println!("Cannot open file '{}': {}", $path, e);
                return
            }
        }
    })
}

macro_rules! create_file {
    ($path: expr) => ({
        match File::create($path) {
            Ok(f) => f,
            Err(e) => {
                println!("Cannot create file '{}': {}", $path, e);
                return
            }
        }
    })
}

macro_rules! read_file {
    ($name: expr, $file: expr, $data: expr) => ({
        match $file.read_to_end($data) {
            Ok(_) => {},
            Err(e) => {
                println!("Cannot read from file '{}': {}", $name, e);
            },
        };
    });
    ($name: expr) => ({
        let file = open_file!($name);
        let mut data: Vec<u8> = vec![];
        read_file!($name, &file, &mut data);
        data
    });
}

macro_rules! write_file {
    ($name: expr, $file: expr, $data: expr) => ({
        match $file.write_all($data) {
            Ok(_) => {},
            Err(e) => {
                println!("Cannot write to file '{}': {}", $name, e);
            },
        };
    })
}

macro_rules! open_tape {
    ($path: expr) => ({
        let mut tape_file = open_file!($path);
        match tape::Tape::read(&mut tape_file) {
            Ok(f) => f,
            Err(e) => {
                println!("Cannot read file '{}': {}", $path, e);
                return
            }
        }
    })
}

macro_rules! open_or_create_tape {
    ($path: expr) => ({
        match File::open($path) {
            Ok(mut f) => {
                match tape::Tape::read(&mut f) {
                    Ok(f) => (f, true),
                    Err(e) => {
                        println!("Cannot read file '{}': {}", $path, e);
                        return
                    }
                }
            },
            Err(_) => (tape::Tape::new(), false),
        }
    })
}

macro_rules! file_name {
    ($file: expr) => ({
        let fname = &$file[..$file.len() - 4];
        if fname.len() > 6 {
            println!("Warning: filename {} is too long, truncating", $file);
        }
        tape::file_name(fname)
    })
}

fn list_files(path: &str) {
    let tape = open_tape!(path);
    for file in tape.files() {
        match file {
            tape::File::Bin(name, begin, end, start, data) => {
                println!("bin    | {:6} | {:5} bytes | [0x{:x},0x{:x}]:0x{:x}",
                    name, data.len(), begin, end, start);
            },
            tape::File::Basic(name, data) => {
                println!("basic  | {:6} | {:5} bytes |", name, data.len());
            },
            tape::File::Ascii(name, data) => {
                let nbytes = data.iter().fold(0, |size, chunk| size + chunk.len());
                println!("ascii  | {:6} | {:5} bytes |", name, nbytes);
            },
            tape::File::Custom(data) => {
                println!("custom |        | {:5} bytes |", data.len());
            }
        };
    }
}

fn extract_all(path: &str) {
    let tape = open_tape!(path);
    let mut next_custom = 0;
    for file in tape.files() {
        let out_path = file.name()
            .map(|n| n.to_string())
            .unwrap_or_else(|| format!("custom.{:03}", { next_custom += 1; next_custom }));
        print!("Extracting {}... ", out_path);
        extract_file(&file, &out_path[..]);
        println!("Done");
    }
}

fn extract_file(file: &tape::File, out_path: &str) {
    let mut ofile = create_file!(out_path);
    match file {
        &tape::File::Bin(_, _, _, _, data) => {
            // First, write the BIN file ID byte not present in cassete
            write_file!(out_path, ofile, &[0xfe]);
            write_file!(out_path, ofile, data);
        },
        &tape::File::Basic(_, data) => {
            write_file!(out_path, ofile, data);
        },
        &tape::File::Ascii(_, ref chunks) => {
            for chunk in chunks {
                let last = chunk.iter().position(|b| *b == 0x1a).unwrap_or(chunk.len());
                write_file!(out_path, ofile, &chunk[..last]);
            }
        },
        &tape::File::Custom(ref data) => {
            write_file!(out_path, ofile, data);
        },
    }
}

fn add_files(path: &str, files: &[String]) {
    let (mut tape, tape_exist) = open_or_create_tape!(path);
    for fname in files {
        print!("Adding {}... ", fname);
        if fname.ends_with(".bin") {
            add_bin_file(&mut tape, &fname[..]);
        } else if fname.ends_with(".asc") {
            add_ascii_file(&mut tape, &fname[..]);
        } else if fname.ends_with(".bas") {
            add_basic_file(&mut tape, &fname[..]);
        } else {
            add_custom_file(&mut tape, &fname[..]);
        }
        println!("Done");
    }
    let otmp = format!("{}.tmp", path);
    save_tape(&tape, &otmp[..]);
    if tape_exist {
        fs::remove_file(path).unwrap();
    }
    fs::rename(otmp, path).unwrap();
}

fn add_bin_file(tape: &mut tape::Tape, file: &str) {
    let data = &read_file!(file)[..];
    // Skip bin file ID byte if present
    let bytes = if data[0] == 0xfe { &data[1..] } else { data };
    let fname = file_name!(file);
    tape.append_bin(&fname, bytes);
}

fn add_basic_file(tape: &mut tape::Tape, file: &str) {
    let data = &read_file!(file)[..];
    let fname = file_name!(file);
    tape.append_basic(&fname, data);
}

fn add_ascii_file(tape: &mut tape::Tape, file: &str) {
    let data = &read_file!(file)[..];
    let fname = file_name!(file);
    tape.append_ascii(&fname, data);
}

fn add_custom_file(tape: &mut tape::Tape, file: &str) {
    let data = &read_file!(file)[..];
    tape.append_custom(data);
}

fn save_tape(tape: &tape::Tape, file: &str) {
    let mut ofile = create_file!(&file);
    for block in tape.blocks() {
        write_file!(&file, &mut ofile, block.data());
    }
}

fn export(cas_path: &str, wav_path: &str) {
    let tape = open_tape!(cas_path);
    let mut exporter = wav::Exporter::new();
    let mut wav_file = create_file!(wav_path);

    for (block, i) in tape.blocks().iter().zip(0..tape.blocks().len()) {
        print!("Encoding block {}... ", i);
        let mut nbytes = 0;
        if block.is_file_header() {
            nbytes += exporter.write_long_silence().ok().unwrap();
            nbytes += exporter.write_long_header().ok().unwrap();
        } else {
            nbytes += exporter.write_short_silence().ok().unwrap();
            nbytes += exporter.write_short_header().ok().unwrap();
        }
        nbytes += exporter.write_data(block.data_without_prefix()).ok().unwrap();
        println!("{} KiB", nbytes / 1024);
    }
    exporter.export(&mut wav_file).ok();
}
<commit_msg>Fix file name inference<commit_after>//
// MSX CAS Packager
// Copyright (c) 2015 Alvaro Polo
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

extern crate byteorder;
extern crate docopt;
extern crate rustc_serialize;

#[cfg(test)]
extern crate quickcheck;

mod args;
mod tape;
mod wav;

use std::convert::From;
use std::fs;
use std::fs::File;
use std::io::{Read, Write};
use std::io::Error as IoError;
use std::path::Path;

#[derive(Debug)]
enum Error {
    InvalidInputFile(String),
    Io(IoError)
}

impl From<IoError> for Error {
    fn from(e: IoError) -> Error { Error::Io(e) }
}

type Result<T> = std::result::Result<T, Error>;

#[allow(dead_code)]
fn main() {
    let cmd = args::parse();
    let result = match cmd {
        args::Command::Version => print_version(),
        args::Command::List(path) => list_files(&path[..]),
        args::Command::Add(path, files) =>  add_files(&path[..], &files[..]),
        args::Command::Extract(path) => extract_all(&path[..]),
        args::Command::Export(path, output) => export(&*path, &*output),
    };
    if result.is_err() {
        match result.unwrap_err() {
            Error::InvalidInputFile(file) =>
                println!("Error: {} is not a valid input file", file),
            Error::Io(e) =>
                println!("Error: IO operation failed: {}", e),
        }
    }
}

fn print_version() -> Result<()> {
    println!("MSX CAS Packager (MCP) v0.1.0");
    println!("Copyright (C) 2015 Alvaro Polo");
    println!("");
    println!("This program is subject to the terms of the Mozilla Public License v2.0.");
    println!("");
    Ok(())
}

macro_rules! read_file {
    ($name: expr) => ({
        let mut file = try!(File::open($name));
        let mut data: Vec<u8> = vec![];
        try!(file.read_to_end(&mut data).map(|_| data))
    });
}

macro_rules! open_tape {
    ($path: expr) => ({
        let mut tape_file = try!(File::open($path));
        try!(tape::Tape::read(&mut tape_file))
    })
}

macro_rules! open_or_create_tape {
    ($path: expr) => ({
        match File::open($path) {
            Ok(mut f) => try!(tape::Tape::read(&mut f).map(|t| (t, true))),
            Err(_) => (tape::Tape::new(), false),
        }
    })
}

macro_rules! file_name {
    ($file: expr) => ({
        let src_name = Path::new($file).file_name().unwrap().to_str().unwrap();
        let fname = &src_name[..src_name.len() - 4];
        if fname.len() > 6 {
            println!("Warning: filename {} is too long, truncating", src_name);
        }
        tape::file_name(fname)
    })
}

fn file_name(path: &str) -> Result<[u8;6]> {
    let src_name = try!(Path::new(path)
        .file_name()
        .map_or(None, |f| f.to_str())
        .ok_or(Error::InvalidInputFile(path.to_string())));
    let fname = &src_name[..src_name.len() - 4];
    if fname.len() > 6 {
        println!("Warning: filename {} is too long, truncating", src_name);
    }
    Ok(tape::file_name(fname))
}

fn list_files(path: &str) -> Result<()> {
    let tape = open_tape!(path);
    for file in tape.files() {
        match file {
            tape::File::Bin(name, begin, end, start, data) => {
                println!("bin    | {:6} | {:5} bytes | [0x{:x},0x{:x}]:0x{:x}",
                    name, data.len(), begin, end, start);
            },
            tape::File::Basic(name, data) => {
                println!("basic  | {:6} | {:5} bytes |", name, data.len());
            },
            tape::File::Ascii(name, data) => {
                let nbytes = data.iter().fold(0, |size, chunk| size + chunk.len());
                println!("ascii  | {:6} | {:5} bytes |", name, nbytes);
            },
            tape::File::Custom(data) => {
                println!("custom |        | {:5} bytes |", data.len());
            }
        };
    }
    Ok(())
}

fn extract_all(path: &str) -> Result<()> {
    let tape = open_tape!(path);
    let mut next_custom = 0;
    for file in tape.files() {
        let out_path = file.name()
            .map(|n| n.to_string())
            .unwrap_or_else(|| format!("custom.{:03}", { next_custom += 1; next_custom }));
        print!("Extracting {}... ", out_path);
        try!(extract_file(&file, &out_path[..]));
        println!("Done");
    }
    Ok(())
}

fn extract_file(file: &tape::File, out_path: &str) -> Result<()> {
    let mut ofile = try!(File::create(out_path));
    match file {
        &tape::File::Bin(_, _, _, _, data) => {
            // First, write the BIN file ID byte not present in cassete
            try!(ofile.write_all(&[0xfe]));
            try!(ofile.write_all(data));
        },
        &tape::File::Basic(_, data) => {
            try!(ofile.write_all(data));
        },
        &tape::File::Ascii(_, ref chunks) => {
            for chunk in chunks {
                let last = chunk.iter().position(|b| *b == 0x1a).unwrap_or(chunk.len());
                try!(ofile.write_all(&chunk[..last]));
            }
        },
        &tape::File::Custom(ref data) => {
            try!(ofile.write_all(data));
        },
    }
    Ok(())
}

fn add_files(path: &str, files: &[String]) -> Result<()> {
    let (mut tape, tape_exist) = open_or_create_tape!(path);
    for fname in files {
        print!("Adding {}... ", fname);
        if fname.ends_with(".bin") {
            try!(add_bin_file(&mut tape, &fname[..]));
        } else if fname.ends_with(".asc") {
            try!(add_ascii_file(&mut tape, &fname[..]));
        } else if fname.ends_with(".bas") {
            try!(add_basic_file(&mut tape, &fname[..]));
        } else {
            try!(add_custom_file(&mut tape, &fname[..]));
        }
        println!("Done");
    }
    let otmp = format!("{}.tmp", path);
    try!(save_tape(&tape, &otmp[..]));
    if tape_exist {
        try!(fs::remove_file(path));
    }
    try!(fs::rename(otmp, path));
    Ok(())
}

fn add_bin_file(tape: &mut tape::Tape, file: &str) -> Result<()> {
    let data = &read_file!(file)[..];
    // Skip bin file ID byte if present
    let bytes = if data[0] == 0xfe { &data[1..] } else { data };
    let fname = try!(file_name(file));
    tape.append_bin(&fname, bytes);
    Ok(())
}

fn add_basic_file(tape: &mut tape::Tape, file: &str) -> Result<()> {
    let data = &read_file!(file)[..];
    let fname = try!(file_name(file));
    tape.append_basic(&fname, data);
    Ok(())
}

fn add_ascii_file(tape: &mut tape::Tape, file: &str) -> Result<()> {
    let data = &read_file!(file)[..];
    let fname = try!(file_name(file));
    tape.append_ascii(&fname, data);
    Ok(())
}

fn add_custom_file(tape: &mut tape::Tape, file: &str) -> Result<()> {
    let data = &read_file!(file)[..];
    tape.append_custom(data);
    Ok(())
}

fn save_tape(tape: &tape::Tape, file: &str) -> Result<()> {
    let mut ofile = try!(File::create(&file));
    for block in tape.blocks() {
        try!(ofile.write_all(block.data()));
    }
    Ok(())
}

fn export(cas_path: &str, wav_path: &str) -> Result<()> {
    let tape = open_tape!(cas_path);
    let mut exporter = wav::Exporter::new();
    let mut wav_file = try!(File::create(wav_path));

    for (block, i) in tape.blocks().iter().zip(0..tape.blocks().len()) {
        print!("Encoding block {}... ", i);
        let mut nbytes = 0;
        if block.is_file_header() {
            nbytes += exporter.write_long_silence().ok().unwrap();
            nbytes += exporter.write_long_header().ok().unwrap();
        } else {
            nbytes += exporter.write_short_silence().ok().unwrap();
            nbytes += exporter.write_short_header().ok().unwrap();
        }
        nbytes += exporter.write_data(block.data_without_prefix()).ok().unwrap();
        println!("{} KiB", nbytes / 1024);
    }
    exporter.export(&mut wav_file).ok();
    Ok(())
}

#[cfg(test)]
mod test {

    use super::file_name;

    #[test]
    fn should_extract_correct_filename() {
        assert_eq!("foo   ".as_bytes(), file_name("foo.bin").ok().unwrap());
        assert_eq!("foo   ".as_bytes(), file_name("foo.asc").ok().unwrap());
        assert_eq!("foo   ".as_bytes(), file_name("foo.bas").ok().unwrap());
        assert_eq!("foo   ".as_bytes(), file_name("../foo.bin").ok().unwrap());
        assert_eq!("foo   ".as_bytes(), file_name("../foo.asc").ok().unwrap());
        assert_eq!("foo   ".as_bytes(), file_name("../foo.bas").ok().unwrap());
    }

    #[test]
    fn should_fail_extract_invalid_filename() {
        assert!(file_name(".").is_err());
        assert!(file_name("..").is_err());
        assert!(file_name("./").is_err());
        assert!(file_name("../").is_err());
        assert!(file_name("/.").is_err());
        assert!(file_name("/..").is_err());
    }
}
<|endoftext|>


<commit_before>#![feature(core)]
#![feature(old_io)]
#![feature(path)]
#![feature(path_ext)]

extern crate term;

mod version;
mod paths;
mod downloader;
mod builder;
mod reporter;

use std::rc::Rc;

use paths::Paths;
use downloader::Downloader;
use builder::Builder;

fn main() {
   dump_env();

   let paths = Paths::new();
   let paths_rc = Rc::new(paths);

   let nginx_downloader = Downloader::new(paths_rc.clone());
   nginx_downloader.download();

   let nginx_builder = Builder::new(paths_rc.clone());
   nginx_builder.build();
}

fn dump_env() {
   use std::env;
   use reporter::report;

   report("Dumping", "Environment");

   for (key, value) in env::vars() {
       println!("{:>30}: {}", key, value);
   }
}
<commit_msg>Does not need to declare path feature anymore with Rust nightly.<commit_after>#![feature(core)]
#![feature(old_io)]
#![feature(path_ext)]

extern crate term;

mod version;
mod paths;
mod downloader;
mod builder;
mod reporter;

use std::rc::Rc;

use paths::Paths;
use downloader::Downloader;
use builder::Builder;

fn main() {
   dump_env();

   let paths = Paths::new();
   let paths_rc = Rc::new(paths);

   let nginx_downloader = Downloader::new(paths_rc.clone());
   nginx_downloader.download();

   let nginx_builder = Builder::new(paths_rc.clone());
   nginx_builder.build();
}

fn dump_env() {
   use std::env;
   use reporter::report;

   report("Dumping", "Environment");

   for (key, value) in env::vars() {
       println!("{:>30}: {}", key, value);
   }
}
<|endoftext|>


<commit_before>extern crate clap;
#[macro_use]
extern crate error_chain;
extern crate xxd;

use clap::{Arg, ArgMatches, App, SubCommand};
use error_chain::*;
use std::io::prelude::*;
use std::process::exit;
use std::fs::File;
use std::fmt::Display;
use std::isize::*;
use std::usize::*;
use std::num::ParseIntError;

mod errors {
    error_chain! {
        foreign_links {
            ParseError(::std::num::ParseIntError);
            Xxd(::xxd::errors::Error);
        }
    }
}

use errors::*;

fn main() {
    let matches = create_arg_parser().get_matches();
    match run(matches) {
        Ok(_) => exit(0),
        Err(e) => {
            report_error(e);
            exit(1)
        }
    }
}

fn run<'a>(args: ArgMatches<'a>) -> Result<()> {
    match args.subcommand_name() {
        Some("dump") => dump(args.subcommand_matches("dump")),
        Some("convert") => convert(args.subcommand_matches("convert")),
        Some("generate") => generate(args.subcommand_matches("generate")),
        _ => bail!(args.usage()),
    }
}

fn dump<'a>(args: Option<&ArgMatches<'a>>) -> Result<()> {
    let args = args.ok_or("No arguments available")?;
    // TODO NiCo: consider using stdin as default instead of reporting an error.
    let input_file = args.value_of("infile").ok_or("No input file specified")?;
    let columns = usize::from_str_radix(args.value_of("columns").unwrap_or("8"), 10)?;
    let format = args.value_of("columns").unwrap_or("hex");
    let seek = usize::from_str_radix(args.value_of("seek").unwrap_or("0"), 10)?;
    let length = isize::from_str_radix(args.value_of("length").unwrap_or("-1"), 10)?;
    // TODO NiCo: add group size as paramter
    let group_size = 1;
    println!("in-file: {}, columns: {}, format: {}, seek: {}, length: {}, group size: {}",
             input_file,
             columns,
             format,
             seek,
             length,
             group_size);
    // Read file chunk by chunk (chunk size = columns * group_size)
    // create output line for each chunk
    Ok(())
}

fn convert<'a>(args: Option<&ArgMatches<'a>>) -> Result<()> {
    command_not_supported()
}

fn generate<'a>(args: Option<&ArgMatches<'a>>) -> Result<()> {
    command_not_supported()
}

fn command_not_supported() -> Result<()> {
    bail!("Command not supported yet!")
}

fn report_error<T: Display>(error: T) {
    println!("xxd-rs: {}", error)
}

fn create_arg_parser<'a, 'b>() -> App<'a, 'b> {
    App::new("A rust based clone of the all time classic xxd tool")
        .version("0.1.0")
        .author("Nicola Coretti <nicola.coretti@gmail.com>")
        .about("make a hexdump or the reverse")
        .arg(Arg::with_name("infile")
                 .required(false)
                 .global(true)
                 .index(1)
                 .help("Input file which shall be used (default: stdin)"))
        .arg(Arg::with_name("outfile")
                 .required(false)
                 .global(true)
                 .index(2)
                 .help("File to which the output will be written (default: stdout)"))
        .arg(Arg::with_name("length")
                 .short("l")
                 .long("length")
                 .required(false)
                 .takes_value(true)
                 .global(true)
                 .help("Amount of bytes which shall be read"))
        .arg(Arg::with_name("seek")
                 .short("s")
                 .long("seek")
                 .required(false)
                 .takes_value(true)
                 .global(true)
                 .help("Offset in the file where to start reading"))
        .subcommand(SubCommand::with_name("dump")
                        .about("Dumps an input file in the appropriate output format")
                        .arg(Arg::with_name("format")
                                 .short("f")
                                 .long("format")
                                 .required(false)
                                 .takes_value(true)
                                 .possible_value("hex")
                                 .possible_value("bin")
                                 .possible_value("oct")
                                 .help("Specifies the output format for the value (default: hex)"))
                        .arg(Arg::with_name("columns")
                                 .short("c")
                                 .long("columns")
                                 .required(false)
                                 .takes_value(true)
                                 .help("Specifies the amount of output columns")))
        .subcommand(SubCommand::with_name("convert")
                        .about("Converts input data to a file (e.g. hexstream -> binary file")
                        .arg(Arg::with_name("format")
                                 .short("f")
                                 .long("format")
                                 .required(false)
                                 .takes_value(true)
                                 .possible_value("binary")
                                 .possible_value("c-array")
                                 .possible_value("oct")
                                 .help("Specifies the output format (default: hex)")))
        .subcommand(SubCommand::with_name("generate")
                        .about("Generates a source file containing the specified file as array")
                        .arg(Arg::with_name("template")
                                 .short("t")
                                 .long("template")
                                 .required(false)
                                 .takes_value(true)
                                 .possible_value("c")
                                 .possible_value("cpp")
                                 .possible_value("rs")
                                 .help("Specifies a template file which shall be used for \
                                        generation"))
                        .arg(Arg::with_name("format")
                                 .short("f")
                                 .long("format")
                                 .required(false)
                                 .takes_value(true)
                                 .possible_value("hex")
                                 .possible_value("oct")
                                 .possible_value("dec")
                                 .possible_value("bin")
                                 .help("Specifies the output format (default: hex)"))
                        .arg(Arg::with_name("Separator")
                                 .long("separator")
                                 .required(false)
                                 .takes_value(true)
                                 .help("Specifies the the separator between single values \
                                        (default: ',')")))
}
<commit_msg>Fix compiler and clippy warnings<commit_after>extern crate clap;
#[macro_use]
extern crate error_chain;
extern crate xxd;

use clap::{Arg, ArgMatches, App, SubCommand};
use std::process::exit;
use std::fmt::Display;

mod errors {
    error_chain! {
        foreign_links {
            ParseError(::std::num::ParseIntError);
            Xxd(::xxd::errors::Error);
        }
    }
}

use errors::*;

fn main() {
    let matches = create_arg_parser().get_matches();
    match run(&matches) {
        Ok(_) => exit(0),
        Err(e) => {
            report_error(&e);
            exit(1)
        }
    }
}

fn run(args: &ArgMatches) -> Result<()> {
    match args.subcommand_name() {
        Some("dump") => dump(args.subcommand_matches("dump")),
        Some("convert") => convert(args.subcommand_matches("convert")),
        Some("generate") => generate(args.subcommand_matches("generate")),
        _ => bail!(args.usage()),
    }
}

fn dump<'a>(args: Option<&ArgMatches<'a>>) -> Result<()> {
    let args = args.ok_or("No arguments available")?;
    // TODO NiCo: consider using stdin as default instead of reporting an error.
    let input_file = args.value_of("infile").ok_or("No input file specified")?;
    let columns = usize::from_str_radix(args.value_of("columns").unwrap_or("8"), 10)?;
    let format = args.value_of("columns").unwrap_or("hex");
    let seek = usize::from_str_radix(args.value_of("seek").unwrap_or("0"), 10)?;
    let length = isize::from_str_radix(args.value_of("length").unwrap_or("-1"), 10)?;
    // TODO NiCo: add group size as paramter
    let group_size = 1;
    println!("in-file: {}, columns: {}, format: {}, seek: {}, length: {}, group size: {}",
             input_file,
             columns,
             format,
             seek,
             length,
             group_size);
    // Read file chunk by chunk (chunk size = columns * group_size)
    // create output line for each chunk
    Ok(())
}

fn convert<'a>(args: Option<&ArgMatches<'a>>) -> Result<()> {
    command_not_supported()
}

fn generate<'a>(args: Option<&ArgMatches<'a>>) -> Result<()> {
    command_not_supported()
}

fn command_not_supported() -> Result<()> {
    bail!("Command not supported yet!")
}

fn report_error<T: Display>(error: &T) {
    println!("xxd-rs: {}", error)
}

fn create_arg_parser<'a, 'b>() -> App<'a, 'b> {
    App::new("A rust based clone of the all time classic xxd tool")
        .version("0.1.0")
        .author("Nicola Coretti <nicola.coretti@gmail.com>")
        .about("make a hexdump or the reverse")
        .arg(Arg::with_name("infile")
                 .required(false)
                 .global(true)
                 .index(1)
                 .help("Input file which shall be used (default: stdin)"))
        .arg(Arg::with_name("outfile")
                 .required(false)
                 .global(true)
                 .index(2)
                 .help("File to which the output will be written (default: stdout)"))
        .arg(Arg::with_name("length")
                 .short("l")
                 .long("length")
                 .required(false)
                 .takes_value(true)
                 .global(true)
                 .help("Amount of bytes which shall be read"))
        .arg(Arg::with_name("seek")
                 .short("s")
                 .long("seek")
                 .required(false)
                 .takes_value(true)
                 .global(true)
                 .help("Offset in the file where to start reading"))
        .subcommand(SubCommand::with_name("dump")
                        .about("Dumps an input file in the appropriate output format")
                        .arg(Arg::with_name("format")
                                 .short("f")
                                 .long("format")
                                 .required(false)
                                 .takes_value(true)
                                 .possible_value("hex")
                                 .possible_value("bin")
                                 .possible_value("oct")
                                 .help("Specifies the output format for the value (default: hex)"))
                        .arg(Arg::with_name("columns")
                                 .short("c")
                                 .long("columns")
                                 .required(false)
                                 .takes_value(true)
                                 .help("Specifies the amount of output columns")))
        .subcommand(SubCommand::with_name("convert")
                        .about("Converts input data to a file (e.g. hexstream -> binary file")
                        .arg(Arg::with_name("format")
                                 .short("f")
                                 .long("format")
                                 .required(false)
                                 .takes_value(true)
                                 .possible_value("binary")
                                 .possible_value("c-array")
                                 .possible_value("oct")
                                 .help("Specifies the output format (default: hex)")))
        .subcommand(SubCommand::with_name("generate")
                        .about("Generates a source file containing the specified file as array")
                        .arg(Arg::with_name("template")
                                 .short("t")
                                 .long("template")
                                 .required(false)
                                 .takes_value(true)
                                 .possible_value("c")
                                 .possible_value("cpp")
                                 .possible_value("rs")
                                 .help("Specifies a template file which shall be used for \
                                        generation"))
                        .arg(Arg::with_name("format")
                                 .short("f")
                                 .long("format")
                                 .required(false)
                                 .takes_value(true)
                                 .possible_value("hex")
                                 .possible_value("oct")
                                 .possible_value("dec")
                                 .possible_value("bin")
                                 .help("Specifies the output format (default: hex)"))
                        .arg(Arg::with_name("Separator")
                                 .long("separator")
                                 .required(false)
                                 .takes_value(true)
                                 .help("Specifies the the separator between single values \
                                        (default: ',')")))
}
<|endoftext|>


<commit_before>#![allow(dead_code)]
extern crate rusoto;
extern crate xml;
extern crate time;
extern crate regex;
extern crate rustc_serialize;
use rusoto::credentials::*;
use rusoto::error::*;
use rusoto::sqs::*;
use rusoto::s3::*;
use time::*;
use std::fs::File;
use std::io::Write;
use std::io::Read;
// use std::thread;

fn main() {
	let mut provider = DefaultAWSCredentialsProviderChain::new();

	match sqs_roundtrip_tests(&provider.get_credentials()) {
		Ok(_) => { println!("Everything worked."); },
		Err(err) => { println!("Got error: {:#?}", err); }
	}

	let bucket_name = format!("rusoto{}", get_time().sec);

	match s3_list_buckets_tests(&provider.get_credentials()) {
		Ok(_) => { println!("Everything worked for S3 list buckets."); },
		Err(err) => { println!("Got error in s3 list buckets: {:#?}", err); }
	}

	match s3_create_bucket_test(&provider.get_credentials(), &bucket_name) {
		Ok(_) => { println!("Everything worked for S3 create bucket."); },
		Err(err) => { println!("Got error in s3 create bucket: {:#?}", err); }
	}

	match s3_put_object_test(&provider.get_credentials(), &bucket_name) {
		Ok(_) => {
			println!("Everything worked for S3 put object.");
		}
		Err(err) => { println!("Got error in s3 put object: {:#?}", err); }
	}

	match s3_get_object_test(&provider.get_credentials(), &bucket_name) {
		Ok(result) => {
			println!("Everything worked for S3 get object.");
			let mut f = File::create("s3-sample-creds").unwrap();
			match f.write(&(result.body)) {
				Err(why) => println!("Couldn't create file to save object from S3: {}", why),
				Ok(_) => println!("Pretend this is a noop"),
			}
		}
		Err(err) => { println!("Got error in s3 get object: {:#?}", err); }
	}

	match s3_delete_object_test(&provider.get_credentials(), &bucket_name) {
		Ok(_) => {
			println!("Everything worked for S3 delete object.");
		}
		Err(err) => { println!("Got error in s3 delete object: {:#?}", err); }
	}

	match s3_put_object_with_reduced_redundancy_test(&provider.get_credentials(), &bucket_name) {
		Ok(_) => {
			println!("Everything worked for S3 put object with reduced redundancy.");
		}
		Err(err) => { println!("Got error in s3 put object: {:#?}", err); }
	}

	match s3_delete_object_test(&provider.get_credentials(), &bucket_name) {
		Ok(_) => {
			println!("Everything worked for S3 delete object.");
		}
		Err(err) => { println!("Got error in s3 delete object: {:#?}", err); }
	}

	match s3_delete_bucket_test(&provider.get_credentials(), &bucket_name) {
		Ok(_) => { println!("Everything worked for S3 delete bucket."); },
		Err(err) => { println!("Got error in s3 delete bucket: {:#?}", err); }
	}
}

fn s3_list_buckets_tests(creds: &AWSCredentials) -> Result<(), AWSError> {
	let s3 = S3Helper::new(&creds, "us-east-1");

	let response = try!(s3.list_buckets());
	// println!("response is {:?}", response);
	for q in response.buckets {
		println!("Existing bucket: {:?}", q.name);
	}

	Ok(())
}

fn s3_get_object_test(creds: &AWSCredentials, bucket: &str) -> Result<GetObjectOutput, AWSError> {
	let s3 = S3Helper::new(&creds, "us-east-1");

	let response = try!(s3.get_object(bucket, "sample-credentials"));
	// println!("get object response is {:?}", response);
	Ok(response)
}

fn s3_delete_object_test(creds: &AWSCredentials, bucket: &str) -> Result<DeleteObjectOutput, AWSError> {
	let s3 = S3Helper::new(&creds, "us-east-1");

	let response = try!(s3.delete_object(bucket, "sample-credentials"));
	// println!("delete object response is {:?}", response);
	Ok(response)
}

fn s3_put_object_test(creds: &AWSCredentials, bucket: &str) -> Result<PutObjectOutput, AWSError> {
	let s3 = S3Helper::new(&creds, "us-east-1");

	let mut f = File::open("src/sample-credentials").unwrap();
	let mut contents = Vec::new();
	match f.read_to_end(&mut contents) {
		Err(why) => return Err(AWSError::new(format!("Error opening file to send to S3: {}", why))),
		Ok(_) => {
			let response = try!(s3.put_object(bucket, "sample-credentials", &contents));
			Ok(response)
		}
	}
}

fn s3_put_object_with_reduced_redundancy_test(creds: &AWSCredentials, bucket: &str) -> Result<PutObjectOutput, AWSError> {
	let s3 = S3Helper::new(&creds, "us-east-1");

	let mut f = File::open("src/sample-credentials").unwrap();
	let mut contents = Vec::new();
	match f.read_to_end(&mut contents) {
		Err(why) => return Err(AWSError::new(format!("Error opening file to send to S3: {}", why))),
		Ok(_) => {
			let response = try!(s3.put_object_with_reduced_redundancy(bucket, "sample-credentials", &contents));
			Ok(response)
		}
	}
}

fn s3_create_bucket_test(creds: &AWSCredentials, bucket: &str) -> Result<(), AWSError> {
	let s3 = S3Helper::new(&creds, "us-east-1");

	try!(s3.create_bucket_in_region(bucket, "us-east-1"));

	Ok(())
}

fn s3_delete_bucket_test(creds: &AWSCredentials, bucket: &str) -> Result<(), AWSError> {
	let s3 = S3Helper::new(&creds, "us-east-1");

	try!(s3.delete_bucket(bucket, "us-east-1"));
	Ok(())
}

fn sqs_roundtrip_tests(creds: &AWSCredentials) -> Result<(), AWSError> {
	let sqs = SQSHelper::new(&creds, "us-east-1");

	// list existing queues
	let response = try!(sqs.list_queues());
	for q in response.queue_urls {
		println!("Existing queue: {}", q);
	}

	// create a new queue
	let q_name = &format!("test_q_{}", get_time().sec);
	let response = try!(sqs.create_queue(q_name));
	println!("Created queue {} with url {}", q_name, response.queue_url);

	// query it by name
	let response = try!(sqs.get_queue_url(q_name));
	let queue_url = &response.queue_url;
	println!("Verified queue url {} for queue name {}", queue_url, q_name);

	// send it a message
	let msg_str = "lorem ipsum dolor sit amet";
	let response = try!(sqs.send_message(queue_url, msg_str));
	println!("Send message with body '{}' and created message_id {}", msg_str, response.message_id);

	// receive a message
	let response = try!(sqs.receive_message(queue_url));
	for msg in response.messages {
		println!("Received message '{}' with id {}", msg.body, msg.message_id);
		try!(sqs.delete_message(queue_url, &msg.receipt_handle));
	}

	// delete the queue
	try!(sqs.delete_queue(queue_url));
	println!("Queue {} deleted", queue_url);

	Ok(())
}
<commit_msg>Makes error message for reduced redundancy s3 object put more clear.<commit_after>#![allow(dead_code)]
extern crate rusoto;
extern crate xml;
extern crate time;
extern crate regex;
extern crate rustc_serialize;
use rusoto::credentials::*;
use rusoto::error::*;
use rusoto::sqs::*;
use rusoto::s3::*;
use time::*;
use std::fs::File;
use std::io::Write;
use std::io::Read;
// use std::thread;

fn main() {
	let mut provider = DefaultAWSCredentialsProviderChain::new();

	match sqs_roundtrip_tests(&provider.get_credentials()) {
		Ok(_) => { println!("Everything worked."); },
		Err(err) => { println!("Got error: {:#?}", err); }
	}

	let bucket_name = format!("rusoto{}", get_time().sec);

	match s3_list_buckets_tests(&provider.get_credentials()) {
		Ok(_) => { println!("Everything worked for S3 list buckets."); },
		Err(err) => { println!("Got error in s3 list buckets: {:#?}", err); }
	}

	match s3_create_bucket_test(&provider.get_credentials(), &bucket_name) {
		Ok(_) => { println!("Everything worked for S3 create bucket."); },
		Err(err) => { println!("Got error in s3 create bucket: {:#?}", err); }
	}

	match s3_put_object_test(&provider.get_credentials(), &bucket_name) {
		Ok(_) => {
			println!("Everything worked for S3 put object.");
		}
		Err(err) => { println!("Got error in s3 put object: {:#?}", err); }
	}

	match s3_get_object_test(&provider.get_credentials(), &bucket_name) {
		Ok(result) => {
			println!("Everything worked for S3 get object.");
			let mut f = File::create("s3-sample-creds").unwrap();
			match f.write(&(result.body)) {
				Err(why) => println!("Couldn't create file to save object from S3: {}", why),
				Ok(_) => println!("Pretend this is a noop"),
			}
		}
		Err(err) => { println!("Got error in s3 get object: {:#?}", err); }
	}

	match s3_delete_object_test(&provider.get_credentials(), &bucket_name) {
		Ok(_) => {
			println!("Everything worked for S3 delete object.");
		}
		Err(err) => { println!("Got error in s3 delete object: {:#?}", err); }
	}

	match s3_put_object_with_reduced_redundancy_test(&provider.get_credentials(), &bucket_name) {
		Ok(_) => {
			println!("Everything worked for S3 put object with reduced redundancy.");
		}
		Err(err) => { println!("Got error in s3 put object with reduced redundancy: {:#?}", err); }
	}

	match s3_delete_object_test(&provider.get_credentials(), &bucket_name) {
		Ok(_) => {
			println!("Everything worked for S3 delete object.");
		}
		Err(err) => { println!("Got error in s3 delete object: {:#?}", err); }
	}

	match s3_delete_bucket_test(&provider.get_credentials(), &bucket_name) {
		Ok(_) => { println!("Everything worked for S3 delete bucket."); },
		Err(err) => { println!("Got error in s3 delete bucket: {:#?}", err); }
	}
}

fn s3_list_buckets_tests(creds: &AWSCredentials) -> Result<(), AWSError> {
	let s3 = S3Helper::new(&creds, "us-east-1");

	let response = try!(s3.list_buckets());
	// println!("response is {:?}", response);
	for q in response.buckets {
		println!("Existing bucket: {:?}", q.name);
	}

	Ok(())
}

fn s3_get_object_test(creds: &AWSCredentials, bucket: &str) -> Result<GetObjectOutput, AWSError> {
	let s3 = S3Helper::new(&creds, "us-east-1");

	let response = try!(s3.get_object(bucket, "sample-credentials"));
	// println!("get object response is {:?}", response);
	Ok(response)
}

fn s3_delete_object_test(creds: &AWSCredentials, bucket: &str) -> Result<DeleteObjectOutput, AWSError> {
	let s3 = S3Helper::new(&creds, "us-east-1");

	let response = try!(s3.delete_object(bucket, "sample-credentials"));
	// println!("delete object response is {:?}", response);
	Ok(response)
}

fn s3_put_object_test(creds: &AWSCredentials, bucket: &str) -> Result<PutObjectOutput, AWSError> {
	let s3 = S3Helper::new(&creds, "us-east-1");

	let mut f = File::open("src/sample-credentials").unwrap();
	let mut contents = Vec::new();
	match f.read_to_end(&mut contents) {
		Err(why) => return Err(AWSError::new(format!("Error opening file to send to S3: {}", why))),
		Ok(_) => {
			let response = try!(s3.put_object(bucket, "sample-credentials", &contents));
			Ok(response)
		}
	}
}

fn s3_put_object_with_reduced_redundancy_test(creds: &AWSCredentials, bucket: &str) -> Result<PutObjectOutput, AWSError> {
	let s3 = S3Helper::new(&creds, "us-east-1");

	let mut f = File::open("src/sample-credentials").unwrap();
	let mut contents = Vec::new();
	match f.read_to_end(&mut contents) {
		Err(why) => return Err(AWSError::new(format!("Error opening file to send to S3: {}", why))),
		Ok(_) => {
			let response = try!(s3.put_object_with_reduced_redundancy(bucket, "sample-credentials", &contents));
			Ok(response)
		}
	}
}

fn s3_create_bucket_test(creds: &AWSCredentials, bucket: &str) -> Result<(), AWSError> {
	let s3 = S3Helper::new(&creds, "us-east-1");

	try!(s3.create_bucket_in_region(bucket, "us-east-1"));

	Ok(())
}

fn s3_delete_bucket_test(creds: &AWSCredentials, bucket: &str) -> Result<(), AWSError> {
	let s3 = S3Helper::new(&creds, "us-east-1");

	try!(s3.delete_bucket(bucket, "us-east-1"));
	Ok(())
}

fn sqs_roundtrip_tests(creds: &AWSCredentials) -> Result<(), AWSError> {
	let sqs = SQSHelper::new(&creds, "us-east-1");

	// list existing queues
	let response = try!(sqs.list_queues());
	for q in response.queue_urls {
		println!("Existing queue: {}", q);
	}

	// create a new queue
	let q_name = &format!("test_q_{}", get_time().sec);
	let response = try!(sqs.create_queue(q_name));
	println!("Created queue {} with url {}", q_name, response.queue_url);

	// query it by name
	let response = try!(sqs.get_queue_url(q_name));
	let queue_url = &response.queue_url;
	println!("Verified queue url {} for queue name {}", queue_url, q_name);

	// send it a message
	let msg_str = "lorem ipsum dolor sit amet";
	let response = try!(sqs.send_message(queue_url, msg_str));
	println!("Send message with body '{}' and created message_id {}", msg_str, response.message_id);

	// receive a message
	let response = try!(sqs.receive_message(queue_url));
	for msg in response.messages {
		println!("Received message '{}' with id {}", msg.body, msg.message_id);
		try!(sqs.delete_message(queue_url, &msg.receipt_handle));
	}

	// delete the queue
	try!(sqs.delete_queue(queue_url));
	println!("Queue {} deleted", queue_url);

	Ok(())
}
<|endoftext|>