<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())?;
        }

        // 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()
    }
}
<commit_msg>Clean up add_cert<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;

            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()) {
                            connector.cert_store_mut().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>// Copyright 2016 Johannes Köster.
// Licensed under the MIT license (http://opensource.org/licenses/MIT)
// This file may not be copied, modified, or distributed
// except according to those terms.

use std::io;
use std::fs;
use std::path::Path;
use std::collections::{HashMap, hash_map, HashSet};
use num::CheckedAdd;

use csv;
use itertools::Itertools;
use bit_vec::BitVec;
use petgraph::prelude::*;
use petgraph::visit;
use bio::alignment::distance;

#[derive(Debug)]
pub struct Record {
    pub name: String,
    pub codeword: BitVec<u32>
}

impl Record {
    pub fn new(name: String, codeword: &[u8]) -> Self {
        let mut _codeword = BitVec::with_capacity(codeword.len());
        for &b in codeword {
            if b == b'1' {
                _codeword.push(true);
            } else if b == b'0' {
                _codeword.push(false)
            } else {
                panic!("invalid codeword {:?}", codeword)
            }
        }

        Record {
            name: name,
            codeword: _codeword
        }
    }

    pub fn dist(&self, other: &Record) -> u8 {
        let mut dist = 0;
        for (a, b) in self.codeword.blocks().zip(other.codeword.blocks()) {
            dist = dist.checked_add(&((a ^ b).count_ones() as u8)).expect(
                "bug: overflow when calculating hamming distance"
            );
        }
        dist
    }
}

/// Codebook representation.
pub struct Codebook {
    index: HashMap<String, NodeIndex<u32>>,
    graph: UnGraph<Record, ()>,
    min_dist: u8
}

impl Codebook {
    /// Read from a given file path.
    pub fn from_file<P: AsRef<Path>>(path: P, min_dist: u8) -> csv::Result<Self> {
        let mut rdr = (csv::Reader::from_file(path)?).delimiter(b'\t');

        let mut graph = Graph::default();
        let index = {
            let mut index = HashMap::new();
            for rec in rdr.decode() {
                let (feature, codeword): (String, String) = rec?;
                let idx = graph.add_node(Record::new(feature.clone(), codeword.as_bytes()));
                index.insert(
                    feature,
                    idx
                );
            }
            index
        };

        for &a in index.values() {
            for &b in index.values() {
                if a != b {
                    let d = {
                        let rec_a = graph.node_weight(a).unwrap();
                        let rec_b = graph.node_weight(b).unwrap();
                        rec_a.dist(&rec_b)
                    };

                    if d == min_dist {
                        graph.add_edge(a, b, ());
                    } else if d < min_dist {
                        panic!("unexpected hamming distance {} (given minimum {})", d, min_dist);
                    }
                }
            }
        }

        Ok(Codebook {
            graph: graph,
            index: index,
            min_dist: min_dist
        })
    }

    pub fn get(&self, feature: &str) -> &Record {
        self.graph.node_weight(
            *self.index.get(feature).expect("bug: feature not in codebook")
        ).unwrap()
    }

    pub fn features(&self) -> hash_map::Keys<String, NodeIndex<u32>> {
        self.index.keys()
    }

    pub fn contains(&self, feature: &str) -> bool {
        self.index.contains_key(feature)
    }

    pub fn neighbors(&self, feature: &str, dist: u8) -> Vec<&Record> {
        assert!(
            dist % self.min_dist == 0,
            "unsupported distance: only multiples of {} allowed",
            self.min_dist
        );

        let feature = *self.index.get(feature).unwrap();

        let mut visited = HashSet::new();
        let mut neighbors = Vec::new();
        self.dfs(feature, 0, &mut visited, &mut neighbors, dist);
        println!("{:?}", neighbors);

        neighbors
    }

    fn dfs<'a>(
        &'a self,
        node: NodeIndex<u32>,
        d: u8,
        visited: &mut HashSet<NodeIndex<u32>>,
        neighbors: &mut Vec<&'a Record>,
        dist: u8) {
        if visited.contains(&node) {
            return;
        }
        visited.insert(node);

        if d == dist {
            neighbors.push(self.graph.node_weight(node).unwrap());
            return;
        }

        for n in self.graph.neighbors(node) {
            self.dfs(n, d + self.min_dist, visited, neighbors, dist);
        }
    }
}
<commit_msg>Docs.<commit_after>// Copyright 2016 Johannes Köster.
// Licensed under the MIT license (http://opensource.org/licenses/MIT)
// This file may not be copied, modified, or distributed
// except according to those terms.

use std::io;
use std::fs;
use std::path::Path;
use std::collections::{HashMap, hash_map, HashSet};
use num::CheckedAdd;

use csv;
use itertools::Itertools;
use bit_vec::BitVec;
use petgraph::prelude::*;
use petgraph::visit;
use bio::alignment::distance;

/// A codebook record.
#[derive(Debug)]
pub struct Record {
    pub name: String,
    pub codeword: BitVec<u32>
}

impl Record {
    /// Create new record.
    pub fn new(name: String, codeword: &[u8]) -> Self {
        let mut _codeword = BitVec::with_capacity(codeword.len());
        for &b in codeword {
            if b == b'1' {
                _codeword.push(true);
            } else if b == b'0' {
                _codeword.push(false)
            } else {
                panic!("invalid codeword {:?}", codeword)
            }
        }

        Record {
            name: name,
            codeword: _codeword
        }
    }

    /// Get distance to other codeword.
    pub fn dist(&self, other: &Record) -> u8 {
        let mut dist = 0;
        for (a, b) in self.codeword.blocks().zip(other.codeword.blocks()) {
            dist = dist.checked_add(&((a ^ b).count_ones() as u8)).expect(
                "bug: overflow when calculating hamming distance"
            );
        }
        dist
    }
}

/// Codebook representation.
pub struct Codebook {
    index: HashMap<String, NodeIndex<u32>>,
    graph: UnGraph<Record, ()>,
    min_dist: u8
}

impl Codebook {
    /// Read from a given file path.
    pub fn from_file<P: AsRef<Path>>(path: P, min_dist: u8) -> csv::Result<Self> {
        let mut rdr = (csv::Reader::from_file(path)?).delimiter(b'\t');

        let mut graph = Graph::default();
        let index = {
            let mut index = HashMap::new();
            for rec in rdr.decode() {
                let (feature, codeword): (String, String) = rec?;
                let idx = graph.add_node(Record::new(feature.clone(), codeword.as_bytes()));
                index.insert(
                    feature,
                    idx
                );
            }
            index
        };

        for &a in index.values() {
            for &b in index.values() {
                if a != b {
                    let d = {
                        let rec_a = graph.node_weight(a).unwrap();
                        let rec_b = graph.node_weight(b).unwrap();
                        rec_a.dist(&rec_b)
                    };

                    if d == min_dist {
                        graph.add_edge(a, b, ());
                    } else if d < min_dist {
                        panic!("unexpected hamming distance {} (given minimum {})", d, min_dist);
                    }
                }
            }
        }

        Ok(Codebook {
            graph: graph,
            index: index,
            min_dist: min_dist
        })
    }

    pub fn get(&self, feature: &str) -> &Record {
        self.graph.node_weight(
            *self.index.get(feature).expect("bug: feature not in codebook")
        ).unwrap()
    }

    pub fn features(&self) -> hash_map::Keys<String, NodeIndex<u32>> {
        self.index.keys()
    }

    pub fn contains(&self, feature: &str) -> bool {
        self.index.contains_key(feature)
    }

