<commit_before>#![feature(box_patterns)]
#[cfg(feature = "trace_log")]
extern crate env_logger;

extern crate base64;
extern crate r2api;
extern crate r2pipe;
extern crate radeco_lib;
extern crate rustyline;

mod cli;
mod core;

use rustyline::completion::{Completer, FilenameCompleter};
use rustyline::error::ReadlineError;
use rustyline::highlight::Highlighter;
use rustyline::hint::Hinter;
use rustyline::{CompletionType, Config, EditMode, Editor, Helper};
use std::fs;
use std::process;

mod scheme {
    pub const HTTP: &'static str = "http://";
    pub const TCP: &'static str = "tcp://";

    pub fn is_http(url: &str) -> bool {
        url.starts_with(HTTP)
    }

    pub fn is_tcp(url: &str) -> bool {
        url.starts_with(TCP)
    }
}

// On unix platforms you can use ANSI escape sequences
#[cfg(unix)]
const PROMPT: &'static str = "\x1b[1;32m>>\x1b[0m ";
// File seperator for *nix based platforms
#[cfg(unix)]
const FILE_SP: char = '/';

// Windows consoles typically don't support ANSI escape sequences out
// of the box
#[cfg(windows)]
const PROMPT: &'static str = ">> ";
#[cfg(windows)]
// File seperator for Windows
const FILE_SP: char = '\\';

#[derive(Default)]
struct Completes {
    file_completer: FilenameCompleter,
}

impl Helper for Completes {}

impl Hinter for Completes {
    fn hint(&self, _line: &str, _pos: usize) -> Option<String> {
        None
    }
}

impl Highlighter for Completes {}

impl Completer for Completes {
    type Candidate = String;
    fn complete(&self, line: &str, _pos: usize) -> rustyline::Result<(usize, Vec<String>)> {
        // TODO Completion for function names
        let cmds = vec![
            command::HELP,
            command::LOAD,
            command::CONNECT,
            command::FNLIST,
            command::ANALYZE,
            command::DOT,
            command::IR,
            command::DECOMPILE,
            command::QUIT,
        ];
        let mut ret: Vec<String> = cmds
            .into_iter()
            .filter(|s| s.starts_with(line))
            .map(|s| s.to_string())
            .collect();
        match self.file_completer.complete(line, _pos) {
            Ok((n, ss)) => {
                let mut completed_lines = ss
                    .into_iter()
                    .map(|s| {
                        if let Some(sep_loc) = line.rfind(FILE_SP) {
                            format!("{}{}", &line[..sep_loc + 1], s.display)
                        } else {
                            format!("{}{}", &line[..n], s.display)
                        }
                    })
                    .collect();
                ret.append(&mut completed_lines);
            }
            Err(_) => {}
        }

        Ok((0, ret))
    }
}

const SEP: &'static str = "END";
const USAGE: &'static str = "
Usage:
  radeco <bin>
  radeco [--append]
  radeco (--help | \
                             --version)
  radeco [--batch <bin>]

  Options:
  -h, --help        \
                             Show this screen.
  -v, --version     Show current version.
  --append          Append separator to the end of every output.
  --batch           Decompile the \
                             whole binary
";

fn main() {
    let (arg, is_append_mode, is_batch_mode) = cli::parse_args(USAGE);
    let config = Config::builder()
        .auto_add_history(true)
        .history_ignore_space(true)
        .completion_type(CompletionType::List)
        .edit_mode(EditMode::Emacs)
        .build();
    let mut rl = Editor::with_config(config);
    rl.set_helper(Some(Completes::default()));
    core::PROJ.with(move |proj| {
        let proj_result = arg.map(|ref s| {
            if scheme::is_http(s) {
                core::load_proj_http(&s[scheme::HTTP.len()..]).map_err(|e| e.to_string())
            } else if scheme::is_tcp(s) {
                core::load_proj_tcp(&s[scheme::TCP.len()..]).map_err(|e| e.to_string())
            } else if is_file(s) {
                Ok(core::load_proj_by_path(s))
            } else {
                Err(format!("Invalid argument {}", s))
            }
        });
        match proj_result {
            Some(Ok(p)) => *proj.borrow_mut() = Some(p),
            Some(Err(ref err)) => println!("{}", err),
            None => {}
        }
    });

    if is_batch_mode {
        core::PROJ.with(|proj_opt| {
            if proj_opt.borrow().is_none() {
                eprintln!("Project was not loaded!");
                return;
            }
            let mut proj_ = proj_opt.borrow_mut();
            let proj = proj_.as_mut().unwrap();
            core::analyze_all_functions(proj);
            let decompiled = core::decompile_all_functions(proj);
            println!("{}", decompiled);
            process::exit(0);
        });
    }

    loop {
        let readline = rl.readline(PROMPT);
        match readline {
            Ok(line) => {
                if !line.is_empty() {
                    let mut terms = line.split_whitespace();
                    let o1 = terms.next();
                    let o2 = terms.next();
                    cmd(o1, o2);
                    if is_append_mode {
                        println!("{}", SEP);
                    }
                }
            }
            Err(ReadlineError::Interrupted) => {}
            Err(ReadlineError::Eof) => break,
            Err(err) => {
                println!("Error: {:?}", err);
                break;
            }
        }
    }
}

mod command {
    pub const HELP: &'static str = "help";
    pub const LOAD: &'static str = "load";
    pub const CONNECT: &'static str = "connect";
    pub const FNLIST: &'static str = "fn_list";
    pub const ANALYZE: &'static str = "analyze";
    pub const DOT: &'static str = "dot";
    pub const IR: &'static str = "ir";
    pub const DECOMPILE: &'static str = "decompile";
    pub const QUIT: &'static str = "quit";

    pub fn help() {
        let width = 30;
        println!("{:width$}    Show this help", HELP, width = width);
        println!(
            "{:width$}    Load binary",
            format!("{} path", LOAD),
            width = width
        );
        println!(
            "{:width$}    Connect to radare2 server",
            format!("{} (http|tcp)://<url>", CONNECT),
            width = width
        );
        println!("{:width$}    Show function list", FNLIST, width = width);
        println!(
            "{:width$}    Analyze <func>",
            format!("{} <func>", ANALYZE),
            width = width
        );
        println!(
            "{:width$}    Emit IR of <func>",
            format!("{} <func>", IR),
            width = width
        );
        println!(
            "{:width$}    Emit graph of the IR in Graphviz dot",
            format!("{} <func>", DOT),
            width = width
        );
        println!(
            "{:width$}    Decompile <func>",
            format!("{} <func>", DECOMPILE),
            width = width
        );
        println!("{:width$}    Quit interactive prompt", QUIT, width = width);
    }
}

fn cmd(op1: Option<&str>, op2: Option<&str>) {
    core::PROJ.with(|proj_opt| {
        match (op1, op2) {
            (Some(command::HELP), _) => {
                command::help();
                return;
            }
            (Some(command::LOAD), Some(path)) => {
                if is_file(path) {
                    *proj_opt.borrow_mut() = Some(core::load_proj_by_path(path));
                    return;
                } else {
                    println!("{} is not found.", path);
                    return;
                }
            }
            (Some(command::CONNECT), Some(url)) => {
                let p_opt = if scheme::is_http(&url) {
                    core::load_proj_http(&url[scheme::HTTP.len()..])
                } else if scheme::is_tcp(&url) {
                    core::load_proj_tcp(&url[scheme::TCP.len()..])
                } else {
                    Err("Invalid url")
                };
                match p_opt {
                    Ok(p) => *proj_opt.borrow_mut() = Some(p),
                    Err(msg) => println!("{}", msg),
                }
                return;
            }
            (Some(command::QUIT), _) => {
                process::exit(0);
            }
            _ => {}
        };
        if proj_opt.borrow().is_none() {
            println!("Load a project first");
            return;
        }
        let mut proj_ = proj_opt.borrow_mut();
        let proj = proj_.as_mut().unwrap();
        match (op1, op2) {
            (Some(command::ANALYZE), Some("*")) => {
                core::analyze_all_functions(proj);
            }
            (Some(command::FNLIST), _) => {
                let funcs = core::fn_list(&proj);
                println!("{}", funcs.join("\n"));
            }
            // TODO Show list of dependency information of analyses
            // TODO Add command for individual analyses
            (Some(command::ANALYZE), Some(f)) => {
                if let Some(rfn) = core::get_function_mut(f, proj) {
                    core::analyze(rfn);
                } else {
                    println!("{} is not found", f);
                }
            }
            (Some(command::DOT), Some(f)) => {
                if let Some(rfn) = core::get_function(f, &proj) {
                    println!("{}", core::emit_dot(rfn.ssa()));
                } else {
                    println!("{} is not found", f);
                }
            }
            (Some(command::IR), Some(f)) => {
                if let Some(rfn) = core::get_function(f, &proj) {
                    println!("{}", core::emit_ir(rfn));
                } else {
                    println!("{} is not found", f);
                }
            }
            (Some(command::DECOMPILE), Some("*")) => {
                let decompiled = core::decompile_all_functions(&proj);
                println!("{}", decompiled);
            }
            (Some(command::DECOMPILE), Some(f)) => match core::decompile(f, &proj) {
                Ok(res) => println!("{}", res),
                Err(err) => println!("{}", err),
            },
            _ => {
                println!(
                    "Invalid command {} {}",
                    op1.unwrap_or(""),
                    op2.unwrap_or("")
                );
            }
        }
    });
}

fn is_file(path: &str) -> bool {
    fs::metadata(path).map(|f| f.is_file()).unwrap_or(false)
}
<commit_msg>Fix indent<commit_after>#![feature(box_patterns)]
#[cfg(feature = "trace_log")]
extern crate env_logger;

extern crate base64;
extern crate r2api;
extern crate r2pipe;
extern crate radeco_lib;
extern crate rustyline;

mod cli;
mod core;

use rustyline::completion::{Completer, FilenameCompleter};
use rustyline::error::ReadlineError;
use rustyline::highlight::Highlighter;
use rustyline::hint::Hinter;
use rustyline::{CompletionType, Config, EditMode, Editor, Helper};
use std::fs;
use std::process;

mod scheme {
    pub const HTTP: &'static str = "http://";
    pub const TCP: &'static str = "tcp://";

    pub fn is_http(url: &str) -> bool {
        url.starts_with(HTTP)
    }

    pub fn is_tcp(url: &str) -> bool {
        url.starts_with(TCP)
    }
}

// On unix platforms you can use ANSI escape sequences
#[cfg(unix)]
const PROMPT: &'static str = "\x1b[1;32m>>\x1b[0m ";
// File seperator for *nix based platforms
#[cfg(unix)]
const FILE_SP: char = '/';

// Windows consoles typically don't support ANSI escape sequences out
// of the box
#[cfg(windows)]
const PROMPT: &'static str = ">> ";
#[cfg(windows)]
// File seperator for Windows
const FILE_SP: char = '\\';

#[derive(Default)]
struct Completes {
    file_completer: FilenameCompleter,
}

impl Helper for Completes {}

impl Hinter for Completes {
    fn hint(&self, _line: &str, _pos: usize) -> Option<String> {
        None
    }
}

impl Highlighter for Completes {}

