<commit_before>//! Execute benchmarks in a sysroot.

use std::env;
use std::path::{Path, PathBuf};
use std::process::{self, Command};
use std::str;
use std::f64;
use std::fs::{self, File};
use std::collections::{HashSet, HashMap};
use std::cmp;

use tempdir::TempDir;

use collector::{Patch, BenchmarkState, Run, Stat, Benchmark as CollectedBenchmark};

use errors::{Result, ResultExt};
use rust_sysroot::sysroot::Sysroot;
use serde_json;

fn run(mut cmd: Command) -> Result<process::Output> {
    info!("running: {:?}", cmd);
    let output = cmd.output()?;
    if !output.status.success() {
        bail!(
            "expected success, got {}\n\nstderr={}\n\n stdout={}",
            output.status,
            String::from_utf8_lossy(&output.stderr),
            String::from_utf8_lossy(&output.stdout)
        );
    }
    Ok(output)
}

fn touch_all(path: &Path) -> Result<()> {
    let mut cmd = Command::new("bash");
    cmd.current_dir(path)
        .args(&["-c", "find . -name *.rs | xargs touch"]);
    run(cmd)?;
    Ok(())
}

fn default_runs() -> usize {
    3
}

fn default_true() -> bool {
    true
}

#[derive(Debug, Clone, Deserialize)]
struct BenchmarkConfig {
    cargo_opts: Option<String>,
    cargo_rustc_opts: Option<String>,
    cargo_toml: Option<String>,
    #[serde(default)]
    disabled: bool,
    #[serde(default = "default_runs")]
    runs: usize,
    #[serde(default = "default_true")]
    run_optimized: bool,
    #[serde(default = "default_true")]
    run_debug: bool,
}

impl Default for BenchmarkConfig {
    fn default() -> BenchmarkConfig {
        BenchmarkConfig {
            cargo_opts: None,
            cargo_rustc_opts: None,
            cargo_toml: None,
            disabled: false,
            runs: default_runs(),
            run_optimized: true,
            run_debug: true,
        }
    }
}

pub struct Benchmark {
    pub name: String,
    pub path: PathBuf,
    patches: Vec<Patch>,
    config: BenchmarkConfig,
}

struct CargoProcess<'sysroot> {
    sysroot: &'sysroot Sysroot,
    incremental: Incremental,
    options: Options,
    perf: bool,
    manifest_path: String,
    cargo_args: Vec<String>,
    rustc_args: Vec<String>,
}

#[derive(Copy, Clone, Debug)]
struct Incremental(bool);

impl<'sysroot> CargoProcess<'sysroot> {
    fn base(&self) -> Command {
        let mut cargo = self.sysroot.command("cargo");
        cargo
            .env("SHELL", env::var_os("SHELL").unwrap_or_default())
            .env("RUSTC", &*FAKE_RUSTC)
            .env("RUSTC_REAL", &self.sysroot.rustc)
            .env("CARGO_INCREMENTAL", &format!("{}", self.incremental.0 as usize))
            .env("USE_PERF", &format!("{}", self.perf as usize));
        cargo
    }

    fn perf(mut self, perf: bool) -> Self {
        self.perf = perf;
        self
    }

    fn run(self, cwd: &Path, mut cargo: Cargo) -> Result<process::Output> {
        let mut cmd = self.base();
        cmd.current_dir(cwd);
        if self.options.check && cargo == Cargo::Build {
            cargo = Cargo::Check;
        }
        cmd.arg(match cargo {
            Cargo::Check | Cargo::Build => {
                "rustc"
            }
            Cargo::Clean => "clean"
        });
        {
            let mut cargo = self.base();
            cargo.current_dir(cwd)
                .arg("pkgid")
                .arg("--manifest-path")
                .arg(&self.manifest_path);
            let out = run(cargo).unwrap_or_else(|e| {
                panic!("failed to obtain pkgid in {:?}: {:?}", cwd, e);
            }).stdout;
            let package_id = str::from_utf8(&out).unwrap();
            cmd.arg("-p").arg(package_id.trim());
        }
        if cargo == Cargo::Check {
            cmd.arg("--profile").arg("check");
        }
        if self.options.release {
            cmd.arg("--release");
        }
        cmd.arg("--manifest-path")
            .arg(&self.manifest_path);
        if cargo.takes_args() {
            cmd.args(&self.cargo_args);
            cmd.arg("--");
            cmd.arg("-Ztime-passes");
            cmd.args(&self.rustc_args);
        }
        run(cmd)
    }
}

#[derive(Copy, Clone, PartialEq, Eq, Debug)]
enum Cargo {
    Build,
    Check,
    Clean,
}

#[derive(Debug, PartialEq, Clone, Copy)]
struct Options {
    release: bool,
    check: bool,
}

impl Cargo {
    fn takes_args(self) -> bool {
        match self {
            Cargo::Build | Cargo::Check => true,
            Cargo::Clean => false,
        }
    }
}

lazy_static! {
    static ref FAKE_RUSTC: PathBuf = {
        let mut fake_rustc = env::current_exe().unwrap();
        fake_rustc.pop();
        fake_rustc.push("rustc-fake");
        fake_rustc
    };
}

impl Benchmark {
    pub fn new(name: String, path: PathBuf) -> Result<Self> {
        let mut patches = vec![];
        for entry in fs::read_dir(&path)? {
            let entry = entry?;
            let path = entry.path();
            if let Some(ext) = path.extension() {
                if ext == "patch" {
                    patches.push(path.clone());
                }
            }
        }

        patches.sort();

        let patches = patches.into_iter().map(|p| Patch::new(p)).collect();

        let config_path = path.join("perf-config.json");
        let config: BenchmarkConfig = if config_path.exists() {
            serde_json::from_reader(File::open(&config_path)
                .chain_err(|| format!("failed to open {:?}", config_path))?)
                .chain_err(|| format!("failed to parse {:?}", config_path))?
        } else {
            BenchmarkConfig::default()
        };

        Ok(Benchmark {
            name, path, patches, config,
        })
    }

    fn make_temp_dir(&self, base: &Path) -> Result<TempDir> {
        let tmp_dir = TempDir::new(&format!("rustc-benchmark-{}", self.name))?;
        let mut cmd = Command::new("cp");
        cmd
            .arg("-r")
            .arg("-T")
            .arg("--")
            .arg(base)
            .arg(tmp_dir.path());
        run(cmd).chain_err(|| format!("copying {} to tmp dir", self.name))?;
        Ok(tmp_dir)
    }

    fn cargo<'a>(
        &self,
        sysroot: &'a Sysroot,
        incremental: Incremental,
        options: Options,
    ) -> CargoProcess<'a> {
        CargoProcess {
            sysroot: sysroot,
            incremental: incremental,
            options: options,
            perf: true,
            manifest_path:
                self.config.cargo_toml.clone().unwrap_or_else(|| String::from("Cargo.toml")),
            cargo_args:
            self.config.cargo_opts.clone()
                    .unwrap_or_default()
                    .split_whitespace().map(String::from)
            .collect(),
            rustc_args: self.config.cargo_rustc_opts.clone()
                .unwrap_or_default()
                .split_whitespace().map(String::from)
                .collect(),
        }
    }

    /// Run a specific benchmark on a specific commit
    pub fn run(&self, sysroot: &Sysroot, iterations: usize) -> Result<CollectedBenchmark> {
        let iterations = cmp::min(iterations, self.config.runs);
        eprintln!("processing {}", self.name);
        if self.config.disabled {
            eprintln!("skipping {}: disabled", self.name);
            bail!("disabled benchmark");
        }

        let has_perf = Command::new("perf").output().is_ok();
        assert!(has_perf);

        let mut ret = CollectedBenchmark {
            name: self.name.clone(),
            runs: Vec::new(),
        };

        let mut opts = Vec::with_capacity(3);
        if self.config.run_debug {
            opts.push(Options { check: true, release: false });
            opts.push(Options { check: false, release: false });
        }
        if self.config.run_optimized {
            opts.push(Options { check: false, release: true });
        }

        for opt in opts {
            let mut clean_stats = Vec::new();
            let mut incr_stats = Vec::new();
            let mut incr_clean_stats = Vec::new();
            let mut incr_patched_stats: Vec<(Patch, Vec<Vec<Stat>>)> = Vec::new();

            eprintln!("Benchmarking {} with {:?}", self.name, opt);

            let base_build = self.make_temp_dir(&self.path)?;
            let clean = self.cargo(sysroot, Incremental(false), opt)
                .run(base_build.path(), Cargo::Build)?;
            clean_stats.push(process_output(clean.stdout)?);
            self.cargo(sysroot, Incremental(false), opt)
                .perf(false)
                .run(base_build.path(), Cargo::Clean)?;

            for _ in 0..iterations {
                let tmp_dir = self.make_temp_dir(base_build.path())?;

                let clean = self.cargo(sysroot, Incremental(false), opt)
                    .run(tmp_dir.path(), Cargo::Build)?;
                touch_all(tmp_dir.path())?;
                let incr = self.cargo(sysroot, Incremental(true), opt)
                    .run(tmp_dir.path(), Cargo::Build)?;
                touch_all(tmp_dir.path())?;
                let incr_clean = self.cargo(sysroot, Incremental(true), opt)
                    .run(tmp_dir.path(), Cargo::Build)?;

                clean_stats.push(process_output(clean.stdout)?);
                incr_stats.push(process_output(incr.stdout)?);
                incr_clean_stats.push(process_output(incr_clean.stdout)?);

                for patch in &self.patches {
                    patch.apply(tmp_dir.path())?;
                    touch_all(tmp_dir.path())?;
                    let out = self.cargo(sysroot, Incremental(true), opt)
                        .run(tmp_dir.path(), Cargo::Build)?;
                    let out = process_output(out.stdout)?;
                    if let Some(mut entry) = incr_patched_stats.iter_mut()
                        .find(|s| &s.0 == patch) {
                        entry.1.push(out);
                        continue;
                    }

                    incr_patched_stats.push((patch.clone(), vec![out]));
                }
            }

            ret.runs.push(process_stats(opt, BenchmarkState::Clean, clean_stats));
            ret.runs.push(process_stats(opt, BenchmarkState::IncrementalStart, incr_stats));
            ret.runs.push(process_stats(opt, BenchmarkState::IncrementalClean, incr_clean_stats));

            for (patch, results) in incr_patched_stats {
                ret.runs.push(process_stats(
                    opt,
                    BenchmarkState::IncrementalPatched(patch),
                    results,
                ));
            }
        }

        Ok(ret)
    }
}