    pub fn neighbors(&self, feature: &str, dist: u8) -> Vec<&Record> {
        assert!(
            dist % self.min_dist == 0,
            "unsupported distance: only multiples of {} allowed",
            self.min_dist
        );

        let feature = *self.index.get(feature).unwrap();

        let mut visited = HashSet::new();
        let mut neighbors = Vec::new();
        self.dfs(feature, 0, &mut visited, &mut neighbors, dist);
        println!("{:?}", neighbors);

        neighbors
    }

    fn dfs<'a>(
        &'a self,
        node: NodeIndex<u32>,
        d: u8,
        visited: &mut HashSet<NodeIndex<u32>>,
        neighbors: &mut Vec<&'a Record>,
        dist: u8) {
        if visited.contains(&node) {
            return;
        }
        visited.insert(node);

        if d == dist {
            neighbors.push(self.graph.node_weight(node).unwrap());
            return;
        }

        for n in self.graph.neighbors(node) {
            self.dfs(n, d + self.min_dist, visited, neighbors, dist);
        }
    }
}
<|endoftext|>


<commit_before>use std::io::fs::PathExtensions;
use std::io::{File, BufferedReader};
use std::cell::RefCell;

pub struct Buffer {
    pub file_path: String,
    pub lines: Vec<RefCell<Line>>,
}

impl Buffer {
    /// Create a new buffer instance
    pub fn new() -> Buffer {
        Buffer {
            file_path: String::new(),
            lines: Vec::new(),
        }
    }

    /// Create a new buffer with a single line
    pub fn new_empty() -> Buffer {
        let mut buffer = Buffer::new();
        buffer.lines.push(RefCell::new(Line::new(Vec::new(), 0)));
        buffer.file_path = String::from_str("untitled");

        buffer
    }

    /// Create a new buffer instance and load the given file
    pub fn new_from_file(path: &Path) -> Buffer {
        let mut buffer = Buffer::new();

        if path.exists() {
            let mut file = BufferedReader::new(File::open(path));

            // for every line in the file we add a corresponding line to the buffer
            for (index, line) in file.lines().enumerate() {
                let mut data = line.unwrap().into_bytes();
                data.pop(); // remove \n chars
                buffer.lines.push(RefCell::new(Line::new(data, index)));
            }
        } else {
            buffer.lines.push(RefCell::new(Line::new(Vec::new(), 0)));
        }

        buffer.file_path = path.as_str().unwrap().to_string();
        buffer
    }

    pub fn get_status_text(&self) -> String {
        let file_path = self.file_path.clone();
        let line_count = self.lines.len();
        format!("{}, lines: {}", file_path, line_count)
    }

    fn fix_linenums(&mut self) {
        for (index, line) in self.lines.iter().enumerate() {
            line.borrow_mut().linenum = index;
        }
    }

    pub fn insert_line(&mut self, offset: uint, mut line_num: uint) {
        // split the current line at the cursor position
        let (_, new_data) = self.split_line(offset, line_num);
        {
            // truncate the current line
            let line = self.get_line_at(line_num);
            line.unwrap().borrow_mut().data.truncate(offset);
        }

        line_num += 1;

        self.lines.insert(line_num, RefCell::new(Line::new(new_data, line_num)));

        self.fix_linenums();
    }

    /// Join the line identified by `line_num` with the one at `line_num - 1 `.
    pub fn join_line_with_previous(&mut self, offset: uint, line_num: uint) -> uint {
        // if the line_num is 0 (ie the first line), don't do anything
        if line_num == 0 { return offset }

        let mut current_line_data: Vec<u8>;
        {
            // get current line data
            let current_line = self.get_line_at(line_num).unwrap();
            current_line_data = current_line.borrow().data.clone();
        }

        // update the previous line
        let new_cursor_offset = match self.get_line_at(line_num -1) {
            None => offset,
            Some(line) => {
                let line_len = line.borrow().data.len();
                line.borrow_mut().data.push_all(current_line_data.as_slice());
                line_len
            }
        };

        self.lines.remove(line_num);
        self.fix_linenums();

        return new_cursor_offset
    }

    // TODO(greg): refactor this to use Vec::partition
    /// Split the line identified by `line_num` at `offset`
    fn split_line(&mut self, offset: uint, line_num: uint) -> (Vec<u8>, Vec<u8>) {
        let line = self.get_line_at(line_num).unwrap();

        let data = line.borrow().data.clone();
        let old_data = data.slice_to(offset);
        let new_data = data.slice_from(offset);

        let mut new = Vec::new(); new.push_all(new_data);
        let mut old = Vec::new(); old.push_all(old_data);

        return (old, new)
    }

    fn get_line_at(&self, line_num: uint) -> Option<&RefCell<Line>> {
        let num_lines = self.lines.len() -1;
        if line_num > num_lines { return None }
        Some(&self.lines[line_num])
    }

}

pub struct Line {
    pub data: Vec<u8>,
    pub linenum: uint,
}

impl Line {
    /// Create a new line instance
    pub fn new(data: Vec<u8>, line_num: uint) -> Line {
        Line{
            data: data,
            linenum: line_num,
        }
    }

    /// Get the length of the current line
    pub fn len(&self) -> uint {
        self.data.len()
    }
}

#[cfg(test)]
mod tests {

    use std::cell::RefCell;
    use buffer::Buffer;
    use buffer::Line;
    use utils::data_from_str;

    fn setup_buffer() -> Buffer {
        let mut buffer = Buffer::new();
        buffer.file_path = String::from_str("/some/file.txt");
        buffer.lines = vec!(
            RefCell::new(Line::new(data_from_str("test"), 0)),
            RefCell::new(Line::new(Vec::new(), 1)),
            RefCell::new(Line::new(data_from_str("text file"), 2)),
            RefCell::new(Line::new(data_from_str("content"), 3)),
        );
        buffer
    }

    #[test]
    fn test_get_status_text() {
        let buffer = setup_buffer();
        assert_eq!(buffer.get_status_text(), "/some/file.txt, lines: 4".to_string())
    }

    #[test]
    fn test_insert_line() {
        let mut buffer = setup_buffer();
        buffer.insert_line(0, 0);
        assert_eq!(buffer.lines.len(), 5);
    }

    #[test]
    fn test_insert_line_in_middle_of_other_line() {
        let mut buffer = setup_buffer();
        buffer.insert_line(1, 0);
        assert_eq!(buffer.lines.len(), 5);

        let ref line = buffer.lines[1];
        assert_eq!(line.borrow().data, data_from_str("est"));
    }

    #[test]
    fn test_line_numbers_are_fixed_after_adding_new_line() {
        let mut buffer = setup_buffer();
        buffer.insert_line(1, 2);
        assert_eq!(buffer.lines.len(), 5);

        // check that all linenums are sequential
        for (index, line) in buffer.lines.iter().enumerate() {
            assert_eq!(index, line.borrow().linenum);
        }
    }

    #[test]
    fn test_join_line_with_previous() {
        let mut buffer = setup_buffer();

        let offset = buffer.join_line_with_previous(0, 3);

        assert_eq!(buffer.lines.len(), 3);
        assert_eq!(buffer.lines[2].borrow().data, data_from_str("text filecontent"));
        assert_eq!(offset, 9);
    }

    #[test]
    fn join_line_with_previous_does_nothing_on_line_zero_offset_zero() {
        let mut buffer = setup_buffer();
        buffer.join_line_with_previous(0, 0);

        assert_eq!(buffer.lines.len(), 4);
        assert_eq!(buffer.lines[0].borrow().data, data_from_str("test"));
    }

    #[test]
    fn test_split_line() {
        let mut buffer = setup_buffer();
        let (old, new) = buffer.split_line(3, 3);

        assert_eq!(old, data_from_str("con"));
        assert_eq!(new, data_from_str("tent"));
    }

}

<commit_msg>Only remove newline chars if they are actually present<commit_after>use std::io::fs::PathExtensions;
use std::io::{File, BufferedReader};
use std::cell::RefCell;

