<commit_before>extern crate openssl;
extern crate openssl_probe;

use self::openssl::error::ErrorStack;
use self::openssl::pkcs12::{ParsedPkcs12, Pkcs12};
use self::openssl::ssl::{
    self, MidHandshakeSslStream, SslAcceptor, SslConnector, SslContextBuilder, SslMethod,
    SslVerifyMode,
};
use self::openssl::x509::{X509, X509VerifyResult};
use std::error;
use std::fmt;
use std::io;
use std::sync::{Once, ONCE_INIT};

use {Protocol, TlsAcceptorBuilder, TlsConnectorBuilder};

#[cfg(have_min_max_version)]
fn supported_protocols(
    min: Option<Protocol>,
    max: Option<Protocol>,
    ctx: &mut SslContextBuilder,
) -> Result<(), ErrorStack> {
    use self::openssl::ssl::SslVersion;

    fn cvt(p: Protocol) -> SslVersion {
        match p {
            Protocol::Sslv3 => SslVersion::SSL3,
            Protocol::Tlsv10 => SslVersion::TLS1,
            Protocol::Tlsv11 => SslVersion::TLS1_1,
            Protocol::Tlsv12 => SslVersion::TLS1_2,
            Protocol::__NonExhaustive => unreachable!(),
        }
    }

    ctx.set_min_proto_version(min.map(cvt))?;
    ctx.set_max_proto_version(max.map(cvt))?;

    Ok(())
}

#[cfg(not(have_min_max_version))]
fn supported_protocols(
    min: Option<Protocol>,
    max: Option<Protocol>,
    ctx: &mut SslContextBuilder,
) -> Result<(), ErrorStack> {
    use self::openssl::ssl::SslOptions;

    let no_ssl_mask = SslOptions::NO_SSLV2
        | SslOptions::NO_SSLV3
        | SslOptions::NO_TLSV1
        | SslOptions::NO_TLSV1_1
        | SslOptions::NO_TLSV1_2;

    ctx.clear_options(no_ssl_mask);
    let mut options = SslOptions::empty();
    options |= match min {
        None => SslOptions::empty(),
        Some(Protocol::Sslv3) => SslOptions::NO_SSLV2,
        Some(Protocol::Tlsv10) => SslOptions::NO_SSLV2 | SslOptions::NO_SSLV3,
        Some(Protocol::Tlsv11) => {
            SslOptions::NO_SSLV2 | SslOptions::NO_SSLV3 | SslOptions::NO_TLSV1
        }
        Some(Protocol::Tlsv12) => {
            SslOptions::NO_SSLV2
                | SslOptions::NO_SSLV3
                | SslOptions::NO_TLSV1
                | SslOptions::NO_TLSV1_1
        }
        Some(Protocol::__NonExhaustive) => unreachable!(),
    };
    options |= match max {
        None | Some(Protocol::Tlsv12) => SslOptions::empty(),
        Some(Protocol::Tlsv11) => SslOptions::NO_TLSV1_2,
        Some(Protocol::Tlsv10) => SslOptions::NO_TLSV1_1 | SslOptions::NO_TLSV1_2,
        Some(Protocol::Sslv3) => {
            SslOptions::NO_TLSV1 | SslOptions::NO_TLSV1_1 | SslOptions::NO_TLSV1_2
        }
        Some(Protocol::__NonExhaustive) => unreachable!(),
    };

    ctx.set_options(options);

    Ok(())
}

fn init_trust() {
    static ONCE: Once = ONCE_INIT;
    ONCE.call_once(|| openssl_probe::init_ssl_cert_env_vars());
}

#[derive(Debug)]
pub enum Error {
    Normal(ErrorStack),
    Ssl(ssl::Error, X509VerifyResult),
}

impl error::Error for Error {
    fn description(&self) -> &str {
        match *self {
            Error::Normal(ref e) => error::Error::description(e),
            Error::Ssl(ref e, _) => error::Error::description(e),
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match *self {
            Error::Normal(ref e) => error::Error::cause(e),
            Error::Ssl(ref e, _) => error::Error::cause(e),
        }
    }
}

impl fmt::Display for Error {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::Normal(ref e) => fmt::Display::fmt(e, fmt),
            Error::Ssl(ref e, X509VerifyResult::OK) => fmt::Display::fmt(e, fmt),
            Error::Ssl(ref e, v) => write!(fmt, "{} ({})", e, v),
        }
    }
}

impl From<ErrorStack> for Error {
    fn from(err: ErrorStack) -> Error {
        Error::Normal(err)
    }
}

pub struct Identity(ParsedPkcs12);

impl Identity {
    pub fn from_pkcs12(buf: &[u8], pass: &str) -> Result<Identity, Error> {
        let pkcs12 = Pkcs12::from_der(buf)?;
        let parsed = pkcs12.parse(pass)?;
        Ok(Identity(parsed))
    }
}

#[derive(Clone)]
pub struct Certificate(X509);

impl Certificate {
    pub fn from_der(buf: &[u8]) -> Result<Certificate, Error> {
        let cert = X509::from_der(buf)?;
        Ok(Certificate(cert))
    }

    pub fn from_pem(buf: &[u8]) -> Result<Certificate, Error> {
        let cert = X509::from_pem(buf)?;
        Ok(Certificate(cert))
    }
}

pub struct MidHandshakeTlsStream<S>(MidHandshakeSslStream<S>);

impl<S> fmt::Debug for MidHandshakeTlsStream<S>
where
    S: fmt::Debug,
{
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt::Debug::fmt(&self.0, fmt)
    }
}

impl<S> MidHandshakeTlsStream<S> {
    pub fn get_ref(&self) -> &S {
        self.0.get_ref()
    }

    pub fn get_mut(&mut self) -> &mut S {
        self.0.get_mut()
    }
}

impl<S> MidHandshakeTlsStream<S>
where
    S: io::Read + io::Write,
{
    pub fn handshake(self) -> Result<TlsStream<S>, HandshakeError<S>> {
        match self.0.handshake() {
            Ok(s) => Ok(TlsStream(s)),
            Err(e) => Err(e.into()),
        }
    }
}

pub enum HandshakeError<S> {
    Failure(Error),
    WouldBlock(MidHandshakeTlsStream<S>),
}

impl<S> From<ssl::HandshakeError<S>> for HandshakeError<S> {
    fn from(e: ssl::HandshakeError<S>) -> HandshakeError<S> {
        match e {
            ssl::HandshakeError::SetupFailure(e) => HandshakeError::Failure(e.into()),
            ssl::HandshakeError::Failure(e) => {
                let v = e.ssl().verify_result();
                HandshakeError::Failure(Error::Ssl(e.into_error(), v))
            }
            ssl::HandshakeError::WouldBlock(s) => {
                HandshakeError::WouldBlock(MidHandshakeTlsStream(s))
            }
        }
    }
}

impl<S> From<ErrorStack> for HandshakeError<S> {
    fn from(e: ErrorStack) -> HandshakeError<S> {
        HandshakeError::Failure(e.into())
    }
}

#[derive(Clone)]
pub struct TlsConnector {
    connector: SslConnector,
    use_sni: bool,
    accept_invalid_hostnames: bool,
    accept_invalid_certs: bool,
}

impl TlsConnector {
    pub fn new(builder: &TlsConnectorBuilder) -> Result<TlsConnector, Error> {
        init_trust();

        let mut connector = SslConnector::builder(SslMethod::tls())?;
        if let Some(ref identity) = builder.identity {
            connector.set_certificate(&(identity.0).0.cert)?;
            connector.set_private_key(&(identity.0).0.pkey)?;
            if let Some(ref chain) = (identity.0).0.chain {
                for cert in chain {
                    connector.add_extra_chain_cert(cert.to_owned())?;
                }
            }
        }
        supported_protocols(builder.min_protocol, builder.max_protocol, &mut connector)?;

        for cert in &builder.root_certificates {
            connector.cert_store_mut().add_cert((cert.0).0.clone())?;
        }

        Ok(TlsConnector {
            connector: connector.build(),
            use_sni: builder.use_sni,
            accept_invalid_hostnames: builder.accept_invalid_hostnames,
            accept_invalid_certs: builder.accept_invalid_certs,
        })
    }

    pub fn connect<S>(&self, domain: &str, stream: S) -> Result<TlsStream<S>, HandshakeError<S>>
    where
        S: io::Read + io::Write,
    {
        let mut ssl = self
            .connector
            .configure()?
            .use_server_name_indication(self.use_sni)
            .verify_hostname(!self.accept_invalid_hostnames);
        if self.accept_invalid_certs {
            ssl.set_verify(SslVerifyMode::NONE);
        }

        let s = ssl.connect(domain, stream)?;
        Ok(TlsStream(s))
    }
}

#[derive(Clone)]
pub struct TlsAcceptor(SslAcceptor);

impl TlsAcceptor {
    pub fn new(builder: &TlsAcceptorBuilder) -> Result<TlsAcceptor, Error> {
        let mut acceptor = SslAcceptor::mozilla_intermediate(SslMethod::tls())?;
        acceptor.set_private_key(&(builder.identity.0).0.pkey)?;
        acceptor.set_certificate(&(builder.identity.0).0.cert)?;
        if let Some(ref chain) = (builder.identity.0).0.chain {
            for cert in chain {
                acceptor.add_extra_chain_cert(cert.to_owned())?;
            }
        }
        supported_protocols(builder.min_protocol, builder.max_protocol, &mut acceptor)?;

        Ok(TlsAcceptor(acceptor.build()))
    }

    pub fn accept<S>(&self, stream: S) -> Result<TlsStream<S>, HandshakeError<S>>
    where
        S: io::Read + io::Write,
    {
        let s = self.0.accept(stream)?;
        Ok(TlsStream(s))
    }
}

pub struct TlsStream<S>(ssl::SslStream<S>);

impl<S: fmt::Debug> fmt::Debug for TlsStream<S> {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt::Debug::fmt(&self.0, fmt)
    }
}

impl<S: io::Read + io::Write> TlsStream<S> {
    pub fn buffered_read_size(&self) -> Result<usize, Error> {
        Ok(self.0.ssl().pending())
    }

    pub fn shutdown(&mut self) -> io::Result<()> {
        match self.0.shutdown() {
            Ok(_) => Ok(()),
            Err(ref e) if e.code() == ssl::ErrorCode::ZERO_RETURN => Ok(()),
            Err(e) => Err(e
                .into_io_error()
                .unwrap_or_else(|e| io::Error::new(io::ErrorKind::Other, e))),
        }
    }

    pub fn get_ref(&self) -> &S {
        self.0.get_ref()
    }

    pub fn get_mut(&mut self) -> &mut S {
        self.0.get_mut()
    }
}

impl<S: io::Read + io::Write> io::Read for TlsStream<S> {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        self.0.read(buf)
    }
}

