<commit_before>#![feature(collections)]
#![feature(convert)]

use std::borrow::Cow;
use std::borrow::Cow::{Borrowed};

pub fn compile(sass: &str, style: &str) -> Result<String, &'static str> {
    let mut st = SassTokenizer::new(&sass);

    match style {
        "nested"     => Ok(nested_output(&mut st)),
        "compressed" => Ok(compressed_output(&mut st)),
        "expanded"   => Ok(expanded_output(&mut st)),
        "compact"    => Ok(compact_output(&mut st)),
        "debug"      => Ok(debug_output(&mut st)),
        _            => Err("Unknown style:. Please specify one of nested, compressed, expanded, or compact."),
    }
}

pub fn nested_output(tokenizer: &mut SassTokenizer) -> String {
    let mut output =  String::from_str("");
    while let Some(token) = tokenizer.next() {
        let print_token = match token {
            Event::Start(_) => continue,
            Event::Selector(name) => format!("{} {{", name),
            Event::Property(name, value) => format!("\n  {}: {};", name, value),
            Event::End(_) => format!(" }}\n"),
        };
        output.push_str(print_token.as_str());
    }
    output
}

pub fn compressed_output(tokenizer: &mut SassTokenizer) -> String {
    let mut output =  String::from_str("");
    while let Some(token) = tokenizer.next() {
        let print_token = match token {
            Event::Start(_) => continue,
            Event::Selector(name) => format!("{}{{", name),
            Event::Property(name, value) => format!("{}:{}", name, value),
            Event::End(_) => format!("}}"),
        };
        output.push_str(print_token.as_str());
    }
    output
}

pub fn expanded_output(tokenizer: &mut SassTokenizer) -> String {
    let mut output =  String::from_str("");
    while let Some(token) = tokenizer.next() {
        let print_token = match token {
            Event::Start(_) => continue,
            Event::Selector(name) => format!("{} {{", name),
            Event::Property(name, value) => format!("\n  {}: {};", name, value),
            Event::End(_) => format!("\n}}"),
        };
        output.push_str(print_token.as_str());
    }
    output
}

pub fn compact_output(tokenizer: &mut SassTokenizer) -> String {
    let mut output =  String::from_str("");
    while let Some(token) = tokenizer.next() {
        let print_token = match token {
            Event::Start(_) => continue,
            Event::Selector(name) => format!("{} {{", name),
            Event::Property(name, value) => format!(" {}: {};", name, value),
            Event::End(_) => format!(" }}"),
        };
        output.push_str(print_token.as_str());
    }
    output
}

pub fn debug_output(tokenizer: &mut SassTokenizer) -> String {
    let mut output =  String::from_str("");
    while let Some(token) = tokenizer.next() {
        output.push_str(format!("{:?}\n", token).as_str());
    }
    output
}

#[derive(PartialEq, Debug)]
pub enum State {
    StartRule,
    InSelectors,
    InProperties,
}

#[derive(Debug)]
pub enum Rule {
    SassRule,
}

#[derive(Debug)]
pub enum Event<'a> {
    Start(Rule),
    End(Rule),
    Selector(Cow<'a, str>),
    Property(Cow<'a, str>, Cow<'a, str>),
}

#[derive(Debug)]
pub struct SassTokenizer<'a> {
    sass: &'a str,
    offset: usize,
    stack: Vec<Rule>,
    state: State,
}

impl<'a> SassTokenizer<'a> {
    pub fn new(sass: &'a str) -> SassTokenizer<'a> {
        SassTokenizer {
            sass: &sass,
            offset: 0,
            stack: Vec::new(),
            state: State::StartRule,
        }
    }

    pub fn start_rule(&mut self) -> Option<Event<'a>> {
        self.skip_leading_whitespace();

        if self.offset == self.sass.len() {
            return None
        }

        self.state = State::InSelectors;
        self.stack.push(Rule::SassRule);

        Some(Event::Start(Rule::SassRule))
    }

    fn end(&mut self) -> Event<'a> {
        let rule = self.stack.pop().unwrap();
        self.state = State::StartRule;
        Event::End(rule)
    }

    fn scan_while<F>(&mut self, data: &str, f: F) -> usize
            where F: Fn(u8) -> bool {
        match data.as_bytes().iter().position(|&c| !f(c)) {
            Some(i) => i,
            None => data.len()
        }
    }

    fn skip_leading_whitespace(&mut self) {
        let i = self.offset;
        self.offset += self.scan_while(&self.sass[i..self.sass.len()], is_ascii_whitespace);
    }

    pub fn next_property(&mut self) -> Event<'a> {
        self.skip_leading_whitespace();

        let bytes = self.sass.as_bytes();
        let name_beginning = self.offset;
        let mut i = name_beginning;
        let limit = self.sass.len();

        let c = bytes[i];
        if c == b'}' {
            self.offset += 1;
            return self.end()
        }

        while i < limit {
            match bytes[i..limit].iter().position(|&c| c == b':') {
                Some(pos) => { i += pos; },
                None => { break; },
            }

            let name_end = i;

            i += 1;
            self.offset = i;
            self.skip_leading_whitespace();

            let value_beginning = self.offset;
            i = value_beginning;

            while i < limit {
                match bytes[i..limit].iter().position(|&c| c == b';') {
                    Some(pos) => { i += pos; },
                    None => { i = limit; break; },
                }

                let value_end = i;
                self.offset = i + 1;

                self.skip_leading_whitespace();

                return Event::Property(
                    Borrowed(&self.sass[name_beginning..name_end]),
                    Borrowed(&self.sass[value_beginning..value_end])
                )

            }
        }
        self.offset = self.sass.len();
        Event::Property(Borrowed(""), Borrowed(""))
    }

    pub fn next_selector(&mut self) -> Event<'a> {
        let bytes = self.sass.as_bytes();
        let beginning = self.offset;
        let mut i = beginning;
        let limit = self.sass.len();

        while i < limit {
            match bytes[i..limit].iter().position(|&c| c == b',' || c == b'{') {
                Some(pos) => { i += pos; },
                None => { i = limit; break; },
            }

            let c = bytes[i];

            if c == b',' || c == b'{' {
                let n = scan_trailing_whitespace(&self.sass[beginning..i]);
                let end = i - n;
                if end > beginning {
                    if c == b'{' {
                        self.state = State::InProperties;
                    }
                    self.offset = i + 1;
                    return Event::Selector(Borrowed(&self.sass[beginning..end]));
                }
            }

            self.offset = i;
            if i > beginning {
                return Event::Selector(Borrowed(&self.sass[beginning..i]))
            }
            i += 1;
        }

        if i > beginning {
            self.offset = i;
            Event::Selector(Borrowed(&self.sass[beginning..i]))
        } else {
            self.end()
        }
    }
}

impl<'a> Iterator for SassTokenizer<'a> {
    type Item = Event<'a>;

    fn next(&mut self) -> Option<Event<'a>> {
        if self.offset < self.sass.len() {
            match self.state {
                State::StartRule => {
                    let ret = self.start_rule();
                    if ret.is_some() {
                        return ret
                    }
                },
                State::InSelectors => return Some(self.next_selector()),
                State::InProperties => return Some(self.next_property()),
            }
        }
        None
    }
}

pub fn char_at(s: &str, byte: usize) -> char {
    s[byte..].chars().next().unwrap()
}

pub fn is_ascii_whitespace(c: u8) -> bool {
    c == b'\n' || c == b'\r' || is_ascii_whitespace_no_nl(c)
}

pub fn is_ascii_whitespace_no_nl(c: u8) -> bool {
    c == b'\t' || c == 0x0b || c == 0x0c || c == b' '
}

// unusual among "scan" functions in that it scans from the _back_ of the string
// TODO: should also scan unicode whitespace?
pub fn scan_trailing_whitespace(data: &str) -> usize {
    match data.as_bytes().iter().rev().position(|&c| !is_ascii_whitespace_no_nl(c)) {
        Some(i) => i,
        None => data.len()
    }
}
<commit_msg>Parsing nestedddd<commit_after>#![feature(collections)]
#![feature(convert)]

use std::borrow::Cow;
use std::borrow::Cow::{Borrowed};

pub fn compile(sass: &str, style: &str) -> Result<String, &'static str> {
    let mut st = SassTokenizer::new(&sass);

    match style {
        "nested"     => Ok(nested_output(&mut st)),
        "compressed" => Ok(compressed_output(&mut st)),
        "expanded"   => Ok(expanded_output(&mut st)),
        "compact"    => Ok(compact_output(&mut st)),
        "debug"      => Ok(debug_output(&mut st)),
        _            => Err("Unknown style:. Please specify one of nested, compressed, expanded, or compact."),
    }
}

pub fn nested_output(tokenizer: &mut SassTokenizer) -> String {
    let mut output =  String::from_str("");
    while let Some(token) = tokenizer.next() {
        let print_token = match token {
            Event::Start(_) => continue,
            Event::Selector(name) => format!("{} {{", name),
            Event::Property(name, value) => format!("\n  {}: {};", name, value),
            Event::End(_) => format!(" }}\n"),
        };
        output.push_str(print_token.as_str());
    }
    output
}

pub fn compressed_output(tokenizer: &mut SassTokenizer) -> String {
    let mut output =  String::from_str("");
    while let Some(token) = tokenizer.next() {
        let print_token = match token {
            Event::Start(_) => continue,
            Event::Selector(name) => format!("{}{{", name),
            Event::Property(name, value) => format!("{}:{}", name, value),
            Event::End(_) => format!("}}"),
        };
        output.push_str(print_token.as_str());
    }
    output
}

pub fn expanded_output(tokenizer: &mut SassTokenizer) -> String {
    let mut output =  String::from_str("");
    while let Some(token) = tokenizer.next() {
        let print_token = match token {
            Event::Start(_) => continue,
            Event::Selector(name) => format!("{} {{", name),
            Event::Property(name, value) => format!("\n  {}: {};", name, value),
            Event::End(_) => format!("\n}}"),
        };
        output.push_str(print_token.as_str());
    }
    output
}

pub fn compact_output(tokenizer: &mut SassTokenizer) -> String {
    let mut output =  String::from_str("");
    while let Some(token) = tokenizer.next() {
        let print_token = match token {
            Event::Start(_) => continue,
            Event::Selector(name) => format!("{} {{", name),
            Event::Property(name, value) => format!(" {}: {};", name, value),
            Event::End(_) => format!(" }}"),
        };
        output.push_str(print_token.as_str());
    }
    output
}

pub fn debug_output(tokenizer: &mut SassTokenizer) -> String {
    let mut output =  String::from_str("");
    while let Some(token) = tokenizer.next() {
        output.push_str(format!("{:?}\n", token).as_str());
    }
    output
}