fn process_output(output: Vec<u8>) -> Result<Vec<Stat>> {
    let output = String::from_utf8(output)?;
    let mut stats = Vec::new();

    for line in output.lines() {
        // https://github.com/torvalds/linux/blob/bc78d646e708/tools/perf/Documentation/perf-stat.txt#L281
        macro_rules! get {
            ($e:expr) => (match $e {
                Some(s) => s,
                None => {
                    warn!("unhandled line: {}", line);
                    continue;
                },
            });
        }
        let mut parts = line.split(';').map(|s| s.trim());
        let cnt = get!(parts.next());
        let _unit = get!(parts.next());
        let name = get!(parts.next());
        let _time = get!(parts.next());
        let pct = get!(parts.next());
        if cnt == "<not supported>" {
            continue;
        }
        if !pct.starts_with("100.") {
            panic!(
                "measurement of `{}` only active for {}% of the time",
                name,
                pct
            );
        }
        stats.push(Stat {
            name: name.to_string(),
            cnt: cnt.parse().chain_err(|| format!("failed to parse `{}` as an float", cnt))?,
        });
    }

    Ok(stats)
}

fn process_stats(options: Options, state: BenchmarkState, runs: Vec<Vec<Stat>>) -> Run {
    let mut stats: HashMap<String, Vec<f64>> = HashMap::new();
    for run in runs.clone() {
        for stat in run {
            stats.entry(stat.name.clone()).or_insert_with(|| Vec::new()).push(stat.cnt);
        }
    }
    // all stats should be present in all runs
    let map = stats.values().map(|v| v.len()).collect::<HashSet<_>>();
    if map.len() != 1 {
        eprintln!("lengths: {:?}", map);
        eprintln!("runs: {:?}", runs);
        eprintln!("stats: {:?}", stats);
        panic!("expected all stats to be present in all runs");
    }
    let stats = stats.into_iter()
        .map(|(stat, counts)| {
            Stat {
                name: stat,
                cnt: counts.into_iter().fold(f64::INFINITY, |acc, v| f64::min(acc, v)),
            }
        })
        .collect();

    Run {
        stats,
        check: options.check,
        release: options.release,
        state: state,
    }
}
<commit_msg>Ensure that all rust files are touched when running benchmarks<commit_after>//! Execute benchmarks in a sysroot.

use std::env;
use std::path::{Path, PathBuf};
use std::process::{self, Command};
use std::str;
use std::f64;
use std::fs::{self, File};
use std::collections::{HashSet, HashMap};
use std::cmp;

use tempdir::TempDir;

use collector::{Patch, BenchmarkState, Run, Stat, Benchmark as CollectedBenchmark};

use errors::{Result, ResultExt};
use rust_sysroot::sysroot::Sysroot;
use serde_json;

fn run(mut cmd: Command) -> Result<process::Output> {
    info!("running: {:?}", cmd);
    let output = cmd.output()?;
    if !output.status.success() {
        bail!(
            "expected success, got {}\n\nstderr={}\n\n stdout={}",
            output.status,
            String::from_utf8_lossy(&output.stderr),
            String::from_utf8_lossy(&output.stdout)
        );
    }
    Ok(output)
}

fn touch_all(path: &Path) -> Result<()> {
    let mut cmd = Command::new("bash");
    cmd.current_dir(path)
        .args(&["-c", "find . -name '*.rs' | xargs touch"]);
    run(cmd)?;
    Ok(())
}

fn default_runs() -> usize {
    3
}

fn default_true() -> bool {
    true
}

#[derive(Debug, Clone, Deserialize)]
struct BenchmarkConfig {
    cargo_opts: Option<String>,
    cargo_rustc_opts: Option<String>,
    cargo_toml: Option<String>,
    #[serde(default)]
    disabled: bool,
    #[serde(default = "default_runs")]
    runs: usize,
    #[serde(default = "default_true")]
    run_optimized: bool,
    #[serde(default = "default_true")]
    run_debug: bool,
}

impl Default for BenchmarkConfig {
    fn default() -> BenchmarkConfig {
        BenchmarkConfig {
            cargo_opts: None,
            cargo_rustc_opts: None,
            cargo_toml: None,
            disabled: false,
            runs: default_runs(),
            run_optimized: true,
            run_debug: true,
        }
    }
}

pub struct Benchmark {
    pub name: String,
    pub path: PathBuf,
    patches: Vec<Patch>,
    config: BenchmarkConfig,
}

struct CargoProcess<'sysroot> {
    sysroot: &'sysroot Sysroot,
    incremental: Incremental,
    options: Options,
    perf: bool,
    manifest_path: String,
    cargo_args: Vec<String>,
    rustc_args: Vec<String>,
}

#[derive(Copy, Clone, Debug)]
struct Incremental(bool);

impl<'sysroot> CargoProcess<'sysroot> {
    fn base(&self) -> Command {
        let mut cargo = self.sysroot.command("cargo");
        cargo
            .env("SHELL", env::var_os("SHELL").unwrap_or_default())
            .env("RUSTC", &*FAKE_RUSTC)
            .env("RUSTC_REAL", &self.sysroot.rustc)
            .env("CARGO_INCREMENTAL", &format!("{}", self.incremental.0 as usize))
            .env("USE_PERF", &format!("{}", self.perf as usize));
        cargo
    }

    fn perf(mut self, perf: bool) -> Self {
        self.perf = perf;
        self
    }

    fn run(self, cwd: &Path, mut cargo: Cargo) -> Result<process::Output> {
        let mut cmd = self.base();
        cmd.current_dir(cwd);
        if self.options.check && cargo == Cargo::Build {
            cargo = Cargo::Check;
        }
        cmd.arg(match cargo {
            Cargo::Check | Cargo::Build => {
                "rustc"
            }
            Cargo::Clean => "clean"
        });
        {
            let mut cargo = self.base();
            cargo.current_dir(cwd)
                .arg("pkgid")
                .arg("--manifest-path")
                .arg(&self.manifest_path);
            let out = run(cargo).unwrap_or_else(|e| {
                panic!("failed to obtain pkgid in {:?}: {:?}", cwd, e);
            }).stdout;
            let package_id = str::from_utf8(&out).unwrap();
            cmd.arg("-p").arg(package_id.trim());
        }
        if cargo == Cargo::Check {
            cmd.arg("--profile").arg("check");
        }
        if self.options.release {
            cmd.arg("--release");
        }
        cmd.arg("--manifest-path")
            .arg(&self.manifest_path);
        if cargo.takes_args() {
            cmd.args(&self.cargo_args);
            cmd.arg("--");
            cmd.arg("-Ztime-passes");
            cmd.args(&self.rustc_args);
        }
        run(cmd)
    }
}

#[derive(Copy, Clone, PartialEq, Eq, Debug)]
enum Cargo {
    Build,
    Check,
    Clean,
}

#[derive(Debug, PartialEq, Clone, Copy)]
struct Options {
    release: bool,
    check: bool,
}

impl Cargo {
    fn takes_args(self) -> bool {
        match self {
            Cargo::Build | Cargo::Check => true,
            Cargo::Clean => false,
        }
    }
}

lazy_static! {
    static ref FAKE_RUSTC: PathBuf = {
        let mut fake_rustc = env::current_exe().unwrap();
        fake_rustc.pop();
        fake_rustc.push("rustc-fake");
        fake_rustc
    };
}

impl Benchmark {
    pub fn new(name: String, path: PathBuf) -> Result<Self> {
        let mut patches = vec![];
        for entry in fs::read_dir(&path)? {
            let entry = entry?;
            let path = entry.path();
            if let Some(ext) = path.extension() {
                if ext == "patch" {
                    patches.push(path.clone());
                }
            }
        }

        patches.sort();

        let patches = patches.into_iter().map(|p| Patch::new(p)).collect();

        let config_path = path.join("perf-config.json");
        let config: BenchmarkConfig = if config_path.exists() {
            serde_json::from_reader(File::open(&config_path)
                .chain_err(|| format!("failed to open {:?}", config_path))?)
                .chain_err(|| format!("failed to parse {:?}", config_path))?
        } else {
            BenchmarkConfig::default()
        };

        Ok(Benchmark {
            name, path, patches, config,
        })
    }

    fn make_temp_dir(&self, base: &Path) -> Result<TempDir> {
        let tmp_dir = TempDir::new(&format!("rustc-benchmark-{}", self.name))?;
        let mut cmd = Command::new("cp");
        cmd
            .arg("-r")
            .arg("-T")
            .arg("--")
            .arg(base)
            .arg(tmp_dir.path());
        run(cmd).chain_err(|| format!("copying {} to tmp dir", self.name))?;
        Ok(tmp_dir)
    }

    fn cargo<'a>(
        &self,
        sysroot: &'a Sysroot,
        incremental: Incremental,
        options: Options,
    ) -> CargoProcess<'a> {
        CargoProcess {
            sysroot: sysroot,
            incremental: incremental,
            options: options,
            perf: true,
            manifest_path:
                self.config.cargo_toml.clone().unwrap_or_else(|| String::from("Cargo.toml")),
            cargo_args:
            self.config.cargo_opts.clone()
                    .unwrap_or_default()
                    .split_whitespace().map(String::from)
            .collect(),
            rustc_args: self.config.cargo_rustc_opts.clone()
                .unwrap_or_default()
                .split_whitespace().map(String::from)
                .collect(),
        }
    }

    /// Run a specific benchmark on a specific commit
    pub fn run(&self, sysroot: &Sysroot, iterations: usize) -> Result<CollectedBenchmark> {
        let iterations = cmp::min(iterations, self.config.runs);
        eprintln!("processing {}", self.name);
        if self.config.disabled {
            eprintln!("skipping {}: disabled", self.name);
            bail!("disabled benchmark");
        }

        let has_perf = Command::new("perf").output().is_ok();
        assert!(has_perf);

        let mut ret = CollectedBenchmark {
            name: self.name.clone(),
            runs: Vec::new(),
        };

        let mut opts = Vec::with_capacity(3);
        if self.config.run_debug {
            opts.push(Options { check: true, release: false });
            opts.push(Options { check: false, release: false });
        }
        if self.config.run_optimized {
            opts.push(Options { check: false, release: true });
        }

        for opt in opts {
            let mut clean_stats = Vec::new();
            let mut incr_stats = Vec::new();
            let mut incr_clean_stats = Vec::new();
            let mut incr_patched_stats: Vec<(Patch, Vec<Vec<Stat>>)> = Vec::new();

            eprintln!("Benchmarking {} with {:?}", self.name, opt);

            let base_build = self.make_temp_dir(&self.path)?;
            let clean = self.cargo(sysroot, Incremental(false), opt)
                .run(base_build.path(), Cargo::Build)?;
            clean_stats.push(process_output(clean.stdout)?);
            self.cargo(sysroot, Incremental(false), opt)
                .perf(false)
                .run(base_build.path(), Cargo::Clean)?;

            for _ in 0..iterations {
                let tmp_dir = self.make_temp_dir(base_build.path())?;

                let clean = self.cargo(sysroot, Incremental(false), opt)
                    .run(tmp_dir.path(), Cargo::Build)?;
                touch_all(tmp_dir.path())?;
                let incr = self.cargo(sysroot, Incremental(true), opt)
                    .run(tmp_dir.path(), Cargo::Build)?;
                touch_all(tmp_dir.path())?;
                let incr_clean = self.cargo(sysroot, Incremental(true), opt)
                    .run(tmp_dir.path(), Cargo::Build)?;

                clean_stats.push(process_output(clean.stdout)?);
                incr_stats.push(process_output(incr.stdout)?);
                incr_clean_stats.push(process_output(incr_clean.stdout)?);

                for patch in &self.patches {
                    patch.apply(tmp_dir.path())?;
                    touch_all(tmp_dir.path())?;
                    let out = self.cargo(sysroot, Incremental(true), opt)
                        .run(tmp_dir.path(), Cargo::Build)?;
                    let out = process_output(out.stdout)?;
                    if let Some(mut entry) = incr_patched_stats.iter_mut()
                        .find(|s| &s.0 == patch) {
                        entry.1.push(out);
                        continue;
                    }

                    incr_patched_stats.push((patch.clone(), vec![out]));
                }
            }

            ret.runs.push(process_stats(opt, BenchmarkState::Clean, clean_stats));
            ret.runs.push(process_stats(opt, BenchmarkState::IncrementalStart, incr_stats));
            ret.runs.push(process_stats(opt, BenchmarkState::IncrementalClean, incr_clean_stats));

            for (patch, results) in incr_patched_stats {
                ret.runs.push(process_stats(
                    opt,
                    BenchmarkState::IncrementalPatched(patch),
                    results,
                ));
            }
        }

        Ok(ret)
    }
}