impl<S: io::Read + io::Write> io::Write for TlsStream<S> {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.0.write(buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.0.flush()
    }
}
<commit_msg>Set Android root certs<commit_after>extern crate openssl;
extern crate openssl_probe;

use self::openssl::error::ErrorStack;
use self::openssl::pkcs12::{ParsedPkcs12, Pkcs12};
use self::openssl::ssl::{
    self, MidHandshakeSslStream, SslAcceptor, SslConnector, SslContextBuilder, SslMethod,
    SslVerifyMode,
};
use self::openssl::x509::{X509, X509VerifyResult};
use std::error;
use std::fmt;
use std::io;
use std::sync::{Once, ONCE_INIT};

use {Protocol, TlsAcceptorBuilder, TlsConnectorBuilder};

#[cfg(have_min_max_version)]
fn supported_protocols(
    min: Option<Protocol>,
    max: Option<Protocol>,
    ctx: &mut SslContextBuilder,
) -> Result<(), ErrorStack> {
    use self::openssl::ssl::SslVersion;

    fn cvt(p: Protocol) -> SslVersion {
        match p {
            Protocol::Sslv3 => SslVersion::SSL3,
            Protocol::Tlsv10 => SslVersion::TLS1,
            Protocol::Tlsv11 => SslVersion::TLS1_1,
            Protocol::Tlsv12 => SslVersion::TLS1_2,
            Protocol::__NonExhaustive => unreachable!(),
        }
    }

    ctx.set_min_proto_version(min.map(cvt))?;
    ctx.set_max_proto_version(max.map(cvt))?;

    Ok(())
}

#[cfg(not(have_min_max_version))]
fn supported_protocols(
    min: Option<Protocol>,
    max: Option<Protocol>,
    ctx: &mut SslContextBuilder,
) -> Result<(), ErrorStack> {
    use self::openssl::ssl::SslOptions;

    let no_ssl_mask = SslOptions::NO_SSLV2
        | SslOptions::NO_SSLV3
        | SslOptions::NO_TLSV1
        | SslOptions::NO_TLSV1_1
        | SslOptions::NO_TLSV1_2;

    ctx.clear_options(no_ssl_mask);
    let mut options = SslOptions::empty();
    options |= match min {
        None => SslOptions::empty(),
        Some(Protocol::Sslv3) => SslOptions::NO_SSLV2,
        Some(Protocol::Tlsv10) => SslOptions::NO_SSLV2 | SslOptions::NO_SSLV3,
        Some(Protocol::Tlsv11) => {
            SslOptions::NO_SSLV2 | SslOptions::NO_SSLV3 | SslOptions::NO_TLSV1
        }
        Some(Protocol::Tlsv12) => {
            SslOptions::NO_SSLV2
                | SslOptions::NO_SSLV3
                | SslOptions::NO_TLSV1
                | SslOptions::NO_TLSV1_1
        }
        Some(Protocol::__NonExhaustive) => unreachable!(),
    };
    options |= match max {
        None | Some(Protocol::Tlsv12) => SslOptions::empty(),
        Some(Protocol::Tlsv11) => SslOptions::NO_TLSV1_2,
        Some(Protocol::Tlsv10) => SslOptions::NO_TLSV1_1 | SslOptions::NO_TLSV1_2,
        Some(Protocol::Sslv3) => {
            SslOptions::NO_TLSV1 | SslOptions::NO_TLSV1_1 | SslOptions::NO_TLSV1_2
        }
        Some(Protocol::__NonExhaustive) => unreachable!(),
    };

    ctx.set_options(options);

    Ok(())
}

fn init_trust() {
    static ONCE: Once = ONCE_INIT;
    ONCE.call_once(|| openssl_probe::init_ssl_cert_env_vars());
}

#[derive(Debug)]
pub enum Error {
    Normal(ErrorStack),
    Ssl(ssl::Error, X509VerifyResult),
}

impl error::Error for Error {
    fn description(&self) -> &str {
        match *self {
            Error::Normal(ref e) => error::Error::description(e),
            Error::Ssl(ref e, _) => error::Error::description(e),
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match *self {
            Error::Normal(ref e) => error::Error::cause(e),
            Error::Ssl(ref e, _) => error::Error::cause(e),
        }
    }
}

impl fmt::Display for Error {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::Normal(ref e) => fmt::Display::fmt(e, fmt),
            Error::Ssl(ref e, X509VerifyResult::OK) => fmt::Display::fmt(e, fmt),
            Error::Ssl(ref e, v) => write!(fmt, "{} ({})", e, v),
        }
    }
}

impl From<ErrorStack> for Error {
    fn from(err: ErrorStack) -> Error {
        Error::Normal(err)
    }
}

pub struct Identity(ParsedPkcs12);

impl Identity {
    pub fn from_pkcs12(buf: &[u8], pass: &str) -> Result<Identity, Error> {
        let pkcs12 = Pkcs12::from_der(buf)?;
        let parsed = pkcs12.parse(pass)?;
        Ok(Identity(parsed))
    }
}

#[derive(Clone)]
pub struct Certificate(X509);

impl Certificate {
    pub fn from_der(buf: &[u8]) -> Result<Certificate, Error> {
        let cert = X509::from_der(buf)?;
        Ok(Certificate(cert))
    }

    pub fn from_pem(buf: &[u8]) -> Result<Certificate, Error> {
        let cert = X509::from_pem(buf)?;
        Ok(Certificate(cert))
    }
}

pub struct MidHandshakeTlsStream<S>(MidHandshakeSslStream<S>);

impl<S> fmt::Debug for MidHandshakeTlsStream<S>
where
    S: fmt::Debug,
{
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt::Debug::fmt(&self.0, fmt)
    }
}

impl<S> MidHandshakeTlsStream<S> {
    pub fn get_ref(&self) -> &S {
        self.0.get_ref()
    }

    pub fn get_mut(&mut self) -> &mut S {
        self.0.get_mut()
    }
}

impl<S> MidHandshakeTlsStream<S>
where
    S: io::Read + io::Write,
{
    pub fn handshake(self) -> Result<TlsStream<S>, HandshakeError<S>> {
        match self.0.handshake() {
            Ok(s) => Ok(TlsStream(s)),
            Err(e) => Err(e.into()),
        }
    }
}

pub enum HandshakeError<S> {
    Failure(Error),
    WouldBlock(MidHandshakeTlsStream<S>),
}

impl<S> From<ssl::HandshakeError<S>> for HandshakeError<S> {
    fn from(e: ssl::HandshakeError<S>) -> HandshakeError<S> {
        match e {
            ssl::HandshakeError::SetupFailure(e) => HandshakeError::Failure(e.into()),
            ssl::HandshakeError::Failure(e) => {
                let v = e.ssl().verify_result();
                HandshakeError::Failure(Error::Ssl(e.into_error(), v))
            }
            ssl::HandshakeError::WouldBlock(s) => {
                HandshakeError::WouldBlock(MidHandshakeTlsStream(s))
            }
        }
    }
}

impl<S> From<ErrorStack> for HandshakeError<S> {
    fn from(e: ErrorStack) -> HandshakeError<S> {
        HandshakeError::Failure(e.into())
    }
}

#[derive(Clone)]
pub struct TlsConnector {
    connector: SslConnector,
    use_sni: bool,
    accept_invalid_hostnames: bool,
    accept_invalid_certs: bool,
}

impl TlsConnector {
    pub fn new(builder: &TlsConnectorBuilder) -> Result<TlsConnector, Error> {
        init_trust();

        let mut connector = SslConnector::builder(SslMethod::tls())?;
        if let Some(ref identity) = builder.identity {
            connector.set_certificate(&(identity.0).0.cert)?;
            connector.set_private_key(&(identity.0).0.pkey)?;
            if let Some(ref chain) = (identity.0).0.chain {
                for cert in chain {
                    connector.add_extra_chain_cert(cert.to_owned())?;
                }
            }
        }
        supported_protocols(builder.min_protocol, builder.max_protocol, &mut connector)?;

        for cert in &builder.root_certificates {
            connector.cert_store_mut().add_cert((cert.0).0.clone())?;
        }

        // Add android root certs

        #[cfg(target_os = "android")]
        {
            use std::fs;
            use std::io::Read;

            //let mut cert_store = builder.builder_mut().cert_store_mut();
            let mut cert_store = connector.cert_store_mut();

            if let Ok(certs) = fs::read_dir("/system/etc/security/cacerts") {
                for entry in certs.filter_map(|r| r.ok()).filter(|e| e.path().is_file()) {
                    let mut cert = String::new();
                    if let Ok(_) = fs::File::open(entry.path())
                        .and_then(|mut f| f.read_to_string(&mut cert)) {
                        if let Ok(cert) = X509::from_pem(cert.as_bytes()) {
                            try!(cert_store.add_cert(cert));
                        }
                    }
                }
            }
        }

        Ok(TlsConnector {
            connector: connector.build(),
            use_sni: builder.use_sni,
            accept_invalid_hostnames: builder.accept_invalid_hostnames,
            accept_invalid_certs: builder.accept_invalid_certs,
        })
    }

    pub fn connect<S>(&self, domain: &str, stream: S) -> Result<TlsStream<S>, HandshakeError<S>>
    where
        S: io::Read + io::Write,
    {
        let mut ssl = self
            .connector
            .configure()?
            .use_server_name_indication(self.use_sni)
            .verify_hostname(!self.accept_invalid_hostnames);
        if self.accept_invalid_certs {
            ssl.set_verify(SslVerifyMode::NONE);
        }

        let s = ssl.connect(domain, stream)?;
        Ok(TlsStream(s))
    }
}

#[derive(Clone)]
pub struct TlsAcceptor(SslAcceptor);

impl TlsAcceptor {
    pub fn new(builder: &TlsAcceptorBuilder) -> Result<TlsAcceptor, Error> {
        let mut acceptor = SslAcceptor::mozilla_intermediate(SslMethod::tls())?;
        acceptor.set_private_key(&(builder.identity.0).0.pkey)?;
        acceptor.set_certificate(&(builder.identity.0).0.cert)?;
        if let Some(ref chain) = (builder.identity.0).0.chain {
            for cert in chain {
                acceptor.add_extra_chain_cert(cert.to_owned())?;
            }
        }
        supported_protocols(builder.min_protocol, builder.max_protocol, &mut acceptor)?;

        Ok(TlsAcceptor(acceptor.build()))
    }

    pub fn accept<S>(&self, stream: S) -> Result<TlsStream<S>, HandshakeError<S>>
    where
        S: io::Read + io::Write,
    {
        let s = self.0.accept(stream)?;
        Ok(TlsStream(s))
    }
}

pub struct TlsStream<S>(ssl::SslStream<S>);

impl<S: fmt::Debug> fmt::Debug for TlsStream<S> {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt::Debug::fmt(&self.0, fmt)
    }
}

