<commit_before>use ct_tools::common::{sha256_hex, Log};
use ct_tools::ct::submit_cert_to_logs;
use ct_tools::google::{fetch_all_ct_logs, fetch_trusted_ct_logs};
use ct_tools::{crtsh, letsencrypt};
use futures::stream::{StreamExt, TryStreamExt};
use net2::unix::UnixTcpBuilderExt;
use rustls::Session;
use std::fs::{self, File};
use std::io::Read;
use std::net::SocketAddr;
use std::process::Stdio;
use std::sync::Arc;
use std::time::Duration;
use structopt::StructOpt;
use tokio::io::AsyncWriteExt;
use tokio::process::Command;

fn pems_to_chain(data: &[u8]) -> Vec<Vec<u8>> {
    pem::parse_many(data)
        .into_iter()
        .filter(|p| p.tag == "CERTIFICATE")
        .map(|p| p.contents)
        .collect()
}

fn compute_paths(paths: &[String]) -> Vec<String> {
    paths
        .iter()
        .flat_map(|p| {
            if fs::metadata(p).unwrap().is_dir() {
                fs::read_dir(p)
                    .unwrap()
                    .map(|d| d.unwrap().path().to_str().unwrap().to_string())
                    .collect()
            } else {
                vec![p.clone()]
            }
        })
        .collect()
}

async fn submit(paths: &[String], all_logs: bool) {
    let http_client = reqwest::Client::new();

    let logs = if all_logs {
        fetch_all_ct_logs(&http_client).await
    } else {
        fetch_trusted_ct_logs(&http_client).await
    };

    let all_paths = compute_paths(paths);

    let work = all_paths
        .iter()
        .map(|path| {
            let path = path.to_string();
            let mut contents = Vec::new();
            File::open(&path)
                .unwrap()
                .read_to_end(&mut contents)
                .unwrap();

            let http_client = &http_client;
            let logs = &logs;
            let mut chain = pems_to_chain(&contents);
            async move {
                if chain.len() == 1 {
                    // TODO: There's got to be some way to do this ourselves, instead of using crt.sh
                    // as a glorified AIA chaser.
                    println!(
                        "[{}] Only one certificate in chain, using crt.sh to build a full chain ...",
                        &path
                    );
                    let new_chain = crtsh::build_chain_for_cert(&http_client, &chain[0]).await;
                    chain = match new_chain {
                        Ok(c) => c,
                        Err(()) => {
                            println!("[{}] Unable to build a chain", path);
                            return futures::future::ready(());
                        }
                    }
                }
                println!("[{}] Submitting ...", &path);
                let timeout = Duration::from_secs(30);
                let scts = submit_cert_to_logs(&http_client, &logs, &chain, timeout).await;

                if !scts.is_empty() {
                    println!(
                        "[{}] Find the cert on crt.sh: {}",
                        path,
                        crtsh::url_for_cert(&chain[0])
                    );
                    let mut table = prettytable::Table::new();
                    table.add_row(prettytable::Row::new(vec![prettytable::Cell::new("Log")]));
                    for (log_idx, _) in scts {
                        let log = &logs[log_idx];
                        table.add_row(prettytable::Row::new(vec![prettytable::Cell::new(
                            &log.description,
                        )]));
                    }
                    table.printstd();
                    println!();
                    println!();
                } else {
                    println!("[{}] No SCTs obtained", &path);
                }

                futures::future::ready(())
            }
        })
        .collect::<futures::stream::FuturesOrdered<_>>()
        .buffered(4)
        .for_each(move |()| async {});
    work.await;
}

async fn check(paths: &[String]) {
    let http_client = reqwest::Client::new();

    let all_paths = compute_paths(paths);

    let work = all_paths
        .iter()
        .map(|path| {
            let path = path.to_string();
            let mut contents = Vec::new();
            File::open(&path)
                .unwrap()
                .read_to_end(&mut contents)
                .unwrap();

            let http_client = &http_client;
            let chain = pems_to_chain(&contents);
            async move {
                if !chain.is_empty() && crtsh::is_cert_logged(&http_client, &chain[0]).await {
                    println!("{} was already logged", path);
                } else {
                    println!("{} has not been logged", path);
                }

                futures::future::ready(())
            }
        })
        .collect::<futures::stream::FuturesOrdered<_>>()
        .buffered(16)
        .for_each(move |()| async {});
    work.await;
}

async fn handle_request(
    request: hyper::Request<hyper::Body>,
    templates: Arc<tera::Tera>,
    http_client: Arc<reqwest::Client>,
    logs: Arc<Vec<Log>>,
    client_cert: Option<rustls::Certificate>,
) -> Result<hyper::Response<hyper::Body>, hyper::Error> {
    let mut crtsh_url = None;
    let mut rendered_cert = None;
    if let Some(cert) = client_cert {
        if request.method() == hyper::Method::POST {
            if let Ok(chain) = crtsh::build_chain_for_cert(&http_client, &cert.0).await {
                let timeout = Duration::from_secs(5);
                let scts = submit_cert_to_logs(&http_client, &logs, &chain, timeout).await;
                if !scts.is_empty() {
                    crtsh_url = Some(crtsh::url_for_cert(&chain[0]));
                    println!("Successfully submitted: {}", sha256_hex(&chain[0]));
                }
            }
        }

        let mut process = Command::new("openssl")
            .arg("x509")
            .arg("-text")
            .arg("-noout")
            .arg("-inform")
            .arg("der")
            .stdin(Stdio::piped())
            .stdout(Stdio::piped())
            .stderr(Stdio::piped())
            .spawn()
            .unwrap();
        let cert_bytes = cert.0.clone();
        AsyncWriteExt::write_all(&mut process.stdin.as_mut().unwrap(), &cert_bytes)
            .await
            .unwrap();
        let out = process.wait_with_output().await.unwrap();
        rendered_cert = Some(String::from_utf8_lossy(&out.stdout).into_owned());
    }

    let mut context = tera::Context::new();
    context.insert("cert", &rendered_cert);
    context.insert("crtsh_url", &crtsh_url);
    let body = templates.render("home.html", &context).unwrap();
    Ok(hyper::Response::builder()
        .status(hyper::StatusCode::OK)
        .body(body.into())
        .unwrap())
}

struct NoVerificationCertificateVerifier;
impl rustls::ClientCertVerifier for NoVerificationCertificateVerifier {
    fn client_auth_mandatory(&self, _: Option<&webpki::DNSName>) -> Option<bool> {
        Some(false)
    }

    fn verify_client_cert(
        &self,
        _: &[rustls::Certificate],
        _: Option<&webpki::DNSName>,
    ) -> Result<rustls::ClientCertVerified, rustls::TLSError> {
        Ok(rustls::ClientCertVerified::assertion())
    }

    fn client_auth_root_subjects(
        &self,
        _: Option<&webpki::DNSName>,
    ) -> Option<rustls::DistinguishedNames> {
        Some(rustls::DistinguishedNames::new())
    }
}

async fn server(local_dev: bool, domain: Option<&str>, letsencrypt_env: Option<&str>) {
    match (local_dev, domain, letsencrypt_env) {
        (true, Some(_), _) | (true, _, Some(_)) => {
            panic!("Can't use both --local-dev and --letsencrypt-env or --domain")
        }
        (_, Some(_), None) | (_, None, Some(_)) => {
            panic!("When using Let's Encrypt, must set both --letsencrypt-env and --domain")
        }
        (false, _, None) => panic!("Must set at least one of --local-dev or --letsencrypt-env"),
        _ => {}
    };

    // Disable certificate verification. In any normal context, this would be horribly insecure!
    // However, all we're doing is taking the certs and then turning around and submitting them to
    // CT logs, so it doesn't matter if they're verified.
    let mut tls_config = rustls::ServerConfig::new(Arc::new(NoVerificationCertificateVerifier));
    if local_dev {
        // TODO: not all the details on the cert are perfect, but it's fine.
        let (cert, pkey) = letsencrypt::generate_temporary_cert("localhost");
        tls_config
            .set_single_cert(
                vec![letsencrypt::openssl_cert_to_rustls(&cert)],
                letsencrypt::openssl_pkey_to_rustls(&pkey),
            )
            .unwrap();
    } else {
        let letsencrypt_url = match letsencrypt_env.unwrap() {
            "prod" => "https://acme-v01.api.letsencrypt.org/directory",
            "dev" => "https://acme-staging.api.letsencrypt.org/directory",
            _ => unreachable!(),
        };
        let cert_cache = letsencrypt::DiskCache::new(
            dirs::home_dir()
                .unwrap()
                .join(".ct-tools")
                .join("certificates"),
        );
        tls_config.cert_resolver = Arc::new(letsencrypt::AutomaticCertResolver::new(
            letsencrypt_url,
            vec![domain.unwrap().to_string()],
            cert_cache,
        ));
    }
    tls_config.set_persistence(rustls::ServerSessionMemoryCache::new(1024));
    tls_config.ticketer = rustls::Ticketer::new();

    let http_client = Arc::new(reqwest::Client::new());
    let logs = Arc::new(fetch_trusted_ct_logs(&http_client).await);
    let templates = Arc::new(tera::Tera::new("templates/*").unwrap());

    let addr = if local_dev {
        "0.0.0.0:8000"
    } else {
        "0.0.0.0:443"
    }
    .parse()
    .unwrap();

    // If there aren't at least two threads, the Let's Encrypt integration will deadlock.
    println!("Listening on https://{} ...", addr);
    let std_listener = match addr {
        SocketAddr::V4(_) => net2::TcpBuilder::new_v4().unwrap(),
        SocketAddr::V6(_) => net2::TcpBuilder::new_v6().unwrap(),
    };
    std_listener.reuse_port(true).unwrap();
    std_listener.reuse_address(true).unwrap();
    std_listener.bind(addr).unwrap();
    let tls_acceptor = tokio_rustls::TlsAcceptor::from(Arc::new(tls_config));
    let mut listener =
        tokio::net::TcpListener::from_std(std_listener.listen(1024).unwrap()).unwrap();
    let connections = listener
        .incoming()
        .and_then(move |sock| tls_acceptor.accept(sock))
        .filter(move |s| futures::future::ready(s.is_ok()))
        .boxed();
    let server = hyper::Server::builder(hyper::server::accept::from_stream(connections)).serve(
        hyper::service::make_service_fn(
            move |conn: &tokio_rustls::server::TlsStream<tokio::net::TcpStream>| {
                let http_client = Arc::clone(&http_client);
                let templates = Arc::clone(&templates);
                let logs = Arc::clone(&logs);
                let client_cert = conn
                    .get_ref()
                    .1
                    .get_peer_certificates()
                    .map(|mut chain| chain.remove(0));

                async {
                    Ok::<_, hyper::Error>(hyper::service::service_fn(
                        move |r: hyper::Request<hyper::Body>| {
                            handle_request(
                                r,
                                Arc::clone(&templates),
                                Arc::clone(&http_client),
                                Arc::clone(&logs),
                                client_cert.clone(),
                            )
                        },
                    ))
                }
            },
        ),
    );

    server.await.unwrap()
}