fn process_output(output: Vec<u8>) -> Result<Vec<Stat>> {
    let output = String::from_utf8(output)?;
    let mut stats = Vec::new();

    for line in output.lines() {
        // https://github.com/torvalds/linux/blob/bc78d646e708/tools/perf/Documentation/perf-stat.txt#L281
        macro_rules! get {
            ($e:expr) => (match $e {
                Some(s) => s,
                None => {
                    warn!("unhandled line: {}", line);
                    continue;
                },
            });
        }
        let mut parts = line.split(';').map(|s| s.trim());
        let cnt = get!(parts.next());
        let _unit = get!(parts.next());
        let name = get!(parts.next());
        let _time = get!(parts.next());
        let pct = get!(parts.next());
        if cnt == "<not supported>" {
            continue;
        }
        if !pct.starts_with("100.") {
            panic!(
                "measurement of `{}` only active for {}% of the time",
                name,
                pct
            );
        }
        stats.push(Stat {
            name: name.to_string(),
            cnt: cnt.parse().chain_err(|| format!("failed to parse `{}` as an float", cnt))?,
        });
    }

    assert!(stats.len() >= 1, "at least one stat collected, stdout: {:?}", output);

    Ok(stats)
}

fn process_stats(options: Options, state: BenchmarkState, runs: Vec<Vec<Stat>>) -> Run {
    let mut stats: HashMap<String, Vec<f64>> = HashMap::new();
    for run in runs.clone() {
        for stat in run {
            stats.entry(stat.name.clone()).or_insert_with(|| Vec::new()).push(stat.cnt);
        }
    }
    // all stats should be present in all runs
    let map = stats.values().map(|v| v.len()).collect::<HashSet<_>>();
    if map.len() != 1 {
        eprintln!("options: {:?}", options);
        eprintln!("state: {:?}", state);
        eprintln!("lengths: {:?}", map);
        eprintln!("runs: {:?}", runs);
        eprintln!("stats: {:?}", stats);
        panic!("expected all stats to be present in all runs");
    }
    let stats = stats.into_iter()
        .map(|(stat, counts)| {
            Stat {
                name: stat,
                cnt: counts.into_iter().fold(f64::INFINITY, |acc, v| f64::min(acc, v)),
            }
        })
        .collect();

    Run {
        stats,
        check: options.check,
        release: options.release,
        state: state,
    }
}
<|endoftext|>


<commit_before>extern crate piston;
extern crate graphics;
extern crate glutin_window;
extern crate opengl_graphics;

use piston::window::WindowSettings;
use piston::event_loop::*;
use piston::input::*;
use glutin_window::GlutinWindow as Window;
use opengl_graphics::{ GlGraphics, OpenGL };

struct App {
    gl: GlGraphics,
    triangles: Vec<[[f64; 2]; 3]>
}

impl App {
    fn render(&mut self, args: &RenderArgs) {
        use graphics::*;

        const WHITE: [f32; 4] = [1.0, 1.0, 1.0, 1.0];
        const BLACK: [f32; 4] = [0.0, 0.0, 0.0, 1.0];

        let triangles = &self.triangles;

        self.gl.draw(args.viewport(), |c, gl| {
            clear(WHITE, gl);

            let transform = c.transform.trans(0.0, 0.0);

            for t in triangles {
              polygon(BLACK, t, transform, gl);
            }
        });
    }
}

fn sub_triangles(v: [[f64; 2]; 3]) -> Vec<[[f64; 2]; 3]> {
    let (a, b, c) = (v[0], v[1], v[2]); // Vector destructuring is not yet stable in Rust 1.3.0.
    let (sax, say, sbx, sby, scx, scy) = (
            a[0] + ((b[0] - a[0]) / 2.0),
            a[1],
            b[0] - ((b[0] - c[0]) / 2.0),
            b[1] - ((b[1] - c[1]) / 2.0),
            a[0] + ((c[0] - a[0]) / 2.0),
            a[1] - ((a[1] - c[1]) / 2.0));

    vec![[[sax, say], [sbx, sby], [scx, scy]],
         [[a[0], a[1]], [sax, say], [scx, scy]],
         [[sax, say], [b[0], b[1]], [sbx, sby]],
         [[scx, scy], [sbx, sby], [c[0], c[1]]]]
}

fn sierpinski(depth: i32, vertices: [[f64; 2]; 3]) -> Vec<[[f64; 2]; 3]> {
    let mut triangles = vec![];

    match depth {
        0 => triangles,
        d @ _ => {
            let sub_tris = sub_triangles(vertices);

            triangles.push(sub_tris[0]);

            for sub_tri in &sub_tris[1..] {
                for t in sierpinski(d - 1, *sub_tri) {
                    triangles.push(t);
                }
            }

            triangles
        },
    }
}

fn main() {
    let opengl = OpenGL::V3_2;
    let window: Window = WindowSettings::new(
        "Sierpinski-Triangle",
        [1000, 800])
        .opengl(opengl)
        .exit_on_esc(true)
        .build()
        .unwrap();

    let triangles = sierpinski(8, [[50.0, 750.0], [950.0, 750.0], [500.0, 50.0]]);
    let mut app = App{gl: GlGraphics::new(opengl), triangles: triangles};

    for e in window.events().max_fps(1) {
        if let Some(r) = e.render_args() {
            app.render(&r);
        }
    }
}
<commit_msg>Small optimization<commit_after>extern crate piston;
extern crate graphics;
extern crate glutin_window;
extern crate opengl_graphics;

use piston::window::WindowSettings;
use piston::event_loop::*;
use piston::input::*;
use glutin_window::GlutinWindow as Window;
use opengl_graphics::{ GlGraphics, OpenGL };

struct App {
    gl: GlGraphics,
    triangles: Vec<[[f64; 2]; 3]>
}

impl App {
    fn render(&mut self, args: &RenderArgs) {
        use graphics::*;

        const WHITE: [f32; 4] = [1.0, 1.0, 1.0, 1.0];
        const BLACK: [f32; 4] = [0.0, 0.0, 0.0, 1.0];

        let triangles = &self.triangles;

        self.gl.draw(args.viewport(), |c, gl| {
            clear(WHITE, gl);

            let transform = c.transform.trans(0.0, 0.0);

            for t in triangles {
              polygon(BLACK, t, transform, gl);
            }
        });
    }
}

fn sub_triangles(v: &[[f64; 2]; 3]) -> [[[f64; 2]; 3]; 4] {
    let (a, b, c) = (v[0], v[1], v[2]); // Vector destructuring is not yet stable in Rust 1.3.0.
    let (sax, say, sbx, sby, scx, scy) = (
            a[0] + ((b[0] - a[0]) / 2.0),
            a[1],
            b[0] - ((b[0] - c[0]) / 2.0),
            b[1] - ((b[1] - c[1]) / 2.0),
            a[0] + ((c[0] - a[0]) / 2.0),
            a[1] - ((a[1] - c[1]) / 2.0));

    [[[sax, say], [sbx, sby], [scx, scy]],
     [[a[0], a[1]], [sax, say], [scx, scy]],
     [[sax, say], [b[0], b[1]], [sbx, sby]],
     [[scx, scy], [sbx, sby], [c[0], c[1]]]]
}

fn sierpinski(depth: i32, vertices: [[f64; 2]; 3]) -> Vec<[[f64; 2]; 3]> {
    let mut triangles = vec![];

    match depth {
        0 => triangles,
        d => {
            let sub_tris = sub_triangles(&vertices);

            triangles.push(sub_tris[0]);

            for sub_tri in &sub_tris[1..] {
                for t in sierpinski(d - 1, *sub_tri) {
                    triangles.push(t);
                }
            }

            triangles
        },
    }
}

fn main() {
    let opengl = OpenGL::V3_2;
    let window: Window = WindowSettings::new(
        "Sierpinski-Triangle",
        [1000, 800])
        .opengl(opengl)
        .exit_on_esc(true)
        .build()
        .unwrap();

    let triangles = sierpinski(8, [[50.0, 750.0], [950.0, 750.0], [500.0, 50.0]]);
    let mut app = App{gl: GlGraphics::new(opengl), triangles: triangles};

    for e in window.events().max_fps(1) {
        if let Some(r) = e.render_args() {
            app.render(&r);
        }
    }
}
<|endoftext|>


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

<%namespace name="helpers" file="/helpers.mako.rs" />

<%helpers:shorthand name="overflow" sub_properties="overflow-x overflow-y"
                    spec="https://drafts.csswg.org/css-overflow/#propdef-overflow">
    use properties::longhands::{overflow_x, overflow_y};

    pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
        let overflow = try!(overflow_x::parse(context, input));
        Ok(Longhands {
            overflow_x: overflow,
            overflow_y: overflow_y::SpecifiedValue(overflow),
        })
    }

    impl<'a> ToCss for LonghandsToSerialize<'a>  {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            if *self.overflow_x == self.overflow_y.0 {
                self.overflow_x.to_css(dest)
            } else {
                Ok(())
            }
        }
    }
</%helpers:shorthand>

macro_rules! try_parse_one {
    ($input: expr, $var: ident, $prop_module: ident) => {
        if $var.is_none() {
            if let Ok(value) = $input.try($prop_module::SingleSpecifiedValue::parse) {
                $var = Some(value);
                continue;
            }
        }
    };
    ($context: expr, $input: expr, $var: ident, $prop_module: ident) => {
        if $var.is_none() {
            if let Ok(value) = $input.try(|i| {
                $prop_module::SingleSpecifiedValue::parse($context, i)
            }) {
                $var = Some(value);
                continue;
            }
        }
    };
}