impl<S: io::Read + io::Write> TlsStream<S> {
    pub fn buffered_read_size(&self) -> Result<usize, Error> {
        Ok(self.0.ssl().pending())
    }

    pub fn shutdown(&mut self) -> io::Result<()> {
        match self.0.shutdown() {
            Ok(_) => Ok(()),
            Err(ref e) if e.code() == ssl::ErrorCode::ZERO_RETURN => Ok(()),
            Err(e) => Err(e
                .into_io_error()
                .unwrap_or_else(|e| io::Error::new(io::ErrorKind::Other, e))),
        }
    }

    pub fn get_ref(&self) -> &S {
        self.0.get_ref()
    }

    pub fn get_mut(&mut self) -> &mut S {
        self.0.get_mut()
    }
}

impl<S: io::Read + io::Write> io::Read for TlsStream<S> {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        self.0.read(buf)
    }
}

impl<S: io::Read + io::Write> io::Write for TlsStream<S> {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.0.write(buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.0.flush()
    }
}
<|endoftext|>


<commit_before>use pancurses::{Input, Window};
use errext::{ErrExt, PResult};
use interpreter::command::run_expression;

fn print_buffer(screen: &Window, buffer: &str) -> PResult {
    let height = (screen.get_max_y() - 3) as usize;
    let width = (screen.get_max_x() - 1) as usize;
    screen.mv(0, 0).unwrap();
    for line in buffer.lines().take(height) {
        let max = ::std::cmp::min(width, line.len());
        screen.addstr(&line[..max]).unwrap();
        screen.addch('\n').unwrap();
    }
    Ok(())
}

fn print_command(screen: &Window, buffer: &str) -> PResult {
    let height = screen.get_max_y() - 1;
    let width = screen.get_max_x() - 1;
    screen.mv(height - 1, 0).unwrap();
    screen.hline(0, width).unwrap();
    screen.mv(height, 0).unwrap();
    let max = ::std::cmp::min(width as usize, buffer.len());
    screen.addstr(&buffer[..max]).unwrap();
    Ok(())
}

pub fn input_loop(screen: Window) -> PResult {
    let mut output_buffer = String::from("");
    let mut command_buffer = String::new();
    // let (mut x, mut y) = (0, 0);
    loop {
        screen.clear().unwrap();
        print_buffer(&screen, &output_buffer)?;
        print_command(&screen, &command_buffer)?;
        screen.refresh().unwrap();
        let maybe_next = screen.getch();
        if let Some(next) = maybe_next {
            match next {
                Input::KeyBackspace |
                Input::Character('\x08') |
                Input::Character('\x7f') => {
                    command_buffer.pop();
                }
                Input::Character('\n') => {
                    if command_buffer == "" {
                        continue;
                    }
                    output_buffer += "$ ";
                    let output = run_expression(&command_buffer);
                    match output {
                        Ok(output) => {
                            output_buffer += &output.command;
                            output_buffer += "\n";
                            output_buffer += &output.interleaved;
                        }
                        Err(message) => {
                            output_buffer += &command_buffer;
                            output_buffer += "\n";
                            output_buffer += &message;
                        }
                    }
                    output_buffer += "\n";
                    command_buffer.clear();
                }
                Input::Character(chr) => {
                    println!("char: {:?}", chr);
                    command_buffer.push(chr);
                }
                _ => {}
            }
        }
    }
}
<commit_msg>remove debug print<commit_after>use pancurses::{Input, Window};
use errext::{ErrExt, PResult};
use interpreter::command::run_expression;

fn print_buffer(screen: &Window, buffer: &str) -> PResult {
    let height = (screen.get_max_y() - 3) as usize;
    let width = (screen.get_max_x() - 1) as usize;
    screen.mv(0, 0).unwrap();
    for line in buffer.lines().take(height) {
        let max = ::std::cmp::min(width, line.len());
        screen.addstr(&line[..max]).unwrap();
        screen.addch('\n').unwrap();
    }
    Ok(())
}

fn print_command(screen: &Window, buffer: &str) -> PResult {
    let height = screen.get_max_y() - 1;
    let width = screen.get_max_x() - 1;
    screen.mv(height - 1, 0).unwrap();
    screen.hline(0, width).unwrap();
    screen.mv(height, 0).unwrap();
    let max = ::std::cmp::min(width as usize, buffer.len());
    screen.addstr(&buffer[..max]).unwrap();
    Ok(())
}

pub fn input_loop(screen: Window) -> PResult {
    let mut output_buffer = String::from("");
    let mut command_buffer = String::new();
    // let (mut x, mut y) = (0, 0);
    loop {
        screen.clear().unwrap();
        print_buffer(&screen, &output_buffer)?;
        print_command(&screen, &command_buffer)?;
        screen.refresh().unwrap();
        let maybe_next = screen.getch();
        if let Some(next) = maybe_next {
            match next {
                Input::KeyBackspace |
                Input::Character('\x08') |
                Input::Character('\x7f') => {
                    command_buffer.pop();
                }
                Input::Character('\n') => {
                    if command_buffer == "" {
                        continue;
                    }
                    output_buffer += "$ ";
                    let output = run_expression(&command_buffer);
                    match output {
                        Ok(output) => {
                            output_buffer += &output.command;
                            output_buffer += "\n";
                            output_buffer += &output.interleaved;
                        }
                        Err(message) => {
                            output_buffer += &command_buffer;
                            output_buffer += "\n";
                            output_buffer += &message;
                        }
                    }
                    output_buffer += "\n";
                    command_buffer.clear();
                }
                Input::Character(chr) => {
                    command_buffer.push(chr);
                }
                _ => {}
            }
        }
    }
}
<|endoftext|>


<commit_before>use edit::buffer::Buffer;
use io::redraw::RedrawTask;
use state::editor::Editor;
use state::mode::{Mode, PrimitiveMode};

#[cfg(feature = "orbital")]
use orbclient::Color;

#[cfg(feature = "orbital")]
impl Editor {
    /// Redraw the window
    pub fn redraw(&mut self) {
        // TODO: Only draw when relevant for the window
        let (pos_x, pos_y) = self.pos();
        // Redraw window
        self.window.set(Color::rgb(25, 25, 25));

        let w = self.window.width();

        if self.options.line_marker {
            self.window.rect(0,
                             (pos_y - self.scroll_y) as i32 * 16,
                             w,
                             16,
                             Color::rgb(45, 45, 45));
        }

        self.window.rect(8 * (pos_x - self.scroll_x) as i32,
                         16 * (pos_y - self.scroll_y) as i32,
                         8,
                         16,
                         Color::rgb(255, 255, 255));

        let mut string = false;

        for (y, row) in self.buffer.lines().enumerate() {
            for (x, c) in row.chars().enumerate() {
                // TODO: Move outta here
                let color = if self.options.highlight {
                    match c {
                        '\'' | '"' => {
                            string = !string;
                            (226, 225, 167) //(167, 222, 156)
                        }
                        _ if string => (226, 225, 167), //(167, 222, 156)
                        '!' |
                        '@' |
                        '#' |
                        '$' |
                        '%' |
                        '^' |
                        '&' |
                        '|' |
                        '*' |
                        '+' |
                        '-' |
                        '/' |
                        ':' |
                        '=' |
                        '<' |
                        '>' => (198, 83, 83), //(228, 190, 175), //(194, 106, 71),
                        '.' | ',' => (241, 213, 226),
                        '(' | ')' | '[' | ']' | '{' | '}' => (164, 212, 125), //(195, 139, 75),
                        '0' ... '9' => (209, 209, 177),
                        _ => (255, 255, 255),
                    }
                } else {
                    (255, 255, 255)
                };

                let c = if c == '\t' {
                    ' '
                } else {
                    c
                };

                if pos_x == x && pos_y == y {
                    self.window.char(8 * (x - self.scroll_x) as i32,
                                     16 * (y - self.scroll_y) as i32,
                                     c,
                                     Color::rgb(color.0 / 3, color.1 / 3, color.2 / 3));
                } else {
                    self.window.char(8 * (x - self.scroll_x) as i32,
                                     16 * (y - self.scroll_y) as i32,
                                     c,
                                     Color::rgb(color.0, color.1, color.2));
                }
            }
        }

        self.redraw_task = RedrawTask::None;

        self.redraw_status_bar();
        self.window.sync();
    }

    /// Redraw the status bar
    pub fn redraw_status_bar(&mut self) {
        let h = self.window.height();
        let w = self.window.width();
        let mode = self.cursor().mode;
        self.window.rect(0,
                         h as i32 - 18 -
                         {
                             if mode == Mode::Primitive(PrimitiveMode::Prompt) {
                                 18
                             } else {
                                 0
                             }
                         },
                         w,
                         18,
                         Color::rgba(74, 74, 74, 255));

        status_bar(self, self.status_bar.mode.to_owned(), 0, 4);
        let sb_file = self.status_bar.file.clone();
        status_bar(self, sb_file, 1, 4);
        let sb_cmd = self.status_bar.cmd.clone();
        status_bar(self, sb_cmd, 2, 4);
        let sb_msg = self.status_bar.msg.clone();
        status_bar(self, sb_msg, 3, 4);

        for (n, c) in self.prompt.chars().enumerate() {
            self.window.char(n as i32 * 8, h as i32 - 16 - 1, c, Color::rgb(255, 255, 255));
        }

        self.window.sync();
    }
}

// TODO take &str instead
#[cfg(feature = "orbital")]
fn status_bar(editor: &mut Editor, text: String, a: u32, b: u32) {

    let h = editor.window.height();
    let w = editor.window.width();
    // let y = editor.y();
    let mode = editor.cursor().mode;

    for (n, c) in (if text.len() as u32 > w / (8 * b) {
                      text.chars().take((w / (8 * b) - 5) as usize).chain(vec!['.'; 3]).collect::<Vec<_>>()
                  } else {
                      text.chars().collect()
                  })
                  .into_iter()
                  .enumerate() {

        editor.window.char(((w * a) / b) as i32 + (n as i32 * 8),
                            h as i32 - 16 - 1 -
                            {
                                if mode == Mode::Primitive(PrimitiveMode::Prompt) {
                                    16 + 1 + 1
                                } else {
                                    0
                                }
                            },
                            c,
                            Color::rgb(255, 255, 255));
    }
}

/// The statubar (showing various info about the current state of the editor)
pub struct StatusBar {
    /// The current mode
    pub mode: &'static str,
    /// The cureent char
    pub file: String,
    /// The current command
    pub cmd: String,
    /// A message (such as an error or other info to the user)
    pub msg: String,
}

impl StatusBar {
    /// Create new status bar
    pub fn new() -> Self {
        StatusBar {
            mode: "Normal",
            file: String::new(),
            cmd: String::new(),
            msg: "Welcome to Sodium!".to_string(),
        }
    }
}
<commit_msg>Removing unnecessary string clones on status bar redraw<commit_after>use edit::buffer::Buffer;
use io::redraw::RedrawTask;
use state::editor::Editor;
use state::mode::{Mode, PrimitiveMode};