impl Completer for Completes {
    type Candidate = String;
    fn complete(&self, line: &str, _pos: usize) -> rustyline::Result<(usize, Vec<String>)> {
        // TODO Completion for function names
        let cmds = vec![
            command::HELP,
            command::LOAD,
            command::CONNECT,
            command::FNLIST,
            command::ANALYZE,
            command::DOT,
            command::IR,
            command::DECOMPILE,
            command::QUIT,
        ];
        let mut ret: Vec<String> = cmds
            .into_iter()
            .filter(|s| s.starts_with(line))
            .map(|s| s.to_string())
            .collect();
        match self.file_completer.complete(line, _pos) {
            Ok((n, ss)) => {
                let mut completed_lines = ss
                    .into_iter()
                    .map(|s| {
                        if let Some(sep_loc) = line.rfind(FILE_SP) {
                            format!("{}{}", &line[..sep_loc + 1], s.display)
                        } else {
                            format!("{}{}", &line[..n], s.display)
                        }
                    })
                    .collect();
                ret.append(&mut completed_lines);
            }
            Err(_) => {}
        }

        Ok((0, ret))
    }
}

const SEP: &'static str = "END";
#[rustfmt::skip]
const USAGE: &'static str = "
Usage:
  radeco <bin>
  radeco [--append]
  radeco (--help | --version)
  radeco [--batch <bin>]

  Options:
  -h, --help        Show this screen.
  -v, --version     Show current version.
  --append          Append separator to the end of every output.
  --batch           Decompile the whole binary
";

fn main() {
    let (arg, is_append_mode, is_batch_mode) = cli::parse_args(USAGE);
    let config = Config::builder()
        .auto_add_history(true)
        .history_ignore_space(true)
        .completion_type(CompletionType::List)
        .edit_mode(EditMode::Emacs)
        .build();
    let mut rl = Editor::with_config(config);
    rl.set_helper(Some(Completes::default()));
    core::PROJ.with(move |proj| {
        let proj_result = arg.map(|ref s| {
            if scheme::is_http(s) {
                core::load_proj_http(&s[scheme::HTTP.len()..]).map_err(|e| e.to_string())
            } else if scheme::is_tcp(s) {
                core::load_proj_tcp(&s[scheme::TCP.len()..]).map_err(|e| e.to_string())
            } else if is_file(s) {
                Ok(core::load_proj_by_path(s))
            } else {
                Err(format!("Invalid argument {}", s))
            }
        });
        match proj_result {
            Some(Ok(p)) => *proj.borrow_mut() = Some(p),
            Some(Err(ref err)) => println!("{}", err),
            None => {}
        }
    });

    if is_batch_mode {
        core::PROJ.with(|proj_opt| {
            if proj_opt.borrow().is_none() {
                eprintln!("Project was not loaded!");
                return;
            }
            let mut proj_ = proj_opt.borrow_mut();
            let proj = proj_.as_mut().unwrap();
            core::analyze_all_functions(proj);
            let decompiled = core::decompile_all_functions(proj);
            println!("{}", decompiled);
            process::exit(0);
        });
    }

    loop {
        let readline = rl.readline(PROMPT);
        match readline {
            Ok(line) => {
                if !line.is_empty() {
                    let mut terms = line.split_whitespace();
                    let o1 = terms.next();
                    let o2 = terms.next();
                    cmd(o1, o2);
                    if is_append_mode {
                        println!("{}", SEP);
                    }
                }
            }
            Err(ReadlineError::Interrupted) => {}
            Err(ReadlineError::Eof) => break,
            Err(err) => {
                println!("Error: {:?}", err);
                break;
            }
        }
    }
}

mod command {
    pub const HELP: &'static str = "help";
    pub const LOAD: &'static str = "load";
    pub const CONNECT: &'static str = "connect";
    pub const FNLIST: &'static str = "fn_list";
    pub const ANALYZE: &'static str = "analyze";
    pub const DOT: &'static str = "dot";
    pub const IR: &'static str = "ir";
    pub const DECOMPILE: &'static str = "decompile";
    pub const QUIT: &'static str = "quit";

    pub fn help() {
        let width = 30;
        println!("{:width$}    Show this help", HELP, width = width);
        println!(
            "{:width$}    Load binary",
            format!("{} path", LOAD),
            width = width
        );
        println!(
            "{:width$}    Connect to radare2 server",
            format!("{} (http|tcp)://<url>", CONNECT),
            width = width
        );
        println!("{:width$}    Show function list", FNLIST, width = width);
        println!(
            "{:width$}    Analyze <func>",
            format!("{} <func>", ANALYZE),
            width = width
        );
        println!(
            "{:width$}    Emit IR of <func>",
            format!("{} <func>", IR),
            width = width
        );
        println!(
            "{:width$}    Emit graph of the IR in Graphviz dot",
            format!("{} <func>", DOT),
            width = width
        );
        println!(
            "{:width$}    Decompile <func>",
            format!("{} <func>", DECOMPILE),
            width = width
        );
        println!("{:width$}    Quit interactive prompt", QUIT, width = width);
    }
}

fn cmd(op1: Option<&str>, op2: Option<&str>) {
    core::PROJ.with(|proj_opt| {
        match (op1, op2) {
            (Some(command::HELP), _) => {
                command::help();
                return;
            }
            (Some(command::LOAD), Some(path)) => {
                if is_file(path) {
                    *proj_opt.borrow_mut() = Some(core::load_proj_by_path(path));
                    return;
                } else {
                    println!("{} is not found.", path);
                    return;
                }
            }
            (Some(command::CONNECT), Some(url)) => {
                let p_opt = if scheme::is_http(&url) {
                    core::load_proj_http(&url[scheme::HTTP.len()..])
                } else if scheme::is_tcp(&url) {
                    core::load_proj_tcp(&url[scheme::TCP.len()..])
                } else {
                    Err("Invalid url")
                };
                match p_opt {
                    Ok(p) => *proj_opt.borrow_mut() = Some(p),
                    Err(msg) => println!("{}", msg),
                }
                return;
            }
            (Some(command::QUIT), _) => {
                process::exit(0);
            }
            _ => {}
        };
        if proj_opt.borrow().is_none() {
            println!("Load a project first");
            return;
        }
        let mut proj_ = proj_opt.borrow_mut();
        let proj = proj_.as_mut().unwrap();
        match (op1, op2) {
            (Some(command::ANALYZE), Some("*")) => {
                core::analyze_all_functions(proj);
            }
            (Some(command::FNLIST), _) => {
                let funcs = core::fn_list(&proj);
                println!("{}", funcs.join("\n"));
            }
            // TODO Show list of dependency information of analyses
            // TODO Add command for individual analyses
            (Some(command::ANALYZE), Some(f)) => {
                if let Some(rfn) = core::get_function_mut(f, proj) {
                    core::analyze(rfn);
                } else {
                    println!("{} is not found", f);
                }
            }
            (Some(command::DOT), Some(f)) => {
                if let Some(rfn) = core::get_function(f, &proj) {
                    println!("{}", core::emit_dot(rfn.ssa()));
                } else {
                    println!("{} is not found", f);
                }
            }
            (Some(command::IR), Some(f)) => {
                if let Some(rfn) = core::get_function(f, &proj) {
                    println!("{}", core::emit_ir(rfn));
                } else {
                    println!("{} is not found", f);
                }
            }
            (Some(command::DECOMPILE), Some("*")) => {
                let decompiled = core::decompile_all_functions(&proj);
                println!("{}", decompiled);
            }
            (Some(command::DECOMPILE), Some(f)) => match core::decompile(f, &proj) {
                Ok(res) => println!("{}", res),
                Err(err) => println!("{}", err),
            },
            _ => {
                println!(
                    "Invalid command {} {}",
                    op1.unwrap_or(""),
                    op2.unwrap_or("")
                );
            }
        }
    });
}

fn is_file(path: &str) -> bool {
    fs::metadata(path).map(|f| f.is_file()).unwrap_or(false)
}
<|endoftext|>


<commit_before>use async_trait::async_trait;
use eyre::{bail, Result};
use serde::{Deserialize, Serialize};
use std::{
    fs::File,
    io::{BufReader, BufWriter, Read, Write},
    path::Path,
};
use stencila_schema::Node;

// Re-export for the convenience of crates that implement `CodecTrait`
pub use ::async_trait;
pub use eyre;
pub use serde;
pub use stencila_schema;
pub use utils;

/// A specification for codecs
///
/// All codecs, including those implemented in plugins, should provide this
/// specification. Rust implementations return a `Codec` instance from the
/// `spec` function of `CodecTrait`. Plugins provide a JSON or YAML serialization
/// as part of their manifest.
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
#[serde(default, rename_all = "camelCase")]
pub struct Codec {
    /// A list of format names (or aliases) that the codec can handle
    pub formats: Vec<String>,

    /// Whether the codec supports decoding from string content
    pub from_string: bool,

    /// Whether the codec supports decoding from a file system path
    pub from_path: bool,

    /// Whether the codec supports encoding to string content
    pub to_string: bool,

    /// Whether the codec supports encoding to a file system path
    pub to_path: bool,

    /// A list of root node types that the codec can encode / decode
    ///
    /// Most codecs usually only handle one root type e.g. `Article`.
    /// Used to provide a list of formats to the user that support
    /// the current document type.
    pub root_types: Vec<String>,

    /// A list of node types that the codec does not support
    ///
    /// Used to provide warnings to the user on potential loss of content
    /// when encoding using the codec.
    pub unsupported_types: Vec<String>,

    /// A list of node properties that the codec does not support
    ///
    /// The format for these strings is `<type>.<property>` e.g. `Article.funders`
    /// Used to provide warnings to the user on potential loss of content
    /// when encoding using the codec.
    pub unsupported_properties: Vec<String>,

    /// The status of the codec e.g. `alpha`, `beta`
    ///
    /// Leave a blank string for stable, production ready codecs.
    pub status: String,
}

/// A trait for codecs
///
/// This trait can be used by Rust implementations of codecs, allowing them to
/// be compiled into the Stencila binaries.
///
/// It defines similar functions to `serde_json` (and other `serde_` crates) for
/// converting nodes to/from strings, files, readers etc.
#[async_trait]
pub trait CodecTrait {
    /// Get the [`Codec`] specification for this implementation
    fn spec() -> Codec;

    /// Decode a document node from a string
    fn from_str(_str: &str, _options: Option<DecodeOptions>) -> Result<Node> {
        bail!("Decoding from string is not implemented for this format")
    }

    /// Decode a document node from a string asynchronously
    async fn from_str_async(str: &str, options: Option<DecodeOptions>) -> Result<Node> {
        Self::from_str(str, options)
    }

    /// Decode a document node from a `BufReader`
    async fn from_buffer<T: Read>(
        reader: &mut BufReader<T>,
        options: Option<DecodeOptions>,
    ) -> Result<Node>
    where
        T: Send + Sync,
    {
        let mut content = String::new();
        reader.read_to_string(&mut content)?;
        Self::from_str(&content, options)
    }

    /// Decode a document node from a file
    ///
    /// This function reads the file as a string and passes that on to `from_str`
    /// for decoding. If working with binary formats, you should override this function
    /// to read the file as bytes instead.
    async fn from_file(file: &mut File, options: Option<DecodeOptions>) -> Result<Node> {
        let mut content = String::new();
        file.read_to_string(&mut content)?;
        Self::from_str(&content, options)
    }

    /// Decode a document node from a file system path
    async fn from_path(path: &Path, options: Option<DecodeOptions>) -> Result<Node> {
        let mut file = File::open(path)?;
        Self::from_file(&mut file, options).await
    }