<%helpers:shorthand name="transition" extra_prefixes="moz webkit"
                    sub_properties="transition-property transition-duration
                                    transition-timing-function
                                    transition-delay"
                    spec="https://drafts.csswg.org/css-transitions/#propdef-transition">
    use parser::Parse;
    use properties::longhands::{transition_delay, transition_duration, transition_property};
    use properties::longhands::{transition_timing_function};

    pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
        struct SingleTransition {
            transition_property: transition_property::SingleSpecifiedValue,
            transition_duration: transition_duration::SingleSpecifiedValue,
            transition_timing_function: transition_timing_function::SingleSpecifiedValue,
            transition_delay: transition_delay::SingleSpecifiedValue,
        }

        fn parse_one_transition(context: &ParserContext, input: &mut Parser) -> Result<SingleTransition,()> {
            let (mut property, mut duration) = (None, None);
            let (mut timing_function, mut delay) = (None, None);
            loop {
                try_parse_one!(input, property, transition_property);
                try_parse_one!(context, input, duration, transition_duration);
                try_parse_one!(context, input, timing_function, transition_timing_function);
                try_parse_one!(context, input, delay, transition_delay);

                break
            }

            if let Some(property) = property {
                Ok(SingleTransition {
                    transition_property: property,
                    transition_duration:
                        duration.unwrap_or_else(transition_duration::single_value::get_initial_value),
                    transition_timing_function:
                        timing_function.unwrap_or_else(transition_timing_function::single_value
                                                                                ::get_initial_specified_value),
                    transition_delay:
                        delay.unwrap_or_else(transition_delay::single_value::get_initial_value),
                })
            } else {
                Err(())
            }
        }

        let (mut properties, mut durations) = (Vec::new(), Vec::new());
        let (mut timing_functions, mut delays) = (Vec::new(), Vec::new());

        if input.try(|input| input.expect_ident_matching("none")).is_err() {
            let results = try!(input.parse_comma_separated(|i| parse_one_transition(context, i)));
            for result in results {
                properties.push(result.transition_property);
                durations.push(result.transition_duration);
                timing_functions.push(result.transition_timing_function);
                delays.push(result.transition_delay);
            }
        }

        Ok(Longhands {
            transition_property: transition_property::SpecifiedValue(properties),
            transition_duration: transition_duration::SpecifiedValue(durations),
            transition_timing_function:
                transition_timing_function::SpecifiedValue(timing_functions),
            transition_delay: transition_delay::SpecifiedValue(delays),
        })
    }

    impl<'a> ToCss for LonghandsToSerialize<'a>  {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            let len = self.transition_property.0.len();
            // There should be at least one declared value
            if len == 0 {
                return Ok(());
            }

            // If any value list length is differs then we don't do a shorthand serialization
            // either.
            % for name in "property duration delay timing_function".split():
                if len != self.transition_${name}.0.len() {
                    return Ok(());
                }
            % endfor

            for i in 0..len {
                if i != 0 {
                    write!(dest, ", ")?;
                }
                self.transition_property.0[i].to_css(dest)?;
                % for name in "duration timing_function delay".split():
                    dest.write_str(" ")?;
                    self.transition_${name}.0[i].to_css(dest)?;
                % endfor
            }
            Ok(())
        }
    }
</%helpers:shorthand>

<%helpers:shorthand name="animation" extra_prefixes="moz webkit"
                    sub_properties="animation-name animation-duration
                                    animation-timing-function animation-delay
                                    animation-iteration-count animation-direction
                                    animation-fill-mode animation-play-state"
                    allowed_in_keyframe_block="False"
                    spec="https://drafts.csswg.org/css-animations/#propdef-animation">
    <%
        props = "name duration timing_function delay iteration_count \
                 direction fill_mode play_state".split()
    %>
    use parser::Parse;
    % for prop in props:
    use properties::longhands::animation_${prop};
    % endfor

    pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
        struct SingleAnimation {
            % for prop in props:
            animation_${prop}: animation_${prop}::SingleSpecifiedValue,
            % endfor
        }

        fn parse_one_animation(context: &ParserContext, input: &mut Parser) -> Result<SingleAnimation,()> {
            % for prop in props:
            let mut ${prop} = None;
            % endfor

            // NB: Name must be the last one here so that keywords valid for other
            // longhands are not interpreted as names.
            //
            // Also, duration must be before delay, see
            // https://drafts.csswg.org/css-animations/#typedef-single-animation
            loop {
                try_parse_one!(context, input, duration, animation_duration);
                try_parse_one!(context, input, timing_function, animation_timing_function);
                try_parse_one!(context, input, delay, animation_delay);
                try_parse_one!(context, input, iteration_count, animation_iteration_count);
                try_parse_one!(input, direction, animation_direction);
                try_parse_one!(input, fill_mode, animation_fill_mode);
                try_parse_one!(input, play_state, animation_play_state);
                try_parse_one!(context, input, name, animation_name);

                break
            }

            Ok(SingleAnimation {
                % for prop in props:
                animation_${prop}: ${prop}.unwrap_or_else(animation_${prop}::single_value
                                                          ::get_initial_specified_value),
                % endfor
            })
        }

        % for prop in props:
        let mut ${prop}s = vec![];
        % endfor

        if input.try(|input| input.expect_ident_matching("none")).is_err() {
            let results = try!(input.parse_comma_separated(|i| parse_one_animation(context, i)));
            for result in results.into_iter() {
                % for prop in props:
                ${prop}s.push(result.animation_${prop});
                % endfor
            }
        }

        Ok(Longhands {
            % for prop in props:
            animation_${prop}: animation_${prop}::SpecifiedValue(${prop}s),
            % endfor
        })
    }

    impl<'a> ToCss for LonghandsToSerialize<'a>  {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            let len = self.animation_name.0.len();
            // There should be at least one declared value
            if len == 0 {
                return Ok(());
            }

            // If any value list length is differs then we don't do a shorthand serialization
            // either.
            % for name in props[1:]:
                if len != self.animation_${name}.0.len() {
                    return Ok(())
                }
            % endfor

            for i in 0..len {
                if i != 0 {
                    try!(write!(dest, ", "));
                }

                % for name in props[1:]:
                    self.animation_${name}.0[i].to_css(dest)?;
                    dest.write_str(" ")?;
                % endfor
                self.animation_name.0[i].to_css(dest)?;
            }
            Ok(())
        }
    }
</%helpers:shorthand>

<%helpers:shorthand name="scroll-snap-type" products="gecko"
                    sub_properties="scroll-snap-type-x scroll-snap-type-y"
                    spec="https://drafts.csswg.org/css-scroll-snap/#propdef-scroll-snap-type">
    use properties::longhands::scroll_snap_type_x;

    pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
        let result = try!(scroll_snap_type_x::parse(context, input));
        Ok(Longhands {
            scroll_snap_type_x: result,
            scroll_snap_type_y: result,
        })
    }

    impl<'a> ToCss for LonghandsToSerialize<'a>  {
        // Serializes into the single keyword value if both scroll-snap-type and scroll-snap-type-y are same.
        // Otherwise into an empty string. This is done to match Gecko's behaviour.
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            if self.scroll_snap_type_x == self.scroll_snap_type_y {
                self.scroll_snap_type_x.to_css(dest)
            } else {
                Ok(())
            }
        }
    }
</%helpers:shorthand>
<commit_msg>Parse none as just a normal animation item<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

<%namespace name="helpers" file="/helpers.mako.rs" />

<%helpers:shorthand name="overflow" sub_properties="overflow-x overflow-y"
                    spec="https://drafts.csswg.org/css-overflow/#propdef-overflow">
    use properties::longhands::{overflow_x, overflow_y};

    pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
        let overflow = try!(overflow_x::parse(context, input));
        Ok(Longhands {
            overflow_x: overflow,
            overflow_y: overflow_y::SpecifiedValue(overflow),
        })
    }

    impl<'a> ToCss for LonghandsToSerialize<'a>  {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            if *self.overflow_x == self.overflow_y.0 {
                self.overflow_x.to_css(dest)
            } else {
                Ok(())
            }
        }
    }
</%helpers:shorthand>

macro_rules! try_parse_one {
    ($input: expr, $var: ident, $prop_module: ident) => {
        if $var.is_none() {
            if let Ok(value) = $input.try($prop_module::SingleSpecifiedValue::parse) {
                $var = Some(value);
                continue;
            }
        }
    };
    ($context: expr, $input: expr, $var: ident, $prop_module: ident) => {
        if $var.is_none() {
            if let Ok(value) = $input.try(|i| {
                $prop_module::SingleSpecifiedValue::parse($context, i)
            }) {
                $var = Some(value);
                continue;
            }
        }
    };
}

<%helpers:shorthand name="transition" extra_prefixes="moz webkit"
                    sub_properties="transition-property transition-duration
                                    transition-timing-function
                                    transition-delay"
                    spec="https://drafts.csswg.org/css-transitions/#propdef-transition">
    use parser::Parse;
    use properties::longhands::{transition_delay, transition_duration, transition_property};
    use properties::longhands::{transition_timing_function};

    pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
        struct SingleTransition {
            transition_property: transition_property::SingleSpecifiedValue,
            transition_duration: transition_duration::SingleSpecifiedValue,
            transition_timing_function: transition_timing_function::SingleSpecifiedValue,
            transition_delay: transition_delay::SingleSpecifiedValue,
        }

        fn parse_one_transition(context: &ParserContext, input: &mut Parser) -> Result<SingleTransition,()> {
            let (mut property, mut duration) = (None, None);
            let (mut timing_function, mut delay) = (None, None);
            loop {
                try_parse_one!(input, property, transition_property);
                try_parse_one!(context, input, duration, transition_duration);
                try_parse_one!(context, input, timing_function, transition_timing_function);
                try_parse_one!(context, input, delay, transition_delay);

                break
            }

            if let Some(property) = property {
                Ok(SingleTransition {
                    transition_property: property,
                    transition_duration:
                        duration.unwrap_or_else(transition_duration::single_value::get_initial_value),
                    transition_timing_function:
                        timing_function.unwrap_or_else(transition_timing_function::single_value
                                                                                ::get_initial_specified_value),
                    transition_delay:
                        delay.unwrap_or_else(transition_delay::single_value::get_initial_value),
                })
            } else {
                Err(())
            }
        }

        let (mut properties, mut durations) = (Vec::new(), Vec::new());
        let (mut timing_functions, mut delays) = (Vec::new(), Vec::new());

        if input.try(|input| input.expect_ident_matching("none")).is_err() {
            let results = try!(input.parse_comma_separated(|i| parse_one_transition(context, i)));
            for result in results {
                properties.push(result.transition_property);
                durations.push(result.transition_duration);
                timing_functions.push(result.transition_timing_function);
                delays.push(result.transition_delay);
            }
        }

        Ok(Longhands {
            transition_property: transition_property::SpecifiedValue(properties),
            transition_duration: transition_duration::SpecifiedValue(durations),
            transition_timing_function:
                transition_timing_function::SpecifiedValue(timing_functions),
            transition_delay: transition_delay::SpecifiedValue(delays),
        })
    }

    impl<'a> ToCss for LonghandsToSerialize<'a>  {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            let len = self.transition_property.0.len();
            // There should be at least one declared value
            if len == 0 {
                return Ok(());
            }

            // If any value list length is differs then we don't do a shorthand serialization
            // either.
            % for name in "property duration delay timing_function".split():
                if len != self.transition_${name}.0.len() {
                    return Ok(());
                }
            % endfor

            for i in 0..len {
                if i != 0 {
                    write!(dest, ", ")?;
                }
                self.transition_property.0[i].to_css(dest)?;
                % for name in "duration timing_function delay".split():
                    dest.write_str(" ")?;
                    self.transition_${name}.0[i].to_css(dest)?;
                % endfor
            }
            Ok(())
        }
    }