pub struct Buffer {
    pub file_path: String,
    pub lines: Vec<RefCell<Line>>,
}

impl Buffer {
    /// Create a new buffer instance
    pub fn new() -> Buffer {
        Buffer {
            file_path: String::new(),
            lines: Vec::new(),
        }
    }

    /// Create a new buffer with a single line
    pub fn new_empty() -> Buffer {
        let mut buffer = Buffer::new();
        buffer.lines.push(RefCell::new(Line::new(Vec::new(), 0)));
        buffer.file_path = String::from_str("untitled");

        buffer
    }

    /// Create a new buffer instance and load the given file
    pub fn new_from_file(path: &Path) -> Buffer {
        let mut buffer = Buffer::new();

        if path.exists() {
            let mut file = BufferedReader::new(File::open(path));

            // for every line in the file we add a corresponding line to the buffer
            for (index, line) in file.lines().enumerate() {
                let mut data = line.unwrap().into_bytes();
                let last_index = data.len() - 1;
                if data[last_index] == '\n' as u8 {
                    data.pop();
                }
                buffer.lines.push(RefCell::new(Line::new(data, index)));
            }
        } else {
            buffer.lines.push(RefCell::new(Line::new(Vec::new(), 0)));
        }

        buffer.file_path = path.as_str().unwrap().to_string();
        buffer
    }

    pub fn get_status_text(&self) -> String {
        let file_path = self.file_path.clone();
        let line_count = self.lines.len();
        format!("{}, lines: {}", file_path, line_count)
    }

    fn fix_linenums(&mut self) {
        for (index, line) in self.lines.iter().enumerate() {
            line.borrow_mut().linenum = index;
        }
    }

    pub fn insert_line(&mut self, offset: uint, mut line_num: uint) {
        // split the current line at the cursor position
        let (_, new_data) = self.split_line(offset, line_num);
        {
            // truncate the current line
            let line = self.get_line_at(line_num);
            line.unwrap().borrow_mut().data.truncate(offset);
        }

        line_num += 1;

        self.lines.insert(line_num, RefCell::new(Line::new(new_data, line_num)));

        self.fix_linenums();
    }

    /// Join the line identified by `line_num` with the one at `line_num - 1 `.
    pub fn join_line_with_previous(&mut self, offset: uint, line_num: uint) -> uint {
        // if the line_num is 0 (ie the first line), don't do anything
        if line_num == 0 { return offset }

        let mut current_line_data: Vec<u8>;
        {
            // get current line data
            let current_line = self.get_line_at(line_num).unwrap();
            current_line_data = current_line.borrow().data.clone();
        }

        // update the previous line
        let new_cursor_offset = match self.get_line_at(line_num -1) {
            None => offset,
            Some(line) => {
                let line_len = line.borrow().data.len();
                line.borrow_mut().data.push_all(current_line_data.as_slice());
                line_len
            }
        };

        self.lines.remove(line_num);
        self.fix_linenums();

        return new_cursor_offset
    }

    // TODO(greg): refactor this to use Vec::partition
    /// Split the line identified by `line_num` at `offset`
    fn split_line(&mut self, offset: uint, line_num: uint) -> (Vec<u8>, Vec<u8>) {
        let line = self.get_line_at(line_num).unwrap();

        let data = line.borrow().data.clone();
        let old_data = data.slice_to(offset);
        let new_data = data.slice_from(offset);

        let mut new = Vec::new(); new.push_all(new_data);
        let mut old = Vec::new(); old.push_all(old_data);

        return (old, new)
    }

    fn get_line_at(&self, line_num: uint) -> Option<&RefCell<Line>> {
        let num_lines = self.lines.len() -1;
        if line_num > num_lines { return None }
        Some(&self.lines[line_num])
    }

}

pub struct Line {
    pub data: Vec<u8>,
    pub linenum: uint,
}

impl Line {
    /// Create a new line instance
    pub fn new(data: Vec<u8>, line_num: uint) -> Line {
        Line{
            data: data,
            linenum: line_num,
        }
    }

    /// Get the length of the current line
    pub fn len(&self) -> uint {
        self.data.len()
    }
}

#[cfg(test)]
mod tests {

    use std::cell::RefCell;
    use buffer::Buffer;
    use buffer::Line;
    use utils::data_from_str;

    fn setup_buffer() -> Buffer {
        let mut buffer = Buffer::new();
        buffer.file_path = String::from_str("/some/file.txt");
        buffer.lines = vec!(
            RefCell::new(Line::new(data_from_str("test"), 0)),
            RefCell::new(Line::new(Vec::new(), 1)),
            RefCell::new(Line::new(data_from_str("text file"), 2)),
            RefCell::new(Line::new(data_from_str("content"), 3)),
        );
        buffer
    }

    #[test]
    fn test_get_status_text() {
        let buffer = setup_buffer();
        assert_eq!(buffer.get_status_text(), "/some/file.txt, lines: 4".to_string())
    }

    #[test]
    fn test_insert_line() {
        let mut buffer = setup_buffer();
        buffer.insert_line(0, 0);
        assert_eq!(buffer.lines.len(), 5);
    }

    #[test]
    fn test_insert_line_in_middle_of_other_line() {
        let mut buffer = setup_buffer();
        buffer.insert_line(1, 0);
        assert_eq!(buffer.lines.len(), 5);

        let ref line = buffer.lines[1];
        assert_eq!(line.borrow().data, data_from_str("est"));
    }

    #[test]
    fn test_line_numbers_are_fixed_after_adding_new_line() {
        let mut buffer = setup_buffer();
        buffer.insert_line(1, 2);
        assert_eq!(buffer.lines.len(), 5);

        // check that all linenums are sequential
        for (index, line) in buffer.lines.iter().enumerate() {
            assert_eq!(index, line.borrow().linenum);
        }
    }

    #[test]
    fn test_join_line_with_previous() {
        let mut buffer = setup_buffer();

        let offset = buffer.join_line_with_previous(0, 3);

        assert_eq!(buffer.lines.len(), 3);
        assert_eq!(buffer.lines[2].borrow().data, data_from_str("text filecontent"));
        assert_eq!(offset, 9);
    }

    #[test]
    fn join_line_with_previous_does_nothing_on_line_zero_offset_zero() {
        let mut buffer = setup_buffer();
        buffer.join_line_with_previous(0, 0);

        assert_eq!(buffer.lines.len(), 4);
        assert_eq!(buffer.lines[0].borrow().data, data_from_str("test"));
    }

    #[test]
    fn test_split_line() {
        let mut buffer = setup_buffer();
        let (old, new) = buffer.split_line(3, 3);

        assert_eq!(old, data_from_str("con"));
        assert_eq!(new, data_from_str("tent"));
    }

}

<|endoftext|>


<commit_before>#![feature(slicing_syntax)]

//TODO: UTF8 support
//TODO: Save cursor x offset as it traverses up/down
//TODO: Write tests

extern crate gapbuffer;

use std::io::{File, Reader, BufferedReader};
use gapbuffer::GapBuffer;

#[deriving(Copy, Show)]
pub enum Direction {
    Up, Down, Left, Right,
}

pub struct Buffer {
    pub file_path: Option<Path>,
    pub text: GapBuffer<char>,
    pub cursor: uint,
}

//Private methods
impl Buffer {

    //Returns the number of newlines in the buffer before the mark.
    fn get_line(&self, mark: uint) -> Option<uint> {
        let mut linenum = 0;
        if mark < self.text.len() {
            for c in self.text[0..mark].iter() {
                if c == &'\n' { linenum += 1; }
            }
            Some(linenum)
        } else { None }
    }