#[cfg(feature = "orbital")]
use orbclient::Color;

#[cfg(feature = "orbital")]
impl Editor {
    /// Redraw the window
    pub fn redraw(&mut self) {
        // TODO: Only draw when relevant for the window
        let (pos_x, pos_y) = self.pos();
        // Redraw window
        self.window.set(Color::rgb(25, 25, 25));

        let w = self.window.width();

        if self.options.line_marker {
            self.window.rect(0,
                             (pos_y - self.scroll_y) as i32 * 16,
                             w,
                             16,
                             Color::rgb(45, 45, 45));
        }

        self.window.rect(8 * (pos_x - self.scroll_x) as i32,
                         16 * (pos_y - self.scroll_y) as i32,
                         8,
                         16,
                         Color::rgb(255, 255, 255));

        let mut string = false;

        for (y, row) in self.buffer.lines().enumerate() {
            for (x, c) in row.chars().enumerate() {
                // TODO: Move outta here
                let color = if self.options.highlight {
                    match c {
                        '\'' | '"' => {
                            string = !string;
                            (226, 225, 167) //(167, 222, 156)
                        }
                        _ if string => (226, 225, 167), //(167, 222, 156)
                        '!' |
                        '@' |
                        '#' |
                        '$' |
                        '%' |
                        '^' |
                        '&' |
                        '|' |
                        '*' |
                        '+' |
                        '-' |
                        '/' |
                        ':' |
                        '=' |
                        '<' |
                        '>' => (198, 83, 83), //(228, 190, 175), //(194, 106, 71),
                        '.' | ',' => (241, 213, 226),
                        '(' | ')' | '[' | ']' | '{' | '}' => (164, 212, 125), //(195, 139, 75),
                        '0' ... '9' => (209, 209, 177),
                        _ => (255, 255, 255),
                    }
                } else {
                    (255, 255, 255)
                };

                let c = if c == '\t' {
                    ' '
                } else {
                    c
                };

                if pos_x == x && pos_y == y {
                    self.window.char(8 * (x - self.scroll_x) as i32,
                                     16 * (y - self.scroll_y) as i32,
                                     c,
                                     Color::rgb(color.0 / 3, color.1 / 3, color.2 / 3));
                } else {
                    self.window.char(8 * (x - self.scroll_x) as i32,
                                     16 * (y - self.scroll_y) as i32,
                                     c,
                                     Color::rgb(color.0, color.1, color.2));
                }
            }
        }

        self.redraw_task = RedrawTask::None;

        self.redraw_status_bar();
        self.window.sync();
    }

    /// Redraw the status bar
    pub fn redraw_status_bar(&mut self) {
        let h = self.window.height();
        let w = self.window.width();
        let mode = self.cursor().mode;
        self.window.rect(0,
                         h as i32 - 18 -
                         {
                             if mode == Mode::Primitive(PrimitiveMode::Prompt) {
                                 18
                             } else {
                                 0
                             }
                         },
                         w,
                         18,
                         Color::rgba(74, 74, 74, 255));

        self.draw_status_bar();

        for (n, c) in self.prompt.chars().enumerate() {
            self.window.char(n as i32 * 8, h as i32 - 16 - 1, c, Color::rgb(255, 255, 255));
        }

        self.window.sync();
    }

    #[cfg(feature = "orbital")]
    fn draw_status_bar(&mut self) {
        let h = self.window.height();
        let w = self.window.width();

        let mode = self.cursor().mode;

        let items = [
            (self.status_bar.mode, 0, 4),
            (&self.status_bar.file, 1, 4),
            (&self.status_bar.cmd, 2, 4),
            (&self.status_bar.msg, 3, 4)
        ];

        for &(text, a, b) in items.iter() {
            for (n, c) in (if text.len() as u32 > w / (8 * b) {
                              text.chars().take((w / (8 * b) - 5) as usize).chain(vec!['.'; 3]).collect::<Vec<_>>()
                          } else {
                              text.chars().collect()
                          })
                          .into_iter()
                          .enumerate() {

                self.window.char(((w * a) / b) as i32 + (n as i32 * 8),
                                    h as i32 - 16 - 1 -
                                    {
                                        if mode == Mode::Primitive(PrimitiveMode::Prompt) {
                                            16 + 1 + 1
                                        } else {
                                            0
                                        }
                                    },
                                    c,
                                    Color::rgb(255, 255, 255));
            }
        }
    }
}

/// The statubar (showing various info about the current state of the editor)
pub struct StatusBar {
    /// The current mode
    pub mode: &'static str,
    /// The cureent char
    pub file: String,
    /// The current command
    pub cmd: String,
    /// A message (such as an error or other info to the user)
    pub msg: String,
}

impl StatusBar {
    /// Create new status bar
    pub fn new() -> Self {
        StatusBar {
            mode: "Normal",
            file: String::new(),
            cmd: String::new(),
            msg: "Welcome to Sodium!".to_string(),
        }
    }
}
<|endoftext|>


<commit_before>//! Command part of IPC

use std::io::prelude::*;
use std::collections::BTreeMap;

use rustc_serialize::json::{encode, Json, ToJson, ParserError};

use super::channel::{self, ReceiveError};

use registry;
use registry::AccessFlags;

macro_rules! expect_key {
    ($in_json:expr; $name:expr, $typ:ident) => {
        match $in_json.remove($name) {
            Some(key) => match key {
                Json::$typ(var) => var,
                _ => return Err(channel::error_expecting_key($name,
                          stringify!($typ)))
            },
            None => return Err(channel::error_expecting_key($name,
            stringify!($typ)))
        }
    }
}

/// Run a thread reading and replying to queries
pub fn thread<S: Read + Write>(mut stream: S) {
    loop {
        match channel::read_packet(&mut stream) {
            Ok(packet) => {
                trace!("Read packet: {}", encode(&packet)
                    .unwrap_or("<packet that was read already??>".to_string()));
                let reply = reply(packet).unwrap_or_else(|e| e);
                trace!("Writing reply: {}", encode(&reply)
                       .unwrap_or("<a reply which is not writable>".to_string()));
                channel::write_packet(&mut stream, &reply)
                    .expect("response: Unable to reply!");
            },
            Err(read_err) => match read_err {
                ReceiveError::IO(ioerr) => {
                    warn!("Unable to read reply! Closing connection.");
                    debug!("Got IOError: {:?}", ioerr);
                    return;
                },
                ReceiveError::InvalidJson(parse_err) => match parse_err {
                    ParserError::SyntaxError(code, start, end) => {
                        let mut responses = BTreeMap::new();
                        responses.insert("code".to_string(),
                                         Json::String(format!("{:?}", code)));
                        responses.insert("start".to_string(),
                                         Json::U64(start as u64));
                        responses.insert("end".to_string(),
                                         Json::U64(end as u64));
                        channel::write_packet(&mut stream,
                                              &channel::error_json_with(
                                                  "invalid json".to_string(),
                                                  responses))
                            .expect("invalid syntax: Unaable to reply!");
                    }
                    _ => unreachable!()
                }
            }
        }
    }
}

/// Generates the response needed to a given command
/// If the request is invalid it returns an Err.
/// If the request is valid but fails it returns an Ok.
fn reply(json: Json) -> Result<Json, Json> {
    let mut object: BTreeMap<String, Json>;
    if let Json::Object(obj) = json {
        object = obj;
    }
    else {
        return Err(channel::error_json(
            "invalid format - object required".to_string()))
    }

    let request_type = expect_key!(object; "type", String);

    // Converts the string to a str in the most Rustic way possible
    match &*request_type {
        // Registry
        "get" => {
            use std::ops::Deref;
            use registry::RegistryError::*;

            let key = expect_key!(object; "key", String);

            match registry::get_data(&key) {
                Ok(data) => {
                    let (flags, arc) = data.resolve();
                    let mut json = BTreeMap::new();
                    json.insert("flags".to_string(), flags.to_json());
                    // I'd return a Cow<Json> because write_packet needs an
                    // &Json, but I dunno how to move the fields over in a
                    // borrow.
                    json.insert("value".to_string(), arc.deref().clone());
                    return Ok(channel::success_json_with(json));
                },
                Err(err) => match err {
                    KeyNotFound =>
                        return Err(channel::error_json("key not found".to_string())),
                    InvalidOperation|WrongKeyType =>
                        return Err(channel::error_json("invalid operation".to_string())),
                    DecoderError(err) => {
                        error!("Got a decoder error from the registry! {:?}", err);
                        return Err(channel::error_json("key not found".to_string()))
                    }
                }
            }
        },
        "set" => {
            use registry::RegistryError::*;

            let key = expect_key!(&mut object; "key", String);
            let value: Json;
            match object.remove("value") {
                Some(val) => value = val,
                None => return Err(channel::error_expecting_key("value", "any"))
            }

            // TODO FIXME properly implement flags
            let mut applied_flags = AccessFlags::all();
            if let Some(json_flags) = object.get("flags") {
                // Parse custom flags
                let flag_list = try!(json_flags.as_array()
                                     .ok_or(channel::error_expecting_key(
                                         "flags", "string array")));
                let mut flags = AccessFlags::empty();
                for flag in flag_list {
                    let flag_str = try!(flag.as_string()
                                        .ok_or(channel::error_expecting_key(
                                            "flags", "string array")));
                    match flag_str {
                        "read"|"r" => flags.insert(AccessFlags::READ()),
                        "write"|"w" => flags.insert(AccessFlags::WRITE()),
                        _ => return Err(channel::error_json(
                            "Flags can either be 'read' or 'write'".to_string()))
                    }
                }
                applied_flags = flags;
            }
            try!(registry::set_json(key, applied_flags, value)
                 .map_err(|e| match e {
                     InvalidOperation =>
                         channel::error_json("invalid operation 'set'".to_string()),
                     WrongKeyType =>
                         channel::error_json("invalid operation; use 'run'".to_string()),
                     _ => unimplemented!() // TODO clean up registry err enum(s)
                 }));
            Ok(channel::success_json())
        },
        "exists" => {
            let key = expect_key!(&mut object; "key", String);

            let reg_key = registry::contains_key(&key);

            // TODO registry::key_info(key)
            Ok(channel::value_json(Json::Boolean(reg_key)))
        },

        // Commands
        "run" => {
            use registry::RegistryError::*;
            let key = expect_key!(&mut object; "key", String);

            let command = try!(registry::get_command(&key).map_err(|e| match e {
                KeyNotFound => channel::error_json("field not found".to_string()),
                WrongKeyType =>
                    channel::error_json("invalid operation; use 'get/'set'".to_string()),
                // may not be relevant
                InvalidOperation =>
                    channel::error_json("invalid operation; use 'set'".to_string()),
                _ => unimplemented!()
            }));

            command();

            Ok(channel::success_json())
        },

        // Meta/API commands
        "version" => {
            Ok(channel::value_json(Json::U64(super::VERSION)))
        },
        "commands" => {
            Ok(channel::value_json(Json::Array(vec![
                Json::String("get".to_string()),
                Json::String("set".to_string()),
                Json::String("exists".to_string()),
                Json::String("run".to_string()),
                Json::String("version".to_string()),
                Json::String("commands".to_string()) ])))
        },
        _ => Err(channel::error_json("invalid request; see 'commands'".to_string()))
    }
}
<commit_msg>Add ping command<commit_after>//! Command part of IPC

