<commit_before>use std::{
    path::{Path, PathBuf},
    process::Command,
};

use build_helper::t;

use crate::builder::Builder;

#[derive(Copy, Clone)]
pub(crate) enum OverlayKind {
    Rust,
    LLVM,
    Cargo,
    Clippy,
    Miri,
    Rustfmt,
    RLS,
    RustAnalyzer,
}

impl OverlayKind {
    fn legal_and_readme(&self) -> &[&str] {
        match self {
            OverlayKind::Rust => &["COPYRIGHT", "LICENSE-APACHE", "LICENSE-MIT", "README.md"],
            OverlayKind::LLVM => {
                &["src/llvm-project/llvm/LICENSE.TXT", "src/llvm-project/llvm/README.txt"]
            }
            OverlayKind::Cargo => &[
                "src/tools/cargo/README.md",
                "src/tools/cargo/LICENSE-MIT",
                "src/tools/cargo/LICENSE-APACHE",
                "src/tools/cargo/LICENSE-THIRD-PARTY",
            ],
            OverlayKind::Clippy => &[
                "src/tools/clippy/README.md",
                "src/tools/clippy/LICENSE-APACHE",
                "src/tools/clippy/LICENSE-MIT",
            ],
            OverlayKind::Miri => &[
                "src/tools/miri/README.md",
                "src/tools/miri/LICENSE-APACHE",
                "src/tools/miri/LICENSE-MIT",
            ],
            OverlayKind::Rustfmt => &[
                "src/tools/rustfmt/README.md",
                "src/tools/rustfmt/LICENSE-APACHE",
                "src/tools/rustfmt/LICENSE-MIT",
            ],
            OverlayKind::RLS => &[
                "src/tools/rls/README.md",
                "src/tools/rls/LICENSE-APACHE",
                "src/tools/rls/LICENSE-MIT",
            ],
            OverlayKind::RustAnalyzer => &[
                "src/tools/rust-analyzer/README.md",
                "src/tools/rust-analyzer/LICENSE-APACHE",
                "src/tools/rust-analyzer/LICENSE-MIT",
            ],
        }
    }

    fn version(&self, builder: &Builder<'_>) -> String {
        match self {
            OverlayKind::Rust => builder.rust_version(),
            OverlayKind::LLVM => builder.rust_version(),
            OverlayKind::Cargo => {
                builder.cargo_info.version(builder, &builder.release_num("cargo"))
            }
            OverlayKind::Clippy => {
                builder.clippy_info.version(builder, &builder.release_num("clippy"))
            }
            OverlayKind::Miri => builder.miri_info.version(builder, &builder.release_num("miri")),
            OverlayKind::Rustfmt => {
                builder.rustfmt_info.version(builder, &builder.release_num("rustfmt"))
            }
            OverlayKind::RLS => builder.rls_info.version(builder, &builder.release_num("rls")),
            OverlayKind::RustAnalyzer => builder
                .rust_analyzer_info
                .version(builder, &builder.release_num("rust-analyzer/crates/rust-analyzer")),
        }
    }
}

pub(crate) struct Tarball<'a> {
    builder: &'a Builder<'a>,

    pkgname: String,
    component: String,
    target: Option<String>,
    product_name: String,
    overlay: OverlayKind,

    temp_dir: PathBuf,
    image_dir: PathBuf,
    overlay_dir: PathBuf,

    include_target_in_component_name: bool,
    is_preview: bool,
    delete_temp_dir: bool,
}

impl<'a> Tarball<'a> {
    pub(crate) fn new(builder: &'a Builder<'a>, component: &str, target: &str) -> Self {
        Self::new_inner(builder, component, Some(target.into()))
    }

    pub(crate) fn new_targetless(builder: &'a Builder<'a>, component: &str) -> Self {
        Self::new_inner(builder, component, None)
    }

    fn new_inner(builder: &'a Builder<'a>, component: &str, target: Option<String>) -> Self {
        let pkgname = crate::dist::pkgname(builder, component);

        let mut temp_dir = builder.out.join("tmp").join("tarball");
        if let Some(target) = &target {
            temp_dir = temp_dir.join(target);
        }
        let _ = std::fs::remove_dir_all(&temp_dir);

        let image_dir = temp_dir.join("image");
        let overlay_dir = temp_dir.join("overlay");

        Self {
            builder,

            pkgname,
            component: component.into(),
            target,
            product_name: "Rust".into(),
            overlay: OverlayKind::Rust,

            temp_dir,
            image_dir,
            overlay_dir,

            include_target_in_component_name: false,
            is_preview: false,
            delete_temp_dir: true,
        }
    }

    pub(crate) fn set_overlay(&mut self, overlay: OverlayKind) {
        self.overlay = overlay;
    }

    pub(crate) fn set_product_name(&mut self, name: &str) {
        self.product_name = name.into();
    }

    pub(crate) fn include_target_in_component_name(&mut self, include: bool) {
        self.include_target_in_component_name = include;
    }

    pub(crate) fn is_preview(&mut self, is: bool) {
        self.is_preview = is;
    }

    pub(crate) fn image_dir(&self) -> &Path {
        t!(std::fs::create_dir_all(&self.image_dir));
        &self.image_dir
    }

    pub(crate) fn add_file(&self, src: impl AsRef<Path>, destdir: impl AsRef<Path>, perms: u32) {
        // create_dir_all fails to create `foo/bar/.`, so when the destination is "." this simply
        // uses the base directory as the destination directory.
        let destdir = if destdir.as_ref() == Path::new(".") {
            self.image_dir.clone()
        } else {
            self.image_dir.join(destdir.as_ref())
        };

        t!(std::fs::create_dir_all(&destdir));
        self.builder.install(src.as_ref(), &destdir, perms);
    }

    pub(crate) fn add_renamed_file(
        &self,
        src: impl AsRef<Path>,
        destdir: impl AsRef<Path>,
        new_name: &str,
    ) {
        let destdir = self.image_dir.join(destdir.as_ref());
        t!(std::fs::create_dir_all(&destdir));
        self.builder.copy(src.as_ref(), &destdir.join(new_name));
    }

    pub(crate) fn add_legal_and_readme_to(&self, destdir: impl AsRef<Path>) {
        for file in self.overlay.legal_and_readme() {
            self.add_file(self.builder.src.join(file), destdir.as_ref(), 0o644);
        }
    }

    pub(crate) fn add_dir(&self, src: impl AsRef<Path>, dest: impl AsRef<Path>) {
        let dest = self.image_dir.join(dest.as_ref());

        t!(std::fs::create_dir_all(&dest));
        self.builder.cp_r(src.as_ref(), &dest);
    }

    pub(crate) fn persist_work_dir(&mut self) -> PathBuf {
        self.delete_temp_dir = false;
        self.temp_dir.clone()
    }

    pub(crate) fn generate(self) -> PathBuf {
        let mut component_name = self.component.clone();
        if self.is_preview {
            component_name.push_str("-preview");
        }
        if self.include_target_in_component_name {
            component_name.push('-');
            component_name.push_str(
                &self
                    .target
                    .as_ref()
                    .expect("include_target_in_component_name used in a targetless tarball"),
            );
        }

        self.run(|this, cmd| {
            cmd.arg("generate")
                .arg("--image-dir")
                .arg(&this.image_dir)
                .arg(format!("--component-name={}", &component_name));
            this.non_bare_args(cmd);
        })
    }

    pub(crate) fn combine(self, tarballs: &[PathBuf]) {
        let mut input_tarballs = tarballs[0].as_os_str().to_os_string();
        for tarball in &tarballs[1..] {
            input_tarballs.push(",");
            input_tarballs.push(tarball);
        }

        self.run(|this, cmd| {
            cmd.arg("combine").arg("--input-tarballs").arg(input_tarballs);
            this.non_bare_args(cmd);
        });
    }

    pub(crate) fn bare(self) -> PathBuf {
        // Bare tarballs should have the top level directory match the package
        // name, not "image". We rename the image directory just before passing
        // into rust-installer.
        let dest = self.temp_dir.join(self.package_name());
        t!(std::fs::rename(&self.image_dir, &dest));

        self.run(|this, cmd| {
            cmd.arg("tarball")
                .arg("--input")
                .arg(&dest)
                .arg("--output")
                .arg(crate::dist::distdir(this.builder).join(this.package_name()));
        })
    }

    fn package_name(&self) -> String {
        if let Some(target) = &self.target {
            format!("{}-{}", self.pkgname, target)
        } else {
            self.pkgname.clone()
        }
    }

    fn non_bare_args(&self, cmd: &mut Command) {
        cmd.arg("--rel-manifest-dir=rustlib")
            .arg("--legacy-manifest-dirs=rustlib,cargo")
            .arg(format!("--product-name={}", self.product_name))
            .arg(format!("--success-message={} installed.", self.component))
            .arg(format!("--package-name={}", self.package_name()))
            .arg("--non-installed-overlay")
            .arg(&self.overlay_dir)
            .arg("--output-dir")
            .arg(crate::dist::distdir(self.builder));
    }

    fn run(self, build_cli: impl FnOnce(&Tarball<'a>, &mut Command)) -> PathBuf {
        t!(std::fs::create_dir_all(&self.overlay_dir));
        self.builder.create(&self.overlay_dir.join("version"), &self.overlay.version(self.builder));
        if let Some(sha) = self.builder.rust_sha() {
            self.builder.create(&self.overlay_dir.join("git-commit-hash"), &sha);
        }
        for file in self.overlay.legal_and_readme() {
            self.builder.install(&self.builder.src.join(file), &self.overlay_dir, 0o644);
        }

        let mut cmd = self.builder.tool_cmd(crate::tool::Tool::RustInstaller);

        let package_name = self.package_name();
        self.builder.info(&format!("Dist {}", package_name));
        let _time = crate::util::timeit(self.builder);

        build_cli(&self, &mut cmd);
        cmd.arg("--work-dir").arg(&self.temp_dir);
        self.builder.run(&mut cmd);
        if self.delete_temp_dir {
            t!(std::fs::remove_dir_all(&self.temp_dir));
        }

        crate::dist::distdir(self.builder).join(format!("{}.tar.gz", package_name))
    }
}
<commit_msg>Rollup merge of #80434 - pietroalbini:tarball-temp-dir, r=Mark-Simulacrum<commit_after>use std::{
    path::{Path, PathBuf},
    process::Command,
};

use build_helper::t;

use crate::builder::Builder;

#[derive(Copy, Clone)]
pub(crate) enum OverlayKind {
    Rust,
    LLVM,
    Cargo,
    Clippy,
    Miri,
    Rustfmt,
    RLS,
    RustAnalyzer,
}