#[derive(PartialEq, Debug)]
pub enum State {
    StartRule,
    InSelectors,
    InProperties,
}

#[derive(Debug)]
pub enum Rule {
    SassRule,
}

#[derive(Debug)]
pub enum Event<'a> {
    Start(Rule),
    End(Rule),
    Selector(Cow<'a, str>),
    Property(Cow<'a, str>, Cow<'a, str>),
}

#[derive(Debug)]
pub struct SassTokenizer<'a> {
    sass: &'a str,
    offset: usize,
    stack: Vec<Rule>,
    state: State,
}

impl<'a> SassTokenizer<'a> {
    pub fn new(sass: &'a str) -> SassTokenizer<'a> {
        SassTokenizer {
            sass: &sass,
            offset: 0,
            stack: Vec::new(),
            state: State::StartRule,
        }
    }

    pub fn start_rule(&mut self) -> Option<Event<'a>> {
        self.skip_leading_whitespace();

        if self.offset == self.sass.len() {
            return None
        }

        let c = self.sass.as_bytes()[self.offset];
        if c == b'}' {
            self.offset += 1;
            return Some(self.end())
        }

        self.state = State::InSelectors;
        self.stack.push(Rule::SassRule);

        Some(Event::Start(Rule::SassRule))
    }

    fn end(&mut self) -> Event<'a> {
        let rule = self.stack.pop().unwrap();
        self.state = State::StartRule;
        Event::End(rule)
    }

    fn scan_while<F>(&mut self, data: &str, f: F) -> usize
            where F: Fn(u8) -> bool {
        match data.as_bytes().iter().position(|&c| !f(c)) {
            Some(i) => i,
            None => data.len()
        }
    }

    fn skip_leading_whitespace(&mut self) {
        let i = self.offset;
        self.offset += self.scan_while(&self.sass[i..self.sass.len()], is_ascii_whitespace);
    }

    pub fn next_property(&mut self) -> Event<'a> {
        self.skip_leading_whitespace();

        let bytes = self.sass.as_bytes();
        let name_beginning = self.offset;
        let mut i = name_beginning;
        let limit = self.sass.len();

        let c = bytes[i];
        if c == b'}' {
            self.offset += 1;
            return self.end()
        }

        while i < limit {
            match bytes[i..limit].iter().position(|&c| c == b':' || c == b'{') {
                Some(pos) => { i += pos; },
                None => { break; },
            }

            // Inefficient since we already skipped the whitespace and we'll have to
            // do it again but oh well
            let c = bytes[i];
            if c == b'{' {
                self.state = State::InSelectors;
                self.stack.push(Rule::SassRule);
                return Event::Start(Rule::SassRule)
            }

            let name_end = i;

            i += 1;
            self.offset = i;
            self.skip_leading_whitespace();

            let value_beginning = self.offset;
            i = value_beginning;

            while i < limit {
                match bytes[i..limit].iter().position(|&c| c == b';') {
                    Some(pos) => { i += pos; },
                    None => { i = limit; break; },
                }

                let value_end = i;
                self.offset = i + 1;

                self.skip_leading_whitespace();

                return Event::Property(
                    Borrowed(&self.sass[name_beginning..name_end]),
                    Borrowed(&self.sass[value_beginning..value_end])
                )

            }
        }
        self.offset = self.sass.len();
        Event::Property(Borrowed(""), Borrowed(""))
    }

    pub fn next_selector(&mut self) -> Event<'a> {
        let bytes = self.sass.as_bytes();
        let beginning = self.offset;
        let mut i = beginning;
        let limit = self.sass.len();

        while i < limit {
            match bytes[i..limit].iter().position(|&c| c == b',' || c == b'{') {
                Some(pos) => { i += pos; },
                None => { i = limit; break; },
            }

            let c = bytes[i];

            if c == b',' || c == b'{' {
                let n = scan_trailing_whitespace(&self.sass[beginning..i]);
                let end = i - n;
                if end > beginning {
                    if c == b'{' {
                        self.state = State::InProperties;
                    }
                    self.offset = i + 1;
                    return Event::Selector(Borrowed(&self.sass[beginning..end]));
                }
            }

            self.offset = i;
            if i > beginning {
                return Event::Selector(Borrowed(&self.sass[beginning..i]))
            }
            i += 1;
        }

        if i > beginning {
            self.offset = i;
            Event::Selector(Borrowed(&self.sass[beginning..i]))
        } else {
            self.end()
        }
    }
}

impl<'a> Iterator for SassTokenizer<'a> {
    type Item = Event<'a>;

    fn next(&mut self) -> Option<Event<'a>> {
        if self.offset < self.sass.len() {
            match self.state {
                State::StartRule => {
                    let ret = self.start_rule();
                    if ret.is_some() {
                        return ret
                    }
                },
                State::InSelectors => return Some(self.next_selector()),
                State::InProperties => return Some(self.next_property()),
            }
        }
        None
    }
}

pub fn char_at(s: &str, byte: usize) -> char {
    s[byte..].chars().next().unwrap()
}

pub fn is_ascii_whitespace(c: u8) -> bool {
    c == b'\n' || c == b'\r' || is_ascii_whitespace_no_nl(c)
}

pub fn is_ascii_whitespace_no_nl(c: u8) -> bool {
    c == b'\t' || c == 0x0b || c == 0x0c || c == b' '
}

// unusual among "scan" functions in that it scans from the _back_ of the string
// TODO: should also scan unicode whitespace?
pub fn scan_trailing_whitespace(data: &str) -> usize {
    match data.as_bytes().iter().rev().position(|&c| !is_ascii_whitespace_no_nl(c)) {
        Some(i) => i,
        None => data.len()
    }
}
<|endoftext|>


<commit_before>use crate::cargo::TestBinary;
use crate::config::*;
use crate::errors::*;
use crate::event_log::*;
use crate::path_utils::*;
use crate::process_handling::*;
use crate::report::report_coverage;
use crate::source_analysis::{LineAnalysis, SourceAnalysis};
use crate::statemachine::*;
use crate::test_loader::*;
use crate::traces::*;
use nix::unistd::*;
use std::collections::HashMap;
use std::env;
use std::ffi::CString;
use std::fs::create_dir_all;
use std::path::{Path, PathBuf};
use tracing::{debug, error, info, trace, warn};
use tracing_subscriber::{filter::LevelFilter, EnvFilter};

pub mod branching;
pub mod breakpoint;
pub mod cargo;
pub mod config;
pub mod errors;
pub mod event_log;
mod path_utils;
mod process_handling;
pub mod report;
mod source_analysis;
mod statemachine;
pub mod test_loader;
pub mod traces;

mod ptrace_control;

const RUST_LOG_ENV: &str = "RUST_LOG";

pub fn setup_logging(debug: bool, verbose: bool) {
    //By default, we set tarpaulin to info,debug,trace while all dependencies stay at INFO
    let base_exceptions = |env: EnvFilter| {
        if debug {
            env.add_directive("cargo_tarpaulin=trace".parse().unwrap())
        } else if verbose {
            env.add_directive("cargo_tarpaulin=debug".parse().unwrap())
        } else {
            env.add_directive("cargo_tarpaulin=info".parse().unwrap())
        }
        .add_directive(LevelFilter::INFO.into())
    };

    //If RUST_LOG is set, then first apply our default directives (which are controlled by debug an verbose).
    // Then RUST_LOG will overwrite those default directives.
    // e.g. `RUST_LOG="trace" cargo-tarpaulin` will end up printing TRACE for everything
    // `cargo-tarpaulin -v` will print DEBUG for tarpaulin and INFO for everything else.
    // `RUST_LOG="error" cargo-tarpaulin -v` will print ERROR for everything.
    let filter = match std::env::var_os(RUST_LOG_ENV).map(|s| s.into_string()) {
        Some(Ok(env)) => {
            let mut filter = base_exceptions(EnvFilter::new(""));
            for s in env.split(',').into_iter() {
                match s.parse() {
                    Ok(d) => filter = filter.add_directive(d),
                    Err(err) => println!("WARN ignoring log directive: `{}`: {}", s, err),
                };
            }
            filter
        }
        _ => base_exceptions(EnvFilter::from_env(RUST_LOG_ENV)),
    };

    tracing_subscriber::FmtSubscriber::builder()
        .with_max_level(tracing::Level::ERROR)
        .with_env_filter(filter)
        .init();

    debug!("set up logging");
}

pub fn trace(configs: &[Config]) -> Result<TraceMap, RunError> {
    let mut tracemap = TraceMap::new();
    let mut tarpaulin_result = Ok(());
    let mut ret = 0i32;
    let logger = if configs.iter().any(|c| c.dump_traces) {
        Some(EventLog::new())
    } else {
        None
    };
    let mut bad_threshold = None;
    for config in configs.iter() {
        if config.name == "report" {
            continue;
        }
        if let Some(log) = logger.as_ref() {
            let name = if config.name.is_empty() {
                "<anonymous>".to_string()
            } else {
                config.name.clone()
            };
            log.push_config(name);
        }
        let tgt = config.target_dir();
        if !tgt.exists() {
            let create_dir_result = create_dir_all(&tgt);
            if let Err(e) = create_dir_result {
                warn!("Failed to create target-dir {}", e);
            }
        }
        match launch_tarpaulin(config, &logger) {
            Ok((t, r)) => {
                ret |= r;
                bad_threshold = check_fail_threshold(&t, config);
                tracemap.merge(&t);
            }
            Err(e) => {
                error!("{}", e);
                tarpaulin_result = tarpaulin_result.and_then(|_| Err(e));
            }
        }
    }
    tracemap.dedup();
    if let Some(bad_limit) = bad_threshold {
        Err(bad_limit)
    } else if ret == 0 {
        tarpaulin_result.map(|_| tracemap)
    } else {
        Err(RunError::TestFailed)
    }
}

fn check_fail_threshold(traces: &TraceMap, config: &Config) -> Option<RunError> {
    let percent = traces.coverage_percentage() * 100.0;
    match config.fail_under.as_ref() {
        Some(limit) if percent < *limit => {
            let error = RunError::BelowThreshold(percent, *limit);
            error!("{}", error);
            Some(error)
        }
        _ => None,
    }
}