    /// Encode a document node to a string
    fn to_string(_node: &Node, _options: Option<EncodeOptions>) -> Result<String> {
        bail!("Encoding to string is not implemented for this format")
    }

    /// Encode a document node to a string asynchronously
    async fn to_string_async(node: &Node, options: Option<EncodeOptions>) -> Result<String> {
        Self::to_string(node, options)
    }

    /// Encode a document node to a `BufWriter`
    async fn to_buffer<T: Write>(
        node: &Node,
        writer: &mut BufWriter<T>,
        options: Option<EncodeOptions>,
    ) -> Result<()>
    where
        T: Send + Sync,
    {
        let content = Self::to_string(node, options)?;
        writer.write_all(content.as_bytes())?;
        Ok(())
    }

    /// Encode a document node to a file
    async fn to_file(node: &Node, file: &mut File, options: Option<EncodeOptions>) -> Result<()> {
        let content = Self::to_string(node, options)?;
        file.write_all(content.as_bytes())?;
        Ok(())
    }

    /// Encode a document node to a file system path
    async fn to_path(node: &Node, path: &Path, options: Option<EncodeOptions>) -> Result<()> {
        let mut file = File::open(path)?;
        Self::to_file(node, &mut file, options).await
    }
}

/// Decoding options
///
/// Decoding functions (including those in plugins) are encouraged to respect these options
/// but are not required to.
#[derive(Clone)]
pub struct DecodeOptions {
    /// The format of the input to be decoded
    ///
    /// Most codecs only decode one format. However, for those that handle multiple
    /// format it may be necessary to specify this option.
    pub format: Option<String>,
}

impl Default for DecodeOptions {
    fn default() -> Self {
        Self { format: None }
    }
}

/// Encoding options
///
/// Encoding functions (including those in plugins) are encouraged to respect these options
/// but are not required to. Indeed, some options do not apply for some formats.
/// For example, a PDF is always `standalone` (so if that option is set to `false` is it will be ignored).
/// Futhermore, some combinations of options are ineffectual e.g. a `theme` when `standalone: false`
#[derive(Clone)]
pub struct EncodeOptions {
    /// Whether to encode in compact form.
    ///
    /// Some formats (e.g HTML and JSON) can be encoded in either compact
    /// or "pretty-printed" ie.e. indented forms.
    pub compact: bool,

    /// Whether to ensure that the encoded document is standalone.
    ///
    /// Some formats (e.g. Markdown, DOCX) are always standalone, others
    /// can be frangments, or standalong documents (e.g HTML).
    pub standalone: bool,

    /// Whether to bundle local media files into the encoded document
    ///
    /// Some formats (e.g. DOCX, PDF) always bundle. For HTML,
    /// bundling means including media as data URIs rather than
    /// links to files.
    pub bundle: bool,

    /// The theme to apply to the encoded document
    ///
    /// Only applies to some formats (e.g. HTML, PDF, PNG).
    pub theme: String,

    /// The format to encode to
    ///
    /// Most codecs only encode to one format. However, for those that handle multiple
    /// formats it may be necessary to specify this option.
    pub format: Option<String>,
}

impl Default for EncodeOptions {
    fn default() -> Self {
        Self {
            compact: true,
            standalone: false,
            bundle: false,
            theme: "stencila".to_string(),
            format: None,
        }
    }
}
<commit_msg>fix(Codec): Use `create` instead of `open` when encoding to a path<commit_after>use async_trait::async_trait;
use eyre::{bail, Result};
use serde::{Deserialize, Serialize};
use std::{
    fs::File,
    io::{BufReader, BufWriter, Read, Write},
    path::Path,
};
use stencila_schema::Node;

// Re-export for the convenience of crates that implement `CodecTrait`
pub use ::async_trait;
pub use eyre;
pub use serde;
pub use stencila_schema;
pub use utils;

/// A specification for codecs
///
/// All codecs, including those implemented in plugins, should provide this
/// specification. Rust implementations return a `Codec` instance from the
/// `spec` function of `CodecTrait`. Plugins provide a JSON or YAML serialization
/// as part of their manifest.
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
#[serde(default, rename_all = "camelCase")]
pub struct Codec {
    /// A list of format names (or aliases) that the codec can handle
    pub formats: Vec<String>,

    /// Whether the codec supports decoding from string content
    pub from_string: bool,

    /// Whether the codec supports decoding from a file system path
    pub from_path: bool,

    /// Whether the codec supports encoding to string content
    pub to_string: bool,

    /// Whether the codec supports encoding to a file system path
    pub to_path: bool,

    /// A list of root node types that the codec can encode / decode
    ///
    /// Most codecs usually only handle one root type e.g. `Article`.
    /// Used to provide a list of formats to the user that support
    /// the current document type.
    pub root_types: Vec<String>,

    /// A list of node types that the codec does not support
    ///
    /// Used to provide warnings to the user on potential loss of content
    /// when encoding using the codec.
    pub unsupported_types: Vec<String>,

    /// A list of node properties that the codec does not support
    ///
    /// The format for these strings is `<type>.<property>` e.g. `Article.funders`
    /// Used to provide warnings to the user on potential loss of content
    /// when encoding using the codec.
    pub unsupported_properties: Vec<String>,

    /// The status of the codec e.g. `alpha`, `beta`
    ///
    /// Leave a blank string for stable, production ready codecs.
    pub status: String,
}

/// A trait for codecs
///
/// This trait can be used by Rust implementations of codecs, allowing them to
/// be compiled into the Stencila binaries.
///
/// It defines similar functions to `serde_json` (and other `serde_` crates) for
/// converting nodes to/from strings, files, readers etc.
#[async_trait]
pub trait CodecTrait {
    /// Get the [`Codec`] specification for this implementation
    fn spec() -> Codec;

    /// Decode a document node from a string
    fn from_str(_str: &str, _options: Option<DecodeOptions>) -> Result<Node> {
        bail!("Decoding from string is not implemented for this format")
    }

    /// Decode a document node from a string asynchronously
    async fn from_str_async(str: &str, options: Option<DecodeOptions>) -> Result<Node> {
        Self::from_str(str, options)
    }

    /// Decode a document node from a `BufReader`
    async fn from_buffer<T: Read>(
        reader: &mut BufReader<T>,
        options: Option<DecodeOptions>,
    ) -> Result<Node>
    where
        T: Send + Sync,
    {
        let mut content = String::new();
        reader.read_to_string(&mut content)?;
        Self::from_str(&content, options)
    }

    /// Decode a document node from a file
    ///
    /// This function reads the file as a string and passes that on to `from_str`
    /// for decoding. If working with binary formats, you should override this function
    /// to read the file as bytes instead.
    async fn from_file(file: &mut File, options: Option<DecodeOptions>) -> Result<Node> {
        let mut content = String::new();
        file.read_to_string(&mut content)?;
        Self::from_str(&content, options)
    }

    /// Decode a document node from a file system path
    async fn from_path(path: &Path, options: Option<DecodeOptions>) -> Result<Node> {
        let mut file = File::open(path)?;
        Self::from_file(&mut file, options).await
    }

    /// Encode a document node to a string
    fn to_string(_node: &Node, _options: Option<EncodeOptions>) -> Result<String> {
        bail!("Encoding to string is not implemented for this format")
    }

    /// Encode a document node to a string asynchronously
    async fn to_string_async(node: &Node, options: Option<EncodeOptions>) -> Result<String> {
        Self::to_string(node, options)
    }

    /// Encode a document node to a `BufWriter`
    async fn to_buffer<T: Write>(
        node: &Node,
        writer: &mut BufWriter<T>,
        options: Option<EncodeOptions>,
    ) -> Result<()>
    where
        T: Send + Sync,
    {
        let content = Self::to_string(node, options)?;
        writer.write_all(content.as_bytes())?;
        Ok(())
    }

    /// Encode a document node to a file
    async fn to_file(node: &Node, file: &mut File, options: Option<EncodeOptions>) -> Result<()> {
        let content = Self::to_string(node, options)?;
        file.write_all(content.as_bytes())?;
        Ok(())
    }

    /// Encode a document node to a file system path
    async fn to_path(node: &Node, path: &Path, options: Option<EncodeOptions>) -> Result<()> {
        let mut file = File::create(path)?;
        Self::to_file(node, &mut file, options).await
    }
}

/// Decoding options
///
/// Decoding functions (including those in plugins) are encouraged to respect these options
/// but are not required to.
#[derive(Clone)]
pub struct DecodeOptions {
    /// The format of the input to be decoded
    ///
    /// Most codecs only decode one format. However, for those that handle multiple
    /// format it may be necessary to specify this option.
    pub format: Option<String>,
}

impl Default for DecodeOptions {
    fn default() -> Self {
        Self { format: None }
    }
}

/// Encoding options
///
/// Encoding functions (including those in plugins) are encouraged to respect these options
/// but are not required to. Indeed, some options do not apply for some formats.
/// For example, a PDF is always `standalone` (so if that option is set to `false` is it will be ignored).
/// Futhermore, some combinations of options are ineffectual e.g. a `theme` when `standalone: false`
#[derive(Clone)]
pub struct EncodeOptions {
    /// Whether to encode in compact form.
    ///
    /// Some formats (e.g HTML and JSON) can be encoded in either compact
    /// or "pretty-printed" ie.e. indented forms.
    pub compact: bool,

    /// Whether to ensure that the encoded document is standalone.
    ///
    /// Some formats (e.g. Markdown, DOCX) are always standalone, others
    /// can be frangments, or standalong documents (e.g HTML).
    pub standalone: bool,

    /// Whether to bundle local media files into the encoded document
    ///
    /// Some formats (e.g. DOCX, PDF) always bundle. For HTML,
    /// bundling means including media as data URIs rather than
    /// links to files.
    pub bundle: bool,

    /// The theme to apply to the encoded document
    ///
    /// Only applies to some formats (e.g. HTML, PDF, PNG).
    pub theme: String,

    /// The format to encode to
    ///
    /// Most codecs only encode to one format. However, for those that handle multiple
    /// formats it may be necessary to specify this option.
    pub format: Option<String>,
}

impl Default for EncodeOptions {
    fn default() -> Self {
        Self {
            compact: true,
            standalone: false,
            bundle: false,
            theme: "stencila".to_string(),
            format: None,
        }
    }
}
<|endoftext|>


<commit_before>use ::cairo;
use ::glib::translate::*;
use ::glib_sys;
use ::libc;

#[cfg(test)]
use std::f64::consts::*;

use cairo::MatrixTrait;
use cssparser::{self, Parser, ParserInput};

use error::*;
use parsers::{ParseError, Parse, optional_comma};

impl Parse for cairo::Matrix {
    type Data = ();
    type Err = AttributeError;

    fn parse (s: &str, _: ()) -> Result<cairo::Matrix, AttributeError> {
        parse_transform (s)
    }
}

pub fn parse_transform(s: &str) -> Result<cairo::Matrix, AttributeError> {
    let matrix = parse_transform_list(s)?;

    matrix.try_invert ().map (|_| matrix)
        .map_err (|_| AttributeError::Value ("invalid transformation matrix".to_string ()))
}

fn parse_transform_list(s: &str) -> Result<cairo::Matrix, AttributeError> {
    let mut input = ParserInput::new(s);
    let mut parser = Parser::new(&mut input);

    let mut matrix = cairo::Matrix::identity();

    loop {
        let m = parse_transform_command(&mut parser)?;
        matrix = cairo::Matrix::multiply(&m, &matrix);

        if parser.is_exhausted() {
            break;
        }

        optional_comma(&mut parser);
    }

    Ok(matrix)
}