impl OverlayKind {
    fn legal_and_readme(&self) -> &[&str] {
        match self {
            OverlayKind::Rust => &["COPYRIGHT", "LICENSE-APACHE", "LICENSE-MIT", "README.md"],
            OverlayKind::LLVM => {
                &["src/llvm-project/llvm/LICENSE.TXT", "src/llvm-project/llvm/README.txt"]
            }
            OverlayKind::Cargo => &[
                "src/tools/cargo/README.md",
                "src/tools/cargo/LICENSE-MIT",
                "src/tools/cargo/LICENSE-APACHE",
                "src/tools/cargo/LICENSE-THIRD-PARTY",
            ],
            OverlayKind::Clippy => &[
                "src/tools/clippy/README.md",
                "src/tools/clippy/LICENSE-APACHE",
                "src/tools/clippy/LICENSE-MIT",
            ],
            OverlayKind::Miri => &[
                "src/tools/miri/README.md",
                "src/tools/miri/LICENSE-APACHE",
                "src/tools/miri/LICENSE-MIT",
            ],
            OverlayKind::Rustfmt => &[
                "src/tools/rustfmt/README.md",
                "src/tools/rustfmt/LICENSE-APACHE",
                "src/tools/rustfmt/LICENSE-MIT",
            ],
            OverlayKind::RLS => &[
                "src/tools/rls/README.md",
                "src/tools/rls/LICENSE-APACHE",
                "src/tools/rls/LICENSE-MIT",
            ],
            OverlayKind::RustAnalyzer => &[
                "src/tools/rust-analyzer/README.md",
                "src/tools/rust-analyzer/LICENSE-APACHE",
                "src/tools/rust-analyzer/LICENSE-MIT",
            ],
        }
    }

    fn version(&self, builder: &Builder<'_>) -> String {
        match self {
            OverlayKind::Rust => builder.rust_version(),
            OverlayKind::LLVM => builder.rust_version(),
            OverlayKind::Cargo => {
                builder.cargo_info.version(builder, &builder.release_num("cargo"))
            }
            OverlayKind::Clippy => {
                builder.clippy_info.version(builder, &builder.release_num("clippy"))
            }
            OverlayKind::Miri => builder.miri_info.version(builder, &builder.release_num("miri")),
            OverlayKind::Rustfmt => {
                builder.rustfmt_info.version(builder, &builder.release_num("rustfmt"))
            }
            OverlayKind::RLS => builder.rls_info.version(builder, &builder.release_num("rls")),
            OverlayKind::RustAnalyzer => builder
                .rust_analyzer_info
                .version(builder, &builder.release_num("rust-analyzer/crates/rust-analyzer")),
        }
    }
}

pub(crate) struct Tarball<'a> {
    builder: &'a Builder<'a>,

    pkgname: String,
    component: String,
    target: Option<String>,
    product_name: String,
    overlay: OverlayKind,

    temp_dir: PathBuf,
    image_dir: PathBuf,
    overlay_dir: PathBuf,

    include_target_in_component_name: bool,
    is_preview: bool,
    delete_temp_dir: bool,
}

impl<'a> Tarball<'a> {
    pub(crate) fn new(builder: &'a Builder<'a>, component: &str, target: &str) -> Self {
        Self::new_inner(builder, component, Some(target.into()))
    }

    pub(crate) fn new_targetless(builder: &'a Builder<'a>, component: &str) -> Self {
        Self::new_inner(builder, component, None)
    }

    fn new_inner(builder: &'a Builder<'a>, component: &str, target: Option<String>) -> Self {
        let pkgname = crate::dist::pkgname(builder, component);

        let mut temp_dir = builder.out.join("tmp").join("tarball").join(component);
        if let Some(target) = &target {
            temp_dir = temp_dir.join(target);
        }
        let _ = std::fs::remove_dir_all(&temp_dir);

        let image_dir = temp_dir.join("image");
        let overlay_dir = temp_dir.join("overlay");

        Self {
            builder,

            pkgname,
            component: component.into(),
            target,
            product_name: "Rust".into(),
            overlay: OverlayKind::Rust,

            temp_dir,
            image_dir,
            overlay_dir,

            include_target_in_component_name: false,
            is_preview: false,
            delete_temp_dir: true,
        }
    }

    pub(crate) fn set_overlay(&mut self, overlay: OverlayKind) {
        self.overlay = overlay;
    }

    pub(crate) fn set_product_name(&mut self, name: &str) {
        self.product_name = name.into();
    }

    pub(crate) fn include_target_in_component_name(&mut self, include: bool) {
        self.include_target_in_component_name = include;
    }

    pub(crate) fn is_preview(&mut self, is: bool) {
        self.is_preview = is;
    }

    pub(crate) fn image_dir(&self) -> &Path {
        t!(std::fs::create_dir_all(&self.image_dir));
        &self.image_dir
    }

    pub(crate) fn add_file(&self, src: impl AsRef<Path>, destdir: impl AsRef<Path>, perms: u32) {
        // create_dir_all fails to create `foo/bar/.`, so when the destination is "." this simply
        // uses the base directory as the destination directory.
        let destdir = if destdir.as_ref() == Path::new(".") {
            self.image_dir.clone()
        } else {
            self.image_dir.join(destdir.as_ref())
        };

        t!(std::fs::create_dir_all(&destdir));
        self.builder.install(src.as_ref(), &destdir, perms);
    }

    pub(crate) fn add_renamed_file(
        &self,
        src: impl AsRef<Path>,
        destdir: impl AsRef<Path>,
        new_name: &str,
    ) {
        let destdir = self.image_dir.join(destdir.as_ref());
        t!(std::fs::create_dir_all(&destdir));
        self.builder.copy(src.as_ref(), &destdir.join(new_name));
    }

    pub(crate) fn add_legal_and_readme_to(&self, destdir: impl AsRef<Path>) {
        for file in self.overlay.legal_and_readme() {
            self.add_file(self.builder.src.join(file), destdir.as_ref(), 0o644);
        }
    }

    pub(crate) fn add_dir(&self, src: impl AsRef<Path>, dest: impl AsRef<Path>) {
        let dest = self.image_dir.join(dest.as_ref());

        t!(std::fs::create_dir_all(&dest));
        self.builder.cp_r(src.as_ref(), &dest);
    }

    pub(crate) fn persist_work_dir(&mut self) -> PathBuf {
        self.delete_temp_dir = false;
        self.temp_dir.clone()
    }

    pub(crate) fn generate(self) -> PathBuf {
        let mut component_name = self.component.clone();
        if self.is_preview {
            component_name.push_str("-preview");
        }
        if self.include_target_in_component_name {
            component_name.push('-');
            component_name.push_str(
                &self
                    .target
                    .as_ref()
                    .expect("include_target_in_component_name used in a targetless tarball"),
            );
        }

        self.run(|this, cmd| {
            cmd.arg("generate")
                .arg("--image-dir")
                .arg(&this.image_dir)
                .arg(format!("--component-name={}", &component_name));
            this.non_bare_args(cmd);
        })
    }

    pub(crate) fn combine(self, tarballs: &[PathBuf]) {
        let mut input_tarballs = tarballs[0].as_os_str().to_os_string();
        for tarball in &tarballs[1..] {
            input_tarballs.push(",");
            input_tarballs.push(tarball);
        }

        self.run(|this, cmd| {
            cmd.arg("combine").arg("--input-tarballs").arg(input_tarballs);
            this.non_bare_args(cmd);
        });
    }

    pub(crate) fn bare(self) -> PathBuf {
        // Bare tarballs should have the top level directory match the package
        // name, not "image". We rename the image directory just before passing
        // into rust-installer.
        let dest = self.temp_dir.join(self.package_name());
        t!(std::fs::rename(&self.image_dir, &dest));

        self.run(|this, cmd| {
            cmd.arg("tarball")
                .arg("--input")
                .arg(&dest)
                .arg("--output")
                .arg(crate::dist::distdir(this.builder).join(this.package_name()));
        })
    }

    fn package_name(&self) -> String {
        if let Some(target) = &self.target {
            format!("{}-{}", self.pkgname, target)
        } else {
            self.pkgname.clone()
        }
    }

    fn non_bare_args(&self, cmd: &mut Command) {
        cmd.arg("--rel-manifest-dir=rustlib")
            .arg("--legacy-manifest-dirs=rustlib,cargo")
            .arg(format!("--product-name={}", self.product_name))
            .arg(format!("--success-message={} installed.", self.component))
            .arg(format!("--package-name={}", self.package_name()))
            .arg("--non-installed-overlay")
            .arg(&self.overlay_dir)
            .arg("--output-dir")
            .arg(crate::dist::distdir(self.builder));
    }