#[derive(StructOpt)]
#[structopt(name = "ct-tools")]
enum Opt {
    #[structopt(name = "submit", about = "Directly submits certificates to CT logs")]
    Submit {
        #[structopt(
            long = "all-logs",
            help = "Submit to all logs, instead of just ones trusted by Chrome"
        )]
        all_logs: bool,
        #[structopt(help = "Path to certificate or chain")]
        paths: Vec<String>,
    },

    #[structopt(
        name = "check",
        about = "Checks whether a certificate exists in CT logs"
    )]
    Check {
        #[structopt(help = "Path to certificate or chain")]
        paths: Vec<String>,
    },

    #[structopt(
        name = "server",
        about = "Run an HTTPS server that submits client certificates to CT logs"
    )]
    Server {
        #[structopt(
            long = "--local-dev",
            help = "Local development, do not obtain a certificate"
        )]
        local_dev: bool,
        #[structopt(long = "domain", help = "Domain this is running as")]
        domain: Option<String>,
        #[structopt(
            long = "letsencrypt-env",
            possible_value = "dev",
            possible_value = "prod",
            help = "Let's Encrypt environment to use"
        )]
        letsencrypt_env: Option<String>,
    },
}

#[tokio::main]
async fn main() {
    match Opt::from_args() {
        Opt::Submit { paths, all_logs } => {
            submit(&paths, all_logs).await;
        }
        Opt::Check { paths } => {
            check(&paths).await;
        }
        Opt::Server {
            local_dev,
            domain,
            letsencrypt_env,
        } => {
            server(
                local_dev,
                domain.as_ref().map(|s| s.as_ref()),
                letsencrypt_env.as_ref().map(|s| s.as_ref()),
            )
            .await;
        }
    }
}
<commit_msg>Fix up warnings<commit_after>use ct_tools::common::{sha256_hex, Log};
use ct_tools::ct::submit_cert_to_logs;
use ct_tools::google::{fetch_all_ct_logs, fetch_trusted_ct_logs};
use ct_tools::{crtsh, letsencrypt};
use futures::stream::{StreamExt, TryStreamExt};
use net2::unix::UnixTcpBuilderExt;
use rustls::Session;
use std::fs::{self, File};
use std::io::Read;
use std::net::SocketAddr;
use std::process::Stdio;
use std::sync::Arc;
use std::time::Duration;
use structopt::StructOpt;
use tokio::io::AsyncWriteExt;
use tokio::process::Command;

fn pems_to_chain(data: &[u8]) -> Vec<Vec<u8>> {
    pem::parse_many(data)
        .into_iter()
        .filter(|p| p.tag == "CERTIFICATE")
        .map(|p| p.contents)
        .collect()
}

fn compute_paths(paths: &[String]) -> Vec<String> {
    paths
        .iter()
        .flat_map(|p| {
            if fs::metadata(p).unwrap().is_dir() {
                fs::read_dir(p)
                    .unwrap()
                    .map(|d| d.unwrap().path().to_str().unwrap().to_string())
                    .collect()
            } else {
                vec![p.clone()]
            }
        })
        .collect()
}

async fn submit(paths: &[String], all_logs: bool) {
    let http_client = reqwest::Client::new();

    let logs = if all_logs {
        fetch_all_ct_logs(&http_client).await
    } else {
        fetch_trusted_ct_logs(&http_client).await
    };

    let all_paths = compute_paths(paths);

    let work = futures::stream::iter(all_paths.iter().map(|path| {
        let path = path.to_string();
        let mut contents = Vec::new();
        File::open(&path)
            .unwrap()
            .read_to_end(&mut contents)
            .unwrap();

        let http_client = &http_client;
        let logs = &logs;
        let mut chain = pems_to_chain(&contents);
        async move {
            if chain.len() == 1 {
                // TODO: There's got to be some way to do this ourselves, instead of using crt.sh
                // as a glorified AIA chaser.
                println!(
                    "[{}] Only one certificate in chain, using crt.sh to build a full chain ...",
                    &path
                );
                let new_chain = crtsh::build_chain_for_cert(&http_client, &chain[0]).await;
                chain = match new_chain {
                    Ok(c) => c,
                    Err(()) => {
                        println!("[{}] Unable to build a chain", path);
                        return;
                    }
                }
            }
            println!("[{}] Submitting ...", &path);
            let timeout = Duration::from_secs(30);
            let scts = submit_cert_to_logs(&http_client, &logs, &chain, timeout).await;

            if !scts.is_empty() {
                println!(
                    "[{}] Find the cert on crt.sh: {}",
                    path,
                    crtsh::url_for_cert(&chain[0])
                );
                let mut table = prettytable::Table::new();
                table.add_row(prettytable::Row::new(vec![prettytable::Cell::new("Log")]));
                for (log_idx, _) in scts {
                    let log = &logs[log_idx];
                    table.add_row(prettytable::Row::new(vec![prettytable::Cell::new(
                        &log.description,
                    )]));
                }
                table.printstd();
                println!();
                println!();
            } else {
                println!("[{}] No SCTs obtained", &path);
            }
        }
    }))
    .buffer_unordered(4)
    .for_each(|_| async {});
    work.await;
}

async fn check(paths: &[String]) {
    let http_client = reqwest::Client::new();

    let all_paths = compute_paths(paths);

    let work = futures::stream::iter(all_paths.iter().map(|path| {
        let path = path.to_string();
        let mut contents = Vec::new();
        File::open(&path)
            .unwrap()
            .read_to_end(&mut contents)
            .unwrap();

        let http_client = &http_client;
        let chain = pems_to_chain(&contents);
        async move {
            if !chain.is_empty() && crtsh::is_cert_logged(&http_client, &chain[0]).await {
                println!("{} was already logged", path);
            } else {
                println!("{} has not been logged", path);
            }
        }
    }))
    .buffer_unordered(16)
    .for_each(|_| async {});
    work.await;
}

async fn handle_request(
    request: hyper::Request<hyper::Body>,
    templates: Arc<tera::Tera>,
    http_client: Arc<reqwest::Client>,
    logs: Arc<Vec<Log>>,
    client_cert: Option<rustls::Certificate>,
) -> Result<hyper::Response<hyper::Body>, hyper::Error> {
    let mut crtsh_url = None;
    let mut rendered_cert = None;
    if let Some(cert) = client_cert {
        if request.method() == hyper::Method::POST {
            if let Ok(chain) = crtsh::build_chain_for_cert(&http_client, &cert.0).await {
                let timeout = Duration::from_secs(5);
                let scts = submit_cert_to_logs(&http_client, &logs, &chain, timeout).await;
                if !scts.is_empty() {
                    crtsh_url = Some(crtsh::url_for_cert(&chain[0]));
                    println!("Successfully submitted: {}", sha256_hex(&chain[0]));
                }
            }
        }

        let mut process = Command::new("openssl")
            .arg("x509")
            .arg("-text")
            .arg("-noout")
            .arg("-inform")
            .arg("der")
            .stdin(Stdio::piped())
            .stdout(Stdio::piped())
            .stderr(Stdio::piped())
            .spawn()
            .unwrap();
        let cert_bytes = cert.0.clone();
        AsyncWriteExt::write_all(&mut process.stdin.as_mut().unwrap(), &cert_bytes)
            .await
            .unwrap();
        let out = process.wait_with_output().await.unwrap();
        rendered_cert = Some(String::from_utf8_lossy(&out.stdout).into_owned());
    }

    let mut context = tera::Context::new();
    context.insert("cert", &rendered_cert);
    context.insert("crtsh_url", &crtsh_url);
    let body = templates.render("home.html", &context).unwrap();
    Ok(hyper::Response::builder()
        .status(hyper::StatusCode::OK)
        .body(body.into())
        .unwrap())
}

struct NoVerificationCertificateVerifier;
impl rustls::ClientCertVerifier for NoVerificationCertificateVerifier {
    fn client_auth_mandatory(&self, _: Option<&webpki::DNSName>) -> Option<bool> {
        Some(false)
    }

    fn verify_client_cert(
        &self,
        _: &[rustls::Certificate],
        _: Option<&webpki::DNSName>,
    ) -> Result<rustls::ClientCertVerified, rustls::TLSError> {
        Ok(rustls::ClientCertVerified::assertion())
    }

    fn client_auth_root_subjects(
        &self,
        _: Option<&webpki::DNSName>,
    ) -> Option<rustls::DistinguishedNames> {
        Some(rustls::DistinguishedNames::new())
    }
}

async fn server(local_dev: bool, domain: Option<&str>, letsencrypt_env: Option<&str>) {
    match (local_dev, domain, letsencrypt_env) {
        (true, Some(_), _) | (true, _, Some(_)) => {
            panic!("Can't use both --local-dev and --letsencrypt-env or --domain")
        }
        (_, Some(_), None) | (_, None, Some(_)) => {
            panic!("When using Let's Encrypt, must set both --letsencrypt-env and --domain")
        }
        (false, _, None) => panic!("Must set at least one of --local-dev or --letsencrypt-env"),
        _ => {}
    };

    // Disable certificate verification. In any normal context, this would be horribly insecure!
    // However, all we're doing is taking the certs and then turning around and submitting them to
    // CT logs, so it doesn't matter if they're verified.
    let mut tls_config = rustls::ServerConfig::new(Arc::new(NoVerificationCertificateVerifier));
    if local_dev {
        // TODO: not all the details on the cert are perfect, but it's fine.
        let (cert, pkey) = letsencrypt::generate_temporary_cert("localhost");
        tls_config
            .set_single_cert(
                vec![letsencrypt::openssl_cert_to_rustls(&cert)],
                letsencrypt::openssl_pkey_to_rustls(&pkey),
            )
            .unwrap();
    } else {
        let letsencrypt_url = match letsencrypt_env.unwrap() {
            "prod" => "https://acme-v01.api.letsencrypt.org/directory",
            "dev" => "https://acme-staging.api.letsencrypt.org/directory",
            _ => unreachable!(),
        };
        let cert_cache = letsencrypt::DiskCache::new(
            dirs::home_dir()
                .unwrap()
                .join(".ct-tools")
                .join("certificates"),
        );
        tls_config.cert_resolver = Arc::new(letsencrypt::AutomaticCertResolver::new(
            letsencrypt_url,
            vec![domain.unwrap().to_string()],
            cert_cache,
        ));
    }
    tls_config.set_persistence(rustls::ServerSessionMemoryCache::new(1024));
    tls_config.ticketer = rustls::Ticketer::new();

    let http_client = Arc::new(reqwest::Client::new());
    let logs = Arc::new(fetch_trusted_ct_logs(&http_client).await);
    let templates = Arc::new(tera::Tera::new("templates/*").unwrap());

    let addr = if local_dev {
        "0.0.0.0:8000"
    } else {
        "0.0.0.0:443"
    }
    .parse()
    .unwrap();

    // If there aren't at least two threads, the Let's Encrypt integration will deadlock.
    println!("Listening on https://{} ...", addr);
    let std_listener = match addr {
        SocketAddr::V4(_) => net2::TcpBuilder::new_v4().unwrap(),
        SocketAddr::V6(_) => net2::TcpBuilder::new_v6().unwrap(),
    };
    std_listener.reuse_port(true).unwrap();
    std_listener.reuse_address(true).unwrap();
    std_listener.bind(addr).unwrap();
    let tls_acceptor = tokio_rustls::TlsAcceptor::from(Arc::new(tls_config));
    let mut listener =
        tokio::net::TcpListener::from_std(std_listener.listen(1024).unwrap()).unwrap();
    let connections = listener
        .incoming()
        .and_then(move |sock| tls_acceptor.accept(sock))
        .filter(move |s| futures::future::ready(s.is_ok()))
        .boxed();
    let server = hyper::Server::builder(hyper::server::accept::from_stream(connections)).serve(
        hyper::service::make_service_fn(
            move |conn: &tokio_rustls::server::TlsStream<tokio::net::TcpStream>| {
                let http_client = Arc::clone(&http_client);
                let templates = Arc::clone(&templates);
                let logs = Arc::clone(&logs);
                let client_cert = conn
                    .get_ref()
                    .1
                    .get_peer_certificates()
                    .map(|mut chain| chain.remove(0));

                async {
                    Ok::<_, hyper::Error>(hyper::service::service_fn(
                        move |r: hyper::Request<hyper::Body>| {
                            handle_request(
                                r,
                                Arc::clone(&templates),
                                Arc::clone(&http_client),
                                Arc::clone(&logs),
                                client_cert.clone(),
                            )
                        },
                    ))
                }
            },
        ),
    );

    server.await.unwrap()
}