pub fn run(configs: &[Config]) -> Result<(), RunError> {
    let mut tracemap = trace(configs)?;
    if !configs.is_empty() {
        // Assumption: all configs are for the same project
        for dir in get_source_walker(&configs[0]) {
            tracemap.add_file(dir.path());
        }
    }
    if configs.len() == 1 {
        if !configs[0].no_run {
            report_coverage(&configs[0], &tracemap)?;
        }
    } else if !configs.is_empty() {
        let mut reported = false;
        for c in configs.iter() {
            if !c.no_run && c.name == "report" {
                reported = true;
                report_coverage(c, &tracemap)?;
                if let Some(e) = check_fail_threshold(&tracemap, c) {
                    return Err(e);
                }
            }
        }
        if !configs[0].no_run && !reported {
            report_coverage(&configs[0], &tracemap)?;
        }
    }

    Ok(())
}

/// Launches tarpaulin with the given configuration.
pub fn launch_tarpaulin(
    config: &Config,
    logger: &Option<EventLog>,
) -> Result<(TraceMap, i32), RunError> {
    if !config.name.is_empty() {
        info!("Running config {}", config.name);
    }

    info!("Running Tarpaulin");

    let mut result = TraceMap::new();
    let mut return_code = 0i32;
    info!("Building project");
    let executables = cargo::get_tests(config)?;
    if !config.no_run {
        let project_analysis = SourceAnalysis::get_analysis(config);
        let project_analysis = project_analysis.lines;
        for exe in &executables {
            if exe.should_panic() {
                info!("Running a test executable that is expected to panic");
            }
            let coverage = get_test_coverage(&exe, &project_analysis, config, false, logger)?;
            if let Some(res) = coverage {
                result.merge(&res.0);
                return_code |= if exe.should_panic() {
                    (res.1 == 0) as i32
                } else {
                    res.1
                };
            }
            if config.run_ignored {
                let coverage = get_test_coverage(&exe, &project_analysis, config, true, logger)?;
                if let Some(res) = coverage {
                    result.merge(&res.0);
                    return_code |= res.1;
                }
            }
        }
        result.dedup();
    }
    Ok((result, return_code))
}

/// Returns the coverage statistics for a test executable in the given workspace
pub fn get_test_coverage(
    test: &TestBinary,
    analysis: &HashMap<PathBuf, LineAnalysis>,
    config: &Config,
    ignored: bool,
    logger: &Option<EventLog>,
) -> Result<Option<(TraceMap, i32)>, RunError> {
    if !test.path().exists() {
        return Ok(None);
    }
    if let Err(e) = limit_affinity() {
        warn!("Failed to set processor affinity {}", e);
    }
    if let Some(log) = logger.as_ref() {
        log.push_binary(test.clone());
    }
    unsafe {
        match fork() {
            Ok(ForkResult::Parent { child }) => {
                match collect_coverage(test.path(), child, analysis, config, logger) {
                    Ok(t) => Ok(Some(t)),
                    Err(e) => Err(RunError::TestCoverage(e.to_string())),
                }
            }
            Ok(ForkResult::Child) => {
                info!("Launching test");
                execute_test(test, ignored, config)?;
                Ok(None)
            }
            Err(err) => Err(RunError::TestCoverage(format!(
                "Failed to run test {}, Error: {}",
                test.path().display(),
                err.to_string()
            ))),
        }
    }
}

/// Collects the coverage data from the launched test
fn collect_coverage(
    test_path: &Path,
    test: Pid,
    analysis: &HashMap<PathBuf, LineAnalysis>,
    config: &Config,
    logger: &Option<EventLog>,
) -> Result<(TraceMap, i32), RunError> {
    let mut ret_code = 0;
    let mut traces = generate_tracemap(test_path, analysis, config)?;
    {
        trace!("Test PID is {}", test);
        let (mut state, mut data) = create_state_machine(test, &mut traces, config, logger);
        loop {
            state = state.step(&mut data, config)?;
            if state.is_finished() {
                if let TestState::End(i) = state {
                    ret_code = i;
                }
                break;
            }
        }
    }
    Ok((traces, ret_code))
}

/// Launches the test executable
fn execute_test(test: &TestBinary, ignored: bool, config: &Config) -> Result<(), RunError> {
    let exec_path = CString::new(test.path().to_str().unwrap()).unwrap();
    info!("running {}", test.path().display());
    let _ = match test.manifest_dir() {
        Some(md) => env::set_current_dir(&md),
        None => env::set_current_dir(&config.root()),
    };

    let mut envars: Vec<CString> = Vec::new();

    for (key, value) in env::vars() {
        let mut temp = String::new();
        temp.push_str(key.as_str());
        temp.push('=');
        temp.push_str(value.as_str());
        envars.push(CString::new(temp).unwrap());
    }
    let mut argv = if ignored {
        vec![exec_path.clone(), CString::new("--ignored").unwrap()]
    } else {
        vec![exec_path.clone()]
    };
    if config.verbose {
        envars.push(CString::new("RUST_BACKTRACE=1").unwrap());
    }
    for s in &config.varargs {
        argv.push(CString::new(s.as_bytes()).unwrap_or_default());
    }

    if let Some(s) = test.pkg_name() {
        envars.push(CString::new(format!("CARGO_PKG_NAME={}", s)).unwrap_or_default());
    }
    if let Some(s) = test.pkg_version() {
        envars.push(CString::new(format!("CARGO_PKG_VERSION={}", s)).unwrap_or_default());
    }
    if let Some(s) = test.pkg_authors() {
        envars.push(CString::new(format!("CARGO_PKG_AUTHORS={}", s.join(":"))).unwrap_or_default());
    }
    if let Some(s) = test.manifest_dir() {
        envars
            .push(CString::new(format!("CARGO_MANIFEST_DIR={}", s.display())).unwrap_or_default());
    }

    execute(exec_path, &argv, envars.as_slice())
}
<commit_msg>Move trace log to a span<commit_after>use crate::cargo::TestBinary;
use crate::config::*;
use crate::errors::*;
use crate::event_log::*;
use crate::path_utils::*;
use crate::process_handling::*;
use crate::report::report_coverage;
use crate::source_analysis::{LineAnalysis, SourceAnalysis};
use crate::statemachine::*;
use crate::test_loader::*;
use crate::traces::*;
use nix::unistd::*;
use std::collections::HashMap;
use std::env;
use std::ffi::CString;
use std::fs::create_dir_all;
use std::path::{Path, PathBuf};
use tracing::{debug, error, info, trace_span, warn};
use tracing_subscriber::{filter::LevelFilter, EnvFilter};

pub mod branching;
pub mod breakpoint;
pub mod cargo;
pub mod config;
pub mod errors;
pub mod event_log;
mod path_utils;
mod process_handling;
pub mod report;
mod source_analysis;
mod statemachine;
pub mod test_loader;
pub mod traces;

mod ptrace_control;

const RUST_LOG_ENV: &str = "RUST_LOG";

pub fn setup_logging(debug: bool, verbose: bool) {
    //By default, we set tarpaulin to info,debug,trace while all dependencies stay at INFO
    let base_exceptions = |env: EnvFilter| {
        if debug {
            env.add_directive("cargo_tarpaulin=trace".parse().unwrap())
        } else if verbose {
            env.add_directive("cargo_tarpaulin=debug".parse().unwrap())
        } else {
            env.add_directive("cargo_tarpaulin=info".parse().unwrap())
        }
        .add_directive(LevelFilter::INFO.into())
    };

    //If RUST_LOG is set, then first apply our default directives (which are controlled by debug an verbose).
    // Then RUST_LOG will overwrite those default directives.
    // e.g. `RUST_LOG="trace" cargo-tarpaulin` will end up printing TRACE for everything
    // `cargo-tarpaulin -v` will print DEBUG for tarpaulin and INFO for everything else.
    // `RUST_LOG="error" cargo-tarpaulin -v` will print ERROR for everything.
    let filter = match std::env::var_os(RUST_LOG_ENV).map(|s| s.into_string()) {
        Some(Ok(env)) => {
            let mut filter = base_exceptions(EnvFilter::new(""));
            for s in env.split(',').into_iter() {
                match s.parse() {
                    Ok(d) => filter = filter.add_directive(d),
                    Err(err) => println!("WARN ignoring log directive: `{}`: {}", s, err),
                };
            }
            filter
        }
        _ => base_exceptions(EnvFilter::from_env(RUST_LOG_ENV)),
    };

    tracing_subscriber::FmtSubscriber::builder()
        .with_max_level(tracing::Level::ERROR)
        .with_env_filter(filter)
        .init();

    debug!("set up logging");
}

pub fn trace(configs: &[Config]) -> Result<TraceMap, RunError> {
    let mut tracemap = TraceMap::new();
    let mut tarpaulin_result = Ok(());
    let mut ret = 0i32;
    let logger = if configs.iter().any(|c| c.dump_traces) {
        Some(EventLog::new())
    } else {
        None
    };
    let mut bad_threshold = None;
    for config in configs.iter() {
        if config.name == "report" {
            continue;
        }
        if let Some(log) = logger.as_ref() {
            let name = if config.name.is_empty() {
                "<anonymous>".to_string()
            } else {
                config.name.clone()
            };
            log.push_config(name);
        }
        let tgt = config.target_dir();
        if !tgt.exists() {
            let create_dir_result = create_dir_all(&tgt);
            if let Err(e) = create_dir_result {
                warn!("Failed to create target-dir {}", e);
            }
        }
        match launch_tarpaulin(config, &logger) {
            Ok((t, r)) => {
                ret |= r;
                bad_threshold = check_fail_threshold(&t, config);
                tracemap.merge(&t);
            }
            Err(e) => {
                error!("{}", e);
                tarpaulin_result = tarpaulin_result.and_then(|_| Err(e));
            }
        }
    }
    tracemap.dedup();
    if let Some(bad_limit) = bad_threshold {
        Err(bad_limit)
    } else if ret == 0 {
        tarpaulin_result.map(|_| tracemap)
    } else {
        Err(RunError::TestFailed)
    }
}

fn check_fail_threshold(traces: &TraceMap, config: &Config) -> Option<RunError> {
    let percent = traces.coverage_percentage() * 100.0;
    match config.fail_under.as_ref() {
        Some(limit) if percent < *limit => {
            let error = RunError::BelowThreshold(percent, *limit);
            error!("{}", error);
            Some(error)
        }
        _ => None,
    }
}

pub fn run(configs: &[Config]) -> Result<(), RunError> {
    let mut tracemap = trace(configs)?;
    if !configs.is_empty() {
        // Assumption: all configs are for the same project
        for dir in get_source_walker(&configs[0]) {
            tracemap.add_file(dir.path());
        }
    }
    if configs.len() == 1 {
        if !configs[0].no_run {
            report_coverage(&configs[0], &tracemap)?;
        }
    } else if !configs.is_empty() {
        let mut reported = false;
        for c in configs.iter() {
            if !c.no_run && c.name == "report" {
                reported = true;
                report_coverage(c, &tracemap)?;
                if let Some(e) = check_fail_threshold(&tracemap, c) {
                    return Err(e);
                }
            }
        }
        if !configs[0].no_run && !reported {
            report_coverage(&configs[0], &tracemap)?;
        }
    }

    Ok(())
}