fn parse_transform_command(parser: &mut Parser) -> Result<cairo::Matrix, AttributeError> {
    let xform = parser.expect_ident_cloned()?;
    let _ = parser.expect_parenthesis_block()?;

    match xform.as_ref() {
        "matrix"    => parse_matrix_args(parser),
        "translate" => parse_translate_args(parser),
        "scale"     => parse_scale_args(parser),
        "rotate"    => parse_rotate_args(parser),
        "skewX"     => parse_skewX_args(parser),
        "skewY"     => parse_skewY_args(parser),
        _           => Err(AttributeError::from(ParseError::new("expected matrix|translate|scale|rotate|skewX|skewY"))),
    }
}

fn parse_matrix_args(parser: &mut Parser) -> Result<cairo::Matrix, AttributeError> {
    parser.parse_nested_block(|p| {
        let xx = p.expect_number()? as f64;
        optional_comma(p);

        let yx = p.expect_number()? as f64;
        optional_comma(p);

        let xy = p.expect_number()? as f64;
        optional_comma(p);

        let yy = p.expect_number()? as f64;
        optional_comma(p);

        let x0 = p.expect_number()? as f64;
        optional_comma(p);

        let y0 = p.expect_number()? as f64;

        Ok(cairo::Matrix::new(xx, yx, xy, yy, x0, y0))
    }).map_err(cssparser::ParseError::<()>::basic)
        .map_err(|e| AttributeError::from(e))
}

fn parse_translate_args(parser: &mut Parser) -> Result<cairo::Matrix, AttributeError> {
    unimplemented!();
}

fn parse_scale_args(parser: &mut Parser) -> Result<cairo::Matrix, AttributeError> {
    unimplemented!();
}

fn parse_rotate_args(parser: &mut Parser) -> Result<cairo::Matrix, AttributeError> {
    unimplemented!();
}

fn parse_skewX_args(parser: &mut Parser) -> Result<cairo::Matrix, AttributeError> {
    unimplemented!();
}

fn parse_skewY_args(parser: &mut Parser) -> Result<cairo::Matrix, AttributeError> {
    unimplemented!();
}

/*
pub fn parse_transform (s: &str) -> Result <cairo::Matrix, AttributeError> {
    let r = parse_TransformList (s);

    match r {
        Ok (m) => {
            m.try_invert ().map (|_| m)
                .map_err (|_| AttributeError::Value ("invalid transformation matrix".to_string ()))
        },

        Err (e) => {
            Err (AttributeError::Parse (ParseError::new (format! ("{:?}", e))))
        }
    }
}
*/

#[cfg(test)]
fn make_rotation_matrix (angle_degrees: f64, tx: f64, ty: f64) -> cairo::Matrix {
    let angle = angle_degrees * PI / 180.0;

    let mut m = cairo::Matrix::new (1.0, 0.0, 0.0, 1.0, tx, ty);

    let mut r = cairo::Matrix::identity ();
    r.rotate (angle);
    m = cairo::Matrix::multiply (&r, &m);

    m = cairo::Matrix::multiply (&cairo::Matrix::new (1.0, 0.0, 0.0, 1.0, -tx, -ty), &m);
    m
}

#[no_mangle]
pub extern fn rsvg_parse_transform (out_matrix: *mut cairo::Matrix, s: *const libc::c_char) -> glib_sys::gboolean {
    assert! (!out_matrix.is_null ());
    assert! (!s.is_null ());

    let string = unsafe { String::from_glib_none (s) };
    let matrix: &mut cairo::Matrix = unsafe { &mut *out_matrix };

    match parse_transform (&string) {
        Ok (m) => {
            *matrix = m;
            true.to_glib ()
        },

        Err (_) => {
            *matrix = cairo::Matrix::identity ();
            false.to_glib ()
        }
    }
}

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

    #[test]
    #[ignore]
    fn parses_valid_transform () {
        let t = cairo::Matrix::new (1.0, 0.0, 0.0, 1.0, 20.0, 30.0);
        let s = cairo::Matrix::new (10.0, 0.0, 0.0, 10.0, 0.0, 0.0);
        let r = make_rotation_matrix (30.0, 10.0, 10.0);

        let a = cairo::Matrix::multiply (&s, &t);
        assert_eq! (parse_transform ("translate(20, 30), scale (10) rotate (30 10 10)").unwrap (),
                    cairo::Matrix::multiply (&r, &a));
    }

    #[test]
    #[ignore]
    fn syntax_error_yields_parse_error () {
        match parse_transform ("foo") {
            Err (AttributeError::Parse (_)) => {},
            _ => { panic! (); }
        }

        match parse_transform ("matrix (1 2 3 4 5)") {
            Err (AttributeError::Parse (_)) => {},
            _ => { panic! (); }
        }
    }

    #[test]
    #[ignore]
    fn invalid_transform_yields_value_error () {
        match parse_transform ("matrix (0 0 0 0 0 0)") {
            Err (AttributeError::Value (_)) => {},
            _ => { panic! (); }
        }

        match parse_transform ("scale (0), translate (10, 10)") {
            Err (AttributeError::Value (_)) => {},
            _ => { panic! (); }
        }

        match parse_transform ("scale (0), skewX (90)") {
            Err (AttributeError::Value (_)) => {},
            _ => { panic! (); }
        }
    }
}

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

    #[test]
    fn parses_matrix () {
        assert_eq! (parse_transform ("matrix (1 2 3 4 5 6)").unwrap (),
                    cairo::Matrix::new (1.0, 2.0, 3.0, 4.0, 5.0, 6.0));

        assert_eq! (parse_transform ("matrix (1,2,3,4 5 6)").unwrap (),
                    cairo::Matrix::new (1.0, 2.0, 3.0, 4.0, 5.0, 6.0));

        assert_eq! (parse_transform ("matrix (1,2.25,-3.25e2,4 5 6)").unwrap (),
                    cairo::Matrix::new (1.0, 2.25, -325.0, 4.0, 5.0, 6.0));
    }

    #[test]
    #[ignore]
    fn parses_translate () {
        assert_eq! (parse_transform ("translate(-1 -2)").unwrap (),
                    cairo::Matrix::new (1.0, 0.0, 0.0, 1.0, -1.0, -2.0));

        assert_eq! (parse_transform ("translate(-1, -2)").unwrap (),
                    cairo::Matrix::new (1.0, 0.0, 0.0, 1.0, -1.0, -2.0));

        assert_eq! (parse_transform ("translate(-1)").unwrap (),
                    cairo::Matrix::new (1.0, 0.0, 0.0, 1.0, -1.0, 0.0));
    }

    #[test]
    #[ignore]
    fn parses_scale () {
        assert_eq! (parse_transform ("scale(-1 -2)").unwrap (),
                    cairo::Matrix::new (-1.0, 0.0, 0.0, -2.0, 0.0, 0.0));

        assert_eq! (parse_transform ("scale(-1, -2)").unwrap (),
                    cairo::Matrix::new (-1.0, 0.0, 0.0, -2.0, 0.0, 0.0));

        assert_eq! (parse_transform ("scale(-1)").unwrap (),
                    cairo::Matrix::new (-1.0, 0.0, 0.0, -1.0, 0.0, 0.0));
    }

    #[test]
    #[ignore]
    fn parses_rotate () {
        assert_eq! (parse_transform ("rotate (30)").unwrap (), make_rotation_matrix (30.0, 0.0, 0.0));
        assert_eq! (parse_transform ("rotate (30,-1,-2)").unwrap (), make_rotation_matrix (30.0, -1.0, -2.0));
        assert_eq! (parse_transform ("rotate (30, -1, -2)").unwrap (), make_rotation_matrix (30.0, -1.0, -2.0));
    }

    fn make_skew_x_matrix (angle_degrees: f64) -> cairo::Matrix {
        let a = angle_degrees * PI / 180.0;
        cairo::Matrix::new (1.0,      0.0,
                            a.tan (), 1.0,
                            0.0, 0.0)
    }

    fn make_skew_y_matrix (angle_degrees: f64) -> cairo::Matrix {
        let mut m = make_skew_x_matrix (angle_degrees);
        m.yx = m.xy;
        m.xy = 0.0;
        m
    }

    #[test]
    #[ignore]
    fn parses_skew_x () {
        assert_eq! (parse_transform ("skewX (30)").unwrap (), make_skew_x_matrix (30.0));
    }

    #[test]
    #[ignore]
    fn parses_skew_y () {
        assert_eq! (parse_transform ("skewY (30)").unwrap (), make_skew_y_matrix (30.0));
    }

    #[test]
    #[ignore]
    fn parses_transform_list () {
        let t = cairo::Matrix::new (1.0, 0.0, 0.0, 1.0, 20.0, 30.0);
        let s = cairo::Matrix::new (10.0, 0.0, 0.0, 10.0, 0.0, 0.0);
        let r = make_rotation_matrix (30.0, 10.0, 10.0);

        assert_eq! (parse_transform ("scale(10)rotate(30, 10, 10)").unwrap (),
                    cairo::Matrix::multiply (&r, &s));

        assert_eq! (parse_transform ("translate(20, 30), scale (10)").unwrap (),
                    cairo::Matrix::multiply (&s, &t));

        let a = cairo::Matrix::multiply (&s, &t);
        assert_eq! (parse_transform ("translate(20, 30), scale (10) rotate (30 10 10)").unwrap (),
                    cairo::Matrix::multiply (&r, &a));
    }
}
<commit_msg>Parse "transform (...)" and "transform(...)" equivalently<commit_after>use ::cairo;
use ::glib::translate::*;
use ::glib_sys;
use ::libc;

#[cfg(test)]
use std::f64::consts::*;

use cairo::MatrixTrait;
use cssparser::{Parser, ParserInput, Token, ParseError as CssParseError};

use error::*;
use parsers::{Parse, ParseError, optional_comma};

impl Parse for cairo::Matrix {
    type Data = ();
    type Err = AttributeError;

    fn parse (s: &str, _: ()) -> Result<cairo::Matrix, AttributeError> {
        parse_transform (s)
    }
}

pub fn parse_transform(s: &str) -> Result<cairo::Matrix, AttributeError> {
    let matrix = parse_transform_list(s)?;

    matrix.try_invert ().map (|_| matrix)
        .map_err (|_| AttributeError::Value ("invalid transformation matrix".to_string ()))
}

fn parse_transform_list(s: &str) -> Result<cairo::Matrix, AttributeError> {
    let mut input = ParserInput::new(s);
    let mut parser = Parser::new(&mut input);

    let mut matrix = cairo::Matrix::identity();

    loop {
        let m = parse_transform_command(&mut parser)?;
        matrix = cairo::Matrix::multiply(&m, &matrix);

        if parser.is_exhausted() {
            break;
        }

        optional_comma(&mut parser);
    }

    Ok(matrix)
}

fn make_expected_function_error() -> AttributeError {
    AttributeError::from(ParseError::new("expected matrix|translate|scale|rotate|skewX|skewY"))
}

fn parse_transform_command(parser: &mut Parser) -> Result<cairo::Matrix, AttributeError> {
    match parser.next()?.clone() {
        Token::Function(ref name) => parse_transform_function(name, parser),

        Token::Ident(ref name) => {
            parser.expect_parenthesis_block()?;
            parse_transform_function(name, parser)
        },

        _ => Err(make_expected_function_error()),
    }
}