    //Returns the index of the nth newline in the buffer.
    fn get_line_idx(&self, line: uint) -> Option<uint> {
        let mut linenum = 0;
        for idx in range(0, self.text.len()) {
            if self.text[idx] == '\n' { linenum += 1; if linenum == line { return Some(idx) } }
        }
        None
    }

    //Returns the index of the point the mark would be at if shifted 'offset' lines.
    fn move_line(&self, mark: uint, offset: int) -> Option<uint> {
        let mut x_offset = 1;
        loop {
            match self.text[0..mark].iter().next_back() {
                Some(&'\n') | None => { break; }
                _                  => { x_offset += 1; }
            }
        }
        if let Some(line) = self.get_line(mark) {
            let newline = (line as int - offset) as uint;
            Some(self.get_line_idx(newline).unwrap() + x_offset)
        } else { None }
    }

}

//Public methods
impl Buffer {

    /// Constructor for empty buffer.
    pub fn new() -> Buffer {
        let text = GapBuffer::new();
        Buffer {
            file_path: None,
            text: text,
            cursor: 0u,
        }
    }

    /// Constructor for buffer from reader.
    pub fn new_from_reader<R: Reader>(reader: R) -> Buffer {
        let mut buff = Buffer::new();
        if let Ok(contents) = BufferedReader::new(reader).read_to_string() {
            buff.text.extend(contents.chars());
        }
        buff
    }

    /// Constructor for buffer from file.
    pub fn new_from_file(path: Path) -> Buffer {
        if let Ok(file) = File::open(&path) {
            let mut buff = Buffer::new_from_reader(file);
            buff.file_path = Some(path);
            buff
        } else {
            Buffer::new()
        }
    }

    //Move the cursor by some amount (can be negative).
    pub fn move_cursor(&mut self, offset: int) {
        let idx = self.cursor as int + offset;
        if 0 >= idx && idx > self.text.len() as int {
            self.set_cursor(idx as uint);
        }
    }

    //Set the cursor to some index.
    pub fn set_cursor(&mut self, location: uint) {
        self.cursor = location;
    }

    //Shift the cursor by one in any of the four directions.
    pub fn shift_cursor(&mut self, direction: Direction) {
        match direction {
            Direction::Up if self.get_line(self.cursor).unwrap() > 0             => {
                self.move_line(self.cursor, -1);
            }
            Direction::Down
            if self.get_line(self.cursor) < self.get_line(self.text.len() - 1)  => {
                self.move_line(self.cursor, 1);
            }
            Direction::Left if self.cursor > 0                                  => {
                self.cursor -= 1;
            }
            Direction::Right if self.cursor < self.text.len()                   => {
                self.cursor += 1;
            }
            _ => { }
        }
    }

    //Remove the char the cursor is at the index of.
    pub fn remove_char(&mut self) -> Option<char> {
        self.text.remove(self.cursor)
    }

    //Insert the char the cursor is at the index of.
    pub fn insert_char(&mut self, c: char) {
        self.text.insert(self.cursor, c);
    }

}

#[cfg(test)]
mod test {

}
<commit_msg>Nothing important.<commit_after>#![feature(slicing_syntax)]

//TODO: UTF8 support
//TODO: Save cursor x offset as it traverses up/down
//TODO: Write tests

extern crate gapbuffer;

use std::io::{File, Reader, BufferedReader};
use gapbuffer::GapBuffer;

#[deriving(Copy, Show)]
pub enum Direction {
    Up, Down, Left, Right,
}

pub struct Buffer {
    pub file_path: Option<Path>,
    pub text: GapBuffer<char>,
    pub cursor: uint,
}

//Private methods
impl Buffer {

    //Returns the number of newlines in the buffer before the mark.
    fn get_line(&self, mark: uint) -> Option<uint> {
        let mut linenum = 0;
        if mark < self.text.len() {
            for c in self.text[0..mark].iter() {
                if c == &'\n' { linenum += 1; }
            }
            Some(linenum)
        } else { None }
    }

    //Returns the index of the nth newline in the buffer.
    fn get_line_idx(&self, line: uint) -> Option<uint> {
        let mut linenum = 0;
        for idx in range(0, self.text.len()) {
            if self.text[idx] == '\n' {
                linenum += 1;
                if linenum == line { return Some(idx) }
            }
        }
        None
    }

    //Returns the index of the point the mark would be at if shifted 'offset' lines.
    fn move_line(&self, mark: uint, offset: int) -> Option<uint> {
        let mut x_offset = 1;
        loop {
            match self.text[0..mark].iter().next_back() {
                Some(&'\n') | None => { break; }
                _                  => { x_offset += 1; }
            }
        }
        if let Some(line) = self.get_line(mark) {
            let newline = (line as int - offset) as uint;
            Some(self.get_line_idx(newline).unwrap() + x_offset)
        } else { None }
    }

}

//Public methods
impl Buffer {

    /// Constructor for empty buffer.
    pub fn new() -> Buffer {
        let text = GapBuffer::new();
        Buffer {
            file_path: None,
            text: text,
            cursor: 0u,
        }
    }

    /// Constructor for buffer from reader.
    pub fn new_from_reader<R: Reader>(reader: R) -> Buffer {
        let mut buff = Buffer::new();
        if let Ok(contents) = BufferedReader::new(reader).read_to_string() {
            buff.text.extend(contents.chars());
        }
        buff
    }

    /// Constructor for buffer from file.
    pub fn new_from_file(path: Path) -> Buffer {
        if let Ok(file) = File::open(&path) {
            let mut buff = Buffer::new_from_reader(file);
            buff.file_path = Some(path);
            buff
        } else { Buffer::new() }
    }

    //Move the cursor by some amount (can be negative).
    pub fn move_cursor(&mut self, offset: int) {
        let idx = self.cursor as int + offset;
        if 0 >= idx && idx > self.text.len() as int {
            self.set_cursor(idx as uint);
        }
    }

    //Set the cursor to some index.
    pub fn set_cursor(&mut self, location: uint) {
        self.cursor = location;
    }

    //Shift the cursor by one in any of the four directions.
    pub fn shift_cursor(&mut self, direction: Direction) {
        match direction {
            Direction::Up if self.get_line(self.cursor).unwrap() > 0 => {
                self.cursor = self.move_line(self.cursor, -1);
            }
            Direction::Down
            if self.get_line(self.cursor) < self.get_line(self.text.len() - 1) => {
                self.cursor = self.move_line(self.cursor, 1);
            }
            Direction::Left if self.cursor > 0 => {
                self.cursor -= 1;
            }
            Direction::Right if self.cursor < self.text.len() => {
                self.cursor += 1;
            }
            _ => { }
        }
    }

    //Remove the char the cursor is at the index of.
    pub fn remove_char(&mut self) -> Option<char> {
        self.text.remove(self.cursor)
    }

    //Insert the char the cursor is at the index of.
    pub fn insert_char(&mut self, c: char) {
        self.text.insert(self.cursor, c);
    }

}

#[cfg(test)]
mod test {

}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::cmp::Ordering;
use std::fmt;
use std::iter::IntoIterator;
use std::rc::Rc;
use chrono::prelude::*;