/// Launches tarpaulin with the given configuration.
pub fn launch_tarpaulin(
    config: &Config,
    logger: &Option<EventLog>,
) -> Result<(TraceMap, i32), RunError> {
    if !config.name.is_empty() {
        info!("Running config {}", config.name);
    }

    info!("Running Tarpaulin");

    let mut result = TraceMap::new();
    let mut return_code = 0i32;
    info!("Building project");
    let executables = cargo::get_tests(config)?;
    if !config.no_run {
        let project_analysis = SourceAnalysis::get_analysis(config);
        let project_analysis = project_analysis.lines;
        for exe in &executables {
            if exe.should_panic() {
                info!("Running a test executable that is expected to panic");
            }
            let coverage = get_test_coverage(&exe, &project_analysis, config, false, logger)?;
            if let Some(res) = coverage {
                result.merge(&res.0);
                return_code |= if exe.should_panic() {
                    (res.1 == 0) as i32
                } else {
                    res.1
                };
            }
            if config.run_ignored {
                let coverage = get_test_coverage(&exe, &project_analysis, config, true, logger)?;
                if let Some(res) = coverage {
                    result.merge(&res.0);
                    return_code |= res.1;
                }
            }
        }
        result.dedup();
    }
    Ok((result, return_code))
}

/// Returns the coverage statistics for a test executable in the given workspace
pub fn get_test_coverage(
    test: &TestBinary,
    analysis: &HashMap<PathBuf, LineAnalysis>,
    config: &Config,
    ignored: bool,
    logger: &Option<EventLog>,
) -> Result<Option<(TraceMap, i32)>, RunError> {
    if !test.path().exists() {
        return Ok(None);
    }
    if let Err(e) = limit_affinity() {
        warn!("Failed to set processor affinity {}", e);
    }
    if let Some(log) = logger.as_ref() {
        log.push_binary(test.clone());
    }
    unsafe {
        match fork() {
            Ok(ForkResult::Parent { child }) => {
                match collect_coverage(test.path(), child, analysis, config, logger) {
                    Ok(t) => Ok(Some(t)),
                    Err(e) => Err(RunError::TestCoverage(e.to_string())),
                }
            }
            Ok(ForkResult::Child) => {
                info!("Launching test");
                execute_test(test, ignored, config)?;
                Ok(None)
            }
            Err(err) => Err(RunError::TestCoverage(format!(
                "Failed to run test {}, Error: {}",
                test.path().display(),
                err.to_string()
            ))),
        }
    }
}

/// Collects the coverage data from the launched test
fn collect_coverage(
    test_path: &Path,
    test: Pid,
    analysis: &HashMap<PathBuf, LineAnalysis>,
    config: &Config,
    logger: &Option<EventLog>,
) -> Result<(TraceMap, i32), RunError> {
    let mut ret_code = 0;
    let mut traces = generate_tracemap(test_path, analysis, config)?;
    {
        let span = trace_span!("Collect coverage", pid=%test);
        let _enter = span.enter();
        let (mut state, mut data) = create_state_machine(test, &mut traces, config, logger);
        loop {
            state = state.step(&mut data, config)?;
            if state.is_finished() {
                if let TestState::End(i) = state {
                    ret_code = i;
                }
                break;
            }
        }
    }
    Ok((traces, ret_code))
}

/// Launches the test executable
fn execute_test(test: &TestBinary, ignored: bool, config: &Config) -> Result<(), RunError> {
    let exec_path = CString::new(test.path().to_str().unwrap()).unwrap();
    info!("running {}", test.path().display());
    let _ = match test.manifest_dir() {
        Some(md) => env::set_current_dir(&md),
        None => env::set_current_dir(&config.root()),
    };

    let mut envars: Vec<CString> = Vec::new();

    for (key, value) in env::vars() {
        let mut temp = String::new();
        temp.push_str(key.as_str());
        temp.push('=');
        temp.push_str(value.as_str());
        envars.push(CString::new(temp).unwrap());
    }
    let mut argv = if ignored {
        vec![exec_path.clone(), CString::new("--ignored").unwrap()]
    } else {
        vec![exec_path.clone()]
    };
    if config.verbose {
        envars.push(CString::new("RUST_BACKTRACE=1").unwrap());
    }
    for s in &config.varargs {
        argv.push(CString::new(s.as_bytes()).unwrap_or_default());
    }

    if let Some(s) = test.pkg_name() {
        envars.push(CString::new(format!("CARGO_PKG_NAME={}", s)).unwrap_or_default());
    }
    if let Some(s) = test.pkg_version() {
        envars.push(CString::new(format!("CARGO_PKG_VERSION={}", s)).unwrap_or_default());
    }
    if let Some(s) = test.pkg_authors() {
        envars.push(CString::new(format!("CARGO_PKG_AUTHORS={}", s.join(":"))).unwrap_or_default());
    }
    if let Some(s) = test.manifest_dir() {
        envars
            .push(CString::new(format!("CARGO_MANIFEST_DIR={}", s.display())).unwrap_or_default());
    }

    execute(exec_path, &argv, envars.as_slice())
}
<|endoftext|>


<commit_before>//! Open URLs in the web browsers available on a platform
//!
//! Inspired by the [webbrowser](https://docs.python.org/2/library/webbrowser.html) python library
//!
//! #Examples
//!
//! ```
//! use webbrowser;
//!
//! if webbrowser::open("http://github.com").is_ok() {
//!     // ...
//! }
//! ```
//!
//! Currently state of platform support is:
//!
//! * macos => default, as well as browsers listed under [Browser](enum.Browser.html)
//! * windows => default browser only
//! * linux => default browser only (uses $BROWSER env var, failing back to xdg-open, gvfs-open and
//! gnome-open, in that order)
//! * android => not supported right now
//! * ios => not supported right now
//!
//! Important note:
//!
//! * This library requires availability of browsers and a graphical environment during runtime
//! * `cargo test` will actually open the browser locally

use std::process::{Command, Output};
use std::io::{Result, Error, ErrorKind};
use std::env;

#[derive(Debug)]
/// Browser types available
pub enum Browser {
    Default,
    Firefox,
    InternetExplorer,
    Chrome,
    Opera,
    Safari
}

/// Opens the URL on the default browser of this platform
///
/// Returns Ok(..) so long as the browser invocation was successful. An Err(..) is returned only if
/// there was an error in running the command, or if the browser was not found
///
/// # Examples
/// ```
/// use webbrowser;
///
/// if webbrowser::open("http://github.com").is_ok() {
///     // ...
/// }
/// ```
pub fn open(url: &str) -> Result<Output> {
    open_browser(Browser::Default, url)
}

/// Opens the specified URL on the specific browser (if available) requested. Return semantics are
/// the same as for [open](fn.open.html)
///
/// # Examples
/// ```
/// use webbrowser::{open_browser, Browser};
///
/// if open_browser(Browser::Firefox, "http://github.com").is_ok() {
///     // ...
/// }
/// ```
pub fn open_browser(browser: Browser, url: &str) -> Result<Output> {
    let os = std::env::consts::OS;
    match os {
        "macos" => open_on_macos(browser, url),
        "windows" => open_on_windows(browser, url),
        "linux" => open_on_linux(browser, url),
        _ => Err(Error::new(ErrorKind::NotFound, format!("Platform {} not yet supported by this library", os)))
    }
}

/// Deal with opening of browsers on Mac OS X, using `open` command
fn open_on_macos(browser: Browser, url: &str) -> Result<Output> {
    let mut cmd = Command::new("open");
    match browser {
        Browser::Default => cmd.arg(url).output(),
        _ => {
            let app: Option<&str> = match browser {
                Browser::Firefox => Some("Firefox"),
                Browser::Chrome => Some("Google Chrome"),
                Browser::Opera => Some("Opera"),
                Browser::Safari => Some("Safari"),
                _ => None
            };
            match app {
                Some(name) => cmd.arg("-a").arg(name).arg(url).output(),
                None => Err(Error::new(ErrorKind::NotFound, format!("Unsupported browser {:?}", browser)))
            }
        }
    }
}

/// Deal with opening of browsers on Windows, using `start` command
fn open_on_windows(browser: Browser, url: &str) -> Result<Output> {
    match browser {
        Browser::Default => Command::new("cmd").arg("/C").arg("start").arg(url).output(),
        _ => Err(Error::new(
                ErrorKind::NotFound,
                "Only the default browser is supported on this platform right now"
            ))
    }
}

/// Deal with opening of browsers on Linux - currently supports only the default browser
///
/// The mechanism of opening the default browser is as follows:
/// 1. Attempt to use $BROWSER env var if available
/// 2. Attempt to open the url via xdg-open, gvfs-open, gnome-open, respectively, whichever works
///    first
fn open_on_linux(browser: Browser, url: &str) -> Result<Output> {
    match browser {
        Browser::Default => open_on_linux_using_browser_env(url)
            .or_else(|_| -> Result<Output> {Command::new("xdg-open").arg(url).output()})
            .or_else(|_| -> Result<Output> {Command::new("gvfs-open").arg(url).output()})
            .or_else(|_| -> Result<Output> {Command::new("gnome-open").arg(url).output()}),
        _ => Err(Error::new(
                ErrorKind::NotFound,
                "Only the default browser is supported on this platform right now"
            ))
    }
}

/// Open on Linux using the $BROWSER env var
fn open_on_linux_using_browser_env(url: &str) -> Result<Output> {
    let browsers = try!(env::var("BROWSER").map_err(|_| -> Error {Error::new(ErrorKind::NotFound, format!("BROWSER env not set"))}));
    for browser in browsers.split(':') { // $BROWSER can contain ':' delimited options, each representing a potential browser command line
        if !browser.is_empty() {
            // each browser command can have %s to represent URL, while %c needs to be replaced
            // with ':' and %% with '%'
            let cmdline = browser.replace("%s", url).replace("%c", ":").replace("%%", "%");
            let cmdarr: Vec<&str> = cmdline.split_whitespace().collect();
            let mut cmd = Command::new(&cmdarr[0]);
            if cmdarr.len() > 1 {
                cmd.args(&cmdarr[1..cmdarr.len()]);
            }
            if !browser.contains("%s") {
                // append the url as an argument only if it was not already set via %s
                cmd.arg(url);
            }
            if let Ok(output) = cmd.output() {
                return Ok(output);
            }
        }
    }
    return Err(Error::new(ErrorKind::NotFound, "No valid command in $BROWSER"));
}