fn parse_transform_function(name: &str, parser: &mut Parser) -> Result<cairo::Matrix, AttributeError> {
    match name {
        "matrix"    => parse_matrix_args(parser),
        "translate" => parse_translate_args(parser),
        "scale"     => parse_scale_args(parser),
        "rotate"    => parse_rotate_args(parser),
        "skewX"     => parse_skewX_args(parser),
        "skewY"     => parse_skewY_args(parser),
        _           => Err(make_expected_function_error()),
    }
}

fn parse_matrix_args(parser: &mut Parser) -> Result<cairo::Matrix, AttributeError> {
    parser.parse_nested_block(|p| {
        let xx = p.expect_number()? as f64;
        optional_comma(p);

        let yx = p.expect_number()? as f64;
        optional_comma(p);

        let xy = p.expect_number()? as f64;
        optional_comma(p);

        let yy = p.expect_number()? as f64;
        optional_comma(p);

        let x0 = p.expect_number()? as f64;
        optional_comma(p);

        let y0 = p.expect_number()? as f64;

        Ok(cairo::Matrix::new(xx, yx, xy, yy, x0, y0))
    }).map_err(CssParseError::<()>::basic)
        .map_err(|e| AttributeError::from(e))
}

fn parse_translate_args(parser: &mut Parser) -> Result<cairo::Matrix, AttributeError> {
    parser.parse_nested_block(|p| {
        let tx = p.expect_number()?;

        optional_comma(p);

        let ty = p.expect_number()?;

        Ok(cairo::Matrix::new(1.0, 0.0, 0.0, 1.0, tx as f64, ty as f64))
    }).map_err(CssParseError::<()>::basic)
        .map_err(|e| AttributeError::from(e))
}

fn parse_scale_args(parser: &mut Parser) -> Result<cairo::Matrix, AttributeError> {
    unimplemented!();
}

fn parse_rotate_args(parser: &mut Parser) -> Result<cairo::Matrix, AttributeError> {
    unimplemented!();
}

fn parse_skewX_args(parser: &mut Parser) -> Result<cairo::Matrix, AttributeError> {
    unimplemented!();
}

fn parse_skewY_args(parser: &mut Parser) -> Result<cairo::Matrix, AttributeError> {
    unimplemented!();
}

/*
pub fn parse_transform (s: &str) -> Result <cairo::Matrix, AttributeError> {
    let r = parse_TransformList (s);

    match r {
        Ok (m) => {
            m.try_invert ().map (|_| m)
                .map_err (|_| AttributeError::Value ("invalid transformation matrix".to_string ()))
        },

        Err (e) => {
            Err (AttributeError::Parse (ParseError::new (format! ("{:?}", e))))
        }
    }
}
*/

#[cfg(test)]
fn make_rotation_matrix (angle_degrees: f64, tx: f64, ty: f64) -> cairo::Matrix {
    let angle = angle_degrees * PI / 180.0;

    let mut m = cairo::Matrix::new (1.0, 0.0, 0.0, 1.0, tx, ty);

    let mut r = cairo::Matrix::identity ();
    r.rotate (angle);
    m = cairo::Matrix::multiply (&r, &m);

    m = cairo::Matrix::multiply (&cairo::Matrix::new (1.0, 0.0, 0.0, 1.0, -tx, -ty), &m);
    m
}

#[no_mangle]
pub extern fn rsvg_parse_transform (out_matrix: *mut cairo::Matrix, s: *const libc::c_char) -> glib_sys::gboolean {
    assert! (!out_matrix.is_null ());
    assert! (!s.is_null ());

    let string = unsafe { String::from_glib_none (s) };
    let matrix: &mut cairo::Matrix = unsafe { &mut *out_matrix };

    match parse_transform (&string) {
        Ok (m) => {
            *matrix = m;
            true.to_glib ()
        },

        Err (_) => {
            *matrix = cairo::Matrix::identity ();
            false.to_glib ()
        }
    }
}

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

    #[test]
    #[ignore]
    fn parses_valid_transform () {
        let t = cairo::Matrix::new (1.0, 0.0, 0.0, 1.0, 20.0, 30.0);
        let s = cairo::Matrix::new (10.0, 0.0, 0.0, 10.0, 0.0, 0.0);
        let r = make_rotation_matrix (30.0, 10.0, 10.0);

        let a = cairo::Matrix::multiply (&s, &t);
        assert_eq! (parse_transform ("translate(20, 30), scale (10) rotate (30 10 10)").unwrap (),
                    cairo::Matrix::multiply (&r, &a));
    }

    fn assert_parse_error(s: &str) {
        match parse_transform(s) {
            Err(AttributeError::Parse(_)) => {},
            _ => { panic!(); }
        }
    }

    #[test]
    #[ignore]
    fn syntax_error_yields_parse_error () {
        assert_parse_error("foo");
        assert_parse_error("matrix (1 2 3 4 5)");
        assert_parse_error("translate(1 2 3 4 5)");
        assert_parse_error("translate (1,)");
    }

    #[test]
    #[ignore]
    fn invalid_transform_yields_value_error () {
        match parse_transform ("matrix (0 0 0 0 0 0)") {
            Err (AttributeError::Value (_)) => {},
            _ => { panic! (); }
        }

        match parse_transform ("scale (0), translate (10, 10)") {
            Err (AttributeError::Value (_)) => {},
            _ => { panic! (); }
        }

        match parse_transform ("scale (0), skewX (90)") {
            Err (AttributeError::Value (_)) => {},
            _ => { panic! (); }
        }
    }
}

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

    #[test]
    fn parses_matrix () {
        assert_eq! (parse_transform ("matrix (1 2 3 4 5 6)").unwrap (),
                    cairo::Matrix::new (1.0, 2.0, 3.0, 4.0, 5.0, 6.0));

        assert_eq! (parse_transform ("matrix(1,2,3,4 5 6)").unwrap (),
                    cairo::Matrix::new (1.0, 2.0, 3.0, 4.0, 5.0, 6.0));

        assert_eq! (parse_transform ("matrix (1,2.25,-3.25e2,4 5 6)").unwrap (),
                    cairo::Matrix::new (1.0, 2.25, -325.0, 4.0, 5.0, 6.0));
    }

    #[test]
    fn parses_translate () {
        assert_eq! (parse_transform ("translate(-1 -2)").unwrap (),
                    cairo::Matrix::new (1.0, 0.0, 0.0, 1.0, -1.0, -2.0));

        // assert_eq! (parse_transform ("translate(-1, -2)").unwrap (),
        //             cairo::Matrix::new (1.0, 0.0, 0.0, 1.0, -1.0, -2.0));

        // assert_eq! (parse_transform ("translate(-1)").unwrap (),
        //             cairo::Matrix::new (1.0, 0.0, 0.0, 1.0, -1.0, 0.0));
    }

    #[test]
    #[ignore]
    fn parses_scale () {
        assert_eq! (parse_transform ("scale(-1 -2)").unwrap (),
                    cairo::Matrix::new (-1.0, 0.0, 0.0, -2.0, 0.0, 0.0));

        assert_eq! (parse_transform ("scale(-1, -2)").unwrap (),
                    cairo::Matrix::new (-1.0, 0.0, 0.0, -2.0, 0.0, 0.0));

        assert_eq! (parse_transform ("scale(-1)").unwrap (),
                    cairo::Matrix::new (-1.0, 0.0, 0.0, -1.0, 0.0, 0.0));
    }

    #[test]
    #[ignore]
    fn parses_rotate () {
        assert_eq! (parse_transform ("rotate (30)").unwrap (), make_rotation_matrix (30.0, 0.0, 0.0));
        assert_eq! (parse_transform ("rotate (30,-1,-2)").unwrap (), make_rotation_matrix (30.0, -1.0, -2.0));
        assert_eq! (parse_transform ("rotate (30, -1, -2)").unwrap (), make_rotation_matrix (30.0, -1.0, -2.0));
    }

    fn make_skew_x_matrix (angle_degrees: f64) -> cairo::Matrix {
        let a = angle_degrees * PI / 180.0;
        cairo::Matrix::new (1.0,      0.0,
                            a.tan (), 1.0,
                            0.0, 0.0)
    }

    fn make_skew_y_matrix (angle_degrees: f64) -> cairo::Matrix {
        let mut m = make_skew_x_matrix (angle_degrees);
        m.yx = m.xy;
        m.xy = 0.0;
        m
    }

    #[test]
    #[ignore]
    fn parses_skew_x () {
        assert_eq! (parse_transform ("skewX (30)").unwrap (), make_skew_x_matrix (30.0));
    }

    #[test]
    #[ignore]
    fn parses_skew_y () {
        assert_eq! (parse_transform ("skewY (30)").unwrap (), make_skew_y_matrix (30.0));
    }

    #[test]
    #[ignore]
    fn parses_transform_list () {
        let t = cairo::Matrix::new (1.0, 0.0, 0.0, 1.0, 20.0, 30.0);
        let s = cairo::Matrix::new (10.0, 0.0, 0.0, 10.0, 0.0, 0.0);
        let r = make_rotation_matrix (30.0, 10.0, 10.0);

        assert_eq! (parse_transform ("scale(10)rotate(30, 10, 10)").unwrap (),
                    cairo::Matrix::multiply (&r, &s));

        assert_eq! (parse_transform ("translate(20, 30), scale (10)").unwrap (),
                    cairo::Matrix::multiply (&s, &t));

        let a = cairo::Matrix::multiply (&s, &t);
        assert_eq! (parse_transform ("translate(20, 30), scale (10) rotate (30 10 10)").unwrap (),
                    cairo::Matrix::multiply (&r, &a));
    }
}
<|endoftext|>


<commit_before>extern crate glob;

use gitignore;
use std::io;
use std::path::{Path, PathBuf};

use self::glob::{Pattern, PatternError};

pub struct NotificationFilter {
    filters: Vec<Pattern>,
    ignores: Vec<Pattern>,
    ignore_file: Option<gitignore::PatternSet>,
}

#[derive(Debug)]
pub enum Error {
    BadPattern(PatternError),
    Io(io::Error),
}

impl NotificationFilter {
    pub fn new(current_dir: &Path,
               filters: Vec<String>,
               ignores: Vec<String>,
               ignore_file: Option<gitignore::PatternSet>)
               -> Result<NotificationFilter, Error> {
        let cwd = try!(current_dir.canonicalize());

        let compiled_filters = try!(filters.iter()
            .map(|p| NotificationFilter::pattern_for(&cwd, p))
            .collect());

        let compiled_ignores = try!(ignores.iter()
            .map(|p| NotificationFilter::pattern_for(&cwd, p))
            .collect());

        for compiled_filter in &compiled_filters {
            debug!("Adding filter: {}", compiled_filter);
        }

        for compiled_ignore in &compiled_ignores {
            debug!("Adding ignore: {}", compiled_ignore);
        }

        Ok(NotificationFilter {
            filters: compiled_filters,
            ignores: compiled_ignores,
            ignore_file: ignore_file,
        })
    }

    fn pattern_for(cwd: &PathBuf, p: &str) -> Result<Pattern, PatternError> {
        let mut path = PathBuf::from(p);
        if path.is_relative() {
            path = cwd.join(path.as_path());
        }

        if let Ok(metadata) = path.metadata() {
            if metadata.is_dir() {
                path = path.join("*");
            }
        }

        Pattern::new(path.to_str().unwrap())
    }