use std::io::prelude::*;
use std::collections::BTreeMap;

use rustc_serialize::json::{encode, Json, ToJson, ParserError};

use super::channel::{self, ReceiveError};

use registry;
use registry::AccessFlags;

macro_rules! expect_key {
    ($in_json:expr; $name:expr, $typ:ident) => {
        match $in_json.remove($name) {
            Some(key) => match key {
                Json::$typ(var) => var,
                _ => return Err(channel::error_expecting_key($name,
                          stringify!($typ)))
            },
            None => return Err(channel::error_expecting_key($name,
            stringify!($typ)))
        }
    }
}

/// Run a thread reading and replying to queries
pub fn thread<S: Read + Write>(mut stream: S) {
    loop {
        match channel::read_packet(&mut stream) {
            Ok(packet) => {
                trace!("Read packet: {}", encode(&packet)
                    .unwrap_or("<packet that was read already??>".to_string()));
                let reply = reply(packet).unwrap_or_else(|e| e);
                trace!("Writing reply: {}", encode(&reply)
                       .unwrap_or("<a reply which is not writable>".to_string()));
                channel::write_packet(&mut stream, &reply)
                    .expect("response: Unable to reply!");
            },
            Err(read_err) => match read_err {
                ReceiveError::IO(ioerr) => {
                    warn!("Unable to read reply! Closing connection.");
                    debug!("Got IOError: {:?}", ioerr);
                    return;
                },
                ReceiveError::InvalidJson(parse_err) => match parse_err {
                    ParserError::SyntaxError(code, start, end) => {
                        let mut responses = BTreeMap::new();
                        responses.insert("code".to_string(),
                                         Json::String(format!("{:?}", code)));
                        responses.insert("start".to_string(),
                                         Json::U64(start as u64));
                        responses.insert("end".to_string(),
                                         Json::U64(end as u64));
                        channel::write_packet(&mut stream,
                                              &channel::error_json_with(
                                                  "invalid json".to_string(),
                                                  responses))
                            .expect("invalid syntax: Unaable to reply!");
                    }
                    _ => unreachable!()
                }
            }
        }
    }
}

/// Generates the response needed to a given command
/// If the request is invalid it returns an Err.
/// If the request is valid but fails it returns an Ok.
pub fn reply(json: Json) -> Result<Json, Json> {
    let mut object: BTreeMap<String, Json>;
    if let Json::Object(obj) = json {
        object = obj;
    }
    else {
        return Err(channel::error_json(
            "invalid format - object required".to_string()))
    }

    let request_type = expect_key!(object; "type", String);

    // Converts the string to a str in the most Rustic way possible
    match &*request_type {
        // Registry
        "get" => {
            use std::ops::Deref;
            use registry::RegistryError::*;

            let key = expect_key!(object; "key", String);

            match registry::get_data(&key) {
                Ok(data) => {
                    let (flags, arc) = data.resolve();
                    let mut json = BTreeMap::new();
                    json.insert("flags".to_string(), flags.to_json());
                    // I'd return a Cow<Json> because write_packet needs an
                    // &Json, but I dunno how to move the fields over in a
                    // borrow.
                    json.insert("value".to_string(), arc.deref().clone());
                    return Ok(channel::success_json_with(json));
                },
                Err(err) => match err {
                    KeyNotFound =>
                        return Err(channel::error_json("key not found".to_string())),
                    InvalidOperation|WrongKeyType =>
                        return Err(channel::error_json("invalid operation".to_string())),
                    DecoderError(err) => {
                        error!("Got a decoder error from the registry! {:?}", err);
                        return Err(channel::error_json("key not found".to_string()))
                    }
                }
            }
        },
        "set" => {
            use registry::RegistryError::*;

            let key = expect_key!(&mut object; "key", String);
            let value: Json;
            match object.remove("value") {
                Some(val) => value = val,
                None => return Err(channel::error_expecting_key("value", "any"))
            }

            // TODO FIXME properly implement flags
            let mut applied_flags = AccessFlags::all();
            if let Some(json_flags) = object.get("flags") {
                // Parse custom flags
                let flag_list = try!(json_flags.as_array()
                                     .ok_or(channel::error_expecting_key(
                                         "flags", "string array")));
                let mut flags = AccessFlags::empty();
                for flag in flag_list {
                    let flag_str = try!(flag.as_string()
                                        .ok_or(channel::error_expecting_key(
                                            "flags", "string array")));
                    match flag_str {
                        "read"|"r" => flags.insert(AccessFlags::READ()),
                        "write"|"w" => flags.insert(AccessFlags::WRITE()),
                        _ => return Err(channel::error_json(
                            "Flags can either be 'read' or 'write'".to_string()))
                    }
                }
                applied_flags = flags;
            }
            try!(registry::set_json(key, applied_flags, value)
                 .map_err(|e| match e {
                     InvalidOperation =>
                         channel::error_json("invalid operation 'set'".to_string()),
                     WrongKeyType =>
                         channel::error_json("invalid operation; use 'run'".to_string()),
                     _ => unimplemented!() // TODO clean up registry err enum(s)
                 }));
            Ok(channel::success_json())
        },
        "exists" => {
            let key = expect_key!(&mut object; "key", String);

            let reg_key = registry::contains_key(&key);

            // TODO registry::key_info(key)
            Ok(channel::value_json(Json::Boolean(reg_key)))
        },

        // Commands
        "run" => {
            use registry::RegistryError::*;
            let key = expect_key!(&mut object; "key", String);

            let command = try!(registry::get_command(&key).map_err(|e| match e {
                KeyNotFound => channel::error_json("field not found".to_string()),
                WrongKeyType =>
                    channel::error_json("invalid operation; use 'get/'set'".to_string()),
                // may not be relevant
                InvalidOperation =>
                    channel::error_json("invalid operation; use 'set'".to_string()),
                _ => unimplemented!()
            }));

            command();

            Ok(channel::success_json())
        },

        // Meta/API commands
        "version" => {
            Ok(channel::value_json(Json::U64(super::VERSION)))
        },

        "commands" => {
            Ok(channel::value_json(Json::Array(vec![
                Json::String("get".to_string()),
                Json::String("set".to_string()),
                Json::String("exists".to_string()),
                Json::String("run".to_string()),
                Json::String("version".to_string()),
                Json::String("commands".to_string()),
                Json::String("ping".to_string())  ])))
        },

        "ping" => {
            Ok(channel::success_json())
        },

        _ => Err(channel::error_json("invalid request; see 'commands'".to_string()))
    }
}
<|endoftext|>


<commit_before>use env_logger;
use std::borrow::Cow::{self, Borrowed, Owned};

use rustyline::completion::{Completer, FilenameCompleter, Pair};
use rustyline::config::OutputStreamType;
use rustyline::error::ReadlineError;
use rustyline::highlight::{Highlighter, MatchingBracketHighlighter};
use rustyline::hint::{Hinter, HistoryHinter};
use rustyline::{Cmd, CompletionType, Config, Context, EditMode, Editor, Helper, KeyPress};

struct MyHelper {
    completer: FilenameCompleter,
    highlighter: MatchingBracketHighlighter,
    hinter: HistoryHinter,
    colored_prompt: String,
}

impl Completer for MyHelper {
    type Candidate = Pair;

    fn complete(
        &self,
        line: &str,
        pos: usize,
        ctx: &Context<'_>,
    ) -> Result<(usize, Vec<Pair>), ReadlineError> {
        self.completer.complete(line, pos, ctx)
    }
}

impl Hinter for MyHelper {
    fn hint(&self, line: &str, pos: usize, ctx: &Context<'_>) -> Option<String> {
        self.hinter.hint(line, pos, ctx)
    }
}

impl Highlighter for MyHelper {
    fn highlight_prompt<'b, 's: 'b, 'p: 'b>(
        &'s self,
        prompt: &'p str,
        default: bool,
    ) -> Cow<'b, str> {
        if default {
            Borrowed(&self.colored_prompt)
        } else {
            Borrowed(prompt)
        }
    }

    fn highlight_hint<'h>(&self, hint: &'h str) -> Cow<'h, str> {
        Owned("\x1b[1m".to_owned() + hint + "\x1b[m")
    }

    fn highlight<'l>(&self, line: &'l str, pos: usize) -> Cow<'l, str> {
        self.highlighter.highlight(line, pos)
    }

    fn highlight_char(&self, line: &str, pos: usize) -> bool {
        self.highlighter.highlight_char(line, pos)
    }
}

impl Helper for MyHelper {}

// To debug rustyline:
// RUST_LOG=rustyline=debug cargo run --example example 2> debug.log
fn main() {
    env_logger::init();
    let config = Config::builder()
        .history_ignore_space(true)
        .completion_type(CompletionType::List)
        .edit_mode(EditMode::Emacs)
        .output_stream(OutputStreamType::Stdout)
        .build();
    let h = MyHelper {
        completer: FilenameCompleter::new(),
        highlighter: MatchingBracketHighlighter::new(),
        hinter: HistoryHinter {},
        colored_prompt: "".to_owned(),
    };
    let mut rl = Editor::with_config(config);
    rl.set_helper(Some(h));
    rl.bind_sequence(KeyPress::Meta('N'), Cmd::HistorySearchForward);
    rl.bind_sequence(KeyPress::Meta('P'), Cmd::HistorySearchBackward);
    if rl.load_history("history.txt").is_err() {
        println!("No previous history.");
    }
    let mut count = 1;
    loop {
        let p = format!("{}> ", count);
        rl.helper_mut().unwrap().colored_prompt = format!("\x1b[1;32m{}\x1b[0m", p);
        let readline = rl.readline(&p);
        match readline {
            Ok(line) => {
                rl.add_history_entry(line.as_str());
                println!("Line: {}", line);
            }
            Err(ReadlineError::Interrupted) => {
                println!("CTRL-C");
                break;
            }
            Err(ReadlineError::Eof) => {
                println!("CTRL-D");
                break;
            }
            Err(err) => {
                println!("Error: {:?}", err);
                break;
            }
        }
        count += 1;
    }
    rl.save_history("history.txt").unwrap();
}
<commit_msg>Remove unwrap from example<commit_after>use env_logger;
use std::borrow::Cow::{self, Borrowed, Owned};