    fn run(self, build_cli: impl FnOnce(&Tarball<'a>, &mut Command)) -> PathBuf {
        t!(std::fs::create_dir_all(&self.overlay_dir));
        self.builder.create(&self.overlay_dir.join("version"), &self.overlay.version(self.builder));
        if let Some(sha) = self.builder.rust_sha() {
            self.builder.create(&self.overlay_dir.join("git-commit-hash"), &sha);
        }
        for file in self.overlay.legal_and_readme() {
            self.builder.install(&self.builder.src.join(file), &self.overlay_dir, 0o644);
        }

        let mut cmd = self.builder.tool_cmd(crate::tool::Tool::RustInstaller);

        let package_name = self.package_name();
        self.builder.info(&format!("Dist {}", package_name));
        let _time = crate::util::timeit(self.builder);

        build_cli(&self, &mut cmd);
        cmd.arg("--work-dir").arg(&self.temp_dir);
        self.builder.run(&mut cmd);
        if self.delete_temp_dir {
            t!(std::fs::remove_dir_all(&self.temp_dir));
        }

        crate::dist::distdir(self.builder).join(format!("{}.tar.gz", package_name))
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate log;

extern crate pretty_env_logger;

extern crate clap;
extern crate cabot;

use std::fmt::Arguments;
use std::fs::OpenOptions;
use std::io::{self, Write, stderr};

use log::LogLevel::Info;

use clap::{App, Arg};

use cabot::results::{CabotResult, CabotError};
use cabot::http;
use cabot::request::RequestBuilder;

const VERSION: &'static str = "0.1.0";

pub fn run() -> CabotResult<()> {
    let matches = App::new("cabot")
        .version(VERSION)
        .author("Guillaume Gauvrit <guillaume@gauvr.it>")
        .about("http(s) client")
        .arg(Arg::with_name("URL")
            .index(1)
            .required(true)
            .help("URL to request"))
        .arg(Arg::with_name("REQUEST")
            .short("X")
            .long("request")
            .default_value("GET")
            .help("Specify request command to use"))
        .arg(Arg::with_name("LINE")
            .short("H")
            .long("header")
            .takes_value(true)
            .multiple(true)
            .help("Pass custom header LINE to server"))
        .arg(Arg::with_name("FILE")
            .short("o")
            .long("output")
            .takes_value(true)
            .help("Write to FILE instead of stdout"))
        .arg(Arg::with_name("VERBOSE")
            .short("v")
            .long("verbose")
            .help("Make the operation more talkative"))
        .arg(Arg::with_name("BODY")
            .short("d")
            .long("data")
            .takes_value(true)
            .help("Post Data (Using utf-8 encoding)"))

        .get_matches();

    let url = matches.value_of("URL").unwrap();
    let http_method = matches.value_of("REQUEST").unwrap();
    let verbose = matches.is_present("VERBOSE");
    let body = matches.value_of("BODY");

    let headers: Vec<&str> = match matches.values_of("LINE") {
        Some(headers) => headers.collect(),
        None => Vec::new(),
    };

    let mut builder = RequestBuilder::new(url)
        .set_http_method(http_method)
        .add_headers(&headers.as_slice());

    if body.is_some() {
        builder = builder.set_body_as_str(body.unwrap());
    }

    let request = builder.build()?;

    if let Some(path) = matches.value_of("FILE") {
        let mut f = OpenOptions::new()
            .write(true)
            .create(true)
            .truncate(true)
            .open(path).unwrap();
        http::http_query(&request, &mut CabotBinWrite::new(&mut f, verbose), verbose)?;
    } else {
        http::http_query(&request, &mut CabotBinWrite::new(&mut io::stdout(), verbose), verbose)?;
    };

    Ok(())
}

fn main() {
    pretty_env_logger::init().unwrap();
    debug!("Starting cabot");
    match run() {
        Ok(()) => {
            debug!("Command cabot ended succesfully");
        }
        Err(CabotError::SchemeError(scheme)) => {
            let _ = writeln!(&mut std::io::stderr(), "Unamanaged scheme: {}", scheme);
            std::process::exit(1);
        }
        Err(CabotError::OpaqueUrlError(err)) => {
            let _ = writeln!(&mut std::io::stderr(), "Opaque URL Error: {}", err);
            std::process::exit(1);
        }
        Err(CabotError::UrlParseError(err)) => {
            let _ = writeln!(&mut std::io::stderr(), "URL Parse Error: {}", err);
            std::process::exit(1);
        }
        Err(CabotError::IOError(err)) => {
            let _ = writeln!(&mut std::io::stderr(), "IOError: {}", err);
            std::process::exit(1);
        }
        Err(CabotError::CertificateError(err)) => {
            let _ = writeln!(&mut std::io::stderr(), "CertificateError: {}", err);
            std::process::exit(1);
        }
        // Unexpexcted Error, not used
        Err(CabotError::HttpResponseParseError(_)) => {
            let _ = writeln!(&mut std::io::stderr(), "Unexpected error");
            std::process::exit(1);
        }
        Err(CabotError::EncodingError(_)) => {
            let _ = writeln!(&mut std::io::stderr(), "Unexpected error");
            std::process::exit(1);
        }
    }
}

// Internal Of the Binary

struct CabotBinWrite<'a> {
    out: &'a mut Write,
    verbose: bool,
}

impl<'a> CabotBinWrite<'a> {
    pub fn new(out: &'a mut Write, verbose: bool) -> Self {
        CabotBinWrite{ out: out, verbose: verbose}
    }
}

impl<'a> Write for CabotBinWrite<'a> {

    fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {

        let response = String::from_utf8_lossy(buf);
        let response: Vec<&str> = response.splitn(2, "\r\n\r\n").collect();

        // If there is headers and we logged them
        if response.len() == 2 && (log_enabled!(Info) || self.verbose) {
            let headers = response.get(0).unwrap();
            let split = headers.split("\n");
            if log_enabled!(Info) {
                for part in split {
                    info!("< {}", part);
                }
            }
            else if self.verbose {
                for part in split {
                    writeln!(&mut stderr(), "< {}", part).unwrap();
                }
            }
        }

        let body = if response.len() == 2 {
            response.get(1).unwrap()
        } else {
            response.get(0).unwrap()
        };

        if log_enabled!(Info) {
            info!("< [[{} bytes]]", body.len());
        }
        else if self.verbose {
            writeln!(&mut stderr(), "< [[{} bytes]]", body.len()).unwrap();
        }

        self.out.write_all(body.as_bytes()).unwrap();
        Ok(())
    }

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

    // Don't implemented unused method

    fn write(&mut self, _: &[u8]) -> io::Result<usize> {
        Err(io::Error::new(io::ErrorKind::Other, "Not Implemented"))
    }

    fn write_fmt(&mut self, _: Arguments) -> io::Result<()> {
        Err(io::Error::new(io::ErrorKind::Other, "Not Implemented"))
    }

}
<commit_msg>Rename argname for header<commit_after>#[macro_use]
extern crate log;

extern crate pretty_env_logger;

extern crate clap;
extern crate cabot;

use std::fmt::Arguments;
use std::fs::OpenOptions;
use std::io::{self, Write, stderr};

use log::LogLevel::Info;

use clap::{App, Arg};

use cabot::results::{CabotResult, CabotError};
use cabot::http;
use cabot::request::RequestBuilder;

const VERSION: &'static str = "0.1.0";

pub fn run() -> CabotResult<()> {
    let matches = App::new("cabot")
        .version(VERSION)
        .author("Guillaume Gauvrit <guillaume@gauvr.it>")
        .about("http(s) client")

        .arg(Arg::with_name("URL")
            .index(1)
            .required(true)
            .help("URL to request"))

        .arg(Arg::with_name("REQUEST")
            .short("X")
            .long("request")
            .default_value("GET")
            .help("Specify request command to use"))

        .arg(Arg::with_name("HEADER")
            .short("H")
            .long("header")
            .takes_value(true)
            .multiple(true)
            .help("Pass custom header to server"))

        .arg(Arg::with_name("FILE")
            .short("o")
            .long("output")
            .takes_value(true)
            .help("Write to FILE instead of stdout"))

        .arg(Arg::with_name("VERBOSE")
            .short("v")
            .long("verbose")
            .help("Make the operation more talkative"))

        .arg(Arg::with_name("BODY")
            .short("d")
            .long("data")
            .takes_value(true)
            .help("Post Data (Using utf-8 encoding)"))

        .get_matches();

    let url = matches.value_of("URL").unwrap();
    let http_method = matches.value_of("REQUEST").unwrap();
    let verbose = matches.is_present("VERBOSE");
    let body = matches.value_of("BODY");

    let headers: Vec<&str> = match matches.values_of("HEADER") {
        Some(headers) => headers.collect(),
        None => Vec::new(),
    };

    let mut builder = RequestBuilder::new(url)
        .set_http_method(http_method)
        .add_headers(&headers.as_slice());

    if body.is_some() {
        builder = builder.set_body_as_str(body.unwrap());
    }

    let request = builder.build()?;

    if let Some(path) = matches.value_of("FILE") {
        let mut f = OpenOptions::new()
            .write(true)
            .create(true)
            .truncate(true)
            .open(path).unwrap();
        http::http_query(&request, &mut CabotBinWrite::new(&mut f, verbose), verbose)?;
    } else {
        http::http_query(&request, &mut CabotBinWrite::new(&mut io::stdout(), verbose), verbose)?;
    };

    Ok(())
}

fn main() {
    pretty_env_logger::init().unwrap();
    debug!("Starting cabot");
    match run() {
        Ok(()) => {
            debug!("Command cabot ended succesfully");
        }
        Err(CabotError::SchemeError(scheme)) => {
            let _ = writeln!(&mut std::io::stderr(), "Unamanaged scheme: {}", scheme);
            std::process::exit(1);
        }
        Err(CabotError::OpaqueUrlError(err)) => {
            let _ = writeln!(&mut std::io::stderr(), "Opaque URL Error: {}", err);
            std::process::exit(1);
        }
        Err(CabotError::UrlParseError(err)) => {
            let _ = writeln!(&mut std::io::stderr(), "URL Parse Error: {}", err);
            std::process::exit(1);
        }
        Err(CabotError::IOError(err)) => {
            let _ = writeln!(&mut std::io::stderr(), "IOError: {}", err);
            std::process::exit(1);
        }
        Err(CabotError::CertificateError(err)) => {
            let _ = writeln!(&mut std::io::stderr(), "CertificateError: {}", err);
            std::process::exit(1);
        }
        // Unexpexcted Error, not used
        Err(CabotError::HttpResponseParseError(_)) => {
            let _ = writeln!(&mut std::io::stderr(), "Unexpected error");
            std::process::exit(1);
        }
        Err(CabotError::EncodingError(_)) => {
            let _ = writeln!(&mut std::io::stderr(), "Unexpected error");
            std::process::exit(1);
        }
    }
}

// Internal Of the Binary

struct CabotBinWrite<'a> {
    out: &'a mut Write,
    verbose: bool,
}

impl<'a> CabotBinWrite<'a> {
    pub fn new(out: &'a mut Write, verbose: bool) -> Self {
        CabotBinWrite{ out: out, verbose: verbose}
    }
}

impl<'a> Write for CabotBinWrite<'a> {

    fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {

        let response = String::from_utf8_lossy(buf);
        let response: Vec<&str> = response.splitn(2, "\r\n\r\n").collect();

        // If there is headers and we logged them
        if response.len() == 2 && (log_enabled!(Info) || self.verbose) {
            let headers = response.get(0).unwrap();
            let split = headers.split("\n");
            if log_enabled!(Info) {
                for part in split {
                    info!("< {}", part);
                }
            }
            else if self.verbose {
                for part in split {
                    writeln!(&mut stderr(), "< {}", part).unwrap();
                }
            }
        }

        let body = if response.len() == 2 {
            response.get(1).unwrap()
        } else {
            response.get(0).unwrap()
        };

        if log_enabled!(Info) {
            info!("< [[{} bytes]]", body.len());
        }
        else if self.verbose {
            writeln!(&mut stderr(), "< [[{} bytes]]", body.len()).unwrap();
        }

        self.out.write_all(body.as_bytes()).unwrap();
        Ok(())
    }

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

    // Don't implemented unused method

    fn write(&mut self, _: &[u8]) -> io::Result<usize> {
        Err(io::Error::new(io::ErrorKind::Other, "Not Implemented"))
    }

    fn write_fmt(&mut self, _: Arguments) -> io::Result<()> {
        Err(io::Error::new(io::ErrorKind::Other, "Not Implemented"))
    }

}
<|endoftext|>


<commit_before>use std::ops::Not;

#[derive(PartialEq, Debug)]
pub enum Bit { Zero, One }

impl Bit {
    pub fn to_i32(&self) -> i32 {
        match *self {
            Bit::Zero => 0,
            Bit::One => 1
        }
    }

    pub fn to_u32(&self) -> u32 {
        match *self {
            Bit::Zero => 0u32,
            Bit::One => 1u32
        }
    }
}

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

    #[test]
    fn to_i32_zero_test() {
        assert_eq!(Bit::Zero.to_i32(), 0);
    }

    #[test]
    fn to_i32_one_test() {
        assert_eq!(Bit::One.to_i32(), 1);
    }
}

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

    #[test]
    fn to_u32_zero_test() {
        assert_eq!(Bit::Zero.to_u32(), 0);
    }

    #[test]
    fn to_u32_one_test() {
        assert_eq!(Bit::One.to_u32(), 1);
    }
}

impl Not for Bit {
    type Output = Bit;

    fn not(self) -> Bit {
        match self {
            Bit::Zero => Bit::One,
            Bit::One => Bit::Zero
        }
    }
}

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

    #[test]
    fn not_zero_test() {
        assert_eq!(!Bit::Zero, Bit::One);
    }

    #[test]
    fn not_one_test() {
        assert_eq!(!Bit::One, Bit::Zero);
    }
}
<commit_msg>Implemented bitwise & operator for Bit<commit_after>use std::ops::Not;
use std::ops::BitAnd;

#[derive(PartialEq, Debug)]
pub enum Bit { Zero, One }

impl Bit {
    pub fn to_i32(&self) -> i32 {
        match *self {
            Bit::Zero => 0,
            Bit::One => 1
        }
    }