#[derive(StructOpt)]
#[structopt(name = "ct-tools")]
enum Opt {
    #[structopt(name = "submit", about = "Directly submits certificates to CT logs")]
    Submit {
        #[structopt(
            long = "all-logs",
            help = "Submit to all logs, instead of just ones trusted by Chrome"
        )]
        all_logs: bool,
        #[structopt(help = "Path to certificate or chain")]
        paths: Vec<String>,
    },

    #[structopt(
        name = "check",
        about = "Checks whether a certificate exists in CT logs"
    )]
    Check {
        #[structopt(help = "Path to certificate or chain")]
        paths: Vec<String>,
    },

    #[structopt(
        name = "server",
        about = "Run an HTTPS server that submits client certificates to CT logs"
    )]
    Server {
        #[structopt(
            long = "--local-dev",
            help = "Local development, do not obtain a certificate"
        )]
        local_dev: bool,
        #[structopt(long = "domain", help = "Domain this is running as")]
        domain: Option<String>,
        #[structopt(
            long = "letsencrypt-env",
            possible_value = "dev",
            possible_value = "prod",
            help = "Let's Encrypt environment to use"
        )]
        letsencrypt_env: Option<String>,
    },
}

#[tokio::main]
async fn main() {
    match Opt::from_args() {
        Opt::Submit { paths, all_logs } => {
            submit(&paths, all_logs).await;
        }
        Opt::Check { paths } => {
            check(&paths).await;
        }
        Opt::Server {
            local_dev,
            domain,
            letsencrypt_env,
        } => {
            server(
                local_dev,
                domain.as_ref().map(|s| s.as_ref()),
                letsencrypt_env.as_ref().map(|s| s.as_ref()),
            )
            .await;
        }
    }
}
<|endoftext|>


<commit_before>extern crate bytes;
extern crate env_logger;
extern crate mio;
#[macro_use]
extern crate log;
#[macro_use]
extern crate clap;
extern crate failure;
extern crate slab;

use clap::{App, Arg};
use failure::Error;
use failure::ResultExt;
use mio::net::{TcpListener, TcpStream};
use slab::Slab;
use std::collections::VecDeque;
use std::io::{Read, Write};
use std::ops;

#[derive(Debug)]
enum MiChatCommand {
    Broadcast(String),
    NewConnection(TcpStream),
}

struct MiChat {
    connections: Slab<EventHandler>,
}

trait RuleHandler {
    fn handle_event(&mut self, _event_loop: &mut mio::Poll, events: mio::Ready);
    fn register(&self, event_loop: &mut mio::Poll, token: mio::Token) -> Result<(), Error>;
    fn process_rules(
        &mut self,
        event_loop: &mut mio::Poll,
        to_parent: &mut FnMut(MiChatCommand),
    ) -> Result<(), Error>;
    fn is_closed(&self) -> bool;
}

impl MiChat {
    fn new() -> MiChat {
        MiChat {
            connections: Slab::new(),
        }
    }

    fn listen(&mut self, event_loop: &mut mio::Poll, listener: TcpListener) -> Result<(), Error> {
        let token = {
            let e = self.connections.vacant_entry();
            let token = mio::Token(e.key());
            let l = EventHandler::Listener(Listener::new(listener, token));
            e.insert(l);
            token
        };
        &self.connections[token.into()].register(event_loop, token)?;
        Ok(())
    }

    fn process_action(&mut self, msg: MiChatCommand, event_loop: &mut mio::Poll) {
        trace!("{:p}; got {:?}", self, msg);
        match msg {
            MiChatCommand::Broadcast(s) => self.process_broadcast(s),
            MiChatCommand::NewConnection(socket) => self.process_new_connection(event_loop, socket),
        }
    }

    fn process_broadcast(&mut self, s: String) {
        for c in self.client_connections_mut() {
            c.enqueue(&s);
        }
    }

    fn process_new_connection(&mut self, event_loop: &mut mio::Poll, socket: mio::tcp::TcpStream) {
        let token = {
            let e = self.connections.vacant_entry();
            let token = mio::Token(e.key());
            let l = EventHandler::Conn(Connection::new(socket, token));
            e.insert(l);
            token
        };
        &self.connections[token.into()].register(event_loop, token);
    }

    fn client_connections_mut<'a>(&'a mut self) -> impl Iterator<Item = &'a mut Connection> + 'a {
        self.connections.iter_mut().filter_map(|(_, it)| match it {
            &mut EventHandler::Conn(ref mut c) => Some(c),
            _ => None,
        })
    }
}

#[derive(Debug)]
struct Connection {
    socket: TcpStream,
    sock_status: mio::Ready,
    token: mio::Token,
    read_buf: Vec<u8>,
    read_eof: bool,
    failed: bool,
    write_buf: Vec<u8>,
}

impl RuleHandler for Connection {
    fn register(&self, event_loop: &mut mio::Poll, token: mio::Token) -> Result<(), Error> {
        event_loop
            .register(
                &self.socket,
                token,
                mio::Ready::readable(),
                mio::PollOpt::edge() | mio::PollOpt::oneshot(),
            ).context("event loop register")?;
        Ok(())
    }

    // Event updates arrive here
    fn handle_event(&mut self, _event_loop: &mut mio::Poll, events: mio::Ready) {
        self.sock_status.insert(events);
        info!(
            "Connection::handle_event: {:?}; this time: {:?}; now: {:?}",
            self.socket.peer_addr(),
            events,
            self.sock_status
        );
    }

    // actions are processed here on down.

    fn process_rules(
        &mut self,
        event_loop: &mut mio::Poll,
        to_parent: &mut FnMut(MiChatCommand),
    ) -> Result<(), Error> {
        if self.sock_status.is_readable() {
            self.sock_status.remove(mio::Ready::readable());
            self.read()?;
        }

        self.process_buffer(to_parent);

        if self.sock_status.is_writable() {
            self.sock_status.remove(mio::Ready::writable());
            self.write()?;
        }

        if !self.is_closed() {
            self.reregister(event_loop)?;
        }

        Ok(())
    }

    fn is_closed(&self) -> bool {
        self.failed || (self.read_eof && self.write_buf.is_empty())
    }
}

impl Connection {
    fn new(socket: TcpStream, token: mio::Token) -> Connection {
        Connection {
            socket: socket,
            sock_status: mio::Ready::empty(),
            token: token,
            read_buf: Vec::with_capacity(1024),
            write_buf: Vec::new(),
            read_eof: false,
            failed: false,
        }
    }
    fn process_buffer(&mut self, to_parent: &mut FnMut(MiChatCommand)) {
        let mut prev = 0;
        info!(
            "{:?}: Read buffer: {:?}",
            self.socket.peer_addr(),
            self.read_buf
        );
        for n in self.read_buf.iter().enumerate().filter_map(|(i, e)| {
            if *e == '\n' as u8 {
                Some(i)
            } else {
                None
            }
        }) {
            info!(
                "{:?}: Pos: {:?}; chunk: {:?}",
                self.socket.peer_addr(),
                n,
                &self.read_buf[prev..n]
            );
            let s = String::from_utf8_lossy(&self.read_buf[prev..n]).to_string();
            let cmd = MiChatCommand::Broadcast(s);
            info!("Send! {:?}", cmd);
            to_parent(cmd);
            prev = n + 1;
        }
        let remainder = self.read_buf[prev..].to_vec();
        info!(
            "{:?}: read Remainder: {}",
            self.socket.peer_addr(),
            remainder.len()
        );
        self.read_buf = remainder;
    }

    fn read(&mut self) -> Result<(), Error> {
        let mut abuf = vec![0; 1024];
        match self.socket.read(&mut abuf).context("Reading socket")? {
            0 => {
                info!("{:?}: EOF!", self.socket.peer_addr());
                self.read_eof = true;
                Ok(())
            }
            n => {
                info!("{:?}: Read {}bytes", self.socket.peer_addr(), n);
                self.read_buf.extend(&abuf[0..n]);
                Ok(())
            }
        }
    }

    fn write(&mut self) -> Result<(), Error> {
        let n = self
            .socket
            .write(&mut self.write_buf)
            .context("writing buffer")?;
        info!(
            "{:?}: Wrote {} of {} in buffer",
            self.socket.peer_addr(),
            n,
            self.write_buf.len()
        );
        self.write_buf = self.write_buf[n..].to_vec();
        info!(
            "{:?}: Now {:?}b",
            self.socket.peer_addr(),
            self.write_buf.len()
        );
        Ok(())
    }

    fn reregister(&mut self, event_loop: &mut mio::Poll) -> Result<(), Error> {
        let mut flags = mio::Ready::readable();
        if !self.write_buf.is_empty() {
            flags.insert(mio::Ready::writable());
        }
        info!("Registering {:?} with {:?}", self, flags);

        event_loop
            .reregister(&self.socket, self.token, flags, mio::PollOpt::oneshot())
            .context("EventLoop#reregister")?;

        Ok(())
    }

    fn enqueue(&mut self, s: &str) {
        self.write_buf.extend(s.as_bytes());
        self.write_buf.push('\n' as u8);
    }
}

#[derive(Debug)]
struct Listener {
    listener: TcpListener,
    sock_status: mio::Ready,
    token: mio::Token,
}