use rustyline::completion::{Completer, FilenameCompleter, Pair};
use rustyline::config::OutputStreamType;
use rustyline::error::ReadlineError;
use rustyline::highlight::{Highlighter, MatchingBracketHighlighter};
use rustyline::hint::{Hinter, HistoryHinter};
use rustyline::{Cmd, CompletionType, Config, Context, EditMode, Editor, Helper, KeyPress};

struct MyHelper {
    completer: FilenameCompleter,
    highlighter: MatchingBracketHighlighter,
    hinter: HistoryHinter,
    colored_prompt: String,
}

impl Completer for MyHelper {
    type Candidate = Pair;

    fn complete(
        &self,
        line: &str,
        pos: usize,
        ctx: &Context<'_>,
    ) -> Result<(usize, Vec<Pair>), ReadlineError> {
        self.completer.complete(line, pos, ctx)
    }
}

impl Hinter for MyHelper {
    fn hint(&self, line: &str, pos: usize, ctx: &Context<'_>) -> Option<String> {
        self.hinter.hint(line, pos, ctx)
    }
}

impl Highlighter for MyHelper {
    fn highlight_prompt<'b, 's: 'b, 'p: 'b>(
        &'s self,
        prompt: &'p str,
        default: bool,
    ) -> Cow<'b, str> {
        if default {
            Borrowed(&self.colored_prompt)
        } else {
            Borrowed(prompt)
        }
    }

    fn highlight_hint<'h>(&self, hint: &'h str) -> Cow<'h, str> {
        Owned("\x1b[1m".to_owned() + hint + "\x1b[m")
    }

    fn highlight<'l>(&self, line: &'l str, pos: usize) -> Cow<'l, str> {
        self.highlighter.highlight(line, pos)
    }

    fn highlight_char(&self, line: &str, pos: usize) -> bool {
        self.highlighter.highlight_char(line, pos)
    }
}

impl Helper for MyHelper {}

// To debug rustyline:
// RUST_LOG=rustyline=debug cargo run --example example 2> debug.log
fn main() -> rustyline::Result<()> {
    env_logger::init();
    let config = Config::builder()
        .history_ignore_space(true)
        .completion_type(CompletionType::List)
        .edit_mode(EditMode::Emacs)
        .output_stream(OutputStreamType::Stdout)
        .build();
    let h = MyHelper {
        completer: FilenameCompleter::new(),
        highlighter: MatchingBracketHighlighter::new(),
        hinter: HistoryHinter {},
        colored_prompt: "".to_owned(),
    };
    let mut rl = Editor::with_config(config);
    rl.set_helper(Some(h));
    rl.bind_sequence(KeyPress::Meta('N'), Cmd::HistorySearchForward);
    rl.bind_sequence(KeyPress::Meta('P'), Cmd::HistorySearchBackward);
    if rl.load_history("history.txt").is_err() {
        println!("No previous history.");
    }
    let mut count = 1;
    loop {
        let p = format!("{}> ", count);
        rl.helper_mut().expect("No helper").colored_prompt = format!("\x1b[1;32m{}\x1b[0m", p);
        let readline = rl.readline(&p);
        match readline {
            Ok(line) => {
                rl.add_history_entry(line.as_str());
                println!("Line: {}", line);
            }
            Err(ReadlineError::Interrupted) => {
                println!("CTRL-C");
                break;
            }
            Err(ReadlineError::Eof) => {
                println!("CTRL-D");
                break;
            }
            Err(err) => {
                println!("Error: {:?}", err);
                break;
            }
        }
        count += 1;
    }
    rl.save_history("history.txt")
}
<|endoftext|>


<commit_before>use codespan::{CodeMap, LineIndex, RawIndex};
use std::io;
use termcolor::{Color, ColorSpec, WriteColor};

use crate::{Diagnostic, LabelStyle, Severity};

pub fn emit<W, S>(mut writer: W, codemap: &CodeMap<S>, diagnostic: &Diagnostic) -> io::Result<()>
where
    W: WriteColor,
    S: AsRef<str>,
{
    let severity_color = severity_color(diagnostic.severity);
    let gutter_spec = ColorSpec::new().set_fg(Some(gutter_color())).clone();
    let primary_spec = ColorSpec::new().set_fg(Some(severity_color)).clone();
    let secondary_spec = ColorSpec::new().set_fg(Some(secondary_color())).clone();
    let header_message_spec = ColorSpec::new().set_bold(true).set_intense(true).clone();
    let header_primary_spec = ColorSpec::new()
        .set_bold(true)
        .set_intense(true)
        .set_fg(Some(severity_color))
        .clone();

    // Diagnostic header

    writer.set_color(&header_primary_spec)?;

    // Severity
    //
    // ```
    // error
    // ```
    write!(writer, "{}", severity_name(diagnostic.severity))?;

    // Error code
    //
    // ```
    // [E0001]
    // ```
    if let Some(code) = &diagnostic.code {
        write!(writer, "[{}]", code)?;
    }

    // Diagnostic message
    //
    // ```
    // : Unexpected type in `+` application
    // ```
    writer.set_color(&header_message_spec)?;
    write!(writer, ": {}", diagnostic.message)?;
    write!(writer, "\n")?;
    writer.reset()?;

    // Diagnostic Labels

    for label in &diagnostic.labels {
        match codemap.find_file(label.span.start()) {
            None => {
                if !label.message.is_empty() {
                    write!(writer, "- {}", label.message)?;
                    write!(writer, "\n")?;
                }
            },
            Some(file) => {
                let (start_line, column) =
                    file.location(label.span.start()).expect("location_start");
                let (end_line, _) = file.location(label.span.end()).expect("location_end");
                // Use the length of the last line number as the gutter padding
                let gutter_padding = format!("{}", end_line.number()).len();

                // File name
                //
                // ```
                // - <test>:2:9
                // ```

                write!(
                    writer,
                    "{: >width$} - {file}:{line}:{column}",
                    "",
                    width = gutter_padding,
                    file = file.name(),
                    line = start_line.number(),
                    column = column.number(),
                )?;
                write!(writer, "\n")?;

                // Source code snippet
                //
                // ```
                //   │
                // 2 │ (+ test "")
                //   │         ^^ Expected integer but got string
                //   │
                // ```

                let start_line_span = file.line_span(start_line).expect("line_span");
                let end_line_span = file.line_span(end_line).expect("line_span");

                let label_spec = match label.style {
                    LabelStyle::Primary => &primary_spec,
                    LabelStyle::Secondary => &secondary_spec,
                };

                // Write prefix to marked section

                writer.set_color(&gutter_spec)?;
                write!(writer, "{: >width$} │ ", "", width = gutter_padding)?;
                write!(writer, "\n")?;
                write!(
                    writer,
                    "{: >width$} │ ",
                    start_line.number(),
                    width = gutter_padding,
                )?;
                writer.reset()?;

                let prefix = file
                    .src_slice(start_line_span.with_end(label.span.start()))
                    .expect("prefix");
                write!(writer, "{}", prefix)?;

                // Write marked section
                let mark_len = if start_line == end_line {
                    // Single line

                    let marked = file.src_slice(label.span).expect("marked");
                    writer.set_color(&label_spec)?;
                    write!(writer, "{}", marked)?;
                    writer.reset()?;
                    marked.len()
                } else {
                    // Multiple lines

                    let marked = file
                        .src_slice(start_line_span.with_start(label.span.start()))
                        .expect("start_of_marked");
                    writer.set_color(&label_spec)?;
                    write!(writer, "{}", marked)?;

                    for line_index in ((start_line.to_usize() + 1)..end_line.to_usize())
                        .map(|i| LineIndex::from(i as RawIndex))
                    {
                        writer.set_color(&gutter_spec)?;
                        write!(
                            writer,
                            "{: >width$} │ ",
                            line_index.number(),
                            width = gutter_padding,
                        )?;

                        let line_span = file.line_span(line_index).expect("marked_line_span");
                        let line = file
                            .src_slice(line_span)
                            .expect("line")
                            .trim_end_matches(|ch: char| ch == '\r' || ch == '\n');
                        writer.set_color(&label_spec)?;
                        write!(writer, "{}", line)?;
                        write!(writer, "\n")?;
                    }

                    writer.set_color(&gutter_spec)?;
                    write!(
                        writer,
                        "{: >width$} │ ",
                        end_line.number(),
                        width = gutter_padding,
                    )?;
                    let line = file
                        .src_slice(end_line_span.with_end(label.span.end()))
                        .expect("line");
                    writer.set_color(&label_spec)?;
                    write!(writer, "{}", line)?;
                    writer.reset()?;
                    line.len()
                };

                // Write suffix to marked section

                let suffix = file
                    .src_slice(end_line_span.with_start(label.span.end()))
                    .expect("suffix")
                    .trim_end_matches(|ch: char| ch == '\r' || ch == '\n');
                write!(writer, "{}", suffix)?;
                write!(writer, "\n")?;

                // Write mark and label

                writer.set_color(&gutter_spec)?;
                write!(writer, "{: >width$} │ ", "", width = gutter_padding)?;
                writer.reset()?;

                writer.set_color(&label_spec)?;
                write!(writer, "{: >width$}", "", width = prefix.len())?;
                for _ in 0..mark_len {
                    write!(writer, "{}", underline_mark(label.style))?;
                }
                if !label.message.is_empty() {
                    write!(writer, " {}", label.message)?;
                    write!(writer, "\n")?;
                }
                writer.reset()?;

                writer.set_color(&gutter_spec)?;
                write!(writer, "{: >width$} │", "", width = gutter_padding)?;
                write!(writer, "\n")?;
                writer.reset()?;
            },
        }
    }
    Ok(())
}

// Blue is really difficult to see on the standard windows command line
// FIXME: Make colors configurable
#[cfg(windows)]
const BLUE: Color = Color::Cyan;
#[cfg(not(windows))]
const BLUE: Color = Color::Blue;

/// Return the termcolor to use when rendering messages of this diagnostic severity.
fn severity_color(severity: Severity) -> Color {
    match severity {
        Severity::Bug | Severity::Error => Color::Red,
        Severity::Warning => Color::Yellow,
        Severity::Note => Color::Green,
        Severity::Help => Color::Cyan,
    }
}

/// The color to use for secondary highlights.
fn secondary_color() -> Color {
    BLUE
}

/// The color to use for gutters highlights.
fn gutter_color() -> Color {
    BLUE
}

/// A string that explains this diagnostic severity.
fn severity_name(severity: Severity) -> &'static str {
    match severity {
        Severity::Bug => "bug",
        Severity::Error => "error",
        Severity::Warning => "warning",
        Severity::Note => "note",
        Severity::Help => "help",
    }
}