use money::Money;
use errors::*;
use expression::*;

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(untagged)]
pub enum Accounts {
    Tree(HashMap<String, Accounts>),
    Leaf(Account),
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(untagged)]
pub enum Account {
    Simple(SimpleAccount),
    Derived(DerivedAccount),
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct SimpleAccount {
    pub amount: Money,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct DerivedAccount {
    pub expression: Expr,
}

fn eval(expr: &Expr, root: &Accounts) -> Option<Money> {
    match *expr {
        Expr::Id(ref name) => root.get(name).map(Accounts::sum),
        Expr::Add(ref left, ref right) => {
            eval(left, root)
                .and_then(|left_val| eval(right, root).map(|right_val| left_val + right_val))
        }
        Expr::Sub(ref left, ref right) => {
            eval(left, root)
                .and_then(|left_val| eval(right, root).map(|right_val| left_val - right_val))
        }
    }
}

impl Account {
    pub fn amount(&self) -> Money {
        match *self {
            Account::Simple(ref s) => s.amount.clone(),
            Account::Derived(ref acc) => Money::zero(),
        }
    }
}

impl Accounts {
    pub fn root() -> Accounts {
        Accounts::Tree(HashMap::new())
    }

    pub fn leaf(account: Account) -> Accounts {
        Accounts::Leaf(account)
    }

    pub fn get(&self, path: &str) -> Option<&Accounts> {
        match self {
            &Accounts::Tree(ref m) => {
                if let Some(index) = path.find(':') {
                    let (account, sub_account) = path.split_at(index);
                    m.get(account).and_then(|a| a.get(&sub_account[1..]))
                } else {
                    m.get(path)
                }
            }
            a => if path.len() == 0 { Some(a) } else { None },
        }
    }

    // TODO get_mut

    pub fn sum(&self) -> Money {
        match *self {
            Accounts::Tree(ref m) => {
                let mut result = Money::from(0);
                for (_, account) in m {
                    result += account.sum();
                }
                result
            }
            Accounts::Leaf(ref a) => a.amount(),
        }
    }

    pub fn fold<B, F>(self, init: B, mut f: F) -> B
        where F: FnMut(B, Account) -> B
    {
        self.fold_internal(init, &mut f)
    }

    fn fold_internal<B, F>(self, init: B, f: &mut F) -> B
        where F: FnMut(B, Account) -> B
    {
        match self {
            Accounts::Tree(m) => {
                let mut result = init;
                for (_, account) in m {
                    result = account.fold_internal(result, f);
                }
                result
            }
            Accounts::Leaf(l) => f(init, l),
        }
    }

    pub fn fold_with_path<B, F>(self, init: B, mut f: F) -> B
        where F: FnMut(B, &str, Account) -> B
    {
        self.fold_with_path_internal(init, "", &mut f)
    }

    fn fold_with_path_internal<B, F>(self, init: B, path: &str, f: &mut F) -> B
        where F: FnMut(B, &str, Account) -> B
    {
        match self {
            Accounts::Tree(m) => {
                let mut result = init;
                for (name, account) in m {
                    result = account.fold_with_path_internal(result,
                                                             &format!("{}{}",
                                                                      if path.len() == 0 {
                                                                          String::new()
                                                                      } else {
                                                                          format!("{}:", path)
                                                                      },
                                                                      name),
                                                             f);
                }
                result
            }
            Accounts::Leaf(l) => f(init, path, l),
        }
    }

    pub fn paths(&self) -> Vec<String> {
        self.paths_internal("")
    }

    fn paths_internal(&self, path: &str) -> Vec<String> {
        match *self {
            Accounts::Tree(ref m) => {
                let mut result = Vec::new();
                for (name, account) in m {
                    result.extend(account.paths_internal(&format!("{}{}",
                                                                  if path.len() == 0 {
                                                                      String::new()
                                                                  } else {
                                                                      format!("{}:", path)
                                                                  },
                                                                  name)));
                }
                result
            }
            _ => vec![String::from(path)],
        }
    }

    pub fn get_account_names(&self) -> Vec<String> {
        self.clone()
            .fold_with_path(Vec::new(), |mut vec, path, _| {
                vec.push(String::from(path));
                vec
            })
    }

    pub fn flatten(self) -> Vec<Account> {
        self.fold(Vec::new(), |mut vec, account| {
            vec.push(account);
            vec
        })
    }

    pub fn flatten_with_path(self) -> Vec<(String, Account)> {
        self.fold_with_path(Vec::new(), |mut vec, path, account| {
            vec.push((String::from(path), account));
            vec
        })
    }

    pub fn create_account(self, path: String, account: Account) -> Result<Accounts> {
        match self {
            Accounts::Tree(mut m) => {
                if let Some(index) = path.find(':') {
                    let (path, sub_path) = path.split_at(index);
                    let sub_account = m.get(path)
                        .cloned()
                        .unwrap_or_else(Accounts::root)
                        .create_account(String::from(&sub_path[1..]), account)?;
                    m.insert(String::from(path), sub_account);
                    Ok(Accounts::Tree(m))
                } else if m.contains_key(&path) {
                    Err(ErrorKind::AlreadyExists(path).into())
                } else {
                    m.insert(path, Accounts::Leaf(account));
                    Ok(Accounts::Tree(m))
                }
            }
            other => {
                if path.len() == 0 {
                    Ok(other)
                } else {
                    Err(ErrorKind::InvalidAccountName(path).into())
                }
            }
        }
    }

    // TODO create_account_unchecked
    // TODO create_account_mut
    // TODO create_account_mut_unchecked

    pub fn deposit(self, path: String, amount: Money) -> Result<Self> {
        match self {
            Accounts::Tree(mut m) => {
                if let Some(index) = path.find(':') {
                    let (path, sub_path) = path.split_at(index);
                    let new_subaccount = m.get(path)
                        .cloned()
                        .unwrap_or_else(Accounts::root)
                        .deposit(String::from(&sub_path[1..]), amount)?;
                    m.insert(String::from(path), new_subaccount);
                    Ok(Accounts::Tree(m))
                } else if m.contains_key(&path) {
                    let new_subaccount =
                        m.get(&path).cloned().unwrap().deposit(path.clone(), amount)?;
                    m.insert(String::from(path), new_subaccount);
                    Ok(Accounts::Tree(m))
                } else {
                    Accounts::Tree(m)
                        .create_account(path, Account::Simple(SimpleAccount { amount: amount }))
                }
            }
            Accounts::Leaf(Account::Simple(mut s)) => {
                s.amount += amount;
                Ok(Accounts::Leaf(Account::Simple(s)))
            }
            Accounts::Leaf(Account::Derived(_)) => {
                Err(ErrorKind::InvalidDeposit(path, amount.to_string()).into())
            }
        }
    }

    // TODO deposit_unchecked
    // TODO deposit_mut
    // TODO deposit_mut_unchecked

    pub fn withdraw(self, path: String, amount: Money) -> Result<Self> {
        self.deposit(path, -amount)
    }

    // TODO withdraw_unchecked

    // TODO withdraw_mut
    // TODO withdraw_mut_unchecked

    pub fn validate(&self) -> Result<()> {
        if let Accounts::Tree(ref m) = *self {
            for (path, account) in m {
                if let Some(_) = path.find(':') {
                    return Err(ErrorKind::InvalidAccountName(path.clone()).into());
                }
                account.validate()?;
            }
        }
        // TODO validate derived accounts makes sense
        Ok(())
    }

    pub fn apply(self, transaction: Transaction) -> Result<Self> {
        self.withdraw(transaction.from, transaction.amount.clone())?
            .deposit(transaction.to, transaction.amount)
    }

    pub fn apply_unchecked(self, transaction: Transaction) -> Self {
        self.withdraw(transaction.from, transaction.amount.clone())
            .unwrap()
            .deposit(transaction.to, transaction.amount)
            .unwrap()
    }

    // TODO apply_mut
    // TODO apply_mut_unchecked

    pub fn eval_unchecked(self) -> HashMap<String, Money> {
        let mut result = HashMap::new();
        for (name, account) in self.clone().flatten_with_path() {
            result.insert(name,
                          match account {
                              Account::Simple(s) => s.amount,
                              Account::Derived(d) => eval(&d.expression, &self).unwrap(),
                          });
        }
        result
    }
}

// TODO impl IntoIterator for Accounts
// TODO impl std::iter::Extend for Accounts

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Transaction {
    pub amount: Money,
    pub from: String,
    pub to: String,
    pub date: NaiveDate,
}

impl Transaction {
    pub fn new(amount: Money, from: String, to: String, date: NaiveDate) -> Transaction {
        Transaction {
            amount: amount,
            from: from,
            to: to,
            date: date,
        }
    }
}

impl Eq for Transaction {}

impl PartialOrd for Transaction {
    fn partial_cmp(&self, other: &Transaction) -> Option<Ordering> {
        self.date.partial_cmp(&other.date)
    }
}

impl Ord for Transaction {
    fn cmp(&self, other: &Self) -> Ordering {
        self.date.cmp(&other.date)
    }
}

impl fmt::Display for Transaction {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{} from {} to {}", self.amount, self.from, self.to)
    }
}

// TODO wrap transaction iterators

pub struct History<T: Iterator<Item = Transaction> + Clone, D: Iterator<Item = NaiveDate>> {
    transactions: T,
    dates: D,
    consumed: usize,
    state: (NaiveDate, Accounts),
}

impl<T, D> History<T, D>
    where T: Iterator<Item = Transaction> + Clone,
          D: Iterator<Item = NaiveDate>
{
    pub fn new(state: (NaiveDate, Accounts), transactions: T, dates: D) -> History<T, D> {
        History {
            transactions: transactions,
            dates: dates,
            consumed: 0,
            state: state,
        }
    }
}

// this assumes that users have validated the transactions first :)
impl<T, D> Iterator for History<T, D>
    where T: Iterator<Item = Transaction> + Clone,
          D: Iterator<Item = NaiveDate>
{
    type Item = (NaiveDate, Accounts);

    fn next(&mut self) -> Option<Self::Item> {
        match self.dates.next() {
            Some(next_date) => {
                // consume the next few transactions
                let next_transactions = self.transactions
                    .clone()
                    .skip(self.consumed)
                    .take_while(|ref t| t.date <= next_date)
                    .collect::<Vec<_>>();
                self.consumed += next_transactions.len();

                // calculate next state
                self.state = (next_date,
                              next_transactions.into_iter()
                    .fold(self.state.1.clone(), Accounts::apply_unchecked));
                Some(self.state.clone())
            }
            None => None,
        }
    }
}

// TODO write tests for the accounts object
<commit_msg>Trigger travis<commit_after>use std::collections::HashMap;
use std::cmp::Ordering;
use std::fmt;
use std::iter::IntoIterator;
use std::rc::Rc;
use chrono::prelude::*;

use money::Money;
use errors::*;
use expression::*;

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(untagged)]
pub enum Accounts {
    Tree(HashMap<String, Accounts>),
    Leaf(Account),
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(untagged)]
pub enum Account {
    Simple(SimpleAccount),
    Derived(DerivedAccount),
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct SimpleAccount {
    pub amount: Money,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct DerivedAccount {
    pub expression: Expr,
}

fn eval(expr: &Expr, root: &Accounts) -> Option<Money> {
    match *expr {
        Expr::Id(ref name) => root.get(name).map(Accounts::sum),
        Expr::Add(ref left, ref right) => {
            eval(left, root)
                .and_then(|left_val| eval(right, root).map(|right_val| left_val + right_val))
        }
        Expr::Sub(ref left, ref right) => {
            eval(left, root)
                .and_then(|left_val| eval(right, root).map(|right_val| left_val - right_val))
        }
    }
}

impl Account {
    pub fn amount(&self) -> Money {
        match *self {
            Account::Simple(ref s) => s.amount.clone(),
            Account::Derived(ref acc) => Money::zero(),
        }
    }
}

impl Accounts {
    pub fn root() -> Accounts {
        Accounts::Tree(HashMap::new())
    }

    pub fn leaf(account: Account) -> Accounts {
        Accounts::Leaf(account)
    }

    pub fn get(&self, path: &str) -> Option<&Accounts> {
        match self {
            &Accounts::Tree(ref m) => {
                if let Some(index) = path.find(':') {
                    let (account, sub_account) = path.split_at(index);
                    m.get(account).and_then(|a| a.get(&sub_account[1..]))
                } else {
                    m.get(path)
                }
            }
            a => if path.len() == 0 { Some(a) } else { None },
        }
    }

    // TODO get_mut

    pub fn sum(&self) -> Money {
        match *self {
            Accounts::Tree(ref m) => {
                let mut result = Money::from(0);
                for (_, account) in m {
                    result += account.sum();
                }
                result
            }
            Accounts::Leaf(ref a) => a.amount(),
        }
    }

    pub fn fold<B, F>(self, init: B, mut f: F) -> B
        where F: FnMut(B, Account) -> B
    {
        self.fold_internal(init, &mut f)
    }

    fn fold_internal<B, F>(self, init: B, f: &mut F) -> B
        where F: FnMut(B, Account) -> B
    {
        match self {
            Accounts::Tree(m) => {
                let mut result = init;
                for (_, account) in m {
                    result = account.fold_internal(result, f);
                }
                result
            }
            Accounts::Leaf(l) => f(init, l),
        }
    }

    pub fn fold_with_path<B, F>(self, init: B, mut f: F) -> B
        where F: FnMut(B, &str, Account) -> B
    {
        self.fold_with_path_internal(init, "", &mut f)
    }

    fn fold_with_path_internal<B, F>(self, init: B, path: &str, f: &mut F) -> B
        where F: FnMut(B, &str, Account) -> B
    {
        match self {
            Accounts::Tree(m) => {
                let mut result = init;
                for (name, account) in m {
                    result = account.fold_with_path_internal(result,
                                                             &format!("{}{}",
                                                                      if path.len() == 0 {
                                                                          String::new()
                                                                      } else {
                                                                          format!("{}:", path)
                                                                      },
                                                                      name),
                                                             f);
                }
                result
            }
            Accounts::Leaf(l) => f(init, path, l),
        }
    }

    pub fn paths(&self) -> Vec<String> {
        self.paths_internal("")
    }

    fn paths_internal(&self, path: &str) -> Vec<String> {
        match *self {
            Accounts::Tree(ref m) => {
                let mut result = Vec::new();
                for (name, account) in m {
                    result.extend(account.paths_internal(&format!("{}{}",
                                                                  if path.len() == 0 {
                                                                      String::new()
                                                                  } else {
                                                                      format!("{}:", path)
                                                                  },
                                                                  name)));
                }
                result
            }
            _ => vec![String::from(path)],
        }
    }

    pub fn get_account_names(&self) -> Vec<String> {
        self.clone()
            .fold_with_path(Vec::new(), |mut vec, path, _| {
                vec.push(String::from(path));
                vec
            })
    }

    pub fn flatten(self) -> Vec<Account> {
        self.fold(Vec::new(), |mut vec, account| {
            vec.push(account);
            vec
        })
    }

    pub fn flatten_with_path(self) -> Vec<(String, Account)> {
        self.fold_with_path(Vec::new(), |mut vec, path, account| {
            vec.push((String::from(path), account));
            vec
        })
    }

    pub fn create_account(self, path: String, account: Account) -> Result<Accounts> {
        match self {
            Accounts::Tree(mut m) => {
                if let Some(index) = path.find(':') {
                    let (path, sub_path) = path.split_at(index);
                    let sub_account = m.get(path)
                        .cloned()
                        .unwrap_or_else(Accounts::root)
                        .create_account(String::from(&sub_path[1..]), account)?;
                    m.insert(String::from(path), sub_account);
                    Ok(Accounts::Tree(m))
                } else if m.contains_key(&path) {
                    Err(ErrorKind::AlreadyExists(path).into())
                } else {
                    m.insert(path, Accounts::Leaf(account));
                    Ok(Accounts::Tree(m))
                }
            }
            other => {
                if path.len() == 0 {
                    Ok(other)
                } else {
                    Err(ErrorKind::InvalidAccountName(path).into())
                }
            }
        }
    }

    // TODO create_account_unchecked
    // TODO create_account_mut
    // TODO create_account_mut_unchecked

    pub fn deposit(self, path: String, amount: Money) -> Result<Self> {
        match self {
            Accounts::Tree(mut m) => {
                if let Some(index) = path.find(':') {
                    let (path, sub_path) = path.split_at(index);
                    let new_subaccount = m.get(path)
                        .cloned()
                        .unwrap_or_else(Accounts::root)
                        .deposit(String::from(&sub_path[1..]), amount)?;
                    m.insert(String::from(path), new_subaccount);
                    Ok(Accounts::Tree(m))
                } else if m.contains_key(&path) {
                    let new_subaccount =
                        m.get(&path).cloned().unwrap().deposit(path.clone(), amount)?;
                    m.insert(String::from(path), new_subaccount);
                    Ok(Accounts::Tree(m))
                } else {
                    Accounts::Tree(m)
                        .create_account(path, Account::Simple(SimpleAccount { amount: amount }))
                }
            }
            Accounts::Leaf(Account::Simple(mut s)) => {
                s.amount += amount;
                Ok(Accounts::Leaf(Account::Simple(s)))
            }
            Accounts::Leaf(Account::Derived(_)) => {
                Err(ErrorKind::InvalidDeposit(path, amount.to_string()).into())
            }
        }
    }

    // TODO deposit_unchecked
    // TODO deposit_mut
    // TODO deposit_mut_unchecked

    pub fn withdraw(self, path: String, amount: Money) -> Result<Self> {
        self.deposit(path, -amount)
    }

    // TODO withdraw_unchecked

    // TODO withdraw_mut
    // TODO withdraw_mut_unchecked

    pub fn validate(&self) -> Result<()> {
        if let Accounts::Tree(ref m) = *self {
            for (path, account) in m {
                if let Some(_) = path.find(':') {
                    return Err(ErrorKind::InvalidAccountName(path.clone()).into());
                }
                account.validate()?;
            }
        }
        // TODO validate derived accounts makes sense
        Ok(())
    }

    pub fn apply(self, transaction: Transaction) -> Result<Self> {
        self.withdraw(transaction.from, transaction.amount.clone())?
            .deposit(transaction.to, transaction.amount)
    }

    pub fn apply_unchecked(self, transaction: Transaction) -> Self {
        self.withdraw(transaction.from, transaction.amount.clone())
            .unwrap()
            .deposit(transaction.to, transaction.amount)
            .unwrap()
    }

    // TODO apply_mut
    // TODO apply_mut_unchecked

    pub fn eval_unchecked(self) -> HashMap<String, Money> {
        let mut result = HashMap::new();
        for (name, account) in self.clone().flatten_with_path() {
            result.insert(name,
                          match account {
                              Account::Simple(s) => s.amount,
                              Account::Derived(d) => eval(&d.expression, &self).unwrap(),
                          });
        }
        result
    }
}

// TODO impl IntoIterator for Accounts
// TODO impl std::iter::Extend for Accounts

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Transaction {
    pub amount: Money,
    pub from: String,
    pub to: String,
    pub date: NaiveDate,
}

impl Transaction {
    pub fn new(amount: Money, from: String, to: String, date: NaiveDate) -> Transaction {
        Transaction {
            amount: amount,
            from: from,
            to: to,
            date: date,
        }
    }
}

impl Eq for Transaction {}

impl PartialOrd for Transaction {
    fn partial_cmp(&self, other: &Transaction) -> Option<Ordering> {
        self.date.partial_cmp(&other.date)
    }
}

impl Ord for Transaction {
    fn cmp(&self, other: &Self) -> Ordering {
        self.date.cmp(&other.date)
    }
}

impl fmt::Display for Transaction {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{} from {} to {}", self.amount, self.from, self.to)
    }
}

// TODO wrap transaction iterators

pub struct History<T: Iterator<Item = Transaction> + Clone, D: Iterator<Item = NaiveDate>> {
    transactions: T,
    dates: D,
    consumed: usize,
    state: (NaiveDate, Accounts),
}

impl<T, D> History<T, D>
    where T: Iterator<Item = Transaction> + Clone,
          D: Iterator<Item = NaiveDate>
{
    pub fn new(state: (NaiveDate, Accounts), transactions: T, dates: D) -> History<T, D> {
        History {
            transactions: transactions,
            dates: dates,
            consumed: 0,
            state: state,
        }
    }
}

// this assumes that users have validated the transactions first :)
impl<T, D> Iterator for History<T, D>
    where T: Iterator<Item = Transaction> + Clone,
          D: Iterator<Item = NaiveDate>
{
    type Item = (NaiveDate, Accounts);

    fn next(&mut self) -> Option<Self::Item> {
        match self.dates.next() {
            Some(next_date) => {
                // consume the next few transactions
                let next_transactions = self.transactions
                    .clone()
                    .skip(self.consumed)
                    .take_while(|ref t| t.date <= next_date)
                    .collect::<Vec<_>>();
                self.consumed += next_transactions.len();

                // calculate next state
                self.state = (next_date,
                              next_transactions.into_iter()
                    .fold(self.state.1.clone(), Accounts::apply_unchecked));
                Some(self.state.clone())
            }
            None => None,
        }
    }
}

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