impl Listener {
    fn new(listener: TcpListener, token: mio::Token) -> Listener {
        Listener {
            listener: listener,
            sock_status: mio::Ready::empty(),
            token: token,
        }
    }
    fn reregister(&self, event_loop: &mut mio::Poll, token: mio::Token) -> Result<(), Error> {
        event_loop
            .reregister(
                &self.listener,
                token,
                mio::Ready::readable(),
                mio::PollOpt::edge() | mio::PollOpt::oneshot(),
            ).context("Register listener")?;
        Ok(())
    }
}

impl RuleHandler for Listener {
    fn register(&self, event_loop: &mut mio::Poll, token: mio::Token) -> Result<(), Error> {
        event_loop
            .register(
                &self.listener,
                token,
                mio::Ready::readable(),
                mio::PollOpt::edge() | mio::PollOpt::oneshot(),
            ).context("Register listener")?;
        Ok(())
    }

    fn handle_event(&mut self, _event_loop: &mut mio::Poll, events: mio::Ready) {
        assert!(events.is_readable());
        self.sock_status.insert(events);
        info!(
            "Listener::handle_event: {:?}; this time: {:?}; now: {:?}",
            self.listener.local_addr(),
            events,
            self.sock_status
        );
    }
    fn process_rules(
        &mut self,
        event_loop: &mut mio::Poll,
        to_parent: &mut FnMut(MiChatCommand),
    ) -> Result<(), Error> {
        if self.sock_status.is_readable() {
            info!("the listener socket is ready to accept a connection");
            self.sock_status.remove(mio::Ready::readable());
            match self.listener.accept().context("Accept listening socket")? {
                (socket, _client) => {
                    let cmd = MiChatCommand::NewConnection(socket);
                    to_parent(cmd);
                }
            }

            self.reregister(event_loop, self.token)?;
        }
        Ok(())
    }

    fn is_closed(&self) -> bool {
        false
    }
}

#[derive(Debug)]
enum EventHandler {
    Listener(Listener),
    Conn(Connection),
}

impl ops::Deref for EventHandler {
    type Target = RuleHandler;
    fn deref(&self) -> &Self::Target {
        match self {
            &EventHandler::Conn(ref conn) => conn,
            &EventHandler::Listener(ref listener) => listener,
        }
    }
}
impl ops::DerefMut for EventHandler {
    fn deref_mut(&mut self) -> &mut Self::Target {
        match self {
            &mut EventHandler::Conn(ref mut conn) => conn,
            &mut EventHandler::Listener(ref mut listener) => listener,
        }
    }
}

impl MiChat {
    fn ready(&mut self, event_loop: &mut mio::Poll, token: mio::Token, events: mio::Ready) {
        info!("{:?}: {:?}", token, events);
        self.connections[token.into()].handle_event(event_loop, events);
        if self.connections[token.into()].is_closed() {
            info!("Removing; {:?}", token);
            self.connections.remove(token.into());
        }
    }

    fn process(&mut self, event_loop: &mut mio::Poll) -> Result<(), Error> {
        let mut parent_actions = VecDeque::new();
        let mut failed = Vec::new();
        loop {
            for (idx, conn) in self.connections.iter_mut() {
                if let Err(e) =
                    conn.process_rules(event_loop, &mut |action| parent_actions.push_back(action))
                {
                    failed.push(idx);
                    error!("Error on connection {:?}; Dropping: {:?}", idx, e);
                }
            }
            for idx in failed.drain(..) {
                self.connections.remove(idx);
            }
            // Anything left to process?
            if parent_actions.is_empty() {
                break;
            }

            for action in parent_actions.drain(..) {
                self.process_action(action, event_loop);
            }
        }

        Ok(())
    }

    fn run(&mut self, event_loop: &mut mio::Poll) -> Result<(), Error> {
        loop {
            self.run_once(event_loop)?;
        }
    }
    fn run_once(&mut self, event_loop: &mut mio::Poll) -> Result<(), Error> {
        let mut events = mio::Events::with_capacity(1024);
        event_loop.poll(&mut events, None)?;
        for ev in &events {
            self.ready(event_loop, ev.token(), ev.readiness())
        }

        self.process(event_loop)
    }
}

fn main() {
    env_logger::init();
    let matches = App::new("michat")
        .arg(
            Arg::with_name("bind")
                .short("l")
                .takes_value(true)
                .required(true),
        ).get_matches();

    let address = value_t_or_exit!(matches.value_of("bind"), std::net::SocketAddr);
    let listener = TcpListener::bind(&address).expect("bind");

    let mut event_loop = mio::Poll::new().expect("Create event loop");
    let mut service = MiChat::new();

    service
        .listen(&mut event_loop, listener)
        .expect("Starting listener");
    info!("running michat listener at: {:?}", address);
    service.run(&mut event_loop).expect("run");
}
<commit_msg>Per-socket failure now superfluous.<commit_after>extern crate bytes;
extern crate env_logger;
extern crate mio;
#[macro_use]
extern crate log;
#[macro_use]
extern crate clap;
extern crate failure;
extern crate slab;

use clap::{App, Arg};
use failure::Error;
use failure::ResultExt;
use mio::net::{TcpListener, TcpStream};
use slab::Slab;
use std::collections::VecDeque;
use std::io::{Read, Write};
use std::ops;

#[derive(Debug)]
enum MiChatCommand {
    Broadcast(String),
    NewConnection(TcpStream),
}

struct MiChat {
    connections: Slab<EventHandler>,
}

trait RuleHandler {
    fn handle_event(&mut self, _event_loop: &mut mio::Poll, events: mio::Ready);
    fn register(&self, event_loop: &mut mio::Poll, token: mio::Token) -> Result<(), Error>;
    fn process_rules(
        &mut self,
        event_loop: &mut mio::Poll,
        to_parent: &mut FnMut(MiChatCommand),
    ) -> Result<(), Error>;
    fn is_closed(&self) -> bool;
}

impl MiChat {
    fn new() -> MiChat {
        MiChat {
            connections: Slab::new(),
        }
    }

    fn listen(&mut self, event_loop: &mut mio::Poll, listener: TcpListener) -> Result<(), Error> {
        let token = {
            let e = self.connections.vacant_entry();
            let token = mio::Token(e.key());
            let l = EventHandler::Listener(Listener::new(listener, token));
            e.insert(l);
            token
        };
        &self.connections[token.into()].register(event_loop, token)?;
        Ok(())
    }

    fn process_action(&mut self, msg: MiChatCommand, event_loop: &mut mio::Poll) {
        trace!("{:p}; got {:?}", self, msg);
        match msg {
            MiChatCommand::Broadcast(s) => self.process_broadcast(s),
            MiChatCommand::NewConnection(socket) => self.process_new_connection(event_loop, socket),
        }
    }

    fn process_broadcast(&mut self, s: String) {
        for c in self.client_connections_mut() {
            c.enqueue(&s);
        }
    }

    fn process_new_connection(&mut self, event_loop: &mut mio::Poll, socket: mio::tcp::TcpStream) {
        let token = {
            let e = self.connections.vacant_entry();
            let token = mio::Token(e.key());
            let l = EventHandler::Conn(Connection::new(socket, token));
            e.insert(l);
            token
        };
        &self.connections[token.into()].register(event_loop, token);
    }

    fn client_connections_mut<'a>(&'a mut self) -> impl Iterator<Item = &'a mut Connection> + 'a {
        self.connections.iter_mut().filter_map(|(_, it)| match it {
            &mut EventHandler::Conn(ref mut c) => Some(c),
            _ => None,
        })
    }
}

#[derive(Debug)]
struct Connection {
    socket: TcpStream,
    sock_status: mio::Ready,
    token: mio::Token,
    read_buf: Vec<u8>,
    read_eof: bool,
    write_buf: Vec<u8>,
}

impl RuleHandler for Connection {
    fn register(&self, event_loop: &mut mio::Poll, token: mio::Token) -> Result<(), Error> {
        event_loop
            .register(
                &self.socket,
                token,
                mio::Ready::readable(),
                mio::PollOpt::edge() | mio::PollOpt::oneshot(),
            ).context("event loop register")?;
        Ok(())
    }

    // Event updates arrive here
    fn handle_event(&mut self, _event_loop: &mut mio::Poll, events: mio::Ready) {
        self.sock_status.insert(events);
        info!(
            "Connection::handle_event: {:?}; this time: {:?}; now: {:?}",
            self.socket.peer_addr(),
            events,
            self.sock_status
        );
    }

    // actions are processed here on down.

    fn process_rules(
        &mut self,
        event_loop: &mut mio::Poll,
        to_parent: &mut FnMut(MiChatCommand),
    ) -> Result<(), Error> {
        if self.sock_status.is_readable() {
            self.sock_status.remove(mio::Ready::readable());
            self.read()?;
        }

        self.process_buffer(to_parent);

        if self.sock_status.is_writable() {
            self.sock_status.remove(mio::Ready::writable());
            self.write()?;
        }

        if !self.is_closed() {
            self.reregister(event_loop)?;
        }

        Ok(())
    }

    fn is_closed(&self) -> bool {
        self.read_eof && self.write_buf.is_empty()
    }
}

impl Connection {
    fn new(socket: TcpStream, token: mio::Token) -> Connection {
        Connection {
            socket: socket,
            sock_status: mio::Ready::empty(),
            token: token,
            read_buf: Vec::with_capacity(1024),
            write_buf: Vec::new(),
            read_eof: false,
        }
    }
    fn process_buffer(&mut self, to_parent: &mut FnMut(MiChatCommand)) {
        let mut prev = 0;
        info!(
            "{:?}: Read buffer: {:?}",
            self.socket.peer_addr(),
            self.read_buf
        );
        for n in self.read_buf.iter().enumerate().filter_map(|(i, e)| {
            if *e == '\n' as u8 {
                Some(i)
            } else {
                None
            }
        }) {
            info!(
                "{:?}: Pos: {:?}; chunk: {:?}",
                self.socket.peer_addr(),
                n,
                &self.read_buf[prev..n]
            );
            let s = String::from_utf8_lossy(&self.read_buf[prev..n]).to_string();
            let cmd = MiChatCommand::Broadcast(s);
            info!("Send! {:?}", cmd);
            to_parent(cmd);
            prev = n + 1;
        }
        let remainder = self.read_buf[prev..].to_vec();
        info!(
            "{:?}: read Remainder: {}",
            self.socket.peer_addr(),
            remainder.len()
        );
        self.read_buf = remainder;
    }

    fn read(&mut self) -> Result<(), Error> {
        let mut abuf = vec![0; 1024];
        match self.socket.read(&mut abuf).context("Reading socket")? {
            0 => {
                info!("{:?}: EOF!", self.socket.peer_addr());
                self.read_eof = true;
                Ok(())
            }
            n => {
                info!("{:?}: Read {}bytes", self.socket.peer_addr(), n);
                self.read_buf.extend(&abuf[0..n]);
                Ok(())
            }
        }
    }