    pub fn is_excluded(&self, path: &Path) -> bool {
        let path_as_str = path.to_str().unwrap();

        for pattern in &self.ignores {
            if pattern.matches(path_as_str) {
                debug!("Ignoring {:?}: matched ignore filter", path);
                return true;
            }
        }

        for pattern in &self.filters {
            if pattern.matches(path_as_str) {
                return false;
            }
        }

        if let Some(ref ignore_file) = self.ignore_file {
            if ignore_file.is_excluded(path) {
                debug!("Ignoring {:?}: matched gitignore file", path);
                return true;
            }
        }

        if !self.filters.is_empty() {
            debug!("Ignoring {:?}: did not match any given filters", path);
        }

        !self.filters.is_empty()
    }
}

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

impl From<PatternError> for Error {
    fn from(err: PatternError) -> Error {
        Error::BadPattern(err)
    }
}
<commit_msg>Add NotificationFilter tests<commit_after>extern crate glob;

use gitignore;
use std::io;
use std::path::{Path, PathBuf};

use self::glob::{Pattern, PatternError};

pub struct NotificationFilter {
    filters: Vec<Pattern>,
    ignores: Vec<Pattern>,
    ignore_file: Option<gitignore::PatternSet>,
}

#[derive(Debug)]
pub enum Error {
    BadPattern(PatternError),
    Io(io::Error),
}

impl NotificationFilter {
    pub fn new(current_dir: &Path,
               filters: Vec<String>,
               ignores: Vec<String>,
               ignore_file: Option<gitignore::PatternSet>)
               -> Result<NotificationFilter, Error> {
        let cwd = try!(current_dir.canonicalize());

        let compiled_filters = try!(filters.iter()
            .map(|p| NotificationFilter::pattern_for(&cwd, p))
            .collect());

        let compiled_ignores = try!(ignores.iter()
            .map(|p| NotificationFilter::pattern_for(&cwd, p))
            .collect());

        for compiled_filter in &compiled_filters {
            debug!("Adding filter: {}", compiled_filter);
        }

        for compiled_ignore in &compiled_ignores {
            debug!("Adding ignore: {}", compiled_ignore);
        }

        Ok(NotificationFilter {
            filters: compiled_filters,
            ignores: compiled_ignores,
            ignore_file: ignore_file,
        })
    }

    fn pattern_for(cwd: &PathBuf, p: &str) -> Result<Pattern, PatternError> {
        let mut path = PathBuf::from(p);
        if path.is_relative() {
            path = cwd.join(path.as_path());
        }

        if let Ok(metadata) = path.metadata() {
            if metadata.is_dir() {
                path = path.join("*");
            }
        }

        Pattern::new(path.to_str().unwrap())
    }

    pub fn is_excluded(&self, path: &Path) -> bool {
        let path_as_str = path.to_str().unwrap();

        for pattern in &self.ignores {
            if pattern.matches(path_as_str) {
                debug!("Ignoring {:?}: matched ignore filter", path);
                return true;
            }
        }

        for pattern in &self.filters {
            if pattern.matches(path_as_str) {
                return false;
            }
        }

        if let Some(ref ignore_file) = self.ignore_file {
            if ignore_file.is_excluded(path) {
                debug!("Ignoring {:?}: matched gitignore file", path);
                return true;
            }
        }

        if !self.filters.is_empty() {
            debug!("Ignoring {:?}: did not match any given filters", path);
        }

        !self.filters.is_empty()
    }
}

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

impl From<PatternError> for Error {
    fn from(err: PatternError) -> Error {
        Error::BadPattern(err)
    }
}

#[cfg(test)]
mod tests {
    use super::NotificationFilter;
    use std::path::Path;

    #[test]
    fn test_allows_everything_by_default() {
        let filter = NotificationFilter::new(&Path::new("."), vec![], vec![], None).unwrap();

        assert!(!filter.is_excluded(&Path::new("foo")));
    }

    #[test]
    fn test_multiple_filters() {
        let filters = vec![String::from("*.rs"), String::from("*.toml")];
        let filter = NotificationFilter::new(&Path::new("."), filters, vec![], None).unwrap();
        let cwd = Path::new(".").canonicalize().unwrap();

        assert!(!filter.is_excluded(&cwd.join("hello.rs")));
        assert!(!filter.is_excluded(&cwd.join("Cargo.toml")));
        assert!(filter.is_excluded(&cwd.join("README.md")));
    }

    #[test]
    fn test_multiple_ignores() {
        let ignores = vec![String::from("*.rs"), String::from("*.toml")];
        let filter = NotificationFilter::new(&Path::new("."), vec![], ignores, None).unwrap();
        let cwd = Path::new(".").canonicalize().unwrap();

        assert!(filter.is_excluded(&cwd.join("hello.rs")));
        assert!(filter.is_excluded(&cwd.join("Cargo.toml")));
        assert!(!filter.is_excluded(&cwd.join("README.md")));
    }