#[test]
fn test_open_default() {
    assert!(open("http://github.com").is_ok());
}

#[test]
#[cfg(target_os = "macos")]
fn test_open_browser() {
    assert!(open_browser(Browser::Firefox, "http://github.com").is_ok());
}
<commit_msg>Change OS test to use conditional compilation<commit_after>//! Open URLs in the web browsers available on a platform
//!
//! Inspired by the [webbrowser](https://docs.python.org/2/library/webbrowser.html) python library
//!
//! #Examples
//!
//! ```
//! use webbrowser;
//!
//! if webbrowser::open("http://github.com").is_ok() {
//!     // ...
//! }
//! ```
//!
//! Currently state of platform support is:
//!
//! * macos => default, as well as browsers listed under [Browser](enum.Browser.html)
//! * windows => default browser only
//! * linux => default browser only (uses $BROWSER env var, failing back to xdg-open, gvfs-open and
//! gnome-open, in that order)
//! * android => not supported right now
//! * ios => not supported right now
//!
//! Important note:
//!
//! * This library requires availability of browsers and a graphical environment during runtime
//! * `cargo test` will actually open the browser locally

use std::process::{Command, Output};
use std::io::{Result, Error, ErrorKind};
use std::env;

#[derive(Debug)]
/// Browser types available
pub enum Browser {
    Default,
    Firefox,
    InternetExplorer,
    Chrome,
    Opera,
    Safari
}

/// Opens the URL on the default browser of this platform
///
/// Returns Ok(..) so long as the browser invocation was successful. An Err(..) is returned only if
/// there was an error in running the command, or if the browser was not found
///
/// # Examples
/// ```
/// use webbrowser;
///
/// if webbrowser::open("http://github.com").is_ok() {
///     // ...
/// }
/// ```
pub fn open(url: &str) -> Result<Output> {
    open_browser(Browser::Default, url)
}

/// Opens the specified URL on the specific browser (if available) requested. Return semantics are
/// the same as for [open](fn.open.html)
///
/// # Examples
/// ```
/// use webbrowser::{open_browser, Browser};
///
/// if open_browser(Browser::Firefox, "http://github.com").is_ok() {
///     // ...
/// }
/// ```
/// Deal with opening of browsers on Windows, using `start` command
#[cfg(target_os = "windows")]
pub fn open_browser(browser: Browser, url: &str) -> Result<Output> {
    match browser {
        Browser::Default => Command::new("cmd").arg("/C").arg("start").arg(url).output(),
        _ => Err(Error::new(
            ErrorKind::NotFound,
            "Only the default browser is supported on this platform right now"
        ))
    }
}

/// Deal with opening of browsers on Mac OS X, using `open` command
#[cfg(target_os = "macos")]
fn open_browser(browser: Browser, url: &str) -> Result<Output> {
    let mut cmd = Command::new("open");
    match browser {
        Browser::Default => cmd.arg(url).output(),
        _ => {
            let app: Option<&str> = match browser {
                Browser::Firefox => Some("Firefox"),
                Browser::Chrome => Some("Google Chrome"),
                Browser::Opera => Some("Opera"),
                Browser::Safari => Some("Safari"),
                _ => None
            };
            match app {
                Some(name) => cmd.arg("-a").arg(name).arg(url).output(),
                None => Err(Error::new(ErrorKind::NotFound, format!("Unsupported browser {:?}", browser)))
            }
        }
    }
}

/*pub fn open_browser(browser: Browser, url: &str) -> Result<Output> {
    let os = std::env::consts::OS;
    match os {
        "macos" => open_on_macos(browser, url),
        "windows" => open_on_windows(browser, url),
        "linux" => open_on_linux(browser, url),
        _ => Err(Error::new(ErrorKind::NotFound, format!("Platform {} not yet supported by this library", os)))
    }
}

/// Deal with opening of browsers on Mac OS X, using `open` command
fn open_on_macos(browser: Browser, url: &str) -> Result<Output> {
    let mut cmd = Command::new("open");
    match browser {
        Browser::Default => cmd.arg(url).output(),
        _ => {
            let app: Option<&str> = match browser {
                Browser::Firefox => Some("Firefox"),
                Browser::Chrome => Some("Google Chrome"),
                Browser::Opera => Some("Opera"),
                Browser::Safari => Some("Safari"),
                _ => None
            };
            match app {
                Some(name) => cmd.arg("-a").arg(name).arg(url).output(),
                None => Err(Error::new(ErrorKind::NotFound, format!("Unsupported browser {:?}", browser)))
            }
        }
    }
}

/// Deal with opening of browsers on Windows, using `start` command
fn open_on_windows(browser: Browser, url: &str) -> Result<Output> {
    match browser {
        Browser::Default => Command::new("cmd").arg("/C").arg("start").arg(url).output(),
        _ => Err(Error::new(
                ErrorKind::NotFound,
                "Only the default browser is supported on this platform right now"
            ))
    }
}

*/
/// Deal with opening of browsers on Linux - currently supports only the default browser
///
/// The mechanism of opening the default browser is as follows:
/// 1. Attempt to use $BROWSER env var if available
/// 2. Attempt to open the url via xdg-open, gvfs-open, gnome-open, respectively, whichever works
///    first
#[cfg(target_os = "linux")]
fn open_browser(browser: Browser, url: &str) -> Result<Output> {
    match browser {
        Browser::Default => open_on_linux_using_browser_env(url)
            .or_else(|_| -> Result<Output> {Command::new("xdg-open").arg(url).output()})
            .or_else(|_| -> Result<Output> {Command::new("gvfs-open").arg(url).output()})
            .or_else(|_| -> Result<Output> {Command::new("gnome-open").arg(url).output()}),
        _ => Err(Error::new(
                ErrorKind::NotFound,
                "Only the default browser is supported on this platform right now"
            ))
    }
}

/// Open on Linux using the $BROWSER env var
#[cfg(target_os = "linux")]
fn open_on_linux_using_browser_env(url: &str) -> Result<Output> {
    let browsers = try!(env::var("BROWSER").map_err(|_| -> Error {Error::new(ErrorKind::NotFound, format!("BROWSER env not set"))}));
    for browser in browsers.split(':') { // $BROWSER can contain ':' delimited options, each representing a potential browser command line
        if !browser.is_empty() {
            // each browser command can have %s to represent URL, while %c needs to be replaced
            // with ':' and %% with '%'
            let cmdline = browser.replace("%s", url).replace("%c", ":").replace("%%", "%");
            let cmdarr: Vec<&str> = cmdline.split_whitespace().collect();
            let mut cmd = Command::new(&cmdarr[0]);
            if cmdarr.len() > 1 {
                cmd.args(&cmdarr[1..cmdarr.len()]);
            }
            if !browser.contains("%s") {
                // append the url as an argument only if it was not already set via %s
                cmd.arg(url);
            }
            if let Ok(output) = cmd.output() {
                return Ok(output);
            }
        }
    }
    return Err(Error::new(ErrorKind::NotFound, "No valid command in $BROWSER"));
}

#[cfg(not(any(target_os = "windows", target_os = "macos", target_os = "linux")))]
compile_error!("Only Windows, Mac OS and Linux are currently supported");

#[test]
fn test_open_default() {
    assert!(open("http://github.com").is_ok());
}

#[test]
#[cfg(target_os = "macos")]
fn test_open_browser() {
    assert!(open_browser(Browser::Firefox, "http://github.com").is_ok());
}
<|endoftext|>


<commit_before>//! *lamport* implements one-time hash-based signatures using the Lamport signature scheme.

#![deny(
    missing_docs,
    missing_debug_implementations, missing_copy_implementations,
    trivial_casts, trivial_numeric_casts,
    unsafe_code, unstable_features,
    unused_import_braces, unused_qualifications
)]

extern crate ring;
extern crate rand;

use std::hash::{Hash, Hasher};
use rand::OsRng;
use rand::Rng;
use ring::digest::{Algorithm, Context};

/// A type alias defining a Lamport signature
pub type LamportSignatureData = Vec<Vec<u8>>;

/// A one-time signing public key
#[derive(Clone, Debug)]
pub struct PublicKey {
    zero_values: Vec<Vec<u8>>,
    one_values: Vec<Vec<u8>>,
    algorithm: &'static Algorithm,
}

impl PartialEq for PublicKey {
    #[allow(trivial_casts)]
    fn eq(&self, other: &Self) -> bool {
       self.zero_values == other.zero_values &&
       self.one_values == other.one_values &&
       self.algorithm as *const Algorithm as usize == other.algorithm as *const Algorithm as usize
   }
}

impl Hash for PublicKey {
    #[allow(trivial_casts)]
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.zero_values.hash(state);
        self.one_values.hash(state);
        (self.algorithm as *const Algorithm as usize).hash(state);
    }
}

impl Eq for PublicKey {}

/// A one-time signing private key
#[derive(Clone, Debug)]
pub struct PrivateKey {
    // For a n bits hash function: (n * n/8 bytes) for zero_values and one_values
    zero_values: Vec<Vec<u8>>,
    one_values: Vec<Vec<u8>>,
    algorithm: &'static Algorithm,
    used: bool,
}

impl From<PublicKey> for Vec<u8> {
    fn from(original: PublicKey) -> Vec<u8> {
        original.to_bytes()
    }
}

impl PublicKey {
    /// Intializes a public key with a byte vector.
    /// Returns `None` if it couldn't parse the provided data
    pub fn from_vec(vec: Vec<u8>, algorithm: &'static Algorithm) -> Option<PublicKey> {
        let size = vec.len();
        let hash_output_size = algorithm.output_len;

        let mut zero_values_merged = vec;
        let one_values_merged = zero_values_merged.split_off(size / 2);

        let mut zero_values = Vec::new();
        for i in (0..zero_values_merged.len()).filter(|x| x % hash_output_size == 0) {
            // indexes for heads
            let mut sub_vec = Vec::new();
            for j in 0..hash_output_size {
                sub_vec.push(zero_values_merged[i + j]);
            }

            zero_values.push(sub_vec);
        }

        let mut one_values = Vec::new();
        for i in (0..one_values_merged.len()).filter(|x| x % hash_output_size == 0) {
            // indexes for heads
            let mut sub_vec = Vec::new();
            for j in 0..hash_output_size {
                sub_vec.push(one_values_merged[i + j]);
            }

            one_values.push(sub_vec);
        }

        Some(PublicKey {
            zero_values: zero_values,
            one_values: one_values,
            algorithm: algorithm,
        })
    }

    /// Serializes a public key into a byte vector
    pub fn to_bytes(&self) -> Vec<u8> {
        self.zero_values.iter().chain(self.one_values.iter()).fold(Vec::new(), |mut acc, i| {
            acc.append(&mut i.clone());
            acc
        })
    }