    pub fn to_u32(&self) -> u32 {
        match *self {
            Bit::Zero => 0u32,
            Bit::One => 1u32
        }
    }
}

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

    #[test]
    fn to_i32_zero_test() {
        assert_eq!(Bit::Zero.to_i32(), 0);
    }

    #[test]
    fn to_i32_one_test() {
        assert_eq!(Bit::One.to_i32(), 1);
    }
}

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

    #[test]
    fn to_u32_zero_test() {
        assert_eq!(Bit::Zero.to_u32(), 0);
    }

    #[test]
    fn to_u32_one_test() {
        assert_eq!(Bit::One.to_u32(), 1);
    }
}

impl Not for Bit {
    type Output = Bit;

    fn not(self) -> Bit {
        match self {
            Bit::Zero => Bit::One,
            Bit::One => Bit::Zero
        }
    }
}

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

    #[test]
    fn not_zero_test() {
        assert_eq!(!Bit::Zero, Bit::One);
    }

    #[test]
    fn not_one_test() {
        assert_eq!(!Bit::One, Bit::Zero);
    }
}

impl BitAnd for Bit {
    type Output = Bit;

    fn bitand(self, rhs: Bit) -> Bit {
        match (self, rhs) {
            (Bit::Zero, Bit::Zero) => Bit::Zero,
            (Bit::Zero, Bit::One) => Bit::Zero,
            (Bit::One, Bit::Zero) => Bit::Zero,
            (Bit::One, Bit::One) => Bit::One
        }
    }
}

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

    #[test]
    fn bitwise_and_zero_zero_test() {
        assert_eq!(Bit::Zero & Bit::Zero, Bit::Zero);
    }

    #[test]
    fn bitwise_and_zero_one_test() {
        assert_eq!(Bit::Zero & Bit::One, Bit::Zero);
    }

    #[test]
    fn bitwise_and_one_zero_test() {
        assert_eq!(Bit::One & Bit::Zero, Bit::Zero);
    }

    #[test]
    fn bitwise_and_one_one_test() {
        assert_eq!(Bit::One & Bit::One, Bit::One);
    }
}
<|endoftext|>


<commit_before>mod document_spec;

use futures::future::{Future, ok, err, result};
use futures::sync::oneshot;
use hyper;
use hyper::{Get, Post, Head, StatusCode};
use hyper::server::{Request, Response, Service, NewService};
use hyper::header::{Authorization, Bearer};
use serde_json;
use slog;
use std::marker::PhantomData;
use tokio_core::reactor::Remote;

use http::*;
use error::{Error, ErrorKind};
pub use self::document_spec::{DocumentSpec, PapersUri};
use renderer::Renderer;
use config::Config;

// TODO: write a request logger middleware instead to avoid repetition
pub fn log_request(logger: &slog::Logger, req: &Request) {
    info!(
        logger,
        "{} {} IP={:?}",
        req.method(),
        req.path(),
        req.remote_addr(),
    );
}

pub struct Papers<R>
    where R: Renderer
{
    remote: Remote,
    config: &'static Config,
    _renderer: PhantomData<R>,
}

impl<R: Renderer> Papers<R> {
    pub fn new(remote: Remote, config: &'static Config) -> Papers<R> {
        Papers {
            remote,
            config,
            _renderer: PhantomData,
        }
    }

    // Check Authorization header if `PAPERS_BEARER` env var is set
    fn check_auth_header(&self, req: &Request) -> Result<(), Error> {
        let headers = req.headers().clone();
        let authorization = headers.get::<Authorization<Bearer>>();
        match authorization {
            Some(header_bearer) => {
                if self.config.auth != "" && header_bearer.token != self.config.auth {
                    return Err(Error::from_kind(ErrorKind::Forbidden));
                }
            }
            None => {
                if self.config.auth != "" {
                    return Err(Error::from_kind(ErrorKind::Forbidden));
                }
            }
        }
        Ok(())
    }

    fn submit(&self, req: Request) -> Box<Future<Item = Response, Error = Error>> {
        log_request(&self.config.logger, &req);
        debug!(self.config.logger, "{:#?}", req);

        if let Err(error) = self.check_auth_header(&req) {
            return Box::new(err(error));
        }

        if !req.has_content_type(mime!(Application / Json)) {
            return Box::new(err(ErrorKind::UnprocessableEntity.into()));
        }

        let body = req.get_body_bytes();

        let document_spec = body.and_then(|body| {
            result(serde_json::from_slice::<DocumentSpec>(body.as_slice())
                       .map_err(|err| Error::with_chain(err, ErrorKind::UnprocessableEntity)))
        });

        let logger = self.config.logger.clone();
        let max_assets_per_document = self.config.max_assets_per_document;
        let document_spec = document_spec.and_then(move |spec| {
            if spec.assets_urls.len() > max_assets_per_document as usize {
                error!(
                    logger,
                    "Assets URLs length exceeds the maximum ({}).\
                    To change it set PAPERS_MAX_ASSETS_PER_DOCUMENT",
                    max_assets_per_document,
                );
                return err(ErrorKind::UnprocessableEntity.into());
            }
            ok(spec)
        });

        let response = {
            let config = self.config;
            let remote = self.remote.clone();
            document_spec.and_then(move |document_spec| {
                                       remote.spawn(move |handle| {
                                                        R::new(config, handle).render(document_spec)
                                                    });
                                       ok(Response::new().with_status(StatusCode::Ok))
                                   })
        };

        Box::new(response)
    }

    fn preview(&self, req: Request) -> Box<Future<Item = Response, Error = Error>> {
        log_request(&self.config.logger, &req);
        debug!(self.config.logger, "{:#?}", req);

        if let Err(error) = self.check_auth_header(&req) {
            return Box::new(err(error));
        }

        if !req.has_content_type(mime!(Application / Json)) {
            return Box::new(err(ErrorKind::UnprocessableEntity.into()));
        }

        let body = req.get_body_bytes();
        let document_spec = body.and_then(|body| {
            result(serde_json::from_slice::<DocumentSpec>(body.as_slice())
                       .map_err(|_| ErrorKind::UnprocessableEntity.into()))
        });

        let preview = {
            let remote = self.remote.clone();
            let config = self.config;
            let (sender, receiver) = oneshot::channel();
            document_spec
                .and_then(move |document_spec| {
                              remote.spawn(move |handle| {
                                               R::new(config, handle).preview(document_spec, sender)
                                           });
                              ok(())
                          })
                .and_then(move |_| receiver.map_err(|err| panic!(err)))
                .flatten()
        };

        let response = preview.and_then(|populated_template| {
                                            ok(Response::new()
                                                   .with_status(StatusCode::Ok)
                                                   .with_body(populated_template))
                                        });

        Box::new(response)

    }

    fn health_check(&self, req: Request) -> Box<Future<Item = Response, Error = Error>> {
        log_request(&self.config.logger, &req);
        Box::new(ok(Response::new().with_status(StatusCode::Ok)))
    }
}

impl<R: Renderer> Service for Papers<R> {
    type Request = Request;
    type Response = Response;
    type Error = hyper::Error;
    type Future = Box<Future<Item = Response, Error = hyper::Error>>;

    fn call(&self, req: Self::Request) -> Self::Future {
        let response = match (req.method(), req.path()) {
                (&Get, "/healthz") |
                (&Head, "/healthz") => self.health_check(req),
                (&Post, "/preview") => self.preview(req),
                (&Post, "/submit") => self.submit(req),
                _ => {
                    log_request(&self.config.logger, &req);
                    Box::new(ok(Response::new().with_status(StatusCode::NotFound)))
                }
            }
            .then(|handler_result| match handler_result {
                      Ok(response) => ok(response),
                      Err(err) => ok(err.into_response()),
                  });

        Box::new(response)
    }
}

impl<R: Renderer> NewService for Papers<R> {
    type Request = Request;
    type Response = Response;
    type Error = hyper::Error;
    type Instance = Papers<R>;

    fn new_service(&self) -> Result<Self::Instance, ::std::io::Error> {
        Ok(Papers {
               remote: self.remote.clone(),
               config: self.config,
               _renderer: PhantomData,
           })
    }
}
<commit_msg>🔨 Refactor log_request<commit_after>mod document_spec;

use futures::future::{Future, ok, err, result};
use futures::sync::oneshot;
use hyper;
use hyper::{Get, Post, Head, StatusCode};
use hyper::server::{Request, Response, Service, NewService};
use hyper::header::{Authorization, Bearer};
use serde_json;
use slog;
use std::marker::PhantomData;
use tokio_core::reactor::Remote;

use http::*;
use error::{Error, ErrorKind};
pub use self::document_spec::{DocumentSpec, PapersUri};
use renderer::Renderer;
use config::Config;

pub fn log_request(logger: &slog::Logger, req: &Request) {
    info!(
        logger,
        "{} {} IP={:?}",
        req.method(),
        req.path(),
        req.remote_addr(),
    );
}

pub struct Papers<R>
    where R: Renderer
{
    remote: Remote,
    config: &'static Config,
    _renderer: PhantomData<R>,
}

impl<R: Renderer> Papers<R> {
    pub fn new(remote: Remote, config: &'static Config) -> Papers<R> {
        Papers {
            remote,
            config,
            _renderer: PhantomData,
        }
    }

    // Check Authorization header if `PAPERS_BEARER` env var is set
    fn check_auth_header(&self, req: &Request) -> Result<(), Error> {
        let headers = req.headers().clone();
        let authorization = headers.get::<Authorization<Bearer>>();
        match authorization {
            Some(header_bearer) => {
                if self.config.auth != "" && header_bearer.token != self.config.auth {
                    return Err(Error::from_kind(ErrorKind::Forbidden));
                }
            }
            None => {
                if self.config.auth != "" {
                    return Err(Error::from_kind(ErrorKind::Forbidden));
                }
            }
        }
        Ok(())
    }

    fn submit(&self, req: Request) -> Box<Future<Item = Response, Error = Error>> {
        debug!(self.config.logger, "{:#?}", req);

        if let Err(error) = self.check_auth_header(&req) {
            return Box::new(err(error));
        }

        if !req.has_content_type(mime!(Application / Json)) {
            return Box::new(err(ErrorKind::UnprocessableEntity.into()));
        }

        let body = req.get_body_bytes();

        let document_spec = body.and_then(|body| {
            result(serde_json::from_slice::<DocumentSpec>(body.as_slice())
                       .map_err(|err| Error::with_chain(err, ErrorKind::UnprocessableEntity)))
        });

        let logger = self.config.logger.clone();
        let max_assets_per_document = self.config.max_assets_per_document;
        let document_spec = document_spec.and_then(move |spec| {
            if spec.assets_urls.len() > max_assets_per_document as usize {
                error!(
                    logger,
                    "Assets URLs length exceeds the maximum ({}).\
                    To change it set PAPERS_MAX_ASSETS_PER_DOCUMENT",
                    max_assets_per_document,
                );
                return err(ErrorKind::UnprocessableEntity.into());
            }
            ok(spec)
        });

        let response = {
            let config = self.config;
            let remote = self.remote.clone();
            document_spec.and_then(move |document_spec| {
                                       remote.spawn(move |handle| {
                                                        R::new(config, handle).render(document_spec)
                                                    });
                                       ok(Response::new().with_status(StatusCode::Ok))
                                   })
        };

        Box::new(response)
    }