    #[test]
    fn test_ignores_take_precedence() {
        let ignores = vec![String::from("*.rs"), String::from("*.toml")];
        let filter = NotificationFilter::new(&Path::new("."), ignores.clone(), ignores, None).unwrap();
        let cwd = Path::new(".").canonicalize().unwrap();

        assert!(filter.is_excluded(&cwd.join("hello.rs")));
        assert!(filter.is_excluded(&cwd.join("Cargo.toml")));
        assert!(filter.is_excluded(&cwd.join("README.md")));
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright 2017 Ben Ashford
 *
 * Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
 * http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
 * <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
 * option. This file may not be copied, modified, or distributed
 * except according to those terms.
 */

#![feature(test)]

extern crate test;

extern crate futures;
extern crate tokio_core;
extern crate redis_async;

use test::Bencher;

use futures::Future;

use tokio_core::reactor::Core;

use redis_async::client;

#[bench]
fn bench_simple_getsetdel(b: &mut Bencher) {
    let mut core = Core::new().unwrap();
    let addr = "127.0.0.1:6379".parse().unwrap();

    let connection = client::paired_connect(&addr, &core.handle());
    let connection = core.run(connection).unwrap();

    b.iter(|| {
               let set = connection.send(["SET", "test_key", "42"].as_ref());
               let get = connection.send(["GET", "test_key"].as_ref());
               let del = connection.send(["DEL", "test_key"].as_ref());
               let get_set = get.join(del);
               let (_, _) = core.run(get_set).unwrap();
           });
}

#[bench]
fn bench_big_pipeline(b: &mut Bencher) {
    let mut core = Core::new().unwrap();
    let addr = "127.0.0.1:6379".parse().unwrap();

    let connection = client::paired_connect(&addr, &core.handle());
    let connection = core.run(connection).unwrap();

    let data_size = 1000;

    b.iter(|| {
        for x in 0..data_size {
            let test_key = format!("test_{}", x);
            connection.send(["SET", &test_key, &x.to_string()].as_ref());
        }
        let mut gets = Vec::with_capacity(data_size);
        for x in 0..data_size {
            let test_key = format!("test_{}", x);
            gets.push(connection.send(["GET", &test_key].as_ref()));
        }
        let last_get = gets.remove(999);
        let _ = core.run(last_get);
    });
}<commit_msg>New, more complex, benchmark<commit_after>/*
 * Copyright 2017 Ben Ashford
 *
 * Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
 * http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
 * <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
 * option. This file may not be copied, modified, or distributed
 * except according to those terms.
 */

#![feature(test)]

extern crate test;

extern crate futures;
extern crate tokio_core;
extern crate redis_async;

use std::sync::Arc;

use test::Bencher;

use futures::Future;

use tokio_core::reactor::Core;

use redis_async::client;

#[bench]
fn bench_simple_getsetdel(b: &mut Bencher) {
    let mut core = Core::new().unwrap();
    let addr = "127.0.0.1:6379".parse().unwrap();

    let connection = client::paired_connect(&addr, &core.handle());
    let connection = core.run(connection).unwrap();

    b.iter(|| {
               let set = connection.send(["SET", "test_key", "42"].as_ref());
               let get = connection.send(["GET", "test_key"].as_ref());
               let del = connection.send(["DEL", "test_key"].as_ref());
               let get_set = get.join(del);
               let (_, _) = core.run(get_set).unwrap();
           });
}

#[bench]
fn bench_big_pipeline(b: &mut Bencher) {
    let mut core = Core::new().unwrap();
    let addr = "127.0.0.1:6379".parse().unwrap();

    let connection = client::paired_connect(&addr, &core.handle());
    let connection = core.run(connection).unwrap();

    let data_size = 100;

    b.iter(|| {
        for x in 0..data_size {
            let test_key = format!("test_{}", x);
            connection.send(["SET", &test_key, &x.to_string()].as_ref());
        }
        let mut gets = Vec::with_capacity(data_size);
        for x in 0..data_size {
            let test_key = format!("test_{}", x);
            gets.push(connection.send(["GET", &test_key].as_ref()));
        }
        let last_get = gets.remove(data_size - 1);
        let _ = core.run(last_get);
    });
}

#[bench]
fn bench_complex_pipeline(b: &mut Bencher) {
    let mut core = Core::new().unwrap();
    let addr = "127.0.0.1:6379".parse().unwrap();

    let connection = client::paired_connect(&addr, &core.handle());
    let connection = Arc::new(core.run(connection).unwrap());

    let data_size = 100;

    b.iter(|| {
        let sets = (0..data_size).map(|x| {
            let connection_inner = connection.clone();
            connection
                .send(["INCR", "id_gen"].as_ref())
                .and_then(move |id| {
                              let id = format!("id_{}", id.into_string().unwrap());
                              connection_inner.send(["SET", &id, &x.to_string()].as_ref())
                          })
        });
        let all_sets = futures::future::join_all(sets);
        let _ = core.run(all_sets);
    });
}<|endoftext|>


<commit_before>#![feature(test)]

extern crate mazes;
extern crate test;

use mazes::squaregrid::SquareGrid;
use mazes::generators;
use test::Bencher;

#[bench]
fn bench_binary_maze_11_u8(b: &mut Bencher) {

    let mut g = SquareGrid::<u8>::new(11);

    b.iter(|| generators::binary_tree(&mut g));
}

#[bench]
fn bench_binary_maze_11_u16(b: &mut Bencher) {

    let mut g = SquareGrid::<u16>::new(11);

    b.iter(|| generators::binary_tree(&mut g));
}

#[bench]
fn bench_binary_maze_11_u32(b: &mut Bencher) {

    let mut g = SquareGrid::<u32>::new(11);

    b.iter(|| generators::binary_tree(&mut g));
}

#[bench]
fn bench_binary_maze_128_u16(b: &mut Bencher) {

    let mut g = SquareGrid::<u16>::new(128);

    b.iter(|| generators::binary_tree(&mut g));
}

#[bench]
fn bench_binary_maze_128_u32(b: &mut Bencher) {

    let mut g = SquareGrid::<u32>::new(128);

    b.iter(|| generators::binary_tree(&mut g));
}

#[bench]
fn bench_binary_maze_500(b: &mut Bencher) {

    let mut g = SquareGrid::<u32>::new(500);

    b.iter(|| generators::binary_tree(&mut g));
}

#[bench]
fn bench_sidewinder_maze_128_u16(b: &mut Bencher) {

    let mut g = SquareGrid::<u16>::new(128);

    b.iter(|| generators::sidewinder(&mut g));
}

#[bench]
fn bench_sidewinder_maze_128_u32(b: &mut Bencher) {

    let mut g = SquareGrid::<u32>::new(128);

    b.iter(|| generators::sidewinder(&mut g));
}

#[bench]
fn bench_sidewinder_maze_500(b: &mut Bencher) {

    let mut g = SquareGrid::<u32>::new(500);

    b.iter(|| generators::sidewinder(&mut g));
}

#[bench]
fn bench_random_walk_maze_128_u16(b: &mut Bencher) {

    let mut g = SquareGrid::<u16>::new(128);

    b.iter(|| generators::random_walk(&mut g));
}

#[bench]
fn bench_random_walk_maze_128_u32(b: &mut Bencher) {

    let mut g = SquareGrid::<u32>::new(128);

    b.iter(|| generators::random_walk(&mut g));
}

#[bench]
fn bench_random_walk_maze_250(b: &mut Bencher) {

    let mut g = SquareGrid::<u32>::new(250);

    b.iter(|| generators::random_walk(&mut g));
}
<commit_msg>Benches update.<commit_after>#![feature(test)]

extern crate mazes;
extern crate test;

use mazes::squaregrid::SquareGrid;
use mazes::generators;
use test::Bencher;

#[bench]
fn bench_binary_maze_32_u16(b: &mut Bencher) {

    let mut g = SquareGrid::<u16>::new(32);

    b.iter(|| generators::binary_tree(&mut g));
}

#[bench]
fn bench_binary_maze_500(b: &mut Bencher) {

    let mut g = SquareGrid::<u32>::new(500);

    b.iter(|| generators::binary_tree(&mut g));
}

#[bench]
fn bench_sidewinder_maze_32_u16(b: &mut Bencher) {

    let mut g = SquareGrid::<u16>::new(32);

    b.iter(|| generators::sidewinder(&mut g));
}

#[bench]
fn bench_sidewinder_maze_500(b: &mut Bencher) {

    let mut g = SquareGrid::<u32>::new(500);

    b.iter(|| generators::sidewinder(&mut g));
}

#[bench]
fn bench_aldous_broder_maze_32_u16(b: &mut Bencher) {

    let mut g = SquareGrid::<u16>::new(32);

    b.iter(|| generators::aldous_broder(&mut g));
}

#[bench]
fn bench_wilson_maze_32_u16(b: &mut Bencher) {

    let mut g = SquareGrid::<u16>::new(32);

    b.iter(|| generators::wilson(&mut g));
}

#[bench]
fn bench_hunt_and_kill_maze_32_u16(b: &mut Bencher) {

    let mut g = SquareGrid::<u16>::new(32);

    b.iter(|| generators::hunt_and_kill(&mut g));
}
<|endoftext|>


<commit_before>//! Dynamic library facilities.
//!
//! A simple wrapper over the platform's dynamic library facilities

use std::ffi::CString;
use std::path::Path;

pub struct DynamicLibrary {
    handle: *mut u8,
}

impl Drop for DynamicLibrary {
    fn drop(&mut self) {
        unsafe { dl::close(self.handle) }
    }
}

impl DynamicLibrary {
    /// Lazily open a dynamic library.
    pub fn open(filename: &Path) -> Result<DynamicLibrary, String> {
        let maybe_library = dl::open(filename.as_os_str());

        // The dynamic library must not be constructed if there is
        // an error opening the library so the destructor does not
        // run.
        match maybe_library {
            Err(err) => Err(err),
            Ok(handle) => Ok(DynamicLibrary { handle }),
        }
    }

    /// Accesses the value at the symbol of the dynamic library.
    pub unsafe fn symbol<T>(&self, symbol: &str) -> Result<*mut T, String> {
        // This function should have a lifetime constraint of 'a on
        // T but that feature is still unimplemented

        let raw_string = CString::new(symbol).unwrap();
        let maybe_symbol_value = dl::symbol(self.handle, raw_string.as_ptr());

        // The value must not be constructed if there is an error so
        // the destructor does not run.
        match maybe_symbol_value {
            Err(err) => Err(err),
            Ok(symbol_value) => Ok(symbol_value as *mut T),
        }
    }
}

#[cfg(test)]
mod tests;

#[cfg(unix)]
mod dl {
    use std::ffi::{CString, OsStr};
    use std::os::unix::prelude::*;

    // `dlerror` is process global, so we can only allow a single thread at a
    // time to call `dlsym` and `dlopen` if we want to check the error message.
    mod error {
        use std::ffi::CStr;
        use std::lazy::SyncLazy;
        use std::sync::{Mutex, MutexGuard};

        pub fn lock() -> MutexGuard<'static, Guard> {
            static LOCK: SyncLazy<Mutex<Guard>> = SyncLazy::new(|| Mutex::new(Guard { _priv: () }));
            LOCK.lock().unwrap()
        }

        pub struct Guard {
            _priv: (),
        }

        impl Guard {
            pub fn get(&mut self) -> Result<(), String> {
                let msg = unsafe { libc::dlerror() };
                if msg.is_null() {
                    Ok(())
                } else {
                    let msg = unsafe { CStr::from_ptr(msg as *const _) };
                    Err(msg.to_string_lossy().into_owned())
                }
            }

            pub fn clear(&mut self) {
                let _ = unsafe { libc::dlerror() };
            }
        }
    }

    pub(super) fn open(filename: &OsStr) -> Result<*mut u8, String> {
        let s = CString::new(filename.as_bytes()).unwrap();

        let mut dlerror = error::lock();
        let ret = unsafe { libc::dlopen(s.as_ptr(), libc::RTLD_LAZY) } as *mut u8;

        if !ret.is_null() {
            return Ok(ret);
        }

        // A NULL return from `dlopen` indicates that an error has
        // definitely occurred, so if nothing is in `dlerror`, we are
        // racing with another thread that has stolen our error message.
        dlerror.get().and_then(|()| Err("Unknown error".to_string()))
    }

    pub(super) unsafe fn symbol(
        handle: *mut u8,
        symbol: *const libc::c_char,
    ) -> Result<*mut u8, String> {
        let mut dlerror = error::lock();

        // Flush `dlerror` since we need to use it to determine whether the subsequent call to
        // `dlsym` is successful.
        dlerror.clear();

        let ret = libc::dlsym(handle as *mut libc::c_void, symbol) as *mut u8;

        // A non-NULL return value *always* indicates success. There's no need
        // to check `dlerror`.
        if !ret.is_null() {
            return Ok(ret);
        }

        dlerror.get().map(|()| ret)
    }

    pub(super) unsafe fn close(handle: *mut u8) {
        libc::dlclose(handle as *mut libc::c_void);
    }
}

#[cfg(windows)]
mod dl {
    use std::ffi::OsStr;
    use std::io;
    use std::os::windows::prelude::*;
    use std::ptr;

    use winapi::shared::minwindef::HMODULE;
    use winapi::um::errhandlingapi::SetThreadErrorMode;
    use winapi::um::libloaderapi::{FreeLibrary, GetProcAddress, LoadLibraryW};
    use winapi::um::winbase::SEM_FAILCRITICALERRORS;

    pub(super) fn open(filename: &OsStr) -> Result<*mut u8, String> {
        // disable "dll load failed" error dialog.
        let prev_error_mode = unsafe {
            let new_error_mode = SEM_FAILCRITICALERRORS;
            let mut prev_error_mode = 0;
            let result = SetThreadErrorMode(new_error_mode, &mut prev_error_mode);
            if result == 0 {
                return Err(io::Error::last_os_error().to_string());
            }
            prev_error_mode
        };

        let filename_str: Vec<_> = filename.encode_wide().chain(Some(0)).collect();
        let result = unsafe { LoadLibraryW(filename_str.as_ptr()) } as *mut u8;
        let result = ptr_result(result);

        unsafe {
            SetThreadErrorMode(prev_error_mode, ptr::null_mut());
        }

        result
    }

    pub(super) unsafe fn symbol(
        handle: *mut u8,
        symbol: *const libc::c_char,
    ) -> Result<*mut u8, String> {
        let ptr = GetProcAddress(handle as HMODULE, symbol) as *mut u8;
        ptr_result(ptr)
    }

    pub(super) unsafe fn close(handle: *mut u8) {
        FreeLibrary(handle as HMODULE);
    }

    fn ptr_result<T>(ptr: *mut T) -> Result<*mut T, String> {
        if ptr.is_null() { Err(io::Error::last_os_error().to_string()) } else { Ok(ptr) }
    }
}
<commit_msg>Treat a NULL return from `dlsym` as an error on illumos<commit_after>//! Dynamic library facilities.
//!
//! A simple wrapper over the platform's dynamic library facilities

use std::ffi::CString;
use std::path::Path;

pub struct DynamicLibrary {
    handle: *mut u8,
}

impl Drop for DynamicLibrary {
    fn drop(&mut self) {
        unsafe { dl::close(self.handle) }
    }
}

impl DynamicLibrary {
    /// Lazily open a dynamic library.
    pub fn open(filename: &Path) -> Result<DynamicLibrary, String> {
        let maybe_library = dl::open(filename.as_os_str());

        // The dynamic library must not be constructed if there is
        // an error opening the library so the destructor does not
        // run.
        match maybe_library {
            Err(err) => Err(err),
            Ok(handle) => Ok(DynamicLibrary { handle }),
        }
    }

    /// Accesses the value at the symbol of the dynamic library.
    pub unsafe fn symbol<T>(&self, symbol: &str) -> Result<*mut T, String> {
        // This function should have a lifetime constraint of 'a on
        // T but that feature is still unimplemented

        let raw_string = CString::new(symbol).unwrap();
        let maybe_symbol_value = dl::symbol(self.handle, raw_string.as_ptr());

        // The value must not be constructed if there is an error so
        // the destructor does not run.
        match maybe_symbol_value {
            Err(err) => Err(err),
            Ok(symbol_value) => Ok(symbol_value as *mut T),
        }
    }
}

#[cfg(test)]
mod tests;

#[cfg(unix)]
mod dl {
    use std::ffi::{CString, OsStr};
    use std::os::unix::prelude::*;

    // `dlerror` is process global, so we can only allow a single thread at a
    // time to call `dlsym` and `dlopen` if we want to check the error message.
    mod error {
        use std::ffi::CStr;
        use std::lazy::SyncLazy;
        use std::sync::{Mutex, MutexGuard};

        pub fn lock() -> MutexGuard<'static, Guard> {
            static LOCK: SyncLazy<Mutex<Guard>> = SyncLazy::new(|| Mutex::new(Guard { _priv: () }));
            LOCK.lock().unwrap()
        }

        pub struct Guard {
            _priv: (),
        }

        impl Guard {
            pub fn get(&mut self) -> Result<(), String> {
                let msg = unsafe { libc::dlerror() };
                if msg.is_null() {
                    Ok(())
                } else {
                    let msg = unsafe { CStr::from_ptr(msg as *const _) };
                    Err(msg.to_string_lossy().into_owned())
                }
            }

            pub fn clear(&mut self) {
                let _ = unsafe { libc::dlerror() };
            }
        }
    }

    pub(super) fn open(filename: &OsStr) -> Result<*mut u8, String> {
        let s = CString::new(filename.as_bytes()).unwrap();

        let mut dlerror = error::lock();
        let ret = unsafe { libc::dlopen(s.as_ptr(), libc::RTLD_LAZY) } as *mut u8;

        if !ret.is_null() {
            return Ok(ret);
        }

        // A NULL return from `dlopen` indicates that an error has
        // definitely occurred, so if nothing is in `dlerror`, we are
        // racing with another thread that has stolen our error message.
        dlerror.get().and_then(|()| Err("Unknown error".to_string()))
    }