    /// Verifies that the signature of the data is correctly signed with the given key
    pub fn verify_signature(&self, signature: &LamportSignatureData, data: &[u8]) -> bool {
        let mut context = Context::new(self.algorithm);
        context.update(data);
        let result = context.finish();
        let data_hash = result.as_ref();

        for (i, byte) in data_hash.iter().enumerate() {
            for j in 0..8 {
                let offset = i * 8 + j;
                if (byte & (1 << j)) > 0 {
                    let mut context = Context::new(self.algorithm);
                    context.update(signature[offset].as_slice());
                    let hashed_value = Vec::from(context.finish().as_ref());

                    if hashed_value != self.one_values[offset] {
                        return false;
                    }
                } else {
                    let mut context = Context::new(self.algorithm);
                    context.update(signature[offset].as_slice());
                    let hashed_value = Vec::from(context.finish().as_ref());

                    if hashed_value != self.zero_values[offset] {
                        return false;
                    }
                }
            }
        }

        true
    }
}

impl PrivateKey {
    /// Generates a new random one-time signing key. This method can panic if OS RNG fails
    pub fn new(algorithm: &'static Algorithm) -> PrivateKey {
        let generate_bit_hash_values = || -> Vec<Vec<u8>> {
            let mut rng = match OsRng::new() {
                Ok(g) => g,
                Err(e) => panic!("Failed to obtain OS RNG: {}", e),
            };
            let buffer_byte = vec![0u8; algorithm.output_len];
            let mut buffer = vec![buffer_byte; algorithm.output_len * 8];

            for hash in &mut buffer {
                rng.fill_bytes(hash)
            }

            buffer
        };

        let zero_values = generate_bit_hash_values();
        let one_values  = generate_bit_hash_values();

        PrivateKey {
            zero_values: zero_values,
            one_values: one_values,
            algorithm: algorithm,
            used: false,
        }
    }

    /// Returns the public key associated with this private key
    pub fn public_key(&self) -> PublicKey {
        let hash_values = |x: &Vec<Vec<u8>>| -> Vec<Vec<u8>> {
            let buffer_byte = vec![0u8; self.algorithm.output_len];
            let mut buffer  = vec![buffer_byte; self.algorithm.output_len * 8];

            for i in 0 .. self.algorithm.output_len * 8 {
                let mut context = Context::new(self.algorithm);
                context.update(x[i].as_slice());
                buffer[i] = Vec::from(context.finish().as_ref());
            }

            buffer
        };

        let hashed_zero_values = hash_values(&self.zero_values);
        let hashed_one_values  = hash_values(&self.one_values);

        PublicKey {
            zero_values: hashed_zero_values,
            one_values: hashed_one_values,
            algorithm: self.algorithm,
        }
    }

    /// Signs the data with the private key and returns the result if successful.
    /// If unsuccesful, an explanation string is returned
    pub fn sign(&mut self, data: &[u8]) -> Result<LamportSignatureData, &'static str> {
        if self.used {
            return Err("Attempting to sign more than once.");
        }

        let mut context = Context::new(self.algorithm);
        context.update(data);
        let result    = context.finish();
        let data_hash = result.as_ref();

        let signature_len = data_hash.len() * 8;
        let mut signature = Vec::with_capacity(signature_len);

        for (i, byte) in data_hash.iter().enumerate() {
            for j in 0..8 {
                let offset = i * 8 + j;
                if (byte & (1 << j)) > 0 {
                    // Bit is 1
                    signature.push(self.one_values[offset].clone());
                } else {
                    // Bit is 0
                    signature.push(self.zero_values[offset].clone());
                }
            }
        }
        self.used = true;
        Ok(signature)
    }
}

impl Drop for PrivateKey {
    fn drop(&mut self) {
        let zeroize_vector = |vector: &mut Vec<Vec<u8>>| {
            for v2 in vector.iter_mut() {
                for byte in v2.iter_mut() {
                    *byte = 0;
                }
            }
        };

        zeroize_vector(&mut self.zero_values);
        zeroize_vector(&mut self.one_values);
    }
}

impl PartialEq for PrivateKey {
    // ⚠ This is not a constant-time implementation
    fn eq(&self, other: &PrivateKey) -> bool {
        if self.one_values.len() != other.one_values.len() {
            return false;
        }
        if self.zero_values.len() != other.zero_values.len() {
            return false;
        }

        for i in 0..self.zero_values.len() {
            if self.zero_values[i] != other.zero_values[i] ||
               self.one_values[i] != other.one_values[i] {
                return false;
            }
        }
        true
    }
}

#[cfg(test)]
pub mod tests;
<commit_msg>Remove unnecessary casts.<commit_after>//! *lamport* implements one-time hash-based signatures using the Lamport signature scheme.

#![deny(
    missing_docs,
    missing_debug_implementations, missing_copy_implementations,
    trivial_casts, trivial_numeric_casts,
    unsafe_code, unstable_features,
    unused_import_braces, unused_qualifications
)]

extern crate ring;
extern crate rand;

use std::hash::{Hash, Hasher};
use rand::OsRng;
use rand::Rng;
use ring::digest::{Algorithm, Context};

/// A type alias defining a Lamport signature
pub type LamportSignatureData = Vec<Vec<u8>>;

/// A one-time signing public key
#[derive(Clone, Debug)]
pub struct PublicKey {
    zero_values: Vec<Vec<u8>>,
    one_values: Vec<Vec<u8>>,
    algorithm: &'static Algorithm,
}

impl PartialEq for PublicKey {
    #[allow(trivial_casts)]
    fn eq(&self, other: &Self) -> bool {
       self.zero_values == other.zero_values &&
       self.one_values == other.one_values &&
       self.algorithm as *const Algorithm == other.algorithm as *const Algorithm
   }
}

impl Hash for PublicKey {
    #[allow(trivial_casts)]
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.zero_values.hash(state);
        self.one_values.hash(state);
        (self.algorithm as *const Algorithm).hash(state);
    }
}

impl Eq for PublicKey {}

/// A one-time signing private key
#[derive(Clone, Debug)]
pub struct PrivateKey {
    // For a n bits hash function: (n * n/8 bytes) for zero_values and one_values
    zero_values: Vec<Vec<u8>>,
    one_values: Vec<Vec<u8>>,
    algorithm: &'static Algorithm,
    used: bool,
}

impl From<PublicKey> for Vec<u8> {
    fn from(original: PublicKey) -> Vec<u8> {
        original.to_bytes()
    }
}

impl PublicKey {
    /// Intializes a public key with a byte vector.
    /// Returns `None` if it couldn't parse the provided data
    pub fn from_vec(vec: Vec<u8>, algorithm: &'static Algorithm) -> Option<PublicKey> {
        let size = vec.len();
        let hash_output_size = algorithm.output_len;

        let mut zero_values_merged = vec;
        let one_values_merged = zero_values_merged.split_off(size / 2);

        let mut zero_values = Vec::new();
        for i in (0..zero_values_merged.len()).filter(|x| x % hash_output_size == 0) {
            // indexes for heads
            let mut sub_vec = Vec::new();
            for j in 0..hash_output_size {
                sub_vec.push(zero_values_merged[i + j]);
            }

            zero_values.push(sub_vec);
        }

        let mut one_values = Vec::new();
        for i in (0..one_values_merged.len()).filter(|x| x % hash_output_size == 0) {
            // indexes for heads
            let mut sub_vec = Vec::new();
            for j in 0..hash_output_size {
                sub_vec.push(one_values_merged[i + j]);
            }

            one_values.push(sub_vec);
        }

        Some(PublicKey {
            zero_values: zero_values,
            one_values: one_values,
            algorithm: algorithm,
        })
    }

    /// Serializes a public key into a byte vector
    pub fn to_bytes(&self) -> Vec<u8> {
        self.zero_values.iter().chain(self.one_values.iter()).fold(Vec::new(), |mut acc, i| {
            acc.append(&mut i.clone());
            acc
        })
    }

    /// Verifies that the signature of the data is correctly signed with the given key
    pub fn verify_signature(&self, signature: &LamportSignatureData, data: &[u8]) -> bool {
        let mut context = Context::new(self.algorithm);
        context.update(data);
        let result = context.finish();
        let data_hash = result.as_ref();

        for (i, byte) in data_hash.iter().enumerate() {
            for j in 0..8 {
                let offset = i * 8 + j;
                if (byte & (1 << j)) > 0 {
                    let mut context = Context::new(self.algorithm);
                    context.update(signature[offset].as_slice());
                    let hashed_value = Vec::from(context.finish().as_ref());

                    if hashed_value != self.one_values[offset] {
                        return false;
                    }
                } else {
                    let mut context = Context::new(self.algorithm);
                    context.update(signature[offset].as_slice());
                    let hashed_value = Vec::from(context.finish().as_ref());

                    if hashed_value != self.zero_values[offset] {
                        return false;
                    }
                }
            }
        }

        true
    }
}

impl PrivateKey {
    /// Generates a new random one-time signing key. This method can panic if OS RNG fails
    pub fn new(algorithm: &'static Algorithm) -> PrivateKey {
        let generate_bit_hash_values = || -> Vec<Vec<u8>> {
            let mut rng = match OsRng::new() {
                Ok(g) => g,
                Err(e) => panic!("Failed to obtain OS RNG: {}", e),
            };
            let buffer_byte = vec![0u8; algorithm.output_len];
            let mut buffer = vec![buffer_byte; algorithm.output_len * 8];

            for hash in &mut buffer {
                rng.fill_bytes(hash)
            }

            buffer
        };

        let zero_values = generate_bit_hash_values();
        let one_values  = generate_bit_hash_values();

        PrivateKey {
            zero_values: zero_values,
            one_values: one_values,
            algorithm: algorithm,
            used: false,
        }
    }

    /// Returns the public key associated with this private key
    pub fn public_key(&self) -> PublicKey {
        let hash_values = |x: &Vec<Vec<u8>>| -> Vec<Vec<u8>> {
            let buffer_byte = vec![0u8; self.algorithm.output_len];
            let mut buffer  = vec![buffer_byte; self.algorithm.output_len * 8];

            for i in 0 .. self.algorithm.output_len * 8 {
                let mut context = Context::new(self.algorithm);
                context.update(x[i].as_slice());
                buffer[i] = Vec::from(context.finish().as_ref());
            }

            buffer
        };

        let hashed_zero_values = hash_values(&self.zero_values);
        let hashed_one_values  = hash_values(&self.one_values);

        PublicKey {
            zero_values: hashed_zero_values,
            one_values: hashed_one_values,
            algorithm: self.algorithm,
        }
    }