    fn preview(&self, req: Request) -> Box<Future<Item = Response, Error = Error>> {
        debug!(self.config.logger, "{:#?}", req);

        if let Err(error) = self.check_auth_header(&req) {
            return Box::new(err(error));
        }

        if !req.has_content_type(mime!(Application / Json)) {
            return Box::new(err(ErrorKind::UnprocessableEntity.into()));
        }

        let body = req.get_body_bytes();
        let document_spec = body.and_then(|body| {
            result(serde_json::from_slice::<DocumentSpec>(body.as_slice())
                       .map_err(|_| ErrorKind::UnprocessableEntity.into()))
        });

        let preview = {
            let remote = self.remote.clone();
            let config = self.config;
            let (sender, receiver) = oneshot::channel();
            document_spec
                .and_then(move |document_spec| {
                              remote.spawn(move |handle| {
                                               R::new(config, handle).preview(document_spec, sender)
                                           });
                              ok(())
                          })
                .and_then(move |_| receiver.map_err(|err| panic!(err)))
                .flatten()
        };

        let response = preview.and_then(|populated_template| {
                                            ok(Response::new()
                                                   .with_status(StatusCode::Ok)
                                                   .with_body(populated_template))
                                        });

        Box::new(response)

    }

    fn health_check(&self, _: Request) -> Box<Future<Item = Response, Error = Error>> {
        Box::new(ok(Response::new().with_status(StatusCode::Ok)))
    }
}

impl<R: Renderer> Service for Papers<R> {
    type Request = Request;
    type Response = Response;
    type Error = hyper::Error;
    type Future = Box<Future<Item = Response, Error = hyper::Error>>;

    fn call(&self, req: Self::Request) -> Self::Future {
        log_request(&self.config.logger, &req);
        let response = match (req.method(), req.path()) {
                (&Get, "/healthz") |
                (&Head, "/healthz") => self.health_check(req),
                (&Post, "/preview") => self.preview(req),
                (&Post, "/submit") => self.submit(req),
                _ => {
                    Box::new(ok(Response::new().with_status(StatusCode::NotFound)))
                }
            }
            .then(|handler_result| match handler_result {
                      Ok(response) => ok(response),
                      Err(err) => ok(err.into_response()),
                  });

        Box::new(response)
    }
}

impl<R: Renderer> NewService for Papers<R> {
    type Request = Request;
    type Response = Response;
    type Error = hyper::Error;
    type Instance = Papers<R>;

    fn new_service(&self) -> Result<Self::Instance, ::std::io::Error> {
        Ok(Papers {
               remote: self.remote.clone(),
               config: self.config,
               _renderer: PhantomData,
           })
    }
}
<|endoftext|>


<commit_before>use std::time::{Duration, SystemTime, UNIX_EPOCH};
use std::collections::{BinaryHeap, VecDeque};
use std::cmp::Ordering;
use std::thread;
use std::sync::{Arc, Mutex, Condvar};
use std::io;

use {Registration, Ready, Evented, Poll, Token, PollOpt};

#[derive(Debug, Clone, Default, Eq)]
pub struct Timespec {
    pub interval: Duration,
    pub value: Duration
}

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

impl Ord for Timespec {
    fn cmp(&self, other: &Timespec) -> Ordering {
        self.value.cmp(&other.value)
    }
}

impl PartialEq for Timespec {
    fn eq(&self, other: &Timespec) -> bool {
        self.value == other.value
    }
}

#[derive(Clone, Debug)]
pub struct Task<T> {
    pub data: T,
    pub timespec: Timespec
}

impl<T> PartialOrd for Task<T> {
    fn partial_cmp(&self, other: &Task<T>) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl<T> Ord for Task<T> {
    fn cmp(&self, other: &Task<T>) -> Ordering {
        match self.timespec.cmp(&other.timespec) {
            Ordering::Equal => Ordering::Equal,
            Ordering::Greater => Ordering::Less,
            Ordering::Less => Ordering::Greater
        }
    }
}

impl<T> PartialEq for Task<T> {
    fn eq(&self, other: &Task<T>) -> bool {
        self.timespec == other.timespec
    }
}

impl<T> Eq for Task<T> {}

pub struct Timer<T: Clone> {
    thread_handle: thread::JoinHandle<()>,
    inner: Arc<TimerInner<T>>
}

struct TimerInner<T> {
    tasks: Mutex<BinaryHeap<Task<T>>>,
    queue: Mutex<VecDeque<Task<T>>>,
    registration: Registration,
    condvar: Condvar
}

impl<T> Timer<T> where T: Clone + Send + PartialEq + 'static {
    pub fn new() -> Timer<T> {

        let inner = Arc::new(TimerInner {
            tasks: Mutex::new(BinaryHeap::new()),
            queue: Mutex::new(VecDeque::new()),
            registration: Registration::new().unwrap(),
            condvar: Condvar::new()
        });

        let inner2 = inner.clone();

        let thread_handle = thread::Builder::new().name("timer".to_owned()).spawn(move || {
            let inner = inner2;

            loop {
                let mut sleep_duration = Duration::from_secs(60);

                loop {
                    let mut tasks = inner.tasks.lock().unwrap();

                    if let Some(ref task) = tasks.peek().map(|v| v.to_owned()) {
                        let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();

                        if task.timespec.value > now {
                            sleep_duration = task.timespec.value - now;
                            break;
                        } else {
                            if let Some(mut task) = tasks.pop() {
                                let mut queue = inner.queue.lock().unwrap();
                                queue.push_back(task.clone());

                                if task.timespec.interval != Duration::default() {
                                    task.timespec.value += task.timespec.interval;
                                    tasks.push(task);
                                }

                                inner.condvar.notify_one();
                                inner.registration.set_readiness(Ready::readable()).unwrap();
                            }
                        }
                    } else {
                        break;
                    }
                }

                thread::park_timeout(sleep_duration);
            }
        }).unwrap();

        Timer {
            thread_handle,
            inner
        }
    }

    pub fn insert(&self, task: Task<T>) {
        let mut tasks = self.inner.tasks.lock().unwrap();
        tasks.push(task);
        self.thread_handle.thread().unpark();
    }

    pub fn remove(&mut self, token: T) {
        let mut tasks = self.inner.tasks.lock().unwrap();

        let mut tasks_vec: Vec<Task<T>> = Vec::from(tasks.clone());

        if let Some(pos) = tasks_vec.iter().position(|x| x.data == token) {
            tasks_vec.remove(pos);
        }

        *tasks = tasks_vec.into();
    }

    pub fn pop(&self) -> Task<T> {
        let mut queue = self.inner.queue.lock().unwrap();

        loop {
            if let Some(task) = queue.pop_front() {
                return task;
            }

            queue = self.inner.condvar.wait(queue).unwrap();
        }
    }

    pub fn try_pop(&self) -> io::Result<Option<Task<T>>> {
        let mut queue = self.inner.queue.lock().unwrap();

        println!("{:?}", queue.len());

        if queue.len() <= 1 {
            self.inner.registration.set_readiness(Ready::empty())?;
        } else {
            self.inner.registration.set_readiness(Ready::readable())?;
        }

        Ok(queue.pop_front())
    }
}

impl<T> Evented for Timer<T> where T: Clone {
    fn register(&self, poll: &Poll, token: Token, interest: Ready, opts: PollOpt) -> io::Result<()> {
        self.inner.registration.register(poll, token, interest, opts)?;

        let queue = self.inner.queue.lock().unwrap();
        if queue.len() > 0 {
            self.inner.registration.set_readiness(Ready::readable())?;
        }

        Ok(())
    }

    fn reregister(&self, poll: &Poll, token: Token, interest: Ready, opts: PollOpt) -> io::Result<()> {
        self.inner.registration.reregister(poll, token, interest, opts)
    }

    fn deregister(&self, poll: &Poll) -> io::Result<()> {
        self.inner.registration.deregister(poll)
    }
}
<commit_msg>update<commit_after>use std::time::{Duration, SystemTime, UNIX_EPOCH};
use std::collections::{BinaryHeap, VecDeque};
use std::cmp::Ordering;
use std::thread;
use std::sync::{Arc, Mutex, Condvar};
use std::io;

use {Registration, Ready, Evented, Poll, Token, PollOpt};

#[derive(Debug, Clone, Default, Eq)]
pub struct Timespec {
    pub interval: Duration,
    pub value: Duration
}

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

impl Ord for Timespec {
    fn cmp(&self, other: &Timespec) -> Ordering {
        self.value.cmp(&other.value)
    }
}

impl PartialEq for Timespec {
    fn eq(&self, other: &Timespec) -> bool {
        self.value == other.value
    }
}

#[derive(Clone, Debug)]
pub struct Task<T> {
    pub data: T,
    pub timespec: Timespec
}

impl<T> PartialOrd for Task<T> {
    fn partial_cmp(&self, other: &Task<T>) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl<T> Ord for Task<T> {
    fn cmp(&self, other: &Task<T>) -> Ordering {
        match self.timespec.cmp(&other.timespec) {
            Ordering::Equal => Ordering::Equal,
            Ordering::Greater => Ordering::Less,
            Ordering::Less => Ordering::Greater
        }
    }
}

impl<T> PartialEq for Task<T> {
    fn eq(&self, other: &Task<T>) -> bool {
        self.timespec == other.timespec
    }
}

impl<T> Eq for Task<T> {}

pub struct Timer<T: Clone> {
    thread_handle: thread::JoinHandle<()>,
    inner: Arc<TimerInner<T>>
}

struct TimerInner<T> {
    tasks: Mutex<BinaryHeap<Task<T>>>,
    queue: Mutex<VecDeque<Task<T>>>,
    registration: Registration,
    condvar: Condvar
}

impl<T> Timer<T> where T: Clone + Send + PartialEq + 'static {
    pub fn new() -> Timer<T> {

        let inner = Arc::new(TimerInner {
            tasks: Mutex::new(BinaryHeap::new()),
            queue: Mutex::new(VecDeque::new()),
            registration: Registration::new().unwrap(),
            condvar: Condvar::new()
        });

        let inner2 = inner.clone();

        let thread_handle = thread::Builder::new().name("timer".to_owned()).spawn(move || {
            let inner = inner2;

            loop {
                let mut sleep_duration = Duration::from_secs(60);

                loop {
                    let mut tasks = inner.tasks.lock().unwrap();

                    if let Some(ref task) = tasks.peek().map(|v| v.to_owned()) {
                        let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();

                        if task.timespec.value > now {
                            sleep_duration = task.timespec.value - now;
                            break;
                        } else {
                            if let Some(mut task) = tasks.pop() {
                                let mut queue = inner.queue.lock().unwrap();
                                queue.push_back(task.clone());

                                if task.timespec.interval != Duration::default() {
                                    task.timespec.value += task.timespec.interval;
                                    tasks.push(task);
                                }

                                inner.condvar.notify_one();
                                inner.registration.set_readiness(Ready::readable()).unwrap();
                            }
                        }
                    } else {
                        break;
                    }
                }

                thread::park_timeout(sleep_duration);
            }
        }).unwrap();

        Timer {
            thread_handle,
            inner
        }
    }