</%helpers:shorthand>

<%helpers:shorthand name="animation" extra_prefixes="moz webkit"
                    sub_properties="animation-name animation-duration
                                    animation-timing-function animation-delay
                                    animation-iteration-count animation-direction
                                    animation-fill-mode animation-play-state"
                    allowed_in_keyframe_block="False"
                    spec="https://drafts.csswg.org/css-animations/#propdef-animation">
    <%
        props = "name duration timing_function delay iteration_count \
                 direction fill_mode play_state".split()
    %>
    use parser::Parse;
    % for prop in props:
    use properties::longhands::animation_${prop};
    % endfor

    pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
        struct SingleAnimation {
            % for prop in props:
            animation_${prop}: animation_${prop}::SingleSpecifiedValue,
            % endfor
        }

        fn parse_one_animation(context: &ParserContext, input: &mut Parser) -> Result<SingleAnimation,()> {
            % for prop in props:
            let mut ${prop} = None;
            % endfor

            // NB: Name must be the last one here so that keywords valid for other
            // longhands are not interpreted as names.
            //
            // Also, duration must be before delay, see
            // https://drafts.csswg.org/css-animations/#typedef-single-animation
            loop {
                try_parse_one!(context, input, duration, animation_duration);
                try_parse_one!(context, input, timing_function, animation_timing_function);
                try_parse_one!(context, input, delay, animation_delay);
                try_parse_one!(context, input, iteration_count, animation_iteration_count);
                try_parse_one!(input, direction, animation_direction);
                try_parse_one!(input, fill_mode, animation_fill_mode);
                try_parse_one!(input, play_state, animation_play_state);
                try_parse_one!(context, input, name, animation_name);

                break
            }

            Ok(SingleAnimation {
                % for prop in props:
                animation_${prop}: ${prop}.unwrap_or_else(animation_${prop}::single_value
                                                          ::get_initial_specified_value),
                % endfor
            })
        }

        % for prop in props:
        let mut ${prop}s = vec![];
        % endfor

        let results = try!(input.parse_comma_separated(|i| parse_one_animation(context, i)));
        for result in results.into_iter() {
            % for prop in props:
            ${prop}s.push(result.animation_${prop});
            % endfor
        }

        Ok(Longhands {
            % for prop in props:
            animation_${prop}: animation_${prop}::SpecifiedValue(${prop}s),
            % endfor
        })
    }

    impl<'a> ToCss for LonghandsToSerialize<'a>  {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            let len = self.animation_name.0.len();
            // There should be at least one declared value
            if len == 0 {
                return Ok(());
            }

            // If any value list length is differs then we don't do a shorthand serialization
            // either.
            % for name in props[1:]:
                if len != self.animation_${name}.0.len() {
                    return Ok(())
                }
            % endfor

            for i in 0..len {
                if i != 0 {
                    try!(write!(dest, ", "));
                }

                % for name in props[1:]:
                    self.animation_${name}.0[i].to_css(dest)?;
                    dest.write_str(" ")?;
                % endfor
                self.animation_name.0[i].to_css(dest)?;
            }
            Ok(())
        }
    }
</%helpers:shorthand>

<%helpers:shorthand name="scroll-snap-type" products="gecko"
                    sub_properties="scroll-snap-type-x scroll-snap-type-y"
                    spec="https://drafts.csswg.org/css-scroll-snap/#propdef-scroll-snap-type">
    use properties::longhands::scroll_snap_type_x;

    pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
        let result = try!(scroll_snap_type_x::parse(context, input));
        Ok(Longhands {
            scroll_snap_type_x: result,
            scroll_snap_type_y: result,
        })
    }

    impl<'a> ToCss for LonghandsToSerialize<'a>  {
        // Serializes into the single keyword value if both scroll-snap-type and scroll-snap-type-y are same.
        // Otherwise into an empty string. This is done to match Gecko's behaviour.
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            if self.scroll_snap_type_x == self.scroll_snap_type_y {
                self.scroll_snap_type_x.to_css(dest)
            } else {
                Ok(())
            }
        }
    }
</%helpers:shorthand>
<|endoftext|>


<commit_before>use std::marker::PhantomData;
use std::mem;
use std::ptr;

/// Unsafe wrapper around a trait object.
///
/// This struct can be used to move around a trait object when its lifetime is not expressible. Be
/// aware though that moving the object from which the trait has been built will cause crashes,
/// since the pointer stored in this struct will not change with it. For this reason, getting the
/// trait back from this object is unsafe.
pub struct UnsafeTraitObject<T: ?Sized> {
    // TODO: Unfortunately we cannot use a fixed size array here, because
    //       [u8; mem::size_of::<&T>()] is illegal in Rust for now (Rust 1.7)
    //       when this will be available, we can avoid double indirection and store in this struct
    //       the array inline. Be aware of alignment issues though!
    raw_ptr: Vec<u8>,
    _phantom: PhantomData<T>,
}

impl<T: ?Sized> UnsafeTraitObject<T> {
    pub fn new(e: &T) -> Self {
        let mut r = vec![0u8; mem::size_of::<&T>()];
        unsafe {
            let ptr: *mut &T = mem::transmute(r.as_mut_ptr());
            ptr::write(ptr, e);
        };
        UnsafeTraitObject {
            raw_ptr: r,
            _phantom: PhantomData,
        }
    }

    #[allow(dead_code)]
    pub unsafe fn as_inner(&self) -> &T {
        let ptr: *mut &T = mem::transmute(self.raw_ptr.as_ptr());
        ptr::read(ptr)
    }

    pub unsafe fn as_inner_mut(&mut self) -> &mut T {
        let ptr: *mut &mut T = mem::transmute(self.raw_ptr.as_ptr());
        ptr::read(ptr)
    }
}

unsafe impl<'a, T: ?Sized + 'a> Send for UnsafeTraitObject<T> where &'a T: Sync
{}
<commit_msg>Fixed Send trait for UnsafeTraitObject<commit_after>use std::marker::PhantomData;
use std::mem;
use std::ptr;

/// Unsafe wrapper around a trait object.
///
/// This struct can be used to move around a trait object when its lifetime is not expressible. Be
/// aware though that moving the object from which the trait has been built will cause crashes,
/// since the pointer stored in this struct will not change with it. For this reason, getting the
/// trait back from this object is unsafe.
pub struct UnsafeTraitObject<T: ?Sized> {
    // TODO: Unfortunately we cannot use a fixed size array here, because
    //       [u8; mem::size_of::<&T>()] is illegal in Rust for now (Rust 1.7)
    //       when this will be available, we can avoid double indirection and store in this struct
    //       the array inline. Be aware of alignment issues though!
    raw_ptr: Vec<u8>,
    _phantom: PhantomData<T>,
}

impl<T: ?Sized> UnsafeTraitObject<T> {
    pub fn new(e: &T) -> Self {
        let mut r = vec![0u8; mem::size_of::<&T>()];
        unsafe {
            let ptr: *mut &T = mem::transmute(r.as_mut_ptr());
            ptr::write(ptr, e);
        };
        UnsafeTraitObject {
            raw_ptr: r,
            _phantom: PhantomData,
        }
    }

    #[allow(dead_code)]
    pub unsafe fn as_inner(&self) -> &T {
        let ptr: *mut &T = mem::transmute(self.raw_ptr.as_ptr());
        ptr::read(ptr)
    }

    pub unsafe fn as_inner_mut(&mut self) -> &mut T {
        let ptr: *mut &mut T = mem::transmute(self.raw_ptr.as_ptr());
        ptr::read(ptr)
    }
}

unsafe impl<'a, T: ?Sized + 'a> Send for UnsafeTraitObject<T> where &'a T: Send
{}
<|endoftext|>


<commit_before>use super::ffi;
use super::Lua;
use super::Pushable;
use super::{ ConsumeReadable, CopyReadable, LoadedVariable };

pub struct UserData<T> {
    value: T
}

impl<T:Clone> UserData<T> {
    pub fn new(val: T) -> UserData<T> {
        UserData{value: val}
    }
}

impl<T> Deref<T> for UserData<T> {
    fn deref<'a>(&'a self)
        -> &'a T
    {
        &self.value
    }
}

impl<T> DerefMut<T> for UserData<T> {
    fn deref_mut<'a>(&'a mut self)
        -> &'a mut T
    {
        &mut self.value
    }
}

// TODO: handle destructors

impl<T:Clone> Pushable for UserData<T> {
    fn push_to_lua(&self, lua: &mut Lua) -> uint {
        let dataRaw = unsafe { ffi::lua_newuserdata(lua.lua, ::std::mem::size_of_val(&self.value) as ::libc::size_t) };
        let data: &mut T = unsafe { ::std::mem::transmute(dataRaw) };
        (*data) = self.value.clone();
        1
    }
}

impl<T:Clone> CopyReadable for UserData<T> {
    fn read_from_lua(lua: &mut Lua, index: i32) -> Option<UserData<T>> {
        // TODO: check type
        let dataPtr = unsafe { ffi::lua_touserdata(lua.lua, index) };
        let data: &T = unsafe { ::std::mem::transmute(dataPtr) };
        Some(UserData{value: data.clone()})
    }
}