/// The mark used for the underlined section of code.
fn underline_mark(label_style: LabelStyle) -> char {
    match label_style {
        LabelStyle::Primary => '^',
        LabelStyle::Secondary => '-',
    }
}
<commit_msg>Color file name prefix<commit_after>use codespan::{CodeMap, LineIndex, RawIndex};
use std::io;
use termcolor::{Color, ColorSpec, WriteColor};

use crate::{Diagnostic, LabelStyle, Severity};

pub fn emit<W, S>(mut writer: W, codemap: &CodeMap<S>, diagnostic: &Diagnostic) -> io::Result<()>
where
    W: WriteColor,
    S: AsRef<str>,
{
    let severity_color = severity_color(diagnostic.severity);
    let gutter_spec = ColorSpec::new().set_fg(Some(gutter_color())).clone();
    let primary_spec = ColorSpec::new().set_fg(Some(severity_color)).clone();
    let secondary_spec = ColorSpec::new().set_fg(Some(secondary_color())).clone();
    let header_message_spec = ColorSpec::new().set_bold(true).set_intense(true).clone();
    let header_primary_spec = ColorSpec::new()
        .set_bold(true)
        .set_intense(true)
        .set_fg(Some(severity_color))
        .clone();

    // Diagnostic header

    writer.set_color(&header_primary_spec)?;

    // Severity
    //
    // ```
    // error
    // ```
    write!(writer, "{}", severity_name(diagnostic.severity))?;

    // Error code
    //
    // ```
    // [E0001]
    // ```
    if let Some(code) = &diagnostic.code {
        write!(writer, "[{}]", code)?;
    }

    // Diagnostic message
    //
    // ```
    // : Unexpected type in `+` application
    // ```
    writer.set_color(&header_message_spec)?;
    write!(writer, ": {}", diagnostic.message)?;
    write!(writer, "\n")?;
    writer.reset()?;

    // Diagnostic Labels

    for label in &diagnostic.labels {
        match codemap.find_file(label.span.start()) {
            None => {
                if !label.message.is_empty() {
                    write!(writer, "- {}", label.message)?;
                    write!(writer, "\n")?;
                }
            },
            Some(file) => {
                let (start_line, column) =
                    file.location(label.span.start()).expect("location_start");
                let (end_line, _) = file.location(label.span.end()).expect("location_end");
                // Use the length of the last line number as the gutter padding
                let gutter_padding = format!("{}", end_line.number()).len();

                // File name
                //
                // ```
                // - <test>:2:9
                // ```

                writer.set_color(&gutter_spec)?;
                write!(writer, "{: >width$} - ", "", width = gutter_padding)?;
                writer.reset()?;
                write!(
                    writer,
                    "{file}:{line}:{column}",
                    file = file.name(),
                    line = start_line.number(),
                    column = column.number(),
                )?;
                write!(writer, "\n")?;

                // Source code snippet
                //
                // ```
                //   │
                // 2 │ (+ test "")
                //   │         ^^ Expected integer but got string
                //   │
                // ```

                let start_line_span = file.line_span(start_line).expect("line_span");
                let end_line_span = file.line_span(end_line).expect("line_span");

                let label_spec = match label.style {
                    LabelStyle::Primary => &primary_spec,
                    LabelStyle::Secondary => &secondary_spec,
                };

                // Write prefix to marked section

                writer.set_color(&gutter_spec)?;
                write!(writer, "{: >width$} │ ", "", width = gutter_padding)?;
                write!(writer, "\n")?;
                write!(
                    writer,
                    "{: >width$} │ ",
                    start_line.number(),
                    width = gutter_padding,
                )?;
                writer.reset()?;

                let prefix = file
                    .src_slice(start_line_span.with_end(label.span.start()))
                    .expect("prefix");
                write!(writer, "{}", prefix)?;

                // Write marked section
                let mark_len = if start_line == end_line {
                    // Single line

                    let marked = file.src_slice(label.span).expect("marked");
                    writer.set_color(&label_spec)?;
                    write!(writer, "{}", marked)?;
                    writer.reset()?;
                    marked.len()
                } else {
                    // Multiple lines

                    let marked = file
                        .src_slice(start_line_span.with_start(label.span.start()))
                        .expect("start_of_marked");
                    writer.set_color(&label_spec)?;
                    write!(writer, "{}", marked)?;

                    for line_index in ((start_line.to_usize() + 1)..end_line.to_usize())
                        .map(|i| LineIndex::from(i as RawIndex))
                    {
                        writer.set_color(&gutter_spec)?;
                        write!(
                            writer,
                            "{: >width$} │ ",
                            line_index.number(),
                            width = gutter_padding,
                        )?;

                        let line_span = file.line_span(line_index).expect("marked_line_span");
                        let line = file
                            .src_slice(line_span)
                            .expect("line")
                            .trim_end_matches(|ch: char| ch == '\r' || ch == '\n');
                        writer.set_color(&label_spec)?;
                        write!(writer, "{}", line)?;
                        write!(writer, "\n")?;
                    }

                    writer.set_color(&gutter_spec)?;
                    write!(
                        writer,
                        "{: >width$} │ ",
                        end_line.number(),
                        width = gutter_padding,
                    )?;
                    let line = file
                        .src_slice(end_line_span.with_end(label.span.end()))
                        .expect("line");
                    writer.set_color(&label_spec)?;
                    write!(writer, "{}", line)?;
                    writer.reset()?;
                    line.len()
                };

                // Write suffix to marked section

                let suffix = file
                    .src_slice(end_line_span.with_start(label.span.end()))
                    .expect("suffix")
                    .trim_end_matches(|ch: char| ch == '\r' || ch == '\n');
                write!(writer, "{}", suffix)?;
                write!(writer, "\n")?;

                // Write mark and label

                writer.set_color(&gutter_spec)?;
                write!(writer, "{: >width$} │ ", "", width = gutter_padding)?;
                writer.reset()?;

                writer.set_color(&label_spec)?;
                write!(writer, "{: >width$}", "", width = prefix.len())?;
                for _ in 0..mark_len {
                    write!(writer, "{}", underline_mark(label.style))?;
                }
                if !label.message.is_empty() {
                    write!(writer, " {}", label.message)?;
                    write!(writer, "\n")?;
                }
                writer.reset()?;

                writer.set_color(&gutter_spec)?;
                write!(writer, "{: >width$} │", "", width = gutter_padding)?;
                write!(writer, "\n")?;
                writer.reset()?;
            },
        }
    }
    Ok(())
}

// Blue is really difficult to see on the standard windows command line
// FIXME: Make colors configurable
#[cfg(windows)]
const BLUE: Color = Color::Cyan;
#[cfg(not(windows))]
const BLUE: Color = Color::Blue;

/// Return the termcolor to use when rendering messages of this diagnostic severity.
fn severity_color(severity: Severity) -> Color {
    match severity {
        Severity::Bug | Severity::Error => Color::Red,
        Severity::Warning => Color::Yellow,
        Severity::Note => Color::Green,
        Severity::Help => Color::Cyan,
    }
}

/// The color to use for secondary highlights.
fn secondary_color() -> Color {
    BLUE
}

/// The color to use for gutters highlights.
fn gutter_color() -> Color {
    BLUE
}

/// A string that explains this diagnostic severity.
fn severity_name(severity: Severity) -> &'static str {
    match severity {
        Severity::Bug => "bug",
        Severity::Error => "error",
        Severity::Warning => "warning",
        Severity::Note => "note",
        Severity::Help => "help",
    }
}

/// The mark used for the underlined section of code.
fn underline_mark(label_style: LabelStyle) -> char {
    match label_style {
        LabelStyle::Primary => '^',
        LabelStyle::Secondary => '-',
    }
}
<|endoftext|>


<commit_before>use codespan::{CodeMap, LineIndex, RawIndex};
use std::io;
use termcolor::{Color, ColorSpec, WriteColor};

use crate::{Diagnostic, LabelStyle, Severity};

pub fn emit<W, S>(mut writer: W, codemap: &CodeMap<S>, diagnostic: &Diagnostic) -> io::Result<()>
where
    W: WriteColor,
    S: AsRef<str>,
{
    let gutter_color = ColorSpec::new().set_fg(Some(gutter_color())).clone();
    let diagnostic_color = ColorSpec::new()
        .set_fg(Some(severity_color(diagnostic.severity)))
        .clone();

    let highlight_color = ColorSpec::new().set_bold(true).set_intense(true).clone();

    // Diagnostic header

    writer.set_color(
        &highlight_color
            .clone()
            .set_fg(Some(severity_color(diagnostic.severity))),
    )?;

    // Severity
    //
    // ```
    // error
    // ```
    write!(writer, "{}", severity_name(diagnostic.severity))?;

    // Error code
    //
    // ```
    // [E0001]
    // ```
    if let Some(code) = &diagnostic.code {
        write!(writer, "[{}]", code)?;
    }

    // Diagnostic message
    //
    // ```
    // : Unexpected type in `+` application
    // ```
    writer.set_color(&highlight_color)?;
    writeln!(writer, ": {}", diagnostic.message)?;
    writer.reset()?;

    // Diagnostic Labels

    for label in &diagnostic.labels {
        match codemap.find_file(label.span.start()) {
            None => {
                if !label.message.is_empty() {
                    writeln!(writer, "- {}", label.message)?
                }
            },
            Some(file) => {
                let (start_line, column) =
                    file.location(label.span.start()).expect("location_start");
                let (end_line, _) = file.location(label.span.end()).expect("location_end");

                // File name
                //
                // ```
                // - <test>:2:9
                // ```

                writeln!(
                    writer,
                    "- {file}:{line}:{column}",
                    file = file.name(),
                    line = start_line.number(),
                    column = column.number(),
                )?;

                // Source code snippet
                //
                // ```
                //   |
                // 2 | (+ test "")
                //   |         ^^ Expected integer but got string
                //   |
                // ```

                let start_line_span = file.line_span(start_line).expect("line_span");
                let end_line_span = file.line_span(end_line).expect("line_span");
                // Use the length of the last line number as the gutter padding
                let gutter_padding = format!("{}", end_line.number()).len();

                let label_color = match label.style {
                    LabelStyle::Primary => diagnostic_color.clone(),
                    LabelStyle::Secondary => {
                        ColorSpec::new().set_fg(Some(secondary_color())).clone()
                    },
                };

                // Write prefix to marked section

                writer.set_color(&gutter_color)?;
                writeln!(writer, "{: >width$} | ", "", width = gutter_padding)?;
                write!(
                    writer,
                    "{: >width$} | ",
                    start_line.number(),
                    width = gutter_padding,
                )?;

                let prefix = file
                    .src_slice(start_line_span.with_end(label.span.start()))
                    .expect("prefix");
                writer.reset()?;
                write!(writer, "{}", prefix)?;

                // Write marked section
                let mark_len = if start_line == end_line {
                    // Single line

                    let marked = file.src_slice(label.span).expect("marked");
                    writer.set_color(&label_color)?;
                    write!(writer, "{}", marked)?;
                    marked.len()
                } else {
                    // Multiple lines

                    let marked = file
                        .src_slice(start_line_span.with_start(label.span.start()))
                        .expect("start_of_marked");
                    writer.set_color(&label_color)?;
                    write!(writer, "{}", marked)?;

                    for line_index in ((start_line.to_usize() + 1)..end_line.to_usize())
                        .map(|i| LineIndex::from(i as RawIndex))
                    {
                        writer.set_color(&gutter_color)?;
                        write!(
                            writer,
                            "{: >width$} | ",
                            line_index.number(),
                            width = gutter_padding,
                        )?;

                        let line_span = file.line_span(line_index).expect("marked_line_span");
                        let line = file
                            .src_slice(line_span)
                            .expect("line")
                            .trim_end_matches(|ch: char| ch == '\r' || ch == '\n');
                        writer.set_color(&label_color)?;
                        writeln!(writer, "{}", line)?;
                    }

                    writer.set_color(&gutter_color)?;
                    write!(
                        writer,
                        "{: >width$} | ",
                        end_line.number(),
                        width = gutter_padding,
                    )?;
                    let line = file
                        .src_slice(end_line_span.with_end(label.span.end()))
                        .expect("line");
                    writer.set_color(&label_color)?;
                    write!(writer, "{}", line)?;
                    line.len()
                };

                // Write suffix to marked section

                let suffix = file
                    .src_slice(end_line_span.with_start(label.span.end()))
                    .expect("suffix")
                    .trim_end_matches(|ch: char| ch == '\r' || ch == '\n');
                writer.reset()?;
                writeln!(writer, "{}", suffix)?;

                // Write mark and label

                writer.set_color(&gutter_color)?;
                write!(writer, "{: >width$} | ", "", width = gutter_padding)?;
                writer.reset()?;

                writer.set_color(&label_color)?;
                write!(writer, "{: >width$}", "", width = prefix.len())?;
                for _ in 0..mark_len {
                    write!(writer, "{}", underline_mark(label.style))?;
                }
                writer.reset()?;

                if !label.message.is_empty() {
                    writer.set_color(&label_color)?;
                    writeln!(writer, " {}", label.message)?;
                    writer.reset()?;
                }
                writer.set_color(&gutter_color)?;
                writeln!(writer, "{: >width$} | ", "", width = gutter_padding)?;
                writer.reset()?;
            },
        }
    }
    Ok(())
}