    pub fn insert(&self, task: Task<T>) {
        let mut tasks = self.inner.tasks.lock().unwrap();
        tasks.push(task);
        self.thread_handle.thread().unpark();
    }

    pub fn remove(&mut self, token: T) {
        let mut tasks = self.inner.tasks.lock().unwrap();

        let mut tasks_vec: Vec<Task<T>> = Vec::from(tasks.clone());

        if let Some(pos) = tasks_vec.iter().position(|x| x.data == token) {
            tasks_vec.remove(pos);
        }

        *tasks = tasks_vec.into();
    }

    pub fn pop(&self) -> Task<T> {
        let mut queue = self.inner.queue.lock().unwrap();

        loop {
            if let Some(task) = queue.pop_front() {
                return task;
            }

            queue = self.inner.condvar.wait(queue).unwrap();
        }
    }

    pub fn try_pop(&self) -> io::Result<Option<Task<T>>> {
        let mut queue = self.inner.queue.lock().unwrap();

        if queue.len() <= 1 {
            self.inner.registration.set_readiness(Ready::empty())?;
        } else {
            self.inner.registration.set_readiness(Ready::readable())?;
        }

        Ok(queue.pop_front())
    }
}

impl<T> Evented for Timer<T> where T: Clone {
    fn register(&self, poll: &Poll, token: Token, interest: Ready, opts: PollOpt) -> io::Result<()> {
        self.inner.registration.register(poll, token, interest, opts)?;

        let queue = self.inner.queue.lock().unwrap();
        if queue.len() > 0 {
            self.inner.registration.set_readiness(Ready::readable())?;
        }

        Ok(())
    }

    fn reregister(&self, poll: &Poll, token: Token, interest: Ready, opts: PollOpt) -> io::Result<()> {
        self.inner.registration.reregister(poll, token, interest, opts)
    }

    fn deregister(&self, poll: &Poll) -> io::Result<()> {
        self.inner.registration.deregister(poll)
    }
}
<|endoftext|>


<commit_before>use std::io::process::{Command,ProcessOutput,ProcessExit,ExitStatus,ExitSignal};
use std::io::IoError;
use std::fmt;
use std::fmt::{Show, Formatter};

use TomlError = toml::Error;

pub trait CargoError {
    fn description(&self) -> String;
    fn detail(&self) -> Option<String> { None }
    fn cause<'a>(&'a self) -> Option<&'a CargoError> { None }
    fn is_human(&self) -> bool { false }

    fn to_error<E: FromError<Self>>(self) -> E {
        FromError::from_error(self)
    }

    fn box_error(self) -> Box<CargoError> {
        box self as Box<CargoError>
    }

    fn concrete(&self) -> ConcreteCargoError {
        ConcreteCargoError {
            description: self.description(),
            detail: self.detail(),
            cause: self.cause().map(|c| box c.concrete() as Box<CargoError>),
            is_human: self.is_human()
        }
    }
}

pub trait FromError<E> {
    fn from_error(error: E) -> Self;
}

impl<E: CargoError> FromError<E> for Box<CargoError> {
    fn from_error(error: E) -> Box<CargoError> {
        error.box_error()
    }
}

macro_rules! from_error (
    ($ty:ty) => {
        impl FromError<$ty> for $ty {
            fn from_error(error: $ty) -> $ty {
                error
            }
        }
    }
)

impl Show for Box<CargoError> {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        try!(write!(f, "{}", self.description()));
        Ok(())
    }
}

impl CargoError for Box<CargoError> {
    fn description(&self) -> String {
        (*self).description()
    }

    fn detail(&self) -> Option<String> {
        (*self).detail()
    }

    fn cause<'a>(&'a self) -> Option<&'a CargoError> {
        (*self).cause()
    }

    fn is_human(&self) -> bool {
        (*self).is_human()
    }

    fn box_error(self) -> Box<CargoError> {
        self
    }
}

pub type CargoResult<T> = Result<T, Box<CargoError>>;

pub trait BoxError<T> {
    fn box_error(self) -> CargoResult<T>;
}

pub trait ChainError<T> {
    fn chain_error<E: CargoError>(self, callback: || -> E) -> CargoResult<T> ;
}

impl<T, E: CargoError> BoxError<T> for Result<T, E> {
    fn box_error(self) -> CargoResult<T> {
        self.map_err(|err| err.box_error())
    }
}

impl<T, E: CargoError> ChainError<T> for Result<T, E> {
    fn chain_error<E: CargoError>(self, callback: || -> E) -> CargoResult<T>  {
        self.map_err(|err| {
            let mut update = callback().concrete();
            update.cause = Some(err.box_error());
            box update as Box<CargoError>
        })
    }
}

impl CargoError for IoError {
    fn description(&self) -> String { self.to_str() }
}

from_error!(IoError)

impl CargoError for TomlError {
    fn description(&self) -> String { self.to_str() }
}

from_error!(TomlError)

pub struct ProcessError {
    pub msg: String,
    pub command: String,
    pub exit: Option<ProcessExit>,
    pub output: Option<ProcessOutput>,
    pub detail: Option<String>,
    pub cause: Option<Box<CargoError>>
}

from_error!(ProcessError)

impl Show for ProcessError {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        let exit = match self.exit {
            Some(ExitStatus(i)) | Some(ExitSignal(i)) => i.to_str(),
            None => "never executed".to_str()
        };
        write!(f, "{} (status={})", self.msg, exit)
    }
}

impl CargoError for ProcessError {
    fn description(&self) -> String {
        let exit = match self.exit {
            Some(ExitStatus(i)) | Some(ExitSignal(i)) => i.to_str(),
            None => "never executed".to_str()
        };
        format!("{} (status={})", self.msg, exit)
    }

    fn detail(&self) -> Option<String> {
        self.detail.clone()
    }

    fn cause<'a>(&'a self) -> Option<&'a CargoError> {
        self.cause.as_ref().map(|c| { let err: &CargoError = *c; err })
    }
}

pub struct ConcreteCargoError {
    description: String,
    detail: Option<String>,
    cause: Option<Box<CargoError>>,
    is_human: bool
}

impl Show for ConcreteCargoError {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "{}", self.description)
    }
}

impl CargoError for ConcreteCargoError {
    fn description(&self) -> String {
        self.description.clone()
    }

    fn detail(&self) -> Option<String> {
        self.detail.clone()
    }

    fn cause<'a>(&'a self) -> Option<&'a CargoError> {
        self.cause.as_ref().map(|c| { let err: &CargoError = *c; err })
    }

    fn is_human(&self) -> bool {
        self.is_human
    }
}

pub type CliResult<T> = Result<T, CliError>;

#[deriving(Show)]
pub struct CliError {
    pub error: Box<CargoError>,
    pub exit_code: uint
}

impl CliError {
    pub fn new<S: Str>(error: S, code: uint) -> CliError {
        let error = human(error.as_slice().to_str());
        CliError::from_boxed(error, code)
    }

    pub fn from_error<E: CargoError + 'static>(error: E, code: uint) -> CliError {
        let error = box error as Box<CargoError>;
        CliError::from_boxed(error, code)
    }

    pub fn from_boxed(error: Box<CargoError>, code: uint) -> CliError {
        let error = if error.is_human() {
            error
        } else {
            chain(error, human("An unknown error occurred"))
        };

        CliError { error: error, exit_code: code }
    }
}

pub fn process_error<S: Str>(msg: S, command: &Command, status: Option<&ProcessExit>, output: Option<&ProcessOutput>) -> ProcessError {
    ProcessError {
        msg: msg.as_slice().to_str(),
        command: command.to_str(),
        exit: status.map(|o| o.clone()),
        output: output.map(|o| o.clone()),
        detail: None,
        cause: None
    }
}

pub fn internal_error<S1: Str, S2: Str>(error: S1, detail: S2) -> Box<CargoError> {
    box ConcreteCargoError {
        description: error.as_slice().to_str(),
        detail: Some(detail.as_slice().to_str()),
        cause: None,
        is_human: false
    } as Box<CargoError>
}

pub fn internal<S1: Str>(error: S1) -> Box<CargoError> {
    box ConcreteCargoError {
        description: error.as_slice().to_str(),
        detail: None,
        cause: None,
        is_human: false
    } as Box<CargoError>
}

pub fn human<S: Str>(error: S) -> Box<CargoError> {
    box ConcreteCargoError {
        description: error.as_slice().to_str(),
        detail: None,
        cause: None,
        is_human: true
    } as Box<CargoError>
}

pub fn chain<E: CargoError>(original: Box<CargoError>, update: E) -> Box<CargoError> {
    let mut concrete = update.concrete();
    concrete.cause = Some(original);
    box concrete as Box<CargoError>
}
<commit_msg>Add mark_human<commit_after>use std::io::process::{Command,ProcessOutput,ProcessExit,ExitStatus,ExitSignal};
use std::io::IoError;
use std::fmt;
use std::fmt::{Show, Formatter};

use TomlError = toml::Error;

pub trait CargoError {
    fn description(&self) -> String;
    fn detail(&self) -> Option<String> { None }
    fn cause<'a>(&'a self) -> Option<&'a CargoError> { None }
    fn is_human(&self) -> bool { false }

    fn to_error<E: FromError<Self>>(self) -> E {
        FromError::from_error(self)
    }

    fn box_error(self) -> Box<CargoError> {
        box self as Box<CargoError>
    }

    fn concrete(&self) -> ConcreteCargoError {
        ConcreteCargoError {
            description: self.description(),
            detail: self.detail(),
            cause: self.cause().map(|c| box c.concrete() as Box<CargoError>),
            is_human: self.is_human()
        }
    }

    fn mark_human(self) -> Box<CargoError> {
        let mut concrete = self.concrete();
        concrete.is_human = true;
        box concrete as Box<CargoError>
    }
}

pub trait FromError<E> {
    fn from_error(error: E) -> Self;
}

impl<E: CargoError> FromError<E> for Box<CargoError> {
    fn from_error(error: E) -> Box<CargoError> {
        error.box_error()
    }
}

macro_rules! from_error (
    ($ty:ty) => {
        impl FromError<$ty> for $ty {
            fn from_error(error: $ty) -> $ty {
                error
            }
        }
    }
)

impl Show for Box<CargoError> {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        try!(write!(f, "{}", self.description()));
        Ok(())
    }
}

impl CargoError for Box<CargoError> {
    fn description(&self) -> String {
        (*self).description()
    }

    fn detail(&self) -> Option<String> {
        (*self).detail()
    }

    fn cause<'a>(&'a self) -> Option<&'a CargoError> {
        (*self).cause()
    }

    fn is_human(&self) -> bool {
        (*self).is_human()
    }

    fn box_error(self) -> Box<CargoError> {
        self
    }
}

pub type CargoResult<T> = Result<T, Box<CargoError>>;

pub trait BoxError<T> {
    fn box_error(self) -> CargoResult<T>;
}

pub trait ChainError<T> {
    fn chain_error<E: CargoError>(self, callback: || -> E) -> CargoResult<T> ;
}

impl<T, E: CargoError> BoxError<T> for Result<T, E> {
    fn box_error(self) -> CargoResult<T> {
        self.map_err(|err| err.box_error())
    }
}