impl<'a, T:Clone> ConsumeReadable<'a> for UserData<T> {
    fn read_from_variable(var: LoadedVariable<'a>) -> Result<UserData<T>, LoadedVariable<'a>> {
        match CopyReadable::read_from_lua(var.lua, -1) {
            None => Err(var),
            Some(a) => Ok(a)
        }
    }
}

#[cfg(test)]
mod tests {
    #[test]
    fn readwrite() {
        let mut lua = super::super::Lua::new();
        let d = super::UserData::new(2i);

        lua.set("a", d);
        let x: super::UserData<int> = lua.get("a").unwrap();
        assert_eq!(x.value, 2)
    }
}
<commit_msg>UserData types now being checked<commit_after>use super::ffi;
use super::Lua;
use super::Pushable;
use super::{ ConsumeReadable, CopyReadable, LoadedVariable };
use std::intrinsics::TypeId;

pub struct UserData<T> {
    value: T
}

impl<T: Clone> UserData<T> {
    pub fn new(val: T) -> UserData<T> {
        UserData{value: val}
    }
}

impl<T> Deref<T> for UserData<T> {
    fn deref<'a>(&'a self)
        -> &'a T
    {
        &self.value
    }
}

impl<T> DerefMut<T> for UserData<T> {
    fn deref_mut<'a>(&'a mut self)
        -> &'a mut T
    {
        &mut self.value
    }
}

impl<T: Clone> Clone for UserData<T> {
    fn clone(&self) -> UserData<T> {
        UserData { value: self.value.clone() }
    }
}

fn destructor<T>(_: UserData<T>) {

}

impl<T: Clone + 'static> Pushable for UserData<T> {
    fn push_to_lua(&self, lua: &mut Lua) -> uint {
        let dataRaw = unsafe { ffi::lua_newuserdata(lua.lua, ::std::mem::size_of_val(&self.value) as ::libc::size_t) };
        let data: &mut T = unsafe { ::std::mem::transmute(dataRaw) };
        (*data) = self.value.clone();

        // creating a metatable
        let typeid = format!("{}", TypeId::of::<T>());
        unsafe {
            ffi::lua_newtable(lua.lua);

            // index "__typeid" corresponds to the hash of the TypeId of T
            "__typeid".push_to_lua(lua);
            typeid.push_to_lua(lua);
            ffi::lua_settable(lua.lua, -3);

            // index "__gc" call the object's destructor
            "__gc".push_to_lua(lua);
            destructor::<T>.push_to_lua(lua);
            ffi::lua_settable(lua.lua, -3);

            ffi::lua_setmetatable(lua.lua, -2);
        }

        1
    }
}

impl<T: Clone + 'static> CopyReadable for UserData<T> {
    fn read_from_lua(lua: &mut Lua, index: i32) -> Option<UserData<T>> {
        unsafe {
            let expectedTypeid = format!("{}", TypeId::of::<T>());

            let dataPtr = ffi::lua_touserdata(lua.lua, index);
            if dataPtr.is_null() {
                return None;
            }

            if ffi::lua_getmetatable(lua.lua, -1) == 0 {
                return None;
            }

            "__typeid".push_to_lua(lua);
            ffi::lua_gettable(lua.lua, -2);
            if CopyReadable::read_from_lua(lua, -1) != Some(expectedTypeid) {
                return None;
            }
            ffi::lua_pop(lua.lua, -2);

            let data: &T = ::std::mem::transmute(dataPtr);
            Some(UserData{value: data.clone()})
        }
    }
}

impl<'a, T:Clone> ConsumeReadable<'a> for UserData<T> {
    fn read_from_variable(var: LoadedVariable<'a>) -> Result<UserData<T>, LoadedVariable<'a>> {
        match CopyReadable::read_from_lua(var.lua, -1) {
            None => Err(var),
            Some(a) => Ok(a)
        }
    }
}

#[cfg(test)]
mod tests {
    #[test]
    fn readwrite() {
        let mut lua = super::super::Lua::new();
        let d = super::UserData::new(2i);

        lua.set("a", d);
        let x: super::UserData<int> = lua.get("a").unwrap();
        assert_eq!(x.value, 2)
    }

    #[test]
    fn type_check() {
        #[deriving(Clone)]
        struct Foo;
        #[deriving(Clone)]
        struct Bar;

        let mut lua = super::super::Lua::new();

        lua.set("a", super::UserData::new(Foo));

        let x: Option<super::UserData<Bar>> = lua.get("a");
        assert!(x.is_none())
    }
}
<|endoftext|>


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

use serde_derive::Serialize;
use cargo_metadata::{metadata_run, CargoOpt};
use ra_syntax::SmolStr;
use rustc_hash::{FxHashMap, FxHashSet};
use failure::{format_err, bail};

use crate::{
    Result,
    thread_watcher::{ThreadWatcher, Worker},
};

#[derive(Debug, Clone)]
pub struct CargoWorkspace {
    packages: Vec<PackageData>,
    targets: Vec<TargetData>,
}

#[derive(Clone, Copy, Debug, Serialize)]
pub struct Package(usize);
#[derive(Clone, Copy, Debug, Serialize)]
pub struct Target(usize);

#[derive(Debug, Clone)]
struct PackageData {
    name: SmolStr,
    manifest: PathBuf,
    targets: Vec<Target>,
    is_member: bool,
}

#[derive(Debug, Clone)]
struct TargetData {
    pkg: Package,
    name: SmolStr,
    root: PathBuf,
    kind: TargetKind,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TargetKind {
    Bin,
    Lib,
    Example,
    Test,
    Bench,
    Other,
}

impl Package {
    pub fn name(self, ws: &CargoWorkspace) -> &str {
        ws.pkg(self).name.as_str()
    }
    pub fn root(self, ws: &CargoWorkspace) -> &Path {
        ws.pkg(self).manifest.parent().unwrap()
    }
    pub fn targets<'a>(self, ws: &'a CargoWorkspace) -> impl Iterator<Item = Target> + 'a {
        ws.pkg(self).targets.iter().cloned()
    }
    pub fn is_member(self, ws: &CargoWorkspace) -> bool {
        ws.pkg(self).is_member
    }
}

impl Target {
    pub fn package(self, ws: &CargoWorkspace) -> Package {
        ws.tgt(self).pkg
    }
    pub fn name(self, ws: &CargoWorkspace) -> &str {
        ws.tgt(self).name.as_str()
    }
    pub fn root(self, ws: &CargoWorkspace) -> &Path {
        ws.tgt(self).root.as_path()
    }
    pub fn kind(self, ws: &CargoWorkspace) -> TargetKind {
        ws.tgt(self).kind
    }
}

impl CargoWorkspace {
    pub fn from_cargo_metadata(path: &Path) -> Result<CargoWorkspace> {
        let cargo_toml = find_cargo_toml(path)?;
        let meta = metadata_run(
            Some(cargo_toml.as_path()),
            true,
            Some(CargoOpt::AllFeatures),
        )
        .map_err(|e| format_err!("cargo metadata failed: {}", e))?;
        let mut pkg_by_id = FxHashMap::default();
        let mut packages = Vec::new();
        let mut targets = Vec::new();

        let ws_members: FxHashSet<String> = meta
            .workspace_members
            .into_iter()
            .map(|it| it.raw)
            .collect();

        for meta_pkg in meta.packages {
            let pkg = Package(packages.len());
            let is_member = ws_members.contains(&meta_pkg.id);
            pkg_by_id.insert(meta_pkg.id.clone(), pkg);
            let mut pkg_data = PackageData {
                name: meta_pkg.name.into(),
                manifest: PathBuf::from(meta_pkg.manifest_path),
                targets: Vec::new(),
                is_member,
            };
            for meta_tgt in meta_pkg.targets {
                let tgt = Target(targets.len());
                targets.push(TargetData {
                    pkg,
                    name: meta_tgt.name.into(),
                    root: PathBuf::from(meta_tgt.src_path),
                    kind: TargetKind::new(meta_tgt.kind.as_slice()),
                });
                pkg_data.targets.push(tgt);
            }
            packages.push(pkg_data)
        }

        Ok(CargoWorkspace { packages, targets })
    }
    pub fn packages<'a>(&'a self) -> impl Iterator<Item = Package> + 'a {
        (0..self.packages.len()).map(Package)
    }
    pub fn target_by_root(&self, root: &Path) -> Option<Target> {
        self.packages()
            .filter_map(|pkg| pkg.targets(self).find(|it| it.root(self) == root))
            .next()
    }
    fn pkg(&self, pkg: Package) -> &PackageData {
        &self.packages[pkg.0]
    }
    fn tgt(&self, tgt: Target) -> &TargetData {
        &self.targets[tgt.0]
    }
}

fn find_cargo_toml(path: &Path) -> Result<PathBuf> {
    if path.ends_with("Cargo.toml") {
        return Ok(path.to_path_buf());
    }
    let mut curr = Some(path);
    while let Some(path) = curr {
        let candidate = path.join("Cargo.toml");
        if candidate.exists() {
            return Ok(candidate);
        }
        curr = path.parent();
    }
    bail!("can't find Cargo.toml at {}", path.display())
}

impl TargetKind {
    fn new(kinds: &[String]) -> TargetKind {
        for kind in kinds {
            return match kind.as_str() {
                "bin" => TargetKind::Bin,
                "test" => TargetKind::Test,
                "bench" => TargetKind::Bench,
                "example" => TargetKind::Example,
                _ if kind.contains("lib") => TargetKind::Lib,
                _ => continue,
            };
        }
        TargetKind::Other
    }
}

pub fn workspace_loader() -> (Worker<PathBuf, Result<CargoWorkspace>>, ThreadWatcher) {
    Worker::<PathBuf, Result<CargoWorkspace>>::spawn(
        "workspace loader",
        1,
        |input_receiver, output_sender| {
            input_receiver
                .map(|path| CargoWorkspace::from_cargo_metadata(path.as_path()))
                .for_each(|it| output_sender.send(it))
        },
    )
}
<commit_msg>track deps in project model<commit_after>use std::path::{Path, PathBuf};

use serde_derive::Serialize;
use cargo_metadata::{metadata_run, CargoOpt};
use ra_syntax::SmolStr;
use rustc_hash::{FxHashMap, FxHashSet};
use failure::{format_err, bail};

use crate::{
    Result,
    thread_watcher::{ThreadWatcher, Worker},
};

#[derive(Debug, Clone)]
pub struct CargoWorkspace {
    packages: Vec<PackageData>,
    targets: Vec<TargetData>,
}

#[derive(Clone, Copy, Debug, Serialize)]
pub struct Package(usize);
#[derive(Clone, Copy, Debug, Serialize)]
pub struct Target(usize);

#[derive(Debug, Clone)]
struct PackageData {
    name: SmolStr,
    manifest: PathBuf,
    targets: Vec<Target>,
    is_member: bool,
    dependencies: Vec<Package>,
}

#[derive(Debug, Clone)]
struct TargetData {
    pkg: Package,
    name: SmolStr,
    root: PathBuf,
    kind: TargetKind,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TargetKind {
    Bin,
    Lib,
    Example,
    Test,
    Bench,
    Other,
}

impl Package {
    pub fn name(self, ws: &CargoWorkspace) -> &str {
        ws.pkg(self).name.as_str()
    }
    pub fn root(self, ws: &CargoWorkspace) -> &Path {
        ws.pkg(self).manifest.parent().unwrap()
    }
    pub fn targets<'a>(self, ws: &'a CargoWorkspace) -> impl Iterator<Item = Target> + 'a {
        ws.pkg(self).targets.iter().cloned()
    }
    pub fn is_member(self, ws: &CargoWorkspace) -> bool {
        ws.pkg(self).is_member
    }
}