    fn write(&mut self) -> Result<(), Error> {
        let n = self
            .socket
            .write(&mut self.write_buf)
            .context("writing buffer")?;
        info!(
            "{:?}: Wrote {} of {} in buffer",
            self.socket.peer_addr(),
            n,
            self.write_buf.len()
        );
        self.write_buf = self.write_buf[n..].to_vec();
        info!(
            "{:?}: Now {:?}b",
            self.socket.peer_addr(),
            self.write_buf.len()
        );
        Ok(())
    }

    fn reregister(&mut self, event_loop: &mut mio::Poll) -> Result<(), Error> {
        let mut flags = mio::Ready::readable();
        if !self.write_buf.is_empty() {
            flags.insert(mio::Ready::writable());
        }
        info!("Registering {:?} with {:?}", self, flags);

        event_loop
            .reregister(&self.socket, self.token, flags, mio::PollOpt::oneshot())
            .context("EventLoop#reregister")?;

        Ok(())
    }

    fn enqueue(&mut self, s: &str) {
        self.write_buf.extend(s.as_bytes());
        self.write_buf.push('\n' as u8);
    }
}

#[derive(Debug)]
struct Listener {
    listener: TcpListener,
    sock_status: mio::Ready,
    token: mio::Token,
}

impl Listener {
    fn new(listener: TcpListener, token: mio::Token) -> Listener {
        Listener {
            listener: listener,
            sock_status: mio::Ready::empty(),
            token: token,
        }
    }
    fn reregister(&self, event_loop: &mut mio::Poll, token: mio::Token) -> Result<(), Error> {
        event_loop
            .reregister(
                &self.listener,
                token,
                mio::Ready::readable(),
                mio::PollOpt::edge() | mio::PollOpt::oneshot(),
            ).context("Register listener")?;
        Ok(())
    }
}

impl RuleHandler for Listener {
    fn register(&self, event_loop: &mut mio::Poll, token: mio::Token) -> Result<(), Error> {
        event_loop
            .register(
                &self.listener,
                token,
                mio::Ready::readable(),
                mio::PollOpt::edge() | mio::PollOpt::oneshot(),
            ).context("Register listener")?;
        Ok(())
    }

    fn handle_event(&mut self, _event_loop: &mut mio::Poll, events: mio::Ready) {
        assert!(events.is_readable());
        self.sock_status.insert(events);
        info!(
            "Listener::handle_event: {:?}; this time: {:?}; now: {:?}",
            self.listener.local_addr(),
            events,
            self.sock_status
        );
    }
    fn process_rules(
        &mut self,
        event_loop: &mut mio::Poll,
        to_parent: &mut FnMut(MiChatCommand),
    ) -> Result<(), Error> {
        if self.sock_status.is_readable() {
            info!("the listener socket is ready to accept a connection");
            self.sock_status.remove(mio::Ready::readable());
            let (socket, _client) = self.listener.accept().context("Accept listening socket")?;
            let cmd = MiChatCommand::NewConnection(socket);
            to_parent(cmd);

            self.reregister(event_loop, self.token)?;
        }
        Ok(())
    }

    fn is_closed(&self) -> bool {
        false
    }
}

#[derive(Debug)]
enum EventHandler {
    Listener(Listener),
    Conn(Connection),
}

impl ops::Deref for EventHandler {
    type Target = RuleHandler;
    fn deref(&self) -> &Self::Target {
        match self {
            &EventHandler::Conn(ref conn) => conn,
            &EventHandler::Listener(ref listener) => listener,
        }
    }
}
impl ops::DerefMut for EventHandler {
    fn deref_mut(&mut self) -> &mut Self::Target {
        match self {
            &mut EventHandler::Conn(ref mut conn) => conn,
            &mut EventHandler::Listener(ref mut listener) => listener,
        }
    }
}

impl MiChat {
    fn ready(&mut self, event_loop: &mut mio::Poll, token: mio::Token, events: mio::Ready) {
        info!("{:?}: {:?}", token, events);
        self.connections[token.into()].handle_event(event_loop, events);
        if self.connections[token.into()].is_closed() {
            info!("Removing; {:?}", token);
            self.connections.remove(token.into());
        }
    }

    fn process(&mut self, event_loop: &mut mio::Poll) -> Result<(), Error> {
        let mut parent_actions = VecDeque::new();
        let mut failed = Vec::new();
        loop {
            for (idx, conn) in self.connections.iter_mut() {
                if let Err(e) =
                    conn.process_rules(event_loop, &mut |action| parent_actions.push_back(action))
                {
                    failed.push(idx);
                    error!("Error on connection {:?}; Dropping: {:?}", idx, e);
                }
            }
            for idx in failed.drain(..) {
                self.connections.remove(idx);
            }
            // Anything left to process?
            if parent_actions.is_empty() {
                break;
            }

            for action in parent_actions.drain(..) {
                self.process_action(action, event_loop);
            }
        }

        Ok(())
    }

    fn run(&mut self, event_loop: &mut mio::Poll) -> Result<(), Error> {
        loop {
            self.run_once(event_loop)?;
        }
    }
    fn run_once(&mut self, event_loop: &mut mio::Poll) -> Result<(), Error> {
        let mut events = mio::Events::with_capacity(1024);
        event_loop.poll(&mut events, None)?;
        for ev in &events {
            self.ready(event_loop, ev.token(), ev.readiness())
        }

        self.process(event_loop)
    }
}

fn main() {
    env_logger::init();
    let matches = App::new("michat")
        .arg(
            Arg::with_name("bind")
                .short("l")
                .takes_value(true)
                .required(true),
        ).get_matches();

    let address = value_t_or_exit!(matches.value_of("bind"), std::net::SocketAddr);
    let listener = TcpListener::bind(&address).expect("bind");

    let mut event_loop = mio::Poll::new().expect("Create event loop");
    let mut service = MiChat::new();

    service
        .listen(&mut event_loop, listener)
        .expect("Starting listener");
    info!("running michat listener at: {:?}", address);
    service.run(&mut event_loop).expect("run");
}
<|endoftext|>


<commit_before>extern crate unlimited;
extern crate clap;

use clap::{App, Arg};

use unlimited::core::config::Config;
use unlimited::core::editor::Editor;

fn main() {
    let config = parse_command_line();
    let mut editor = Editor::new(config);
    editor.run();
}

fn parse_command_line() -> Config {

    let matches = App::new("unlimited")
        .version("0.0.1")
        .author("Carl-Erwin Griffith <carl.erwin@gmail.com>")
        .about("an experimental editor")
        .arg(Arg::with_name("NO_CORE")
                 .help("disable core")
                 .long("no-core"))
        .arg(Arg::with_name("NO_UI").help("disable ui").long("no-ui"))
        .arg(Arg::with_name("FILES")
                 .help("Sets the input file to use")
                 .required(false)
                 .multiple(true))
        .get_matches();

    let mut files_list = Vec::new();
    if matches.is_present("FILES") == true {
        let strs: Vec<&str> = matches.values_of("FILES").unwrap().collect();
        files_list = strs.into_iter().map(|x| x.to_owned()).collect()
    }

    Config {
        start_core: !matches.is_present("NO_CORE"),
        start_ui: !matches.is_present("NO_UI"),
        files_list: files_list,
    }
}
<commit_msg>update command line help/description<commit_after>extern crate unlimited;
extern crate clap;

use clap::{App, Arg};

use unlimited::core::config::Config;
use unlimited::core::editor::Editor;

fn main() {
    let config = parse_command_line();
    let mut editor = Editor::new(config);
    editor.run();
}

fn parse_command_line() -> Config {

    let matches = App::new("unlimited")
        .version("0.0.1")
        .author("Carl-Erwin Griffith <carl.erwin@gmail.com>")
        .about("unlimited is an experimental editor")
        .arg(Arg::with_name("NO_CORE")
                 .help("disable core")
                 .long("no-core"))
        .arg(Arg::with_name("NO_UI").help("disable ui").long("no-ui"))
        .arg(Arg::with_name("FILES")
                 .help("list of the files to open")
                 .required(false)
                 .multiple(true))
        .get_matches();

    let mut files_list = Vec::new();
    if matches.is_present("FILES") == true {
        let strs: Vec<&str> = matches.values_of("FILES").unwrap().collect();
        files_list = strs.into_iter().map(|x| x.to_owned()).collect()
    }

    Config {
        start_core: !matches.is_present("NO_CORE"),
        start_ui: !matches.is_present("NO_UI"),
        files_list: files_list,
    }
}
<|endoftext|>


<commit_before>macro_rules! opt {
    ($opt:expr) => {
        match $opt {
            Some(item) => item,
            None => {
                return None;
            }
        }
    }
}

extern crate image;
extern crate imageproc;
extern crate rusttype;

use image::{DynamicImage, GenericImage, ImageFormat, Luma, Rgba};
use imageproc::drawing;
use imageproc::rect::Rect;
use rusttype::Scale;

#[cfg(windows)]
const DEFAULT_FONT: Option<&str> = Some("C:/Windows/Fonts/Impact.ttf");

#[cfg(macos)]
const DEFAULT_FONT: Option<&str> = Some("/Library/Fonts/Impact.ttf");

#[cfg(not(any(windows, macos)))]
const DEFAULT_FONT: Option<&str> = None;

fn main() {
    // The final value in the array here is the *opacity* of the pixel. Not the transparency.
    // Apparently, this is not CSS...
    let white_pixel = Rgba([255, 255, 255, 255]);
    let black_pixel = Rgba([0, 0, 0, 255]);

    let (image_path, text) = match read_command() {
        Some(command) => command,
        None => {
            println!("Try `annatar <image path> <text>");
            std::process::exit(1);
        }
    };

    let mut pixels = match image::open(image_path) {
        Ok(pixels) => pixels,
        _ => {
            println!("Unable to open image file");
            std::process::exit(2);
        }
    };

    // Not sure what the &[u8] thing is for, but that was in the example on PistonDevelopers.
    // I'm guessing that's to prevent the font from blowing the stack frame if it's too large.
    // Furthermore, I'm stealing this font from the Mac Sierra shared font folder, so there is
    // exactly zero chance of this compiling on Windows right now.
    let font = read_font(DEFAULT_FONT);
    let scale_factor = pixels.height() as f32 / 10.0;

    // This scales the font size itself. Using the same multiplier for both just makes it bigger
    // as the multiplier increases. Making X larger makes the font wider, while making Y larger
    // makes the font taller.
    //
    // let scale = Scale { x: height, y: height };
    //
    // The above form is equivalent to what I'm currently using:
    let scale = Scale::uniform(scale_factor);

    // Apparently, `Scale` is copy.
    let (text_width, text_height) = text_size(&text, &font, scale);

    // Seems like the coordinates x and y designate the top left corner of the region being drawn.
    // In order to center this, I'm going to have to figure out how to determine the size of the
    // region being drawn.
    let (width, height) = pixels.dimensions();

    // What follows is a little fourth grade math that attempts to stick the text at the center
    // of the bottom fifth of the image. This, by the way, is the closest I have ever come to
    // using anything I learned in Mrs. Vye's 9th grade keyboarding class. Thank God for the
    // IBM Selectric III, huh?
    let x = (width / 2) - (text_width / 2);
    let y = height - ((height / 5) - (text_height / 2));

    let mut scratch = image::ImageBuffer::from_pixel(text_width, text_height, black_pixel);
    drawing::draw_text_mut(&mut scratch, white_pixel, 0, 0, scale, &font, &text);

    // Here I set the "low threshold" and the "high threshold" for edge detection to 0.5 I have
    // not the first fucking clue what either of these thresholds is for or what sort of values
    // they might accept. I can only assume that even a drunk wallaby could detect these edges.
    for (idx, &pixel) in imageproc::edges::canny(&image::imageops::grayscale(&scratch), 255.0, 255.0).pixels().enumerate() {
        if Luma([255u8]) == pixel {
            let idx = idx as u32;
            let x = idx % text_width + x;
            let y = idx / text_width + y;

            // I bet this isn't cheap, but... meh.
            let rect = Rect::square(x as i32, y as i32, (0.1 * scale_factor) as u32);
            drawing::draw_filled_rect_mut(&mut pixels, rect, Rgba([0, 0, 0, 255]));
        }
    }

    drawing::draw_text_mut(&mut pixels, white_pixel, x, y, scale, &font, &text);

    // Here I'm printing the scratch image used for edge detection. This is pretty much just as
    // a smoke test; I'll get around to pulling it out of here eventually.
    save("scratch.png", &DynamicImage::ImageRgba8(scratch));
    save("output.png", &pixels);
}