impl<T, E: CargoError> ChainError<T> for Result<T, E> {
    fn chain_error<E: CargoError>(self, callback: || -> E) -> CargoResult<T>  {
        self.map_err(|err| {
            let mut update = callback().concrete();
            update.cause = Some(err.box_error());
            box update as Box<CargoError>
        })
    }
}

impl CargoError for IoError {
    fn description(&self) -> String { self.to_str() }
}

from_error!(IoError)

impl CargoError for TomlError {
    fn description(&self) -> String { self.to_str() }
}

from_error!(TomlError)

pub struct ProcessError {
    pub msg: String,
    pub command: String,
    pub exit: Option<ProcessExit>,
    pub output: Option<ProcessOutput>,
    pub detail: Option<String>,
    pub cause: Option<Box<CargoError>>
}

from_error!(ProcessError)

impl Show for ProcessError {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        let exit = match self.exit {
            Some(ExitStatus(i)) | Some(ExitSignal(i)) => i.to_str(),
            None => "never executed".to_str()
        };
        write!(f, "{} (status={})", self.msg, exit)
    }
}

impl CargoError for ProcessError {
    fn description(&self) -> String {
        let exit = match self.exit {
            Some(ExitStatus(i)) | Some(ExitSignal(i)) => i.to_str(),
            None => "never executed".to_str()
        };
        format!("{} (status={})", self.msg, exit)
    }

    fn detail(&self) -> Option<String> {
        self.detail.clone()
    }

    fn cause<'a>(&'a self) -> Option<&'a CargoError> {
        self.cause.as_ref().map(|c| { let err: &CargoError = *c; err })
    }
}

pub struct ConcreteCargoError {
    description: String,
    detail: Option<String>,
    cause: Option<Box<CargoError>>,
    is_human: bool
}

impl Show for ConcreteCargoError {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "{}", self.description)
    }
}

impl CargoError for ConcreteCargoError {
    fn description(&self) -> String {
        self.description.clone()
    }

    fn detail(&self) -> Option<String> {
        self.detail.clone()
    }

    fn cause<'a>(&'a self) -> Option<&'a CargoError> {
        self.cause.as_ref().map(|c| { let err: &CargoError = *c; err })
    }

    fn is_human(&self) -> bool {
        self.is_human
    }
}

pub type CliResult<T> = Result<T, CliError>;

#[deriving(Show)]
pub struct CliError {
    pub error: Box<CargoError>,
    pub exit_code: uint
}

impl CliError {
    pub fn new<S: Str>(error: S, code: uint) -> CliError {
        let error = human(error.as_slice().to_str());
        CliError::from_boxed(error, code)
    }

    pub fn from_error<E: CargoError + 'static>(error: E, code: uint) -> CliError {
        let error = box error as Box<CargoError>;
        CliError::from_boxed(error, code)
    }

    pub fn from_boxed(error: Box<CargoError>, code: uint) -> CliError {
        let error = if error.is_human() {
            error
        } else {
            chain(error, human("An unknown error occurred"))
        };

        CliError { error: error, exit_code: code }
    }
}

pub fn process_error<S: Str>(msg: S, command: &Command, status: Option<&ProcessExit>, output: Option<&ProcessOutput>) -> ProcessError {
    ProcessError {
        msg: msg.as_slice().to_str(),
        command: command.to_str(),
        exit: status.map(|o| o.clone()),
        output: output.map(|o| o.clone()),
        detail: None,
        cause: None
    }
}

pub fn internal_error<S1: Str, S2: Str>(error: S1, detail: S2) -> Box<CargoError> {
    box ConcreteCargoError {
        description: error.as_slice().to_str(),
        detail: Some(detail.as_slice().to_str()),
        cause: None,
        is_human: false
    } as Box<CargoError>
}

pub fn internal<S1: Str>(error: S1) -> Box<CargoError> {
    box ConcreteCargoError {
        description: error.as_slice().to_str(),
        detail: None,
        cause: None,
        is_human: false
    } as Box<CargoError>
}

pub fn human<S: Str>(error: S) -> Box<CargoError> {
    box ConcreteCargoError {
        description: error.as_slice().to_str(),
        detail: None,
        cause: None,
        is_human: true
    } as Box<CargoError>
}

pub fn chain<E: CargoError>(original: Box<CargoError>, update: E) -> Box<CargoError> {
    let mut concrete = update.concrete();
    concrete.cause = Some(original);
    box concrete as Box<CargoError>
}
<|endoftext|>


<commit_before>use ast::*;
use std::collections::HashMap;
use std::io;
use std::io::prelude::*;

#[derive(Debug, PartialEq, Clone)]
pub enum Value {
    Nil,
    Boolean(bool),
    Number(f64),
    String(String),
}

impl Value {
    fn is_true(&self) -> bool {
        match *self {
            Value::Nil => false,
            Value::Boolean(b) => b,
            _ => true,
        }
    }

    fn to_string(&self) -> String {
        match *self {
            Value::Nil => "nil".into(),
            Value::Boolean(ref b) => b.to_string(),
            Value::Number(ref n) => n.to_string(),
            Value::String(ref s) => s.clone(),
        }
    }
}

struct Environment {
    values: HashMap<Identifier, Value>,
}

impl Environment {
    fn new() -> Environment {
        Environment { values: HashMap::new() }
    }

    fn define(&mut self, identifier: Identifier, value: Value) {
        // NOTE that this allow for variable redefinition. See the chapter.
        self.values.insert(identifier, value);
    }

    fn contains(&self, identifier: &Identifier) -> bool{
        self.values.contains_key(&identifier)
    }

    fn set(&mut self, identifier: Identifier, value: Value) {
        self.values.insert(identifier, value);
    }

    fn get(&self, identifier: &Identifier) -> Option<&Value> {
        self.values.get(identifier)
    }
}

pub trait Interpreter {
    fn execute(&mut self, statement: &Statement) -> Result<Option<Value>, RuntimeError>;
}

pub struct StatementInterpreter {
    environment: Environment,
}

impl StatementInterpreter {
    pub fn new() -> StatementInterpreter {
        StatementInterpreter { environment: Environment::new() }
    }
}
impl Interpreter for StatementInterpreter {
    fn execute(&mut self, statement: &Statement) -> Result<Option<Value>, RuntimeError> {
        statement.execute(&mut self.environment)
    }
}

#[derive(Debug)]
pub enum RuntimeError {
    UnaryMinusTypeMismatch(Value),
    BinaryOperatorTypeMismatch(BinaryOperator, Value, Value),
    UndefinedIdentifier(Identifier),
}

trait Interpret {
    fn interpret(&self, environment: &mut Environment) -> Result<Value, RuntimeError>;
}

trait Execute {
    fn execute(&self, environment: &mut Environment) -> Result<Option<Value>, RuntimeError>;
}

impl Interpret for Expr {
    fn interpret(&self, environment: &mut Environment) -> Result<Value, RuntimeError> {
        match *self {
            Expr::Literal(ref l) => l.interpret(environment),
            Expr::Unary(ref u) => u.interpret(environment),
            Expr::Binary(ref b) => b.interpret(environment),
            Expr::Grouping(ref g) => g.interpret(environment),
            Expr::Identifier(ref i) => i.interpret(environment),
            Expr::Assignment(ref a) => a.interpret(environment),
        }
    }
}

impl Interpret for Literal {
    fn interpret(&self, _: &mut Environment) -> Result<Value, RuntimeError> {
        match *self {
            Literal::NilLiteral => Ok(Value::Nil),
            Literal::BoolLiteral(b) => Ok(Value::Boolean(b)),
            Literal::StringLiteral(ref s) => Ok(Value::String(s.clone())),
            Literal::NumberLiteral(n) => Ok(Value::Number(n)),
        }
    }
}

impl Interpret for Identifier {
    fn interpret(&self, environment: &mut Environment) -> Result<Value, RuntimeError> {
        match environment.get(self) {
            Some(value) => Ok(value.clone()),
            None => Err(RuntimeError::UndefinedIdentifier(self.clone())),
        }
    }
}

impl Interpret for Assignment {
    fn interpret(&self, environment: &mut Environment) -> Result<Value, RuntimeError> {
        let target = match self.lvalue {
            Target::Identifier(ref i) => Identifier { name: i.name.clone() },
        };
        match self.rvalue.interpret(environment){
            Ok(value) =>{
            if environment.contains(&target){
                environment.set(target, value.clone());
                Ok(value)
            }
            else{
                Err(RuntimeError::UndefinedIdentifier(target))
            }
            },
            Err(error) => Err(error)
        }
    }
}

impl Interpret for Grouping {
    fn interpret(&self, environment: &mut Environment) -> Result<Value, RuntimeError> {
        self.expr.interpret(environment)
    }
}

impl Interpret for UnaryExpr {
    fn interpret(&self, environment: &mut Environment) -> Result<Value, RuntimeError> {
        let value = try!(self.right.interpret(environment));
        match self.operator {
            UnaryOperator::Bang => Ok(Value::Boolean(!value.is_true())),
            UnaryOperator::Minus => {
                match value {
                    Value::Number(n) => Ok(Value::Number(-n)),
                    _ => Err(RuntimeError::UnaryMinusTypeMismatch(value)),
                }
            }
        }
    }
}

impl Interpret for BinaryExpr {
    fn interpret(&self, environment: &mut Environment) -> Result<Value, RuntimeError> {
        let left = try!(self.left.interpret(environment));
        let right = try!(self.right.interpret(environment));
        match (&self.operator, &left, &right) {
            (&BinaryOperator::Minus, &Value::Number(l), &Value::Number(r)) => {
                Ok(Value::Number(l - r))
            }
            (&BinaryOperator::Slash, &Value::Number(l), &Value::Number(r)) => {
                Ok(Value::Number(l / r))
            }
            (&BinaryOperator::Star, &Value::Number(l), &Value::Number(r)) => {
                Ok(Value::Number(l * r))
            }
            (&BinaryOperator::Plus, &Value::Number(l), &Value::Number(r)) => {
                Ok(Value::Number(l + r))
            }
            (&BinaryOperator::Plus, &Value::String(ref l), &Value::String(ref r)) => {
                let mut result = String::new();
                result.push_str(l);
                result.push_str(r);
                Ok(Value::String(result))
            }
            (&BinaryOperator::Greater, &Value::Number(l), &Value::Number(r)) => {
                Ok(Value::Boolean(l > r))
            }
            (&BinaryOperator::Less, &Value::Number(l), &Value::Number(r)) => {
                Ok(Value::Boolean(l < r))
            }
            (&BinaryOperator::GreaterEqual, &Value::Number(l), &Value::Number(r)) => {
                Ok(Value::Boolean(l >= r))
            }
            (&BinaryOperator::LessEqual, &Value::Number(l), &Value::Number(r)) => {
                Ok(Value::Boolean(l <= r))
            }
            (&BinaryOperator::NotEqual, l, r) => Ok(Value::Boolean(l != r)),
            (&BinaryOperator::Equal, l, r) => Ok(Value::Boolean(l == r)),
            _ => {
                Err(RuntimeError::BinaryOperatorTypeMismatch(self.operator,
                                                             left.clone(),
                                                             right.clone()))
            }
        }
    }
}