// Blue is really difficult to see on the standard windows command line
// FIXME: Make colors configurable
#[cfg(windows)]
const BLUE: Color = Color::Cyan;
#[cfg(not(windows))]
const BLUE: Color = Color::Blue;

/// Return the termcolor to use when rendering messages of this diagnostic severity.
fn severity_color(severity: Severity) -> Color {
    match severity {
        Severity::Bug | Severity::Error => Color::Red,
        Severity::Warning => Color::Yellow,
        Severity::Note => Color::Green,
        Severity::Help => Color::Cyan,
    }
}

/// The color to use for secondary highlights.
fn secondary_color() -> Color {
    BLUE
}

/// The color to use for gutters highlights.
fn gutter_color() -> Color {
    BLUE
}

/// A string that explains this diagnostic severity.
fn severity_name(severity: Severity) -> &'static str {
    match severity {
        Severity::Bug => "bug",
        Severity::Error => "error",
        Severity::Warning => "warning",
        Severity::Note => "note",
        Severity::Help => "help",
    }
}

/// The mark used for the underlined section of code.
fn underline_mark(label_style: LabelStyle) -> char {
    match label_style {
        LabelStyle::Primary => '^',
        LabelStyle::Secondary => '-',
    }
}
<commit_msg>Use proper box drawing characters<commit_after>use codespan::{CodeMap, LineIndex, RawIndex};
use std::io;
use termcolor::{Color, ColorSpec, WriteColor};

use crate::{Diagnostic, LabelStyle, Severity};

pub fn emit<W, S>(mut writer: W, codemap: &CodeMap<S>, diagnostic: &Diagnostic) -> io::Result<()>
where
    W: WriteColor,
    S: AsRef<str>,
{
    let gutter_color = ColorSpec::new().set_fg(Some(gutter_color())).clone();
    let diagnostic_color = ColorSpec::new()
        .set_fg(Some(severity_color(diagnostic.severity)))
        .clone();

    let highlight_color = ColorSpec::new().set_bold(true).set_intense(true).clone();

    // Diagnostic header

    writer.set_color(
        &highlight_color
            .clone()
            .set_fg(Some(severity_color(diagnostic.severity))),
    )?;

    // Severity
    //
    // ```
    // error
    // ```
    write!(writer, "{}", severity_name(diagnostic.severity))?;

    // Error code
    //
    // ```
    // [E0001]
    // ```
    if let Some(code) = &diagnostic.code {
        write!(writer, "[{}]", code)?;
    }

    // Diagnostic message
    //
    // ```
    // : Unexpected type in `+` application
    // ```
    writer.set_color(&highlight_color)?;
    writeln!(writer, ": {}", diagnostic.message)?;
    writer.reset()?;

    // Diagnostic Labels

    for label in &diagnostic.labels {
        match codemap.find_file(label.span.start()) {
            None => {
                if !label.message.is_empty() {
                    writeln!(writer, "- {}", label.message)?
                }
            },
            Some(file) => {
                let (start_line, column) =
                    file.location(label.span.start()).expect("location_start");
                let (end_line, _) = file.location(label.span.end()).expect("location_end");
                // Use the length of the last line number as the gutter padding
                let gutter_padding = format!("{}", end_line.number()).len();

                // File name
                //
                // ```
                // - <test>:2:9
                // ```

                writeln!(
                    writer,
                    "{: >width$} - {file}:{line}:{column}",
                    "",
                    width = gutter_padding,
                    file = file.name(),
                    line = start_line.number(),
                    column = column.number(),
                )?;

                // Source code snippet
                //
                // ```
                //   │
                // 2 │ (+ test "")
                //   │         ^^ Expected integer but got string
                //   │
                // ```

                let start_line_span = file.line_span(start_line).expect("line_span");
                let end_line_span = file.line_span(end_line).expect("line_span");

                let label_color = match label.style {
                    LabelStyle::Primary => diagnostic_color.clone(),
                    LabelStyle::Secondary => {
                        ColorSpec::new().set_fg(Some(secondary_color())).clone()
                    },
                };

                // Write prefix to marked section

                writer.set_color(&gutter_color)?;
                writeln!(writer, "{: >width$} │ ", "", width = gutter_padding)?;
                write!(
                    writer,
                    "{: >width$} │ ",
                    start_line.number(),
                    width = gutter_padding,
                )?;

                let prefix = file
                    .src_slice(start_line_span.with_end(label.span.start()))
                    .expect("prefix");
                writer.reset()?;
                write!(writer, "{}", prefix)?;

                // Write marked section
                let mark_len = if start_line == end_line {
                    // Single line

                    let marked = file.src_slice(label.span).expect("marked");
                    writer.set_color(&label_color)?;
                    write!(writer, "{}", marked)?;
                    marked.len()
                } else {
                    // Multiple lines

                    let marked = file
                        .src_slice(start_line_span.with_start(label.span.start()))
                        .expect("start_of_marked");
                    writer.set_color(&label_color)?;
                    write!(writer, "{}", marked)?;

                    for line_index in ((start_line.to_usize() + 1)..end_line.to_usize())
                        .map(|i| LineIndex::from(i as RawIndex))
                    {
                        writer.set_color(&gutter_color)?;
                        write!(
                            writer,
                            "{: >width$} │ ",
                            line_index.number(),
                            width = gutter_padding,
                        )?;

                        let line_span = file.line_span(line_index).expect("marked_line_span");
                        let line = file
                            .src_slice(line_span)
                            .expect("line")
                            .trim_end_matches(|ch: char| ch == '\r' || ch == '\n');
                        writer.set_color(&label_color)?;
                        writeln!(writer, "{}", line)?;
                    }

                    writer.set_color(&gutter_color)?;
                    write!(
                        writer,
                        "{: >width$} │ ",
                        end_line.number(),
                        width = gutter_padding,
                    )?;
                    let line = file
                        .src_slice(end_line_span.with_end(label.span.end()))
                        .expect("line");
                    writer.set_color(&label_color)?;
                    write!(writer, "{}", line)?;
                    line.len()
                };

                // Write suffix to marked section

                let suffix = file
                    .src_slice(end_line_span.with_start(label.span.end()))
                    .expect("suffix")
                    .trim_end_matches(|ch: char| ch == '\r' || ch == '\n');
                writer.reset()?;
                writeln!(writer, "{}", suffix)?;

                // Write mark and label

                writer.set_color(&gutter_color)?;
                write!(writer, "{: >width$} │ ", "", width = gutter_padding)?;
                writer.reset()?;

                writer.set_color(&label_color)?;
                write!(writer, "{: >width$}", "", width = prefix.len())?;
                for _ in 0..mark_len {
                    write!(writer, "{}", underline_mark(label.style))?;
                }
                writer.reset()?;

                if !label.message.is_empty() {
                    writer.set_color(&label_color)?;
                    writeln!(writer, " {}", label.message)?;
                    writer.reset()?;
                }
                writer.set_color(&gutter_color)?;
                writeln!(writer, "{: >width$} │", "", width = gutter_padding)?;
                writer.reset()?;
            },
        }
    }
    Ok(())
}

// Blue is really difficult to see on the standard windows command line
// FIXME: Make colors configurable
#[cfg(windows)]
const BLUE: Color = Color::Cyan;
#[cfg(not(windows))]
const BLUE: Color = Color::Blue;

/// Return the termcolor to use when rendering messages of this diagnostic severity.
fn severity_color(severity: Severity) -> Color {
    match severity {
        Severity::Bug | Severity::Error => Color::Red,
        Severity::Warning => Color::Yellow,
        Severity::Note => Color::Green,
        Severity::Help => Color::Cyan,
    }
}

/// The color to use for secondary highlights.
fn secondary_color() -> Color {
    BLUE
}

/// The color to use for gutters highlights.
fn gutter_color() -> Color {
    BLUE
}

/// A string that explains this diagnostic severity.
fn severity_name(severity: Severity) -> &'static str {
    match severity {
        Severity::Bug => "bug",
        Severity::Error => "error",
        Severity::Warning => "warning",
        Severity::Note => "note",
        Severity::Help => "help",
    }
}

/// The mark used for the underlined section of code.
fn underline_mark(label_style: LabelStyle) -> char {
    match label_style {
        LabelStyle::Primary => '^',
        LabelStyle::Secondary => '-',
    }
}
<|endoftext|>