    pub(super) unsafe fn symbol(
        handle: *mut u8,
        symbol: *const libc::c_char,
    ) -> Result<*mut u8, String> {
        // HACK(#74469): On some platforms, users observed foreign code
        // (specifically libc) invoking `dlopen`/`dlsym` in parallel with the
        // functions in this module. This is problematic because, according to
        // the POSIX API documentation, `dlerror` must be called to determine
        // whether `dlsym` succeeded. Unlike `dlopen`, a NULL return value may
        // indicate a successfully resolved symbol with an address of zero.
        //
        // Because symbols with address zero shouldn't occur in practice, we
        // treat them as errors on platforms with misbehaving libc
        // implementations.
        const DLSYM_NULL_IS_ERROR: bool = cfg!(target_os = "illumos");

        let mut dlerror = error::lock();

        // No need to flush `dlerror` if we aren't using it to determine whether
        // the subsequent call to `dlsym` succeeded. If an error occurs, any
        // stale value will be overwritten.
        if !DLSYM_NULL_IS_ERROR {
            dlerror.clear();
        }

        let ret = libc::dlsym(handle as *mut libc::c_void, symbol) as *mut u8;

        // A non-NULL return value *always* indicates success. There's no need
        // to check `dlerror`.
        if !ret.is_null() {
            return Ok(ret);
        }

        match dlerror.get() {
            Ok(()) if DLSYM_NULL_IS_ERROR => Err("Unknown error".to_string()),
            Ok(()) => Ok(ret),

            Err(msg) => Err(msg),
        }
    }

    pub(super) unsafe fn close(handle: *mut u8) {
        libc::dlclose(handle as *mut libc::c_void);
    }
}

#[cfg(windows)]
mod dl {
    use std::ffi::OsStr;
    use std::io;
    use std::os::windows::prelude::*;
    use std::ptr;

    use winapi::shared::minwindef::HMODULE;
    use winapi::um::errhandlingapi::SetThreadErrorMode;
    use winapi::um::libloaderapi::{FreeLibrary, GetProcAddress, LoadLibraryW};
    use winapi::um::winbase::SEM_FAILCRITICALERRORS;

    pub(super) fn open(filename: &OsStr) -> Result<*mut u8, String> {
        // disable "dll load failed" error dialog.
        let prev_error_mode = unsafe {
            let new_error_mode = SEM_FAILCRITICALERRORS;
            let mut prev_error_mode = 0;
            let result = SetThreadErrorMode(new_error_mode, &mut prev_error_mode);
            if result == 0 {
                return Err(io::Error::last_os_error().to_string());
            }
            prev_error_mode
        };

        let filename_str: Vec<_> = filename.encode_wide().chain(Some(0)).collect();
        let result = unsafe { LoadLibraryW(filename_str.as_ptr()) } as *mut u8;
        let result = ptr_result(result);

        unsafe {
            SetThreadErrorMode(prev_error_mode, ptr::null_mut());
        }

        result
    }

    pub(super) unsafe fn symbol(
        handle: *mut u8,
        symbol: *const libc::c_char,
    ) -> Result<*mut u8, String> {
        let ptr = GetProcAddress(handle as HMODULE, symbol) as *mut u8;
        ptr_result(ptr)
    }

    pub(super) unsafe fn close(handle: *mut u8) {
        FreeLibrary(handle as HMODULE);
    }

    fn ptr_result<T>(ptr: *mut T) -> Result<*mut T, String> {
        if ptr.is_null() { Err(io::Error::last_os_error().to_string()) } else { Ok(ptr) }
    }
}
<|endoftext|>


<commit_before>// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use ast::{MetaItem, Item, Expr};
use codemap::Span;
use ext::base::ExtCtxt;
use ext::build::AstBuilder;
use ext::deriving::generic::*;
use ext::deriving::generic::ty::*;
use parse::token::InternedString;
use ptr::P;

pub fn expand_deriving_eq(cx: &mut ExtCtxt,
                          span: Span,
                          mitem: &MetaItem,
                          item: &Item,
                          push: |P<Item>|) {
    // structures are equal if all fields are equal, and non equal, if
    // any fields are not equal or if the enum variants are different
    fn cs_eq(cx: &mut ExtCtxt, span: Span, substr: &Substructure) -> P<Expr> {
        cs_and(|cx, span, _, _| cx.expr_bool(span, false),
                                 cx, span, substr)
    }
    fn cs_ne(cx: &mut ExtCtxt, span: Span, substr: &Substructure) -> P<Expr> {
        cs_or(|cx, span, _, _| cx.expr_bool(span, true),
              cx, span, substr)
    }

    macro_rules! md (
        ($name:expr, $f:ident) => { {
            let inline = cx.meta_word(span, InternedString::new("inline"));
            let attrs = vec!(cx.attribute(span, inline));
            MethodDef {
                name: $name,
                generics: LifetimeBounds::empty(),
                explicit_self: borrowed_explicit_self(),
                args: vec!(borrowed_self()),
                ret_ty: Literal(Path::new(vec!("bool"))),
                attributes: attrs,
                combine_substructure: combine_substructure(|a, b, c| {
                    $f(a, b, c)
                })
            }
        } }
    );

    let trait_def = TraitDef {
        span: span,
        attributes: Vec::new(),
        path: Path::new(vec!("std", "cmp", "PartialEq")),
        additional_bounds: Vec::new(),
        generics: LifetimeBounds::empty(),
        methods: vec!(
            md!("eq", cs_eq),
            md!("ne", cs_ne)
        )
    };
    trait_def.expand(cx, mitem, item, push)
}
<commit_msg>Use operator sugar in the expansion of `#[deriving(PartialEq)]`<commit_after>// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use ast::{MetaItem, Item, Expr, mod};
use codemap::Span;
use ext::base::ExtCtxt;
use ext::build::AstBuilder;
use ext::deriving::generic::*;
use ext::deriving::generic::ty::*;
use parse::token::InternedString;
use ptr::P;

pub fn expand_deriving_eq(cx: &mut ExtCtxt,
                          span: Span,
                          mitem: &MetaItem,
                          item: &Item,
                          push: |P<Item>|) {
    // structures are equal if all fields are equal, and non equal, if
    // any fields are not equal or if the enum variants are different
    fn cs_eq(cx: &mut ExtCtxt, span: Span, substr: &Substructure) -> P<Expr> {
        cs_fold(
            true,  // use foldl
            |cx, span, subexpr, self_f, other_fs| {
                let other_f = match other_fs {
                    [ref o_f] => o_f,
                    _ => cx.span_bug(span, "not exactly 2 arguments in `deriving(Eq)`")
                };

                let eq = cx.expr_binary(span, ast::BiEq, self_f, other_f.clone());

                cx.expr_binary(span, ast::BiAnd, subexpr, eq)
            },
            cx.expr_bool(span, true),
            |cx, span, _, _| cx.expr_bool(span, false),
            cx, span, substr)
    }
    fn cs_ne(cx: &mut ExtCtxt, span: Span, substr: &Substructure) -> P<Expr> {
        cs_fold(
            true,  // use foldl
            |cx, span, subexpr, self_f, other_fs| {
                let other_f = match other_fs {
                    [ref o_f] => o_f,
                    _ => cx.span_bug(span, "not exactly 2 arguments in `deriving(Eq)`")
                };

                let eq = cx.expr_binary(span, ast::BiNe, self_f, other_f.clone());

                cx.expr_binary(span, ast::BiOr, subexpr, eq)
            },
            cx.expr_bool(span, false),
            |cx, span, _, _| cx.expr_bool(span, true),
            cx, span, substr)
    }

    macro_rules! md (
        ($name:expr, $f:ident) => { {
            let inline = cx.meta_word(span, InternedString::new("inline"));
            let attrs = vec!(cx.attribute(span, inline));
            MethodDef {
                name: $name,
                generics: LifetimeBounds::empty(),
                explicit_self: borrowed_explicit_self(),
                args: vec!(borrowed_self()),
                ret_ty: Literal(Path::new(vec!("bool"))),
                attributes: attrs,
                combine_substructure: combine_substructure(|a, b, c| {
                    $f(a, b, c)
                })
            }
        } }
    );

    let trait_def = TraitDef {
        span: span,
        attributes: Vec::new(),
        path: Path::new(vec!("std", "cmp", "PartialEq")),
        additional_bounds: Vec::new(),
        generics: LifetimeBounds::empty(),
        methods: vec!(
            md!("eq", cs_eq),
            md!("ne", cs_ne)
        )
    };
    trait_def.expand(cx, mitem, item, push)
}
<|endoftext|>


<commit_before>use ::handle::Handle;

/// A libudev context.
pub struct Context {
    udev: *mut ::ffi::udev,
}

impl Clone for Context {
    /// Increments reference count of `libudev` context.
    fn clone(&self) -> Self {
        Context {
            udev: unsafe { ::ffi::udev_ref(self.udev) },
        }
    }
}

impl Drop for Context {
    /// Decrements reference count of `libudev` context.
    fn drop(&mut self) {
        unsafe {
            ::ffi::udev_unref(self.udev);
        }
    }
}

#[doc(hidden)]
impl Handle<::ffi::udev> for Context {
    fn as_ptr(&self) -> *mut ::ffi::udev {
        self.udev
    }
}

impl Context {
    /// Creates a new context.
    pub fn new() -> ::Result<Self> {
        Ok(Context {
            udev: try_alloc!(unsafe { ::ffi::udev_new() }),
        })
    }
}
<commit_msg>Document !Send and !Sync for Context<commit_after>use ::handle::Handle;

/// A libudev context. Contexts may not be sent or shared between threads. The `libudev(3)` manpage
/// says:
///
/// > All functions require a libudev context to operate. This context can be create via
/// > udev_new(3). It is used to track library state and link objects together. No global state is
/// > used by libudev, everything is always linked to a udev context. Furthermore, multiple
/// > different udev contexts can be used in parallel by multiple threads. However, a single
/// > context must not be accessed by multiple threads in parallel.
///
/// In Rust, that means that `Context` is `!Send` and `!Sync`. This means a `Context` must be
/// created in the thread where it will be used. Several contexts can exist in separate threads,
/// but they can not be sent between threads.
///
/// Other types in this library (`Device`, `Enumerator`, `Monitor`, etc.) share a reference to a
/// context, which means that these types must also be `!Send` and `!Sync`.
pub struct Context {
    udev: *mut ::ffi::udev,
}

impl Clone for Context {
    /// Increments reference count of `libudev` context.
    fn clone(&self) -> Self {
        Context {
            udev: unsafe { ::ffi::udev_ref(self.udev) },
        }
    }
}

impl Drop for Context {
    /// Decrements reference count of `libudev` context.
    fn drop(&mut self) {
        unsafe {
            ::ffi::udev_unref(self.udev);
        }
    }
}

#[doc(hidden)]
impl Handle<::ffi::udev> for Context {
    fn as_ptr(&self) -> *mut ::ffi::udev {
        self.udev
    }
}

impl Context {
    /// Creates a new context.
    pub fn new() -> ::Result<Self> {
        Ok(Context {
            udev: try_alloc!(unsafe { ::ffi::udev_new() }),
        })
    }
}
<|endoftext|>