    #[test]
    fn test_sum() {
        // TODO
    }
}
<|endoftext|>


<commit_before>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with
// this file, You can obtain one at https://mozilla.org/MPL/2.0/.

extern crate crowbook_text_processing;

use crowbook_text_processing::{FrenchFormatter, clean, escape};

use std::env;
use std::io;
use std::io::BufRead;
use std::process::exit;

const TOOLS: &'static[(&'static str, &'static str)] = &[
    ("escape_html", "escape text for HTML display"),
    ("escape_tex", "escape text for LaTeX display"),
    ("escape_nb_spaces", "escape non-breaking spaces using HTML entities"),
    ("escape_nb_spaces_tex", "escape non-breaking spaces using ‘~’ symbol"),
    ("clean_ellipsis", "use unicode character ‘…’ for ellipsis"),
    ("clean_quotes", "try to replace straight quotes with curly ones"),
    ("format_french", "try to apply french typographic rules"),
];

fn print_transformations() {
    for &(name, desc) in TOOLS {
        println!("    {name}: {desc}",
                 name = name,
                 desc = desc);
    }
}

fn main() {
    let args:Vec<_> = env::args()
        .collect();
    if args.len() == 1 {
        println!("\
{bin} {version}

USAGE: {bin} <TRANSFORMATIONS>

Read standard input, sequentially apply each TRANSFORMATION on the text, and print the
result on standard output.

Valid transformations are the following:",
                 bin = args[0],
                 version = env!("CARGO_PKG_VERSION"));
        print_transformations();
        println!("");
        println!("EXAMPLE: {bin} clean_quotes clean_ellipsis escape_html",
                 bin = args[0]);
    } else {
        let french = FrenchFormatter::new();

        let stdin = io::stdin();
        for line in stdin.lock().lines() {
            let line = line.expect("Error reading from standard input");
            let mut output = clean::whitespaces(line);
            for argument in &args[1..] {
                output = match argument.as_ref() {
                    "escape_html" => escape::html(output),
                    "escape_tex" => escape::tex(output),
                    "escape_nb_spaces" => escape::nb_spaces(output),
                    "escape_nb_spaces_tex" => escape::nb_spaces_tex(output),
                    "clean_quotes" => clean::quotes(output),
                    "clean_ellipsis" => clean::ellipsis(output),
                    "format_french" => french.format(output),
                    t => {
                        println!("Error: transformation “{}” not recognized.", t);
                        println!("Valid transformations are:");
                        print_transformations();
                        exit(0);
                    },
                }
            }
            println!("{}", output);
        }
    }
}
<commit_msg>Add clean::guillemets and clean::dashes in CLI interface<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with
// this file, You can obtain one at https://mozilla.org/MPL/2.0/.