    /// Signs the data with the private key and returns the result if successful.
    /// If unsuccesful, an explanation string is returned
    pub fn sign(&mut self, data: &[u8]) -> Result<LamportSignatureData, &'static str> {
        if self.used {
            return Err("Attempting to sign more than once.");
        }

        let mut context = Context::new(self.algorithm);
        context.update(data);
        let result    = context.finish();
        let data_hash = result.as_ref();

        let signature_len = data_hash.len() * 8;
        let mut signature = Vec::with_capacity(signature_len);

        for (i, byte) in data_hash.iter().enumerate() {
            for j in 0..8 {
                let offset = i * 8 + j;
                if (byte & (1 << j)) > 0 {
                    // Bit is 1
                    signature.push(self.one_values[offset].clone());
                } else {
                    // Bit is 0
                    signature.push(self.zero_values[offset].clone());
                }
            }
        }
        self.used = true;
        Ok(signature)
    }
}

impl Drop for PrivateKey {
    fn drop(&mut self) {
        let zeroize_vector = |vector: &mut Vec<Vec<u8>>| {
            for v2 in vector.iter_mut() {
                for byte in v2.iter_mut() {
                    *byte = 0;
                }
            }
        };

        zeroize_vector(&mut self.zero_values);
        zeroize_vector(&mut self.one_values);
    }
}

impl PartialEq for PrivateKey {
    // ⚠ This is not a constant-time implementation
    fn eq(&self, other: &PrivateKey) -> bool {
        if self.one_values.len() != other.one_values.len() {
            return false;
        }
        if self.zero_values.len() != other.zero_values.len() {
            return false;
        }

        for i in 0..self.zero_values.len() {
            if self.zero_values[i] != other.zero_values[i] ||
               self.one_values[i] != other.one_values[i] {
                return false;
            }
        }
        true
    }
}

#[cfg(test)]
pub mod tests;
<|endoftext|>


<commit_before>#![feature(core)]

/// Calculate the crc64 checksum of the given data, starting with the given crc.
///
/// Implements the CRC64 used by Redis, which is the variant with "Jones" coefficients and init value of 0.
///
/// Specification of this CRC64 variant follows:
///
/// ```text
/// Name: crc-64-jones
/// Width: 64 bites
/// Poly: 0xad93d23594c935a9
/// Reflected In: True
/// Xor_In: 0xffffffffffffffff
/// Reflected_Out: True
/// Xor_Out: 0x0
/// Check("123456789"): 0xe9c6d914c4b8d9ca
/// ```
///
/// Example:
///
/// ```rust
/// crc64::crc64(0, "123456789".as_bytes());
/// ```

use std::mem;
use crc_table::CRC64_TAB;
mod crc_table;

fn crc_reflect(data: u64, len: usize) -> u64 {
    let mut data = data;
    let mut ret = data & 0x01;

    let mut i = 1usize;
    while i < len {
        data >>= 1;
        ret = (ret << 1) | (data & 0x01);
        i+=1;
    }

    ret
}

fn crc64_trivial(crc: u64, in_data: &[u8]) -> u64 {
    let mut crc = crc;
    let len = in_data.len();

    let poly : u64 = 0xad93d23594c935a9;

    let mut bit : bool;

    let mut offset = 0usize;

    while offset < len {
        let c = in_data[offset];
        let mut i = 0x01;
        while i & 0xff != 0x0 {
            bit = crc & 0x8000000000000000 != 0x0;
            if c & i != 0x0 {
                bit = !bit;
            }
            crc <<= 1;
            if bit {
                crc ^= poly;
            }
            i <<= 1;
        }
        crc &= 0xffffffffffffffff;
        offset+=1;
    }
    crc = crc & 0xffffffffffffffff;

    return crc_reflect(crc, 64) ^ 0x0000000000000000;
}

pub fn crc64_init() -> Vec<Vec<u64>> {
    let mut crc : u64;

    let mut table : Vec<Vec<u64>> = Vec::with_capacity(8);

    for _ in (0..8) {
        table.push(Vec::with_capacity(256));
    };

    for n in (0usize..256) {
        table[0].push(crc64_trivial(0, vec![n as u8].as_slice()));
        table[1].push(0);
        table[2].push(0);
        table[3].push(0);
        table[4].push(0);
        table[5].push(0);
        table[6].push(0);
        table[7].push(0);
    }

    for n in (0usize..256) {
        crc = table[0][n];
        for k in (1usize..8) {
            let idx  = (crc as usize) & 0xff;
            crc = table[0][idx] ^ (crc >> 8);
            table[k][n] = crc;
        }
    };

    table
}

// transmute slice of 8 u8 values to one u64 (drop the length)
macro_rules! slice_to_long {
    ($curVec:expr) => {
        {
            unsafe {
                let (tmp, _) : (*const u64, u64) = mem::transmute(&$curVec);
                *tmp
            }
        }
    }
}

pub fn crc64(crc: u64, data: &[u8]) -> u64 {
    let mut crc = crc;
    let mut len = data.len();
    let mut offset = 0usize;

    while len >= 8 {
        crc ^= slice_to_long!(data[offset..(offset+8)]);
        crc = CRC64_TAB[7][(crc & 0xff) as usize] ^
              CRC64_TAB[6][((crc >> 8) & 0xff) as usize] ^
              CRC64_TAB[5][((crc >> 16) & 0xff) as usize] ^
              CRC64_TAB[4][((crc >> 24) & 0xff) as usize] ^
              CRC64_TAB[3][((crc >> 32) & 0xff) as usize] ^
              CRC64_TAB[2][((crc >> 40) & 0xff) as usize] ^
              CRC64_TAB[1][((crc >> 48) & 0xff) as usize] ^
              CRC64_TAB[0][(crc >> 56) as usize];

        offset += 8;
        len -= 8;
    }

    while len > 0 {
        crc = CRC64_TAB[0][((crc ^ data[offset] as u64) & 0xff) as usize] ^ (crc >> 8);
        offset += 1;
        len -= 1;
    }

    crc
}

#[test]
fn test_crc64_works() {
    assert_eq!(0xe9c6d914c4b8d9ca, crc64(0, "123456789".as_bytes()))
}
<commit_msg>Use native size<commit_after>#![feature(core)]

/// Calculate the crc64 checksum of the given data, starting with the given crc.
///
/// Implements the CRC64 used by Redis, which is the variant with "Jones" coefficients and init value of 0.
///
/// Specification of this CRC64 variant follows:
///
/// ```text
/// Name: crc-64-jones
/// Width: 64 bites
/// Poly: 0xad93d23594c935a9
/// Reflected In: True
/// Xor_In: 0xffffffffffffffff
/// Reflected_Out: True
/// Xor_Out: 0x0
/// Check("123456789"): 0xe9c6d914c4b8d9ca
/// ```
///
/// Example:
///
/// ```rust
/// crc64::crc64(0, "123456789".as_bytes());
/// ```

use std::mem;
use crc_table::CRC64_TAB;
mod crc_table;

fn crc_reflect(data: u64, len: usize) -> u64 {
    let mut data = data;
    let mut ret = data & 0x01;

    let mut i = 1usize;
    while i < len {
        data >>= 1;
        ret = (ret << 1) | (data & 0x01);
        i+=1;
    }

    ret
}

fn crc64_trivial(crc: u64, in_data: &[u8]) -> u64 {
    let mut crc = crc;
    let len = in_data.len();

    let poly : u64 = 0xad93d23594c935a9;

    let mut bit : bool;

    let mut offset = 0usize;

    while offset < len {
        let c = in_data[offset];
        let mut i = 0x01;
        while i & 0xff != 0x0 {
            bit = crc & 0x8000000000000000 != 0x0;
            if c & i != 0x0 {
                bit = !bit;
            }
            crc <<= 1;
            if bit {
                crc ^= poly;
            }
            i <<= 1;
        }
        crc &= 0xffffffffffffffff;
        offset+=1;
    }
    crc = crc & 0xffffffffffffffff;

    return crc_reflect(crc, 64) ^ 0x0000000000000000;
}

pub fn crc64_init() -> Vec<Vec<u64>> {
    let mut crc : u64;

    let mut table : Vec<Vec<u64>> = Vec::with_capacity(8);

    for _ in (0..8) {
        table.push(Vec::with_capacity(256));
    };

    for n in (0usize..256) {
        table[0].push(crc64_trivial(0, vec![n as u8].as_slice()));
        table[1].push(0);
        table[2].push(0);
        table[3].push(0);
        table[4].push(0);
        table[5].push(0);
        table[6].push(0);
        table[7].push(0);
    }

    for n in (0usize..256) {
        crc = table[0][n];
        for k in (1usize..8) {
            let idx  = (crc as usize) & 0xff;
            crc = table[0][idx] ^ (crc >> 8);
            table[k][n] = crc;
        }
    };

    table
}

// transmute slice of 8 u8 values to one u64 (drop the length)
macro_rules! slice_to_long {
    ($curVec:expr) => {
        {
            unsafe {
                let (tmp, _) : (*const u64, usize) = mem::transmute(&$curVec);
                *tmp
            }
        }
    }
}

pub fn crc64(crc: u64, data: &[u8]) -> u64 {
    let mut crc = crc;
    let mut len = data.len();
    let mut offset = 0usize;

    while len >= 8 {
        crc ^= slice_to_long!(data[offset..(offset+8)]);
        crc = CRC64_TAB[7][(crc & 0xff) as usize] ^
              CRC64_TAB[6][((crc >> 8) & 0xff) as usize] ^
              CRC64_TAB[5][((crc >> 16) & 0xff) as usize] ^
              CRC64_TAB[4][((crc >> 24) & 0xff) as usize] ^
              CRC64_TAB[3][((crc >> 32) & 0xff) as usize] ^
              CRC64_TAB[2][((crc >> 40) & 0xff) as usize] ^
              CRC64_TAB[1][((crc >> 48) & 0xff) as usize] ^
              CRC64_TAB[0][(crc >> 56) as usize];

        offset += 8;
        len -= 8;
    }

    while len > 0 {
        crc = CRC64_TAB[0][((crc ^ data[offset] as u64) & 0xff) as usize] ^ (crc >> 8);
        offset += 1;
        len -= 1;
    }

    crc
}

#[test]
fn test_crc64_works() {
    assert_eq!(0xe9c6d914c4b8d9ca, crc64(0, "123456789".as_bytes()))
}
<|endoftext|>


<commit_before>//! Crate ruma_api contains core types used to define the requests and responses for each endpoint
//! in the various [Matrix](https://matrix.org) API specifications.
//! These types can be shared by client and server code for all Matrix APIs.
//!
//! When implementing a new Matrix API, each endpoint has a type that implements `Endpoint`, plus
//! the necessary associated types.
//! An implementation of `Endpoint` contains all the information about the HTTP method, the path and
//! input parameters for requests, and the structure of a successful response.
//! Such types can then be used by client code to make requests, and by server code to fulfill
//! those requests.