/// Calculate the dimensions of the bounding box for a given string, font, and scale.
///
/// This works by summing the "advance width" of each glyph in the text, entirely ignoring
/// kerning as each character is considered in isolation. Because this is used primarily to
/// center text in the image, it's close enough for government work.
fn text_size(s: &str, font: &[u8], scale: Scale) -> (u32, u32) {
    use rusttype::{FontCollection, VMetrics};

    // Font collections apparently consist of a collection of fonts. That is, more than one will
    // be defined in any given bag of bytes. Life's imperfect. The common case, however, is that
    // a given bag of bytes will contain a single font, in which case this will not explode.
    let font = FontCollection::from_bytes(font).into_font().expect("Font collection contains multiple fonts.");

    let text_width = font.glyphs_for(s.chars())
        .map(|glyph| glyph.scaled(scale).h_metrics().advance_width)
        .sum::<f32>();

    // The "v-metrics" for any given letter in a font are the same for a given scale, so we don't
    // need to check this for each glyph.
    let text_height = {
        let VMetrics { ascent, descent, ..} = font.v_metrics(scale);
        (ascent - descent) as u32
    };

    // I know I'm truncating the length and this is probably wrong, but it's not wrong by enough
    // to be noticeable when you print it to an image.
    (text_width as u32, text_height)
}

fn save(path: &str, pixels: &DynamicImage) {
    use std::fs::OpenOptions;

    let mut out = OpenOptions::new()
        .write(true)
        .create(true)
        .truncate(true)
        .open(path).unwrap();

    pixels.save(&mut out, ImageFormat::PNG).unwrap();
}

fn read_font(path: Option<&'static str>) -> Vec<u8> {
    use std::fs::File;
    use std::io::Read;

    let path = path.expect("Unsupported platform--please annoy the maintainer until this is fixed");
    let mut file = File::open(path).expect("Default font not found");
    let mut buf = Vec::new();

    file.read_to_end(&mut buf).expect("Unable to read file");
    buf
}

fn read_command() -> Option<(String, String)> {
    let mut args = std::env::args().skip(1);
    Some((
        opt!(args.next()),
        opt!(args.next()),
    ))
}
<commit_msg>Update comment<commit_after>macro_rules! opt {
    ($opt:expr) => {
        match $opt {
            Some(item) => item,
            None => {
                return None;
            }
        }
    }
}

extern crate image;
extern crate imageproc;
extern crate rusttype;

use image::{DynamicImage, GenericImage, ImageFormat, Luma, Rgba};
use imageproc::drawing;
use imageproc::rect::Rect;
use rusttype::Scale;

#[cfg(windows)]
const DEFAULT_FONT: Option<&str> = Some("C:/Windows/Fonts/Impact.ttf");

#[cfg(macos)]
const DEFAULT_FONT: Option<&str> = Some("/Library/Fonts/Impact.ttf");

#[cfg(not(any(windows, macos)))]
const DEFAULT_FONT: Option<&str> = None;

fn main() {
    // The final value in the array here is the *opacity* of the pixel. Not the transparency.
    // Apparently, this is not CSS...
    let white_pixel = Rgba([255, 255, 255, 255]);
    let black_pixel = Rgba([0, 0, 0, 255]);

    let (image_path, text) = match read_command() {
        Some(command) => command,
        None => {
            println!("Try `annatar <image path> <text>");
            std::process::exit(1);
        }
    };

    let mut pixels = match image::open(image_path) {
        Ok(pixels) => pixels,
        _ => {
            println!("Unable to open image file");
            std::process::exit(2);
        }
    };

    let font = read_font(DEFAULT_FONT);
    let scale_factor = pixels.height() as f32 / 10.0;

    // This scales the font size itself. Using the same multiplier for both just makes it bigger
    // as the multiplier increases. Making X larger makes the font wider, while making Y larger
    // makes the font taller.
    //
    // let scale = Scale { x: height, y: height };
    //
    // The above form is equivalent to what I'm currently using:
    let scale = Scale::uniform(scale_factor);

    // Apparently, `Scale` is copy.
    let (text_width, text_height) = text_size(&text, &font, scale);

    // Seems like the coordinates x and y designate the top left corner of the region being drawn.
    // In order to center this, I'm going to have to figure out how to determine the size of the
    // region being drawn.
    let (width, height) = pixels.dimensions();

    // What follows is a little fourth grade math that attempts to stick the text at the center
    // of the bottom fifth of the image. This, by the way, is the closest I have ever come to
    // using anything I learned in Mrs. Vye's 9th grade keyboarding class. Thank God for the
    // IBM Selectric III, huh?
    let x = (width / 2) - (text_width / 2);
    let y = height - ((height / 5) - (text_height / 2));

    let mut scratch = image::ImageBuffer::from_pixel(text_width, text_height, black_pixel);
    drawing::draw_text_mut(&mut scratch, white_pixel, 0, 0, scale, &font, &text);

    // Here I set the "low threshold" and the "high threshold" for edge detection to 0.5 I have
    // not the first fucking clue what either of these thresholds is for or what sort of values
    // they might accept. I can only assume that even a drunk wallaby could detect these edges.
    for (idx, &pixel) in imageproc::edges::canny(&image::imageops::grayscale(&scratch), 255.0, 255.0).pixels().enumerate() {
        if Luma([255u8]) == pixel {
            let idx = idx as u32;
            let x = idx % text_width + x;
            let y = idx / text_width + y;

            // I bet this isn't cheap, but... meh.
            let rect = Rect::square(x as i32, y as i32, (0.1 * scale_factor) as u32);
            drawing::draw_filled_rect_mut(&mut pixels, rect, Rgba([0, 0, 0, 255]));
        }
    }

    drawing::draw_text_mut(&mut pixels, white_pixel, x, y, scale, &font, &text);

    // Here I'm printing the scratch image used for edge detection. This is pretty much just as
    // a smoke test; I'll get around to pulling it out of here eventually.
    save("scratch.png", &DynamicImage::ImageRgba8(scratch));
    save("output.png", &pixels);
}

/// Calculate the dimensions of the bounding box for a given string, font, and scale.
///
/// This works by summing the "advance width" of each glyph in the text, entirely ignoring
/// kerning as each character is considered in isolation. Because this is used primarily to
/// center text in the image, it's close enough for government work.
fn text_size(s: &str, font: &[u8], scale: Scale) -> (u32, u32) {
    use rusttype::{FontCollection, VMetrics};

    // Font collections apparently consist of a collection of fonts. That is, more than one will
    // be defined in any given bag of bytes. Life's imperfect. The common case, however, is that
    // a given bag of bytes will contain a single font, in which case this will not explode.
    let font = FontCollection::from_bytes(font).into_font().expect("Font collection contains multiple fonts.");

    let text_width = font.glyphs_for(s.chars())
        .map(|glyph| glyph.scaled(scale).h_metrics().advance_width)
        .sum::<f32>();

    // The "v-metrics" for any given letter in a font are the same for a given scale, so we don't
    // need to check this for each glyph.
    let text_height = {
        let VMetrics { ascent, descent, ..} = font.v_metrics(scale);
        (ascent - descent) as u32
    };

    // I know I'm truncating the length and this is probably wrong, but it's not wrong by enough
    // to be noticeable when you print it to an image.
    (text_width as u32, text_height)
}

fn save(path: &str, pixels: &DynamicImage) {
    use std::fs::OpenOptions;

    let mut out = OpenOptions::new()
        .write(true)
        .create(true)
        .truncate(true)
        .open(path).unwrap();

    pixels.save(&mut out, ImageFormat::PNG).unwrap();
}

fn read_font(path: Option<&'static str>) -> Vec<u8> {
    use std::fs::File;
    use std::io::Read;

    let path = path.expect("Unsupported platform--please annoy the maintainer until this is fixed");
    let mut file = File::open(path).expect("Default font not found");
    let mut buf = Vec::new();

    file.read_to_end(&mut buf).expect("Unable to read file");
    buf
}

fn read_command() -> Option<(String, String)> {
    let mut args = std::env::args().skip(1);
    Some((
        opt!(args.next()),
        opt!(args.next()),
    ))
}
<|endoftext|>


<commit_before>use std::result;
use common::combinator::*;

pub type Result<T> = result::Result<T, LexError>;

macro_rules! token_defs {
    ($enum_name:ident => {
        $($name:ident,)*
    }) => {
        #[derive(Debug)]
        pub enum $enum_name {
            $(
                $name(i32, i32, String),
            )*
        }

        impl $enum_name {
            pub fn pos(&self) -> (i32, i32) {
                match *self {
                    $(
                        $enum_name::$name(row, col, _) => (row, col),
                    )*
                }
            }

            pub fn literal(&self) -> &String {
                match *self {
                    $(
                        $enum_name::$name(_, _, ref lit) => lit,
                    )*
                }
            }
        }
    }
}

#[macro_export]
macro_rules! token {
    ($token:ident, $row:expr, $col:expr, $literal:expr) => {
        Token::$token($row, $col, $literal.to_string())
    }
}

token_defs!(Token => {
    Illegal,

    // identifier and literals
    Ident,
    IntLiteral,
    BoolLiteral,
    StringLiteral,

    // statements
    Assign,
    If,
    Else,

    // operators
    Plus,
    Minus,
    Divide,
    Multiply,
    Eq,
    NotEq,
    GreaterThan,
    LessThan,
    Not,

    // reserved words
    Function,
    Let,
    Return,

    // punctuations
    Comma,
    Colon,
    SemiColon,
    LParen,
    RParen,
    LBrace,
    RBrace,
    LBracket,
    RBracket,
});