extern crate crowbook_text_processing;

use crowbook_text_processing::{FrenchFormatter, clean, escape};

use std::env;
use std::io;
use std::io::BufRead;
use std::process::exit;

const TOOLS: &'static[(&'static str, &'static str)] = &[
    ("escape_html", "escape text for HTML display"),
    ("escape_tex", "escape text for LaTeX display"),
    ("escape_nb_spaces", "escape non-breaking spaces using HTML entities"),
    ("escape_nb_spaces_tex", "escape non-breaking spaces using ‘~’ symbol"),
    ("clean_ellipsis", "use unicode character ‘…’ for ellipsis"),
    ("clean_quotes", "try to replace straight quotes with curly ones"),
    ("ligature_dashes", "replace ‘--’ by ‘–’ and ‘---’ by ‘—’"),
    ("ligature_guillemets", "replace ‘<<’ by ‘«’ and ‘>>’ by ‘»’"),
    ("format_french", "try to apply french typographic rules"),
];

fn print_transformations() {
    for &(name, desc) in TOOLS {
        println!("    {name}: {desc}",
                 name = name,
                 desc = desc);
    }
}

fn main() {
    let args:Vec<_> = env::args()
        .collect();
    if args.len() == 1 {
        println!("\
{bin} {version}

USAGE: {bin} <TRANSFORMATIONS>

Read standard input, sequentially apply each TRANSFORMATION on the text, and print the
result on standard output.

Valid transformations are the following:",
                 bin = args[0],
                 version = env!("CARGO_PKG_VERSION"));
        print_transformations();
        println!("");
        println!("EXAMPLE: {bin} clean_quotes clean_ellipsis escape_html",
                 bin = args[0]);
    } else {
        let french = FrenchFormatter::new();

        let stdin = io::stdin();
        for line in stdin.lock().lines() {
            let line = line.expect("Error reading from standard input");
            let mut output = clean::whitespaces(line);
            for argument in &args[1..] {
                output = match argument.as_ref() {
                    "escape_html" => escape::html(output),
                    "escape_tex" => escape::tex(output),
                    "escape_nb_spaces" => escape::nb_spaces(output),
                    "escape_nb_spaces_tex" => escape::nb_spaces_tex(output),
                    "clean_quotes" => clean::quotes(output),
                    "clean_ellipsis" => clean::ellipsis(output),
                    "format_french" => french.format(output),
                    "ligature_dashes" => clean::dashes(output),
                    "ligature_guillemets" => clean::guillemets(output),
                    t => {
                        println!("Error: transformation “{}” not recognized.", t);
                        println!("Valid transformations are:");
                        print_transformations();
                        exit(0);
                    },
                }
            }
            println!("{}", output);
        }
    }
}
<|endoftext|>