impl Target {
    pub fn package(self, ws: &CargoWorkspace) -> Package {
        ws.tgt(self).pkg
    }
    pub fn name(self, ws: &CargoWorkspace) -> &str {
        ws.tgt(self).name.as_str()
    }
    pub fn root(self, ws: &CargoWorkspace) -> &Path {
        ws.tgt(self).root.as_path()
    }
    pub fn kind(self, ws: &CargoWorkspace) -> TargetKind {
        ws.tgt(self).kind
    }
}

impl CargoWorkspace {
    pub fn from_cargo_metadata(path: &Path) -> Result<CargoWorkspace> {
        let cargo_toml = find_cargo_toml(path)?;
        let meta = metadata_run(
            Some(cargo_toml.as_path()),
            true,
            Some(CargoOpt::AllFeatures),
        )
        .map_err(|e| format_err!("cargo metadata failed: {}", e))?;
        let mut pkg_by_id = FxHashMap::default();
        let mut packages = Vec::new();
        let mut targets = Vec::new();

        let ws_members: FxHashSet<String> = meta
            .workspace_members
            .into_iter()
            .map(|it| it.raw)
            .collect();

        for meta_pkg in meta.packages {
            let pkg = Package(packages.len());
            let is_member = ws_members.contains(&meta_pkg.id);
            pkg_by_id.insert(meta_pkg.id.clone(), pkg);
            let mut pkg_data = PackageData {
                name: meta_pkg.name.into(),
                manifest: PathBuf::from(meta_pkg.manifest_path),
                targets: Vec::new(),
                is_member,
                dependencies: Vec::new(),
            };
            for meta_tgt in meta_pkg.targets {
                let tgt = Target(targets.len());
                targets.push(TargetData {
                    pkg,
                    name: meta_tgt.name.into(),
                    root: PathBuf::from(meta_tgt.src_path),
                    kind: TargetKind::new(meta_tgt.kind.as_slice()),
                });
                pkg_data.targets.push(tgt);
            }
            packages.push(pkg_data)
        }
        let resolve = meta.resolve.expect("metadata executed with deps");
        for node in resolve.nodes {
            let source = pkg_by_id[&node.id];
            for id in node.dependencies {
                let target = pkg_by_id[&id];
                packages[source.0].dependencies.push(target);
            }
        }

        Ok(CargoWorkspace { packages, targets })
    }
    pub fn packages<'a>(&'a self) -> impl Iterator<Item = Package> + 'a {
        (0..self.packages.len()).map(Package)
    }
    pub fn target_by_root(&self, root: &Path) -> Option<Target> {
        self.packages()
            .filter_map(|pkg| pkg.targets(self).find(|it| it.root(self) == root))
            .next()
    }
    fn pkg(&self, pkg: Package) -> &PackageData {
        &self.packages[pkg.0]
    }
    fn tgt(&self, tgt: Target) -> &TargetData {
        &self.targets[tgt.0]
    }
}

fn find_cargo_toml(path: &Path) -> Result<PathBuf> {
    if path.ends_with("Cargo.toml") {
        return Ok(path.to_path_buf());
    }
    let mut curr = Some(path);
    while let Some(path) = curr {
        let candidate = path.join("Cargo.toml");
        if candidate.exists() {
            return Ok(candidate);
        }
        curr = path.parent();
    }
    bail!("can't find Cargo.toml at {}", path.display())
}

impl TargetKind {
    fn new(kinds: &[String]) -> TargetKind {
        for kind in kinds {
            return match kind.as_str() {
                "bin" => TargetKind::Bin,
                "test" => TargetKind::Test,
                "bench" => TargetKind::Bench,
                "example" => TargetKind::Example,
                _ if kind.contains("lib") => TargetKind::Lib,
                _ => continue,
            };
        }
        TargetKind::Other
    }
}

pub fn workspace_loader() -> (Worker<PathBuf, Result<CargoWorkspace>>, ThreadWatcher) {
    Worker::<PathBuf, Result<CargoWorkspace>>::spawn(
        "workspace loader",
        1,
        |input_receiver, output_sender| {
            input_receiver
                .map(|path| CargoWorkspace::from_cargo_metadata(path.as_path()))
                .for_each(|it| output_sender.send(it))
        },
    )
}
<|endoftext|>


<commit_before>use std::borrow::Borrow;
use std::hash::Hash;
use std::ops;
use std::fmt;
use std::marker::PhantomData;

use super::{ ASTNode
           , SymbolTable
           , SymbolAnnotation
           };
use position::Position;
//==-----------------------------------------------------==
//      Warning: evil typesystem hacking to follow
//==-----------------------------------------------------==
// This is basically a reimplementation of the typestate
// system that Rust briefly had.
macro_rules! scope_typestate_err {
    ($err_site:expr) => {
        panic!("VERY TRAGIC ERROR: Typestate assertion failed during {}.\n \
            A node in the scoped typestate had no scope. Something has gone \
            terribly, terribly wrong. Contact the Mnemosyne implementors.",
            $err_site)
        }
}
pub trait ScopednessTypestate { fn is_scoped() -> bool; }
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct ScopedState;
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct UnscopedState;
impl ScopednessTypestate for ScopedState { fn is_scoped() -> bool {true} }
impl ScopednessTypestate for UnscopedState { fn is_scoped() -> bool {false} }
impl fmt::Display for ScopedState {
    fn fmt(&self, f: &mut fmt::Formatter)
        -> fmt::Result { write!(f, "Scoped") }
}
impl fmt::Display for UnscopedState {
    fn fmt(&self, f: &mut fmt::Formatter)
        -> fmt::Result { write!(f,"Unscoped") }
}
//==------- exiting typesystem danger zone --------------==

/// An AST node which has been annotated with position &
/// (possibly) scope information.
#[derive(Clone, Debug)]
pub struct Annotated<'a, T, S>
where S: ScopednessTypestate {
    pub node: T
  , pub position: Position
  , scope: Option<SymbolTable<'a>>
  , my_typestate: PhantomData<S>
}

pub type Scoped<'a, T> = Annotated<'a, T, ScopedState>;
pub type Unscoped<'a, T> = Annotated<'a, T, UnscopedState>;

/// Due to Evil Typesystem Hacking reasons, this impl only exists
/// for annotations which are in the Scoped typestate.
impl<'a, T> Scoped<'a, T> {

    /// Get the type signature associated with the given name.
    ///
    /// This returns a borrowed reference to the type signature
    /// associated with that name in the current scope. The argument
    /// can be any type `Q` such that `String: Borrow<Q>` (i.e.
    /// you can pass an `&str` to this function).
    pub fn get_type<Q: ?Sized>( &'a self
                               , name: &'a Q) -> Option<&'a SymbolAnnotation>
    where String: Borrow<Q>
             , Q: Hash + Eq
    {
        match self.scope {
            Some(ref table) => table.get(name),
            None => scope_typestate_err!("get_type()")
        }
    }

    /// Check if the given name is defined in this scope.
    ///
    /// The argument can be any type `Q` such that `String: Borrow<Q>`
    /// (i.e. you can pass an `&str` to this function).
    pub fn is_defined_here<Q: ?Sized>(&self, name: &Q) -> bool
    where String: Borrow<Q>
             , Q: Hash + Eq {
        match self.scope {
            Some(ref table) => table.contains_key(name),
            None => scope_typestate_err!("is_defined_here()")
        }
    }

}

impl<'a, T> Unscoped<'a, T> {

    /// Consume this unscoped annotation to produce a new
    /// annotation in the scoped typestate with the given
    /// scope.
    pub fn with_scope(self, scope: SymbolTable<'a>) -> Scoped<'a, T>{
        Annotated {
            node: self.node
          , position: self.position
          , scope: Some(scope)
          , my_typestate: PhantomData
        }
    }
}

impl<'a, T, S> ops::Deref for Annotated<'a, T, S>
where S: ScopednessTypestate
    , T: ASTNode
{
    type Target = T;
    fn deref(&self) -> &T { &self.node }
}

impl<'a, T, S> fmt::Display for Annotated<'a, T, S>
where S: ScopednessTypestate
    , T: fmt::Display
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{} at {}", self.node, self.position)
    }
}

// impl<'a, T> fmt::Debug for Annotated<'a, T, Scoped>
// where T: fmt::Debug {
//     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
//         write!(f, "{:?}, annotated with scope", self.node)
//     }
// }
//
// impl<'a, T> fmt::Debug for Annotated<'a, T, Unscoped>
// where T: fmt::Debug {
//     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
//         write!(f, "{:?}, unscoped", self.node)
//     }
// }

impl<'a, T, S> PartialEq for Annotated<'a, T, S>
where S: ScopednessTypestate
    , T: PartialEq
{
    fn eq(&self, other: &Annotated<'a, T, S>) -> bool {
        self.node == other.node
    }
}
<commit_msg>Aesthetic<commit_after>use std::borrow::Borrow;
use std::hash::Hash;
use std::ops;
use std::fmt;
use std::marker::PhantomData;

use super::{ ASTNode
           , SymbolTable
           , SymbolAnnotation
           };
use position::Position;
//==-----------------------------------------------------==
//      Warning: evil typesystem hacking to follow
//==-----------------------------------------------------==
// This is basically a reimplementation of the typestate
// system that Rust briefly had.
macro_rules! scope_typestate_err {
    ($err_site:expr) => {
        panic!("VERY TRAGIC ERROR: Typestate assertion failed during {}.\n \
            A node in the scoped typestate had no scope. Something has gone \
            terribly, terribly wrong. Contact the Mnemosyne implementors.",
            $err_site)
        }
}
pub trait ScopednessTypestate { fn is_scoped() -> bool; }
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct ScopedState;
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct UnscopedState;
impl ScopednessTypestate for ScopedState { fn is_scoped() -> bool {true} }
impl ScopednessTypestate for UnscopedState { fn is_scoped() -> bool {false} }
impl fmt::Display for ScopedState {
    fn fmt(&self, f: &mut fmt::Formatter)
        -> fmt::Result { write!(f, "Scoped") }
}
impl fmt::Display for UnscopedState {
    fn fmt(&self, f: &mut fmt::Formatter)
        -> fmt::Result { write!(f,"Unscoped") }
}
//==------- exiting typesystem danger zone --------------==

/// An AST node which has been annotated with position &
/// (possibly) scope information.
#[derive(Clone, Debug)]
pub struct Annotated<'a, T, S>
where S: ScopednessTypestate {
    pub node: T
  , pub position: Position
  , scope: Option<SymbolTable<'a>>
  , my_typestate: PhantomData<S>
}