#![deny(missing_debug_implementations)]
#![deny(missing_docs)]
#![feature(try_from)]

extern crate futures;
extern crate http;
#[cfg(test)] extern crate ruma_identifiers;
#[cfg(test)] extern crate serde;
#[cfg(test)] #[macro_use] extern crate serde_derive;
extern crate serde_json;
extern crate serde_urlencoded;

use std::convert::TryInto;
use std::io;

use futures::future::FutureFrom;
use http::{Method, Request, Response, StatusCode};

/// A Matrix API endpoint.
pub trait Endpoint<T, U> {
    /// Data needed to make a request to the endpoint.
    type Request: TryInto<Request<T>, Error = Error>;
    /// Data returned from the endpoint.
    type Response: FutureFrom<Response<U>, Error = Error>;

    /// Metadata about the endpoint.
    const METADATA: Metadata;
}

/// An error when converting an `Endpoint::Request` to a `http::Request` or a `http::Response` to
/// an `Endpoint::Response`.
#[derive(Debug)]
pub enum Error {
    /// An HTTP error.
    Http(http::Error),
    /// A I/O error.
    Io(io::Error),
    /// A Serde JSON error.
    SerdeJson(serde_json::Error),
    /// A Serde URL encoding error.
    SerdeUrlEncoded(serde_urlencoded::ser::Error),
    /// An HTTP status code indicating error.
    StatusCode(StatusCode),
}

impl From<http::Error> for Error {
    fn from(error: http::Error) -> Self {
        Error::Http(error)
    }
}

impl From<io::Error> for Error {
    fn from(error: io::Error) -> Self {
        Error::Io(error)
    }
}

impl From<serde_json::Error> for Error {
    fn from(error: serde_json::Error) -> Self {
        Error::SerdeJson(error)
    }
}

impl From<serde_urlencoded::ser::Error> for Error {
    fn from(error: serde_urlencoded::ser::Error) -> Self {
        Error::SerdeUrlEncoded(error)
    }
}

/// Metadata about an API endpoint.
#[derive(Clone, Debug)]
pub struct Metadata {
    /// A human-readable description of the endpoint.
    pub description: &'static str,
    /// The HTTP method used by this endpoint.
    pub method: Method,
    /// A unique identifier for this endpoint.
    pub name: &'static str,
    /// The path of this endpoint's URL, with variable names where path parameters should be filled
    /// in during a request.
    pub path: &'static str,
    /// Whether or not this endpoint is rate limited by the server.
    pub rate_limited: bool,
    /// Whether or not the server requires an authenticated user for this endpoint.
    pub requires_authentication: bool,
}

#[cfg(test)]
mod tests {
    /// PUT /_matrix/client/r0/directory/room/:room_alias
    pub mod create {
        use std::convert::TryFrom;

        use futures::future::{FutureFrom, FutureResult, err, ok};
        use http::method::PUT;
        use http::{Request as HttpRequest, Response as HttpResponse};
        use ruma_identifiers::{RoomAliasId, RoomId};
        use serde_json;

        use super::super::{Endpoint as ApiEndpoint, Error, Metadata};

        #[derive(Debug)]
        pub struct Endpoint;

        impl ApiEndpoint<Vec<u8>, Vec<u8>> for Endpoint {
            type Request = Request;
            type Response = Response;

            const METADATA: Metadata = Metadata {
                description: "Add an alias to a room.",
                method: PUT,
                name: "create_alias",
                path: "/_matrix/client/r0/directory/room/:room_alias",
                rate_limited: false,
                requires_authentication: true,
            };

        }

        /// A request to create a new room alias.
        #[derive(Debug)]
        pub struct Request {
            pub room_id: RoomId, // body
            pub room_alias: RoomAliasId, // path
        }

        #[derive(Debug, Serialize)]
        struct RequestBody {
            room_id: RoomId,
        }

        impl TryFrom<Request> for HttpRequest<Vec<u8>> {
            type Error = Error;

            fn try_from(request: Request) -> Result<HttpRequest<Vec<u8>>, Self::Error> {
                let metadata = Endpoint::METADATA;

                let path = metadata.path
                    .to_string()
                    .replace(":room_alias", &request.room_alias.to_string());

                let request_body = RequestBody {
                    room_id: request.room_id,
                };

                let http_request = HttpRequest::builder()
                    .method(metadata.method)
                    .uri(path.as_ref())
                    .body(serde_json::to_vec(&request_body).map_err(Error::from)?)?;

                Ok(http_request)
            }
        }

        /// The response to a request to create a new room alias.
        pub struct Response;

        impl FutureFrom<HttpResponse<Vec<u8>>> for Response {
            type Future = FutureResult<Self, Self::Error>;
            type Error = Error;

            fn future_from(http_response: HttpResponse<Vec<u8>>) -> FutureResult<Self, Self::Error> {
                if http_response.status().is_success() {
                    ok(Response)
                } else {
                    err(Error::StatusCode(http_response.status().clone()))
                }
            }
        }
    }
}
<commit_msg>rustfmt<commit_after>//! Crate ruma_api contains core types used to define the requests and responses for each endpoint
//! in the various [Matrix](https://matrix.org) API specifications.
//! These types can be shared by client and server code for all Matrix APIs.
//!
//! When implementing a new Matrix API, each endpoint has a type that implements `Endpoint`, plus
//! the necessary associated types.
//! An implementation of `Endpoint` contains all the information about the HTTP method, the path and
//! input parameters for requests, and the structure of a successful response.
//! Such types can then be used by client code to make requests, and by server code to fulfill
//! those requests.
#![deny(missing_debug_implementations)]
#![deny(missing_docs)]
#![feature(try_from)]

extern crate futures;
extern crate http;
#[cfg(test)]
extern crate ruma_identifiers;
#[cfg(test)]
extern crate serde;
#[cfg(test)]
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
extern crate serde_urlencoded;

use std::convert::TryInto;
use std::io;

use futures::future::FutureFrom;
use http::{Method, Request, Response, StatusCode};

/// A Matrix API endpoint.
pub trait Endpoint<T, U> {
    /// Data needed to make a request to the endpoint.
    type Request: TryInto<Request<T>, Error = Error>;
    /// Data returned from the endpoint.
    type Response: FutureFrom<Response<U>, Error = Error>;

    /// Metadata about the endpoint.
    const METADATA: Metadata;
}

/// An error when converting an `Endpoint::Request` to a `http::Request` or a `http::Response` to
/// an `Endpoint::Response`.
#[derive(Debug)]
pub enum Error {
    /// An HTTP error.
    Http(http::Error),
    /// A I/O error.
    Io(io::Error),
    /// A Serde JSON error.
    SerdeJson(serde_json::Error),
    /// A Serde URL encoding error.
    SerdeUrlEncoded(serde_urlencoded::ser::Error),
    /// An HTTP status code indicating error.
    StatusCode(StatusCode),
}

impl From<http::Error> for Error {
    fn from(error: http::Error) -> Self {
        Error::Http(error)
    }
}

impl From<io::Error> for Error {
    fn from(error: io::Error) -> Self {
        Error::Io(error)
    }
}

impl From<serde_json::Error> for Error {
    fn from(error: serde_json::Error) -> Self {
        Error::SerdeJson(error)
    }
}

impl From<serde_urlencoded::ser::Error> for Error {
    fn from(error: serde_urlencoded::ser::Error) -> Self {
        Error::SerdeUrlEncoded(error)
    }
}

/// Metadata about an API endpoint.
#[derive(Clone, Debug)]
pub struct Metadata {
    /// A human-readable description of the endpoint.
    pub description: &'static str,
    /// The HTTP method used by this endpoint.
    pub method: Method,
    /// A unique identifier for this endpoint.
    pub name: &'static str,
    /// The path of this endpoint's URL, with variable names where path parameters should be filled
    /// in during a request.
    pub path: &'static str,
    /// Whether or not this endpoint is rate limited by the server.
    pub rate_limited: bool,
    /// Whether or not the server requires an authenticated user for this endpoint.
    pub requires_authentication: bool,
}

#[cfg(test)]
mod tests {
    /// PUT /_matrix/client/r0/directory/room/:room_alias
    pub mod create {
        use std::convert::TryFrom;

        use futures::future::{err, ok, FutureFrom, FutureResult};
        use http::method::PUT;
        use http::{Request as HttpRequest, Response as HttpResponse};
        use ruma_identifiers::{RoomAliasId, RoomId};
        use serde_json;

        use super::super::{Endpoint as ApiEndpoint, Error, Metadata};

        #[derive(Debug)]
        pub struct Endpoint;

        impl ApiEndpoint<Vec<u8>, Vec<u8>> for Endpoint {
            type Request = Request;
            type Response = Response;

            const METADATA: Metadata = Metadata {
                description: "Add an alias to a room.",
                method: PUT,
                name: "create_alias",
                path: "/_matrix/client/r0/directory/room/:room_alias",
                rate_limited: false,
                requires_authentication: true,
            };
        }

        /// A request to create a new room alias.
        #[derive(Debug)]
        pub struct Request {
            pub room_id: RoomId,         // body
            pub room_alias: RoomAliasId, // path
        }

        #[derive(Debug, Serialize)]
        struct RequestBody {
            room_id: RoomId,
        }

        impl TryFrom<Request> for HttpRequest<Vec<u8>> {
            type Error = Error;

            fn try_from(request: Request) -> Result<HttpRequest<Vec<u8>>, Self::Error> {
                let metadata = Endpoint::METADATA;

                let path = metadata
                    .path
                    .to_string()
                    .replace(":room_alias", &request.room_alias.to_string());

                let request_body = RequestBody {
                    room_id: request.room_id,
                };

                let http_request = HttpRequest::builder()
                    .method(metadata.method)
                    .uri(path.as_ref())
                    .body(serde_json::to_vec(&request_body).map_err(Error::from)?)?;

                Ok(http_request)
            }
        }

        /// The response to a request to create a new room alias.
        pub struct Response;

        impl FutureFrom<HttpResponse<Vec<u8>>> for Response {
            type Future = FutureResult<Self, Self::Error>;
            type Error = Error;

            fn future_from(
                http_response: HttpResponse<Vec<u8>>,
            ) -> FutureResult<Self, Self::Error> {
                if http_response.status().is_success() {
                    ok(Response)
                } else {
                    err(Error::StatusCode(http_response.status().clone()))
                }
            }
        }
    }
}
<|endoftext|>