<commit_before>extern crate exa;
use exa::Exa;

use std::ffi::OsString;
use std::env::{args_os, var_os};
use std::io::{stdout, stderr, Write, ErrorKind};
use std::process::exit;

fn main() {
    configure_logger();

    let args: Vec<OsString> = args_os().skip(1).collect();
    match Exa::from_args(args.iter(), &mut stdout()) {
        Ok(mut exa) => {
            match exa.run() {
                Ok(exit_status) => exit(exit_status),
                Err(e) => {
                    match e.kind() {
                        ErrorKind::BrokenPipe => exit(exits::SUCCESS),
                        _ => {
                            eprintln!("{}", e);
                            exit(exits::RUNTIME_ERROR);
                        },
                    };
                }
            };
        },

        Err(ref e) if e.is_error() => {
            let mut stderr = stderr();
            writeln!(stderr, "{}", e).unwrap();

            if let Some(s) = e.suggestion() {
                let _ = writeln!(stderr, "{}", s);
            }

            exit(exits::OPTIONS_ERROR);
        },

        Err(ref e) => {
            println!("{}", e);
            exit(exits::SUCCESS);
        },
    };
}

/// Sets up a global logger if one is asked for.
/// The ‘EXA_DEBUG’ environment variable controls whether log messages are
/// displayed or not. Currently there are just two settings (on and off).
///
/// This can’t be done in exa’s own option parsing because that part of it
/// logs as well, so by the time execution gets there, the logger needs to
/// have already been set up.
pub fn configure_logger() {
    extern crate env_logger;
    extern crate log;

    let present = match var_os(exa::vars::EXA_DEBUG) {
        Some(debug)  => debug.len() > 0,
        None         => false,
    };

    let mut logs = env_logger::Builder::new();
    if present {
        logs.filter(None, log::LevelFilter::Debug);
    }
    else {
        logs.filter(None, log::LevelFilter::Off);
    }

    logs.init()
}

extern crate libc;
#[allow(trivial_numeric_casts)]
mod exits {
    use libc::{self, c_int};

    pub const SUCCESS:       c_int = libc::EXIT_SUCCESS;
    pub const RUNTIME_ERROR: c_int = libc::EXIT_FAILURE;
    pub const OPTIONS_ERROR: c_int = 3 as c_int;
}
<commit_msg>Use 0 and 1 rather than EXIT_SUCCESS/FAILURE<commit_after>extern crate exa;
use exa::Exa;

use std::ffi::OsString;
use std::env::{args_os, var_os};
use std::io::{stdout, stderr, Write, ErrorKind};
use std::process::exit;

fn main() {
    configure_logger();

    let args: Vec<OsString> = args_os().skip(1).collect();
    match Exa::from_args(args.iter(), &mut stdout()) {
        Ok(mut exa) => {
            match exa.run() {
                Ok(exit_status) => exit(exit_status),
                Err(e) => {
                    match e.kind() {
                        ErrorKind::BrokenPipe => exit(exits::SUCCESS),
                        _ => {
                            eprintln!("{}", e);
                            exit(exits::RUNTIME_ERROR);
                        },
                    };
                }
            };
        },

        Err(ref e) if e.is_error() => {
            let mut stderr = stderr();
            writeln!(stderr, "{}", e).unwrap();

            if let Some(s) = e.suggestion() {
                let _ = writeln!(stderr, "{}", s);
            }

            exit(exits::OPTIONS_ERROR);
        },

        Err(ref e) => {
            println!("{}", e);
            exit(exits::SUCCESS);
        },
    };
}

/// Sets up a global logger if one is asked for.
/// The ‘EXA_DEBUG’ environment variable controls whether log messages are
/// displayed or not. Currently there are just two settings (on and off).
///
/// This can’t be done in exa’s own option parsing because that part of it
/// logs as well, so by the time execution gets there, the logger needs to
/// have already been set up.
pub fn configure_logger() {
    extern crate env_logger;
    extern crate log;

    let present = match var_os(exa::vars::EXA_DEBUG) {
        Some(debug)  => debug.len() > 0,
        None         => false,
    };

    let mut logs = env_logger::Builder::new();
    if present {
        logs.filter(None, log::LevelFilter::Debug);
    }
    else {
        logs.filter(None, log::LevelFilter::Off);
    }

    logs.init()
}

mod exits {

    /// Exit code for when exa runs OK.
    pub const SUCCESS: i32 = 0;

    /// Exit code for when there was at least one I/O error during execution.
    pub const RUNTIME_ERROR: i32 = 1;

    /// Exit code for when the command-line options are invalid.
    pub const OPTIONS_ERROR: i32 = 3;
}
<|endoftext|>