#[derive(Debug)]
pub struct LexError {
    message: String,
    pos: (i32, i32),
}

struct Lexer {
    input: Vec<char>,
    cursor: usize,
    saved_cursor: usize,
    row: i32,
    col: i32,
}

impl Lexer {
    fn new<I>(input: I) -> Lexer
        where I: Iterator<Item = char>
    {
        Lexer {
            input: input.collect(),
            cursor: 0,
            saved_cursor: 0,
            row: 1,
            col: 1,
        }
    }
}

impl Parser<char, LexError> for Lexer {
    fn consume(&mut self) -> Option<char> {
        match self.input.get(self.cursor) {
            Some(x) => {
                self.cursor += 1;
                if *x == '\n' {
                    self.row += 1;
                    self.col = 1;
                } else {
                    self.col += 1;
                }
                Some(x.clone())
            }
            None => None,
        }
    }

    fn preview(&self) -> Option<&char> {
        self.input.get(self.cursor)
    }

    fn current_pos(&self) -> (i32, i32) {
        (self.row, self.col)
    }

    fn error<S: Into<String>>(&self, message: S) -> LexError {
        LexError {
            message: message.into(),
            pos: self.current_pos(),
        }
    }

    fn save(&mut self) {
        self.saved_cursor = self.cursor;
    }

    fn load(&mut self) {
        self.cursor = self.saved_cursor;
    }
}
<commit_msg>Change starting column number to 0<commit_after>use std::result;
use common::combinator::*;

pub type Result<T> = result::Result<T, LexError>;

macro_rules! token_defs {
    ($enum_name:ident => {
        $($name:ident,)*
    }) => {
        #[derive(Debug)]
        pub enum $enum_name {
            $(
                $name(i32, i32, String),
            )*
        }

        impl $enum_name {
            pub fn pos(&self) -> (i32, i32) {
                match *self {
                    $(
                        $enum_name::$name(row, col, _) => (row, col),
                    )*
                }
            }

            pub fn literal(&self) -> &String {
                match *self {
                    $(
                        $enum_name::$name(_, _, ref lit) => lit,
                    )*
                }
            }
        }
    }
}

#[macro_export]
macro_rules! token {
    ($token:ident, $row:expr, $col:expr, $literal:expr) => {
        Token::$token($row, $col, $literal.to_string())
    }
}

token_defs!(Token => {
    Illegal,

    // identifier and literals
    Ident,
    IntLiteral,
    BoolLiteral,
    StringLiteral,

    // statements
    Assign,
    If,
    Else,

    // operators
    Plus,
    Minus,
    Divide,
    Multiply,
    Eq,
    NotEq,
    GreaterThan,
    LessThan,
    Not,

    // reserved words
    Function,
    Let,
    Return,

    // punctuations
    Comma,
    Colon,
    SemiColon,
    LParen,
    RParen,
    LBrace,
    RBrace,
    LBracket,
    RBracket,
});

#[derive(Debug)]
pub struct LexError {
    message: String,
    pos: (i32, i32),
}

struct Lexer {
    input: Vec<char>,
    cursor: usize,
    saved_cursor: usize,
    row: i32,
    col: i32,
}

impl Lexer {
    fn new<I>(input: I) -> Lexer
        where I: Iterator<Item = char>
    {
        Lexer {
            input: input.collect(),
            cursor: 0,
            saved_cursor: 0,
            row: 1,
            col: 0,
        }
    }
}

impl Parser<char, LexError> for Lexer {
    fn consume(&mut self) -> Option<char> {
        match self.input.get(self.cursor) {
            Some(x) => {
                self.cursor += 1;
                if *x == '\n' {
                    self.row += 1;
                    self.col = 1;
                } else {
                    self.col += 1;
                }
                Some(x.clone())
            }
            None => None,
        }
    }

    fn preview(&self) -> Option<&char> {
        self.input.get(self.cursor)
    }

    fn current_pos(&self) -> (i32, i32) {
        (self.row, self.col)
    }

    fn error<S: Into<String>>(&self, message: S) -> LexError {
        LexError {
            message: message.into(),
            pos: self.current_pos(),
        }
    }

    fn save(&mut self) {
        self.saved_cursor = self.cursor;
    }

    fn load(&mut self) {
        self.cursor = self.saved_cursor;
    }
}
<|endoftext|>


<commit_before>use std::env;
use std::io::{self, Write};
use std::process::{self, Command, Stdio};

extern crate isatty;
use isatty::{stdout_isatty, stderr_isatty};

fn main() {
    // Build cargo command
    let mut cargo = Command::new("cargo");
    cargo.args(&wrap_args(env::args()));

    // Run cargo command, print errors, exit if failed
    let expanded = cargo.output().unwrap();
    for line in String::from_utf8_lossy(&expanded.stderr).lines() {
        writeln!(io::stderr(), "{}", line).unwrap();
    }
    if !expanded.status.success() {
        process::exit(expanded.status.code().unwrap_or(1));
    }

    // Just print the expanded output if rustfmt is not available
    if !have_rustfmt() {
        io::stdout().write_all(&expanded.stdout).unwrap();
        return;
    }

    // Build rustfmt command and give it the expanded code
    let mut rustfmt = Command::new("rustfmt")
        .stdin(Stdio::piped())
        .stdout(Stdio::piped())
        .stderr(Stdio::piped())
        .spawn()
        .unwrap();
    rustfmt.stdin.as_mut().unwrap().write_all(&expanded.stdout).unwrap();

    // Print rustfmt errors and exit if failed
    let formatted = rustfmt.wait_with_output().unwrap();
    for line in String::from_utf8_lossy(&formatted.stderr).lines() {
        if !ignore_rustfmt_err(line) {
            writeln!(io::stderr(), "{}", line).unwrap();
        }
    }
    if !formatted.status.success() {
        let code = formatted.status.code().unwrap_or(1);
        // Ignore code 3 which is formatting errors
        if code != 3 {
            process::exit(code);
        }
    }

    // Print formatted output
    io::stdout().write_all(&formatted.stdout).unwrap();
}

// Based on https://github.com/rsolomo/cargo-check
fn wrap_args<T, I>(it: I) -> Vec<String>
    where T: AsRef<str>,
          I: IntoIterator<Item=T>
{
    let mut args = vec!["rustc".to_string()];
    let mut has_color = false;
    let mut has_double_hyphen = false;

    for arg in it.into_iter().skip(2) {
        let arg = arg.as_ref().to_string();
        has_color |= arg.starts_with("--color");
        has_double_hyphen |= arg == "--";
        args.push(arg);
    }

    if !has_color {
        let color = stdout_isatty() && stderr_isatty();
        let setting = if color { "always" } else { "never" };
        args.push(format!("--color={}", setting));
    }

    if !has_double_hyphen {
        args.push("--".to_string());
    }

    args.push("-Zunstable-options".to_string());
    args.push("--pretty=expanded".to_string());

    args
}

fn have_rustfmt() -> bool {
    Command::new("rustfmt")
        .stdin(Stdio::null())
        .stdout(Stdio::null())
        .stderr(Stdio::null())
        .spawn()
        .is_ok()
}

fn ignore_rustfmt_err(line: &str) -> bool {
    line.is_empty()
        || line.ends_with("line exceeded maximum length (sorry)")
}
<commit_msg>Add NO_RUSTFMT flag to disable rustfmt<commit_after>use std::env;
use std::io::{self, Write};
use std::process::{self, Command, Stdio};

extern crate isatty;
use isatty::{stdout_isatty, stderr_isatty};

fn main() {
    // Build cargo command
    let mut cargo = Command::new("cargo");
    cargo.args(&wrap_args(env::args()));

    // Run cargo command, print errors, exit if failed
    let expanded = cargo.output().unwrap();
    for line in String::from_utf8_lossy(&expanded.stderr).lines() {
        writeln!(io::stderr(), "{}", line).unwrap();
    }
    if !expanded.status.success() {
        process::exit(expanded.status.code().unwrap_or(1));
    }

    // Just print the expanded output if rustfmt is not available
    if env::var("NO_RUSTFMT").is_ok() || !have_rustfmt() {
        io::stdout().write_all(&expanded.stdout).unwrap();
        return;
    }

    // Build rustfmt command and give it the expanded code
    let mut rustfmt = Command::new("rustfmt")
        .stdin(Stdio::piped())
        .stdout(Stdio::piped())
        .stderr(Stdio::piped())
        .spawn()
        .unwrap();
    rustfmt.stdin.as_mut().unwrap().write_all(&expanded.stdout).unwrap();

    // Print rustfmt errors and exit if failed
    let formatted = rustfmt.wait_with_output().unwrap();
    for line in String::from_utf8_lossy(&formatted.stderr).lines() {
        if !ignore_rustfmt_err(line) {
            writeln!(io::stderr(), "{}", line).unwrap();
        }
    }
    if !formatted.status.success() {
        let code = formatted.status.code().unwrap_or(1);
        // Ignore code 3 which is formatting errors
        if code != 3 {
            process::exit(code);
        }
    }

    // Print formatted output
    io::stdout().write_all(&formatted.stdout).unwrap();
}

// Based on https://github.com/rsolomo/cargo-check
fn wrap_args<T, I>(it: I) -> Vec<String>
    where T: AsRef<str>,
          I: IntoIterator<Item=T>
{
    let mut args = vec!["rustc".to_string()];
    let mut has_color = false;
    let mut has_double_hyphen = false;

    for arg in it.into_iter().skip(2) {
        let arg = arg.as_ref().to_string();
        has_color |= arg.starts_with("--color");
        has_double_hyphen |= arg == "--";
        args.push(arg);
    }

    if !has_color {
        let color = stdout_isatty() && stderr_isatty();
        let setting = if color { "always" } else { "never" };
        args.push(format!("--color={}", setting));
    }

    if !has_double_hyphen {
        args.push("--".to_string());
    }

    args.push("-Zunstable-options".to_string());
    args.push("--pretty=expanded".to_string());

    args
}

fn have_rustfmt() -> bool {
    Command::new("rustfmt")
        .stdin(Stdio::null())
        .stdout(Stdio::null())
        .stderr(Stdio::null())
        .spawn()
        .is_ok()
}

fn ignore_rustfmt_err(line: &str) -> bool {
    line.is_empty()
        || line.ends_with("line exceeded maximum length (sorry)")
}
<|endoftext|>


<commit_before>// Copyright 2015 Till Höppner
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

extern crate ilc;
extern crate chrono;
#[macro_use]
extern crate clap;
extern crate rustc_serialize;
#[macro_use]
extern crate log;
extern crate env_logger;
extern crate glob;
extern crate blist;

use clap::{ Arg, App, AppSettings,  SubCommand };

mod chain;
mod ageset;
mod app;