impl Execute for Statement {
    fn execute(&self, mut environment: &mut Environment) -> Result<Option<Value>, RuntimeError> {
        match *self {
            Statement::Expression(ref e) => e.interpret(environment).map(Some),
            Statement::Print(ref e) => {
                e.interpret(environment).map(|value| {
                    println!("{}", value.to_string());
                    let _ = io::stdout().flush(); //TODO: is this okay?
                    None
                })
            }
            Statement::VariableDefinition(ref identifier) => {
                let identifier = Identifier { name: identifier.name.clone() };
                environment.define(identifier, Value::Nil);
                Ok(None)
            }
            Statement::VariableDefinitionWithInitalizer(ref identifier, ref expression) => {
                let identifier = Identifier { name: identifier.name.clone() };
                expression.interpret(environment).map(|initializer| {
                    environment.define(identifier, initializer);
                    None
                })
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use ast::*;
    use interpreter::{Interpret, Environment, Value};

    #[test]
    fn literal() {
        let mut environment = Environment::new();
        let string = String::from("abc");
        let expr = Expr::Literal(Literal::StringLiteral(string.clone()));
        assert_eq!(Value::String(string),
                   expr.interpret(&mut environment).unwrap());
    }

    #[test]
    fn grouping() {
        let mut environment = Environment::new();
        let expr = Grouping { expr: Expr::Literal(Literal::NumberLiteral(45.67f64)) };
        assert_eq!(Value::Number(45.67f64),
                   expr.interpret(&mut environment).unwrap());
    }

    #[test]
    fn unary() {
        let mut environment = Environment::new();
        let expr = UnaryExpr {
            operator: UnaryOperator::Bang,
            right: Expr::Literal(Literal::BoolLiteral(false)),
        };
        assert_eq!(Value::Boolean(true),
                   expr.interpret(&mut environment).unwrap());
    }

    #[test]
    fn binary() {
        let mut environment = Environment::new();
        let expr = BinaryExpr {
            operator: BinaryOperator::Plus,
            left: Expr::Literal(Literal::NumberLiteral(1.0f64)),
            right: Expr::Literal(Literal::NumberLiteral(1.0f64)),
        };
        assert_eq!(Value::Number(2.0f64),
                   expr.interpret(&mut environment).unwrap());
    }

}
<commit_msg>Clippy warning<commit_after>use ast::*;
use std::collections::HashMap;
use std::io;
use std::io::prelude::*;

#[derive(Debug, PartialEq, Clone)]
pub enum Value {
    Nil,
    Boolean(bool),
    Number(f64),
    String(String),
}

impl Value {
    fn is_true(&self) -> bool {
        match *self {
            Value::Nil => false,
            Value::Boolean(b) => b,
            _ => true,
        }
    }

    fn to_string(&self) -> String {
        match *self {
            Value::Nil => "nil".into(),
            Value::Boolean(ref b) => b.to_string(),
            Value::Number(ref n) => n.to_string(),
            Value::String(ref s) => s.clone(),
        }
    }
}

struct Environment {
    values: HashMap<Identifier, Value>,
}

impl Environment {
    fn new() -> Environment {
        Environment { values: HashMap::new() }
    }

    fn define(&mut self, identifier: Identifier, value: Value) {
        // NOTE that this allow for variable redefinition. See the chapter.
        self.values.insert(identifier, value);
    }

    fn contains(&self, identifier: &Identifier) -> bool {
        self.values.contains_key(identifier)
    }

    fn set(&mut self, identifier: Identifier, value: Value) {
        self.values.insert(identifier, value);
    }

    fn get(&self, identifier: &Identifier) -> Option<&Value> {
        self.values.get(identifier)
    }
}

pub trait Interpreter {
    fn execute(&mut self, statement: &Statement) -> Result<Option<Value>, RuntimeError>;
}

pub struct StatementInterpreter {
    environment: Environment,
}

impl StatementInterpreter {
    pub fn new() -> StatementInterpreter {
        StatementInterpreter { environment: Environment::new() }
    }
}
impl Interpreter for StatementInterpreter {
    fn execute(&mut self, statement: &Statement) -> Result<Option<Value>, RuntimeError> {
        statement.execute(&mut self.environment)
    }
}

#[derive(Debug)]
pub enum RuntimeError {
    UnaryMinusTypeMismatch(Value),
    BinaryOperatorTypeMismatch(BinaryOperator, Value, Value),
    UndefinedIdentifier(Identifier),
}

trait Interpret {
    fn interpret(&self, environment: &mut Environment) -> Result<Value, RuntimeError>;
}

trait Execute {
    fn execute(&self, environment: &mut Environment) -> Result<Option<Value>, RuntimeError>;
}

impl Interpret for Expr {
    fn interpret(&self, environment: &mut Environment) -> Result<Value, RuntimeError> {
        match *self {
            Expr::Literal(ref l) => l.interpret(environment),
            Expr::Unary(ref u) => u.interpret(environment),
            Expr::Binary(ref b) => b.interpret(environment),
            Expr::Grouping(ref g) => g.interpret(environment),
            Expr::Identifier(ref i) => i.interpret(environment),
            Expr::Assignment(ref a) => a.interpret(environment),
        }
    }
}

impl Interpret for Literal {
    fn interpret(&self, _: &mut Environment) -> Result<Value, RuntimeError> {
        match *self {
            Literal::NilLiteral => Ok(Value::Nil),
            Literal::BoolLiteral(b) => Ok(Value::Boolean(b)),
            Literal::StringLiteral(ref s) => Ok(Value::String(s.clone())),
            Literal::NumberLiteral(n) => Ok(Value::Number(n)),
        }
    }
}

impl Interpret for Identifier {
    fn interpret(&self, environment: &mut Environment) -> Result<Value, RuntimeError> {
        match environment.get(self) {
            Some(value) => Ok(value.clone()),
            None => Err(RuntimeError::UndefinedIdentifier(self.clone())),
        }
    }
}

impl Interpret for Assignment {
    fn interpret(&self, environment: &mut Environment) -> Result<Value, RuntimeError> {
        let target = match self.lvalue {
            Target::Identifier(ref i) => Identifier { name: i.name.clone() },
        };
        match self.rvalue.interpret(environment) {
            Ok(value) => {
                if environment.contains(&target) {
                    environment.set(target, value.clone());
                    Ok(value)
                } else {
                    Err(RuntimeError::UndefinedIdentifier(target))
                }
            }
            Err(error) => Err(error),
        }
    }
}

impl Interpret for Grouping {
    fn interpret(&self, environment: &mut Environment) -> Result<Value, RuntimeError> {
        self.expr.interpret(environment)
    }
}

impl Interpret for UnaryExpr {
    fn interpret(&self, environment: &mut Environment) -> Result<Value, RuntimeError> {
        let value = try!(self.right.interpret(environment));
        match self.operator {
            UnaryOperator::Bang => Ok(Value::Boolean(!value.is_true())),
            UnaryOperator::Minus => {
                match value {
                    Value::Number(n) => Ok(Value::Number(-n)),
                    _ => Err(RuntimeError::UnaryMinusTypeMismatch(value)),
                }
            }
        }
    }
}

impl Interpret for BinaryExpr {
    fn interpret(&self, environment: &mut Environment) -> Result<Value, RuntimeError> {
        let left = try!(self.left.interpret(environment));
        let right = try!(self.right.interpret(environment));
        match (&self.operator, &left, &right) {
            (&BinaryOperator::Minus, &Value::Number(l), &Value::Number(r)) => {
                Ok(Value::Number(l - r))
            }
            (&BinaryOperator::Slash, &Value::Number(l), &Value::Number(r)) => {
                Ok(Value::Number(l / r))
            }
            (&BinaryOperator::Star, &Value::Number(l), &Value::Number(r)) => {
                Ok(Value::Number(l * r))
            }
            (&BinaryOperator::Plus, &Value::Number(l), &Value::Number(r)) => {
                Ok(Value::Number(l + r))
            }
            (&BinaryOperator::Plus, &Value::String(ref l), &Value::String(ref r)) => {
                let mut result = String::new();
                result.push_str(l);
                result.push_str(r);
                Ok(Value::String(result))
            }
            (&BinaryOperator::Greater, &Value::Number(l), &Value::Number(r)) => {
                Ok(Value::Boolean(l > r))
            }
            (&BinaryOperator::Less, &Value::Number(l), &Value::Number(r)) => {
                Ok(Value::Boolean(l < r))
            }
            (&BinaryOperator::GreaterEqual, &Value::Number(l), &Value::Number(r)) => {
                Ok(Value::Boolean(l >= r))
            }
            (&BinaryOperator::LessEqual, &Value::Number(l), &Value::Number(r)) => {
                Ok(Value::Boolean(l <= r))
            }
            (&BinaryOperator::NotEqual, l, r) => Ok(Value::Boolean(l != r)),
            (&BinaryOperator::Equal, l, r) => Ok(Value::Boolean(l == r)),
            _ => {
                Err(RuntimeError::BinaryOperatorTypeMismatch(self.operator,
                                                             left.clone(),
                                                             right.clone()))
            }
        }
    }
}

impl Execute for Statement {
    fn execute(&self, mut environment: &mut Environment) -> Result<Option<Value>, RuntimeError> {
        match *self {
            Statement::Expression(ref e) => e.interpret(environment).map(Some),
            Statement::Print(ref e) => {
                e.interpret(environment).map(|value| {
                    println!("{}", value.to_string());
                    let _ = io::stdout().flush(); //TODO: is this okay?
                    None
                })
            }
            Statement::VariableDefinition(ref identifier) => {
                let identifier = Identifier { name: identifier.name.clone() };
                environment.define(identifier, Value::Nil);
                Ok(None)
            }
            Statement::VariableDefinitionWithInitalizer(ref identifier, ref expression) => {
                let identifier = Identifier { name: identifier.name.clone() };
                expression.interpret(environment).map(|initializer| {
                    environment.define(identifier, initializer);
                    None
                })
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use ast::*;
    use interpreter::{Interpret, Environment, Value};

    #[test]
    fn literal() {
        let mut environment = Environment::new();
        let string = String::from("abc");
        let expr = Expr::Literal(Literal::StringLiteral(string.clone()));
        assert_eq!(Value::String(string),
                   expr.interpret(&mut environment).unwrap());
    }

    #[test]
    fn grouping() {
        let mut environment = Environment::new();
        let expr = Grouping { expr: Expr::Literal(Literal::NumberLiteral(45.67f64)) };
        assert_eq!(Value::Number(45.67f64),
                   expr.interpret(&mut environment).unwrap());
    }

    #[test]
    fn unary() {
        let mut environment = Environment::new();
        let expr = UnaryExpr {
            operator: UnaryOperator::Bang,
            right: Expr::Literal(Literal::BoolLiteral(false)),
        };
        assert_eq!(Value::Boolean(true),
                   expr.interpret(&mut environment).unwrap());
    }

    #[test]
    fn binary() {
        let mut environment = Environment::new();
        let expr = BinaryExpr {
            operator: BinaryOperator::Plus,
            left: Expr::Literal(Literal::NumberLiteral(1.0f64)),
            right: Expr::Literal(Literal::NumberLiteral(1.0f64)),
        };
        assert_eq!(Value::Number(2.0f64),
                   expr.interpret(&mut environment).unwrap());
    }

}
<|endoftext|>