pub type Scoped<'a, T> = Annotated<'a, T, ScopedState>;
pub type Unscoped<'a, T> = Annotated<'a, T, UnscopedState>;

/// Due to Evil Typesystem Hacking reasons, this impl only exists
/// for annotations which are in the Scoped typestate.
impl<'a, T> Scoped<'a, T> {

    /// Get the type signature associated with the given name.
    ///
    /// This returns a borrowed reference to the type signature
    /// associated with that name in the current scope. The argument
    /// can be any type `Q` such that `String: Borrow<Q>` (i.e.
    /// you can pass an `&str` to this function).
    pub fn get_type<Q: ?Sized>( &'a self
                               , name: &'a Q) -> Option<&'a SymbolAnnotation>
    where String: Borrow<Q>
             , Q: Hash + Eq
    {
        match self.scope {
            Some(ref table) => table.get(name),
            None => scope_typestate_err!("get_type()")
        }
    }

    /// Check if the given name is defined in this scope.
    ///
    /// The argument can be any type `Q` such that `String: Borrow<Q>`
    /// (i.e. you can pass an `&str` to this function).
    pub fn is_defined_here<Q: ?Sized>(&self, name: &Q) -> bool
    where String: Borrow<Q>
             , Q: Hash + Eq {
        match self.scope {
            Some(ref table) => table.contains_key(name),
            None => scope_typestate_err!("is_defined_here()")
        }
    }

}

impl<'a, T> Unscoped<'a, T> {

    /// Consume this unscoped annotation to produce a new
    /// annotation in the scoped typestate with the given
    /// scope.
    pub fn with_scope(self, scope: SymbolTable<'a>) -> Scoped<'a, T>{
        Annotated { node: self.node
                  , position: self.position
                  , scope: Some(scope)
                  , my_typestate: PhantomData
                  }
    }
}

impl<'a, T, S> ops::Deref for Annotated<'a, T, S>
where S: ScopednessTypestate
    , T: ASTNode
{
    type Target = T;
    fn deref(&self) -> &T { &self.node }
}

impl<'a, T, S> fmt::Display for Annotated<'a, T, S>
where S: ScopednessTypestate
    , T: fmt::Display
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{} at {}", self.node, self.position)
    }
}

// impl<'a, T> fmt::Debug for Annotated<'a, T, Scoped>
// where T: fmt::Debug {
//     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
//         write!(f, "{:?}, annotated with scope", self.node)
//     }
// }
//
// impl<'a, T> fmt::Debug for Annotated<'a, T, Unscoped>
// where T: fmt::Debug {
//     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
//         write!(f, "{:?}, unscoped", self.node)
//     }
// }

impl<'a, T, S> PartialEq for Annotated<'a, T, S>
where S: ScopednessTypestate
    , T: PartialEq
{
    fn eq(&self, other: &Annotated<'a, T, S>) -> bool {
        self.node == other.node
    }
}
<|endoftext|>


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

//! text records for storing arbitrary data

use std::slice::Iter;

use crate::error::*;
use crate::serialize::binary::*;

/// [RFC 1035, DOMAIN NAMES - IMPLEMENTATION AND SPECIFICATION, November 1987](https://tools.ietf.org/html/rfc1035)
///
/// ```text
/// 3.3.14. TXT RDATA format
///
///     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
///     /                   TXT-DATA                    /
///     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
///
///
/// TXT RRs are used to hold descriptive text.  The semantics of the text
/// depends on the domain where it is found.
/// ```
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub struct TXT {
    txt_data: Box<[Box<[u8]>]>,
}

impl TXT {
    /// Creates a new TXT record data.
    ///
    /// # Arguments
    ///
    /// * `txt_data` - the set of strings which make up the txt_data.
    ///
    /// # Return value
    ///
    /// The new TXT record data.
    pub fn new(txt_data: Vec<String>) -> TXT {
        TXT {
            txt_data: txt_data
                .into_iter()
                .map(|s| s.into_bytes().into_boxed_slice())
                .collect::<Vec<_>>()
                .into_boxed_slice(),
        }
    }

    /// ```text
    /// TXT-DATA        One or more <character-string>s.
    /// ```
    pub fn txt_data(&self) -> &[Box<[u8]>] {
        &self.txt_data
    }

    /// Returns an iterator over the arrays in the txt data
    pub fn iter(&self) -> Iter<Box<[u8]>> {
        self.txt_data.iter()
    }
}

/// Read the RData from the given Decoder
pub fn read(decoder: &mut BinDecoder, rdata_length: Restrict<u16>) -> ProtoResult<TXT> {
    let data_len = decoder.len();
    let mut strings = Vec::with_capacity(1);

    // no unsafe usage of rdata length after this point
    let rdata_length =
        rdata_length.map(|u| u as usize).unverified(/*used as a higher bound, safely*/);
    while data_len - decoder.len() < rdata_length {
        let string =
            decoder.read_character_data()?.unverified(/*any data should be validate in TXT usage*/);
        strings.push(string.to_vec().into_boxed_slice());
    }
    Ok(TXT {
        txt_data: strings.into_boxed_slice(),
    })
}

/// Write the RData from the given Decoder
pub fn emit(encoder: &mut BinEncoder, txt: &TXT) -> ProtoResult<()> {
    for s in txt.txt_data() {
        encoder.emit_character_data(s)?;
    }

    Ok(())
}

#[cfg(test)]
mod tests {
    #![allow(clippy::dbg_macro, clippy::print_stdout)]

    use super::*;

    #[test]
    fn test() {
        let rdata = TXT::new(vec!["Test me some".to_string(), "more please".to_string()]);

        let mut bytes = Vec::new();
        let mut encoder: BinEncoder = BinEncoder::new(&mut bytes);
        assert!(emit(&mut encoder, &rdata).is_ok());
        let bytes = encoder.into_bytes();

        println!("bytes: {:?}", bytes);

        let mut decoder: BinDecoder = BinDecoder::new(bytes);
        let restrict = Restrict::new(bytes.len() as u16);
        let read_rdata = read(&mut decoder, restrict).expect("Decoding error");
        assert_eq!(rdata, read_rdata);
    }
}
<commit_msg>Allow creating TXT Rdata with binary data<commit_after>/*
 * Copyright (C) 2015 Benjamin Fry <benjaminfry@me.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

//! text records for storing arbitrary data

use std::slice::Iter;

use crate::error::*;
use crate::serialize::binary::*;

/// [RFC 1035, DOMAIN NAMES - IMPLEMENTATION AND SPECIFICATION, November 1987](https://tools.ietf.org/html/rfc1035)
///
/// ```text
/// 3.3.14. TXT RDATA format
///
///     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
///     /                   TXT-DATA                    /
///     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
///
///
/// TXT RRs are used to hold descriptive text.  The semantics of the text
/// depends on the domain where it is found.
/// ```
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub struct TXT {
    txt_data: Box<[Box<[u8]>]>,
}

impl TXT {
    /// Creates a new TXT record data.
    ///
    /// # Arguments
    ///
    /// * `txt_data` - the set of strings which make up the txt_data.
    ///
    /// # Return value
    ///
    /// The new TXT record data.
    pub fn new(txt_data: Vec<String>) -> TXT {
        TXT {
            txt_data: txt_data
                .into_iter()
                .map(|s| s.into_bytes().into_boxed_slice())
                .collect::<Vec<_>>()
                .into_boxed_slice(),
        }
    }

    /// Creates a new TXT record data from bytes.
    /// Allows creating binary record data.
    ///
    /// # Arguments
    ///
    /// * `txt_data` - the set of bytes which make up the txt_data.
    ///
    /// # Return value
    ///
    /// The new TXT record data.
    pub fn from_bytes(txt_data: Vec<&[u8]>) -> TXT {
        TXT {
            txt_data: txt_data
                .into_iter()
                .map(|s| s.to_vec().into_boxed_slice())
                .collect::<Vec<_>>()
                .into_boxed_slice(),
        }
    }

    /// ```text
    /// TXT-DATA        One or more <character-string>s.
    /// ```
    pub fn txt_data(&self) -> &[Box<[u8]>] {
        &self.txt_data
    }

    /// Returns an iterator over the arrays in the txt data
    pub fn iter(&self) -> Iter<Box<[u8]>> {
        self.txt_data.iter()
    }
}

/// Read the RData from the given Decoder
pub fn read(decoder: &mut BinDecoder, rdata_length: Restrict<u16>) -> ProtoResult<TXT> {
    let data_len = decoder.len();
    let mut strings = Vec::with_capacity(1);

    // no unsafe usage of rdata length after this point
    let rdata_length =
        rdata_length.map(|u| u as usize).unverified(/*used as a higher bound, safely*/);
    while data_len - decoder.len() < rdata_length {
        let string =
            decoder.read_character_data()?.unverified(/*any data should be validate in TXT usage*/);
        strings.push(string.to_vec().into_boxed_slice());
    }
    Ok(TXT {
        txt_data: strings.into_boxed_slice(),
    })
}

/// Write the RData from the given Decoder
pub fn emit(encoder: &mut BinEncoder, txt: &TXT) -> ProtoResult<()> {
    for s in txt.txt_data() {
        encoder.emit_character_data(s)?;
    }

    Ok(())
}

#[cfg(test)]
mod tests {
    #![allow(clippy::dbg_macro, clippy::print_stdout)]

    use super::*;

    #[test]
    fn test() {
        let rdata = TXT::new(vec!["Test me some".to_string(), "more please".to_string()]);

        let mut bytes = Vec::new();
        let mut encoder: BinEncoder = BinEncoder::new(&mut bytes);
        assert!(emit(&mut encoder, &rdata).is_ok());
        let bytes = encoder.into_bytes();

        println!("bytes: {:?}", bytes);

        let mut decoder: BinDecoder = BinDecoder::new(bytes);
        let restrict = Restrict::new(bytes.len() as u16);
        let read_rdata = read(&mut decoder, restrict).expect("Decoding error");
        assert_eq!(rdata, read_rdata);
    }

    #[test]
    fn publish_binary_txt_record() {
        let bin_data = vec![0, 1, 2, 3, 4, 5, 6, 7, 8];
        let rdata = TXT::from_bytes(vec![b"Test me some", &bin_data]);

        let mut bytes = Vec::new();
        let mut encoder: BinEncoder = BinEncoder::new(&mut bytes);
        assert!(emit(&mut encoder, &rdata).is_ok());
        let bytes = encoder.into_bytes();

        println!("bytes: {:?}", bytes);

        let mut decoder: BinDecoder = BinDecoder::new(bytes);
        let restrict = Restrict::new(bytes.len() as u16);
        let read_rdata = read(&mut decoder, restrict).expect("Decoding error");
        assert_eq!(rdata, read_rdata);
    }
}
<|endoftext|>