fn main() {
    env_logger::init().unwrap();
    let args = App::new("ilc")
        .version(crate_version!())
        .setting(AppSettings::GlobalVersion)
        .setting(AppSettings::VersionlessSubcommands)
        .setting(AppSettings::ArgRequiredElseHelp)
        .author("Till Höppner <till@hoeppner.ws>")
        .about("A converter and statistics utility for IRC log files")
        .arg(Arg::with_name("timezone")
             .help("UTC offset in the direction of the western hemisphere")
             .global(true)
             .takes_value(true)
             .long("timezone")
             .short("t"))
        .arg(Arg::with_name("date")
             .help("Override the date for this log, ISO 8601, YYYY-MM-DD")
             .global(true)
             .takes_value(true)
             .long("date")
             .short("d"))
        .arg(Arg::with_name("channel")
             .help("Set a channel for the current log")
             .global(true)
             .takes_value(true)
             .long("channel")
             .short("c"))
        .arg(Arg::with_name("input_format")
             .help("Set the input format for the current log")
             .global(true)
             .takes_value(true)
             .long("inf"))
        .arg(Arg::with_name("output_format")
             .help("Set the output format for the current log")
             .global(true)
             .takes_value(true)
             .long("outf"))
        .arg(Arg::with_name("input_files")
             .help("Specify an input file, instead of stdin")
             .global(true)
             .takes_value(true).multiple(true)
             .long("input")
             .short("i"))
        .arg(Arg::with_name("output_file")
             .help("Specify an output file, instead of stdout")
             .global(true)
             .takes_value(true)
             .long("output")
             .short("o"))
        .subcommand(SubCommand::with_name("parse")
                    .about("Parse the input, checking the format"))
        .subcommand(SubCommand::with_name("convert"))
        .subcommand(SubCommand::with_name("freq"))
        .subcommand(SubCommand::with_name("seen")
                    .arg(Arg::with_name("nick")
                         .help("The nick you're looking for")
                         .takes_value(true).required(true)
                         .index(1)))
        .subcommand(SubCommand::with_name("sort"))
        .subcommand(SubCommand::with_name("dedup"))
        .get_matches();

    match args.subcommand() {
        ("parse", Some(args)) => app::parse::parse(args),
        ("convert", Some(args)) => app::convert::convert(args),
        ("freq", Some(args)) => app::freq::freq(args),
        ("seen", Some(args)) => app::seen::seen(args),
        ("sort", Some(args)) => app::sort::sort(args),
        ("dedup", Some(args)) => app::dedup::dedup(args),
        _ => panic!("Unimplemented subcommand, this is a bug")
    }
}
<commit_msg>Add about lines to subcommands<commit_after>// Copyright 2015 Till Höppner
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

extern crate ilc;
extern crate chrono;
#[macro_use]
extern crate clap;
extern crate rustc_serialize;
#[macro_use]
extern crate log;
extern crate env_logger;
extern crate glob;
extern crate blist;

use clap::{ Arg, App, AppSettings,  SubCommand };

mod chain;
mod ageset;
mod app;

fn main() {
    env_logger::init().unwrap();
    let args = App::new("ilc")
        .version(crate_version!())
        .setting(AppSettings::GlobalVersion)
        .setting(AppSettings::VersionlessSubcommands)
        .setting(AppSettings::ArgRequiredElseHelp)
        .author("Till Höppner <till@hoeppner.ws>")
        .about("A converter and statistics utility for IRC log files")
        .arg(Arg::with_name("timezone")
             .help("UTC offset in the direction of the western hemisphere")
             .global(true)
             .takes_value(true)
             .long("timezone")
             .short("t"))
        .arg(Arg::with_name("date")
             .help("Override the date for this log, ISO 8601, YYYY-MM-DD")
             .global(true)
             .takes_value(true)
             .long("date")
             .short("d"))
        .arg(Arg::with_name("channel")
             .help("Set a channel for the current log")
             .global(true)
             .takes_value(true)
             .long("channel")
             .short("c"))
        .arg(Arg::with_name("input_format")
             .help("Set the input format for the current log")
             .global(true)
             .takes_value(true)
             .long("inf"))
        .arg(Arg::with_name("output_format")
             .help("Set the output format for the current log")
             .global(true)
             .takes_value(true)
             .long("outf"))
        .arg(Arg::with_name("input_files")
             .help("Specify an input file, instead of stdin")
             .global(true)
             .takes_value(true).multiple(true)
             .long("input")
             .short("i"))
        .arg(Arg::with_name("output_file")
             .help("Specify an output file, instead of stdout")
             .global(true)
             .takes_value(true)
             .long("output")
             .short("o"))
        .subcommand(SubCommand::with_name("parse")
                    .about("Parse the input, checking the format"))
        .subcommand(SubCommand::with_name("convert")
                    .about("Convert from a source to a target format"))
        .subcommand(SubCommand::with_name("freq")
                    .about("Analyse the activity of users by certain metrics"))
        .subcommand(SubCommand::with_name("seen")
                    .about("Print the last line a nick was active")
                    .arg(Arg::with_name("nick")
                         .help("The nick you're looking for")
                         .takes_value(true).required(true)
                         .index(1)))
        .subcommand(SubCommand::with_name("sort")
                    .about("Sorts a log by time"))
        .subcommand(SubCommand::with_name("dedup")
                    .about("Removes duplicate log entries in close proximity"))
        .get_matches();

    match args.subcommand() {
        ("parse", Some(args)) => app::parse::parse(args),
        ("convert", Some(args)) => app::convert::convert(args),
        ("freq", Some(args)) => app::freq::freq(args),
        ("seen", Some(args)) => app::seen::seen(args),
        ("sort", Some(args)) => app::sort::sort(args),
        ("dedup", Some(args)) => app::dedup::dedup(args),
        _ => panic!("Unimplemented subcommand, this is a bug")
    }
}
<|endoftext|>


<commit_before>#![feature(env, old_path, old_io)]
use std::old_io as io;

enum ParserResult {
    Something(Op),
    Nothing,
    EOF
}

use ParserResult::*;

type Ops = Vec<Op>;
enum Op {
    Add(u8),
    Mov(usize),
    In,
    Out,
    Loop(Ops)
}

use Op::*;

struct State {
    index: usize,
    memory: [u8; 256]
}

impl State {
    fn peek(&self) -> u8 {
        self.memory[self.index % 256]
    }

    fn poke(&mut self, value: u8) {
        self.memory[self.index % 256] = value;
    }

    fn step(&mut self, op: &Op) {
        match op {
            &Add(i) => { let x = self.peek(); self.poke(x + i); },
            &Mov(n) => self.index += n,
            &In => {
                self.poke(io::stdio::stdin().read_u8().unwrap());
            },
            &Out => {
                io::stdio::stdout().write_u8(self.peek()).unwrap();
            },
            &Loop(ref ops) => {
                while self.peek() != 0 {
                    self.run(ops);
                }
            }
        }
    }

    fn run(&mut self, ops: &[Op]) {
        for op in ops {
            self.step(op);
        }
    }
}

fn main() {
    let filenames: Vec<String> = std::iter::FromIterator::from_iter(std::env::args());

    for filename in &filenames[1..] {
        let mut reader = reader(&Path::new(filename));
        let mut state = State { index: 0, memory: [0; 256] };
        let mut chars = reader.chars().peekable();

        loop {
            match parse(&mut chars) {
                Something(op) => state.step(&op),
                EOF => break,
                _ => {}
            }
        }
    }
}

fn parse(mut chars: &mut std::iter::Peekable<io::Chars<io::BufferedReader<io::fs::File>>>) -> ParserResult {
    match chars.next() {
        Some(Ok(c)) => match c {
            '+' => Something(Add(1)),
            '-' => Something(Add(-1)),
            '<' => Something(Mov(-1)),
            '>' => Something(Mov(1)),
            ',' => Something(In),
            '.' => Something(Out),
            '[' => {
                let mut children: Ops = Vec::new();
                while !(chars.peek() == Some(&Ok(']'))) {
                    match parse(&mut chars) {
                        Something(op) => children.push(op),
                        Nothing => {},
                        EOF => panic!()
                    }
                }
                chars.next();
                Something(Loop(children))
            },
            _ => Nothing  // other characters
        },
        _ => EOF
    }
}

fn reader(path: &Path) -> io::BufferedReader<io::File> {
    match io::File::open_mode(path, io::Open, io::Read) {
        Ok(f) => io::BufferedReader::new(f),
        Err(e) => panic!(e)
    }
}
<commit_msg>refactor: switch to new io, path<commit_after>#![feature(env, fs, io, old_io, path)]
use std::fs;
use std::io;
use std::io::ReadExt;
use std::old_io::stdio;
use std::path::Path;

enum ParserResult {
    Something(Op),
    Nothing,
    EOF
}

use ParserResult::*;

type Ops = Vec<Op>;
enum Op {
    Add(u8),
    Mov(usize),
    In,
    Out,
    Loop(Ops)
}

use Op::*;

struct State {
    index: usize,
    memory: [u8; 256]
}

impl State {
    fn peek(&self) -> u8 {
        self.memory[self.index % 256]
    }

    fn poke(&mut self, value: u8) {
        self.memory[self.index % 256] = value;
    }

    fn step(&mut self, op: &Op) {
        match op {
            &Add(i) => { let x = self.peek(); self.poke(x + i); },
            &Mov(n) => self.index += n,
            &In => {
                self.poke(stdio::stdin().read_u8().unwrap());
            },
            &Out => {
                stdio::stdout().write_u8(self.peek()).unwrap();
            },
            &Loop(ref ops) => {
                while self.peek() != 0 {
                    self.run(ops);
                }
            }
        }
    }

    fn run(&mut self, ops: &[Op]) {
        for op in ops {
            self.step(op);
        }
    }
}

fn main() {
    let filenames: Vec<String> = std::iter::FromIterator::from_iter(std::env::args());

    for filename in &filenames[1..] {
        let reader = reader(&Path::new(filename));
        let mut state = State { index: 0, memory: [0; 256] };
        let mut chars = reader.chars().peekable();

        loop {
            match parse(&mut chars) {
                Something(op) => state.step(&op),
                EOF => break,
                _ => {}
            }
        }
    }
}

fn parse(mut chars: &mut std::iter::Peekable<io::Chars<io::BufReader<fs::File>>>) -> ParserResult {
    match chars.next() {
        Some(Ok(c)) => match c {
            '+' => Something(Add(1)),
            '-' => Something(Add(-1)),
            '<' => Something(Mov(-1)),
            '>' => Something(Mov(1)),
            ',' => Something(In),
            '.' => Something(Out),
            '[' => {
                let mut children: Ops = Vec::new();
                while !(chars.peek() == Some(&Ok(']'))) {
                    match parse(&mut chars) {
                        Something(op) => children.push(op),
                        Nothing => {},
                        EOF => panic!()
                    }
                }
                chars.next();
                Something(Loop(children))
            },
            _ => Nothing  // other characters
        },
        _ => EOF
    }
}

fn reader(path: &Path) -> io::BufReader<fs::File> {
    match fs::File::open(path) {
        Ok(f) => io::BufReader::new(f),
        Err(e) => panic!(e)
    }
}
<|endoftext|>