<commit_before>//! A library for build scripts to compile custom C code
//!
//! This library is intended to be used as a `build-dependencies` entry in
//! `Cargo.toml`:
//!
//! ```toml
//! [build-dependencies]
//! gcc = "0.2"
//! ```
//!
//! The purpose of this crate is to provide the utility functions necessary to
//! compile C code into a static archive which is then linked into a Rust crate.
//! The top-level `compile_library` function serves as a convenience and more
//! advanced configuration is available through the `Config` builder.
//!
//! This crate will automatically detect situations such as cross compilation or
//! other environment variables set by Cargo and will build code appropriately.
//!
//! # Examples
//!
//! Use the default configuration:
//!
//! ```no_run
//! extern crate gcc;
//!
//! fn main() {
//!     gcc::compile_library("libfoo.a", &["src/foo.c"]);
//! }
//! ```
//!
//! Use more advanced configuration:
//!
//! ```no_run
//! extern crate gcc;
//!
//! fn main() {
//!     gcc::Config::new()
//!                 .file("src/foo.c")
//!                 .define("FOO", Some("bar"))
//!                 .include("src")
//!                 .compile("libfoo.a");
//! }
//! ```

#![doc(html_root_url = "http://alexcrichton.com/gcc-rs")]
#![cfg_attr(test, deny(warnings))]

use std::env;
use std::ffi::OsString;
use std::fs;
use std::io;
use std::path::{PathBuf, Path};
use std::process::{Command, Stdio};

/// Extra configuration to pass to gcc.
pub struct Config {
    include_directories: Vec<PathBuf>,
    definitions: Vec<(String, Option<String>)>,
    objects: Vec<PathBuf>,
    flags: Vec<String>,
    files: Vec<PathBuf>,
    cpp: bool,
    cpp_stdlib: Option<String>,
}

/// Returns the default C++ standard library for the current target: `libc++`
/// for OS X and `libstdc++` for anything else.
fn target_default_cpp_stdlib() -> Option<&'static str> {
    let target = getenv_unwrap("TARGET");
    if target.contains("msvc") {
        None
    } else if target.contains("darwin") {
        Some("c++")
    } else {
        Some("stdc++")
    }
}

fn getenv(v: &str) -> Option<String> {
    let r = env::var(v).ok();
    println!("{} = {:?}", v, r);
    r
}

fn getenv_unwrap(v: &str) -> String {
    match getenv(v) {
        Some(s) => s,
        None => fail(&format!("environment variable `{}` not defined", v)),
    }
}

/// Compile a library from the given set of input C files.
///
/// This will simply compile all files into object files and then assemble them
/// into the output. This will read the standard environment variables to detect
/// cross compilations and such.
///
/// This function will also print all metadata on standard output for Cargo.
///
/// # Example
///
/// ```no_run
/// gcc::compile_library("libfoo.a", &["foo.c", "bar.c"]);
/// ```
pub fn compile_library(output: &str, files: &[&str]) {
    let mut c = Config::new();
    for f in files.iter() {
        c.file(*f);
    }
    c.compile(output)
}

impl Config {
    /// Construct a new instance of a blank set of configuration.
    ///
    /// This builder is finished with the `compile` function.
    pub fn new() -> Config {
        Config {
            include_directories: Vec::new(),
            definitions: Vec::new(),
            objects: Vec::new(),
            flags: Vec::new(),
            files: Vec::new(),
            cpp: false,
            cpp_stdlib: target_default_cpp_stdlib().map(|s| s.into()),
        }
    }

    /// Add a directory to the `-I` or include path for headers
    pub fn include<P: AsRef<Path>>(&mut self, dir: P) -> &mut Config {
        self.include_directories.push(dir.as_ref().to_path_buf());
        self
    }

    /// Specify a `-D` variable with an optional value.
    pub fn define(&mut self, var: &str, val: Option<&str>) -> &mut Config {
        self.definitions.push((var.to_string(), val.map(|s| s.to_string())));
        self
    }

    /// Add an arbitrary object file to link in
    pub fn object<P: AsRef<Path>>(&mut self, obj: P) -> &mut Config {
        self.objects.push(obj.as_ref().to_path_buf());
        self
    }

    /// Add an arbitrary flag to the invocation of the compiler
    pub fn flag(&mut self, flag: &str) -> &mut Config {
        self.flags.push(flag.to_string());
        self
    }

    /// Add a file which will be compiled
    pub fn file<P: AsRef<Path>>(&mut self, p: P) -> &mut Config {
        self.files.push(p.as_ref().to_path_buf());
        self
    }

    /// Set C++ support
    pub fn cpp(&mut self, cpp: bool) -> &mut Config {
        self.cpp = cpp;
        self
    }

    /// Set the standard library to use when compiling with C++ support.
    ///
    /// The default value of this property depends on the current target: On
    /// OS X `Some("c++")` is used and for other supported targets
    /// `Some("stdc++")` is used.
    ///
    /// The choosen library is specified when compiling using the `-stdlib` flag
    /// and Cargo is instructed to link to the given library.
    ///
    /// A value of `None` indicates that no special actions regarding the C++
    /// standard library should be take. This means that neither are linking
    /// instructions provided to Cargo nor is the compiler told to use a
    /// specific standard library.
    ///
    /// The given library name must not contain the `lib` prefix.
    pub fn cpp_stdlib(&mut self, cpp_stdlib: Option<&str>) -> &mut Config {
        self.cpp_stdlib = cpp_stdlib.map(|s| s.into());
        self
    }

    /// Run the compiler, generating the file `output`
    ///
    /// The name `output` must begin with `lib` and end with `.a`
    pub fn compile(&self, output: &str) {
        assert!(output.starts_with("lib"));
        assert!(output.ends_with(".a"));

        let target = getenv_unwrap("TARGET");
        let src = PathBuf::from(getenv_unwrap("CARGO_MANIFEST_DIR"));
        let dst = PathBuf::from(getenv_unwrap("OUT_DIR"));
        let mut objects = Vec::new();
        for file in self.files.iter() {
            let mut cmd = self.compile_cmd(&target);
            cmd.arg(src.join(file));

            let obj = dst.join(file).with_extension("o");
            fs::create_dir_all(&obj.parent().unwrap()).unwrap();
            if target.contains("msvc") {
                let mut s = OsString::from("/Fo:");
                s.push(&obj);
                cmd.arg(s);
            } else {
                cmd.arg("-o").arg(&obj);
            }
            run(&mut cmd, &self.compiler(&target));
            objects.push(obj);
        }

        if target.contains("msvc") {
            let mut out = OsString::from("/OUT:");
            out.push(dst.join(output));
            run(Command::new("lib").arg(out).args(&objects).args(&self.objects),
                "lib");
        } else {
            run(Command::new(&ar(&target)).arg("crus")
                                          .arg(&dst.join(output))
                                          .args(&objects)
                                          .args(&self.objects),
                &ar(&target));
        }
        println!("cargo:rustc-link-search=native={}", dst.display());
        println!("cargo:rustc-link-lib=static={}",
                 &output[3..output.len() - 2]);

        // Add specific C++ libraries, if enabled.
        if self.cpp {
            if let Some(ref stdlib) = self.cpp_stdlib {
                println!("cargo:rustc-link-lib={}", stdlib);
            }
        }
    }

    fn compiler(&self, target: &str) -> String {
        if self.cpp {
            gxx(target)
        } else {
            gcc(target)
        }
    }

    fn compile_flags(&self) -> Vec<String> {
        if self.cpp {
            cxxflags()
        } else {
            cflags()
        }
    }

    fn compile_cmd(&self, target: &str) -> Command {
        let opt_level = getenv_unwrap("OPT_LEVEL");
        let profile = getenv_unwrap("PROFILE");
        println!("{} {}", profile, opt_level);

        let mut cmd = Command::new(self.compiler(&target));

        if target.contains("msvc") {
            cmd.arg("/c");
            cmd.arg(format!("/O{}", opt_level));
        } else {
            cmd.arg(format!("-O{}", opt_level));
            cmd.arg("-c");
            cmd.arg("-ffunction-sections").arg("-fdata-sections");
        }
        cmd.args(&self.compile_flags());

        if target.contains("-ios") {
            cmd.args(&ios_flags(&target));
        } else if !target.contains("msvc") {
            if target.contains("windows") {
                cmd.arg("-mwin32");
            }

            if target.contains("i686") {
                cmd.arg("-m32");
            } else if target.contains("x86_64") {
                cmd.arg("-m64");
            }

            if !target.contains("i686") {
                cmd.arg("-fPIC");
            }
        }

        if self.cpp {
            if let Some(ref stdlib) = self.cpp_stdlib {
                cmd.arg(&format!("-stdlib=lib{}", stdlib));
            }
        }

        for directory in self.include_directories.iter() {
            cmd.arg(if target.contains("msvc") {"/I"} else {"-I"});
            cmd.arg(directory);
        }

        for flag in self.flags.iter() {
            cmd.arg(flag);
        }

        for &(ref key, ref value) in self.definitions.iter() {
            let lead = if target.contains("msvc") {"/"} else {"-"};
            if let &Some(ref value) = value {
                cmd.arg(&format!("{}D{}={}", lead, key, value));
            } else {
                cmd.arg(&format!("{}D{}", lead, key));
            }
        }
        return cmd;
    }
}

fn run(cmd: &mut Command, program: &str) {
    println!("running: {:?}", cmd);
    let status = match cmd.status() {
        Ok(status) => status,
        Err(ref e) if e.kind() == io::ErrorKind::NotFound => {
            let extra = if cfg!(windows) {
                " (see https://github.com/alexcrichton/gcc-rs#windows-notes \
                   for help)"
            } else {
                ""
            };
            fail(&format!("failed to execute command: {}\nIs `{}` \
                           not installed?{}", e, program, extra));
        }
        Err(e) => fail(&format!("failed to execute command: {}", e)),
    };
    if !status.success() {
        fail(&format!("command did not execute successfully, got: {}", status));
    }
}

fn get_var(var_base: &str) -> Result<String, String> {
    let target = getenv_unwrap("TARGET");
    let host = getenv_unwrap("HOST");
    let kind = if host == target {"HOST"} else {"TARGET"};
    let target_u = target.replace("-", "_");
    let res = getenv(&format!("{}_{}", var_base, target))
        .or_else(|| getenv(&format!("{}_{}", var_base, target_u)))
        .or_else(|| getenv(&format!("{}_{}", kind, var_base)))
        .or_else(|| getenv(var_base));

    match res {
        Some(res) => Ok(res),
        None => Err("Could not get environment variable".to_string()),
    }
}

fn gcc(target: &str) -> String {
    let is_android = target.find("android").is_some();

    get_var("CC").unwrap_or(if cfg!(windows) {
        if target.contains("msvc") {
            "cl".to_string()
        } else {
            "gcc".to_string()
        }
    } else if is_android {
        format!("{}-gcc", target)
    } else {
        "cc".to_string()
    })
}

fn gxx(target: &str) -> String {
    let is_android = target.find("android").is_some();

    get_var("CXX").unwrap_or(if cfg!(windows) {
        if target.contains("msvc") {
            "cl".to_string()
        } else {
            "g++".to_string()
        }
    } else if is_android {
        format!("{}-g++", target)
    } else {
        "c++".to_string()
    })
}

fn ar(target: &str) -> String {
    let is_android = target.find("android").is_some();

    get_var("AR").unwrap_or(if is_android {
        format!("{}-ar", target)
    } else {
        "ar".to_string()
    })
}

fn envflags(name: &str) -> Vec<String> {
    get_var(name).unwrap_or(String::new())
       .split(|c: char| c.is_whitespace()).filter(|s| !s.is_empty())
       .map(|s| s.to_string())
       .collect()
}

fn cflags() -> Vec<String> {
    envflags("CFLAGS")
}

fn cxxflags() -> Vec<String> {
    envflags("CXXFLAGS")
}

fn ios_flags(target: &str) -> Vec<String> {
    enum ArchSpec {
        Device(&'static str),
        Simulator(&'static str),
    }

    let mut res = Vec::new();

    let arch = target.split('-').nth(0).expect("expected target in format `arch-vendor-os`");
    let arch = match arch {
        "arm" | "armv7" | "thumbv7" => ArchSpec::Device("armv7"),
        "armv7s" | "thumbv7s" => ArchSpec::Device("armv7s"),
        "arm64" | "aarch64" => ArchSpec::Device("arm64"),
        "i386" | "i686" => ArchSpec::Simulator("-m32"),
        "x86_64" => ArchSpec::Simulator("-m64"),
        _ => unreachable!("Unknown arch for iOS target")
    };

    let sdk = match arch {
        ArchSpec::Device(arch) => {
            res.push("-arch".to_string());
            res.push(arch.to_string());
            "iphoneos"
        },
        ArchSpec::Simulator(arch) => {
            res.push(arch.to_string());
            "iphonesimulator"
        }
    };

    println!("Detecting iOS SDK path for {}", sdk);
    let sdk_path = Command::new("xcrun")
        .arg("--show-sdk-path")
        .arg("--sdk")
        .arg(sdk)
        .stderr(Stdio::inherit())
        .output()
        .unwrap()
        .stdout;

    let sdk_path = String::from_utf8(sdk_path).unwrap();

    res.push("-isysroot".to_string());
    res.push(sdk_path.trim().to_string());

    res
}

fn fail(s: &str) -> ! {
    println!("\n\n{}\n\n", s);
    panic!()
}
<commit_msg>Create foo.lib files for MSVC<commit_after>//! A library for build scripts to compile custom C code
//!
//! This library is intended to be used as a `build-dependencies` entry in
//! `Cargo.toml`:
//!
//! ```toml
//! [build-dependencies]
//! gcc = "0.2"
//! ```
//!
//! The purpose of this crate is to provide the utility functions necessary to
//! compile C code into a static archive which is then linked into a Rust crate.
//! The top-level `compile_library` function serves as a convenience and more
//! advanced configuration is available through the `Config` builder.
//!
//! This crate will automatically detect situations such as cross compilation or
//! other environment variables set by Cargo and will build code appropriately.
//!
//! # Examples
//!
//! Use the default configuration:
//!
//! ```no_run
//! extern crate gcc;
//!
//! fn main() {
//!     gcc::compile_library("libfoo.a", &["src/foo.c"]);
//! }
//! ```
//!
//! Use more advanced configuration:
//!
//! ```no_run
//! extern crate gcc;
//!
//! fn main() {
//!     gcc::Config::new()
//!                 .file("src/foo.c")
//!                 .define("FOO", Some("bar"))
//!                 .include("src")
//!                 .compile("libfoo.a");
//! }
//! ```

#![doc(html_root_url = "http://alexcrichton.com/gcc-rs")]
#![cfg_attr(test, deny(warnings))]

use std::env;
use std::ffi::OsString;
use std::fs;
use std::io;
use std::path::{PathBuf, Path};
use std::process::{Command, Stdio};

/// Extra configuration to pass to gcc.
pub struct Config {
    include_directories: Vec<PathBuf>,
    definitions: Vec<(String, Option<String>)>,
    objects: Vec<PathBuf>,
    flags: Vec<String>,
    files: Vec<PathBuf>,
    cpp: bool,
    cpp_stdlib: Option<String>,
}

/// Returns the default C++ standard library for the current target: `libc++`
/// for OS X and `libstdc++` for anything else.
fn target_default_cpp_stdlib() -> Option<&'static str> {
    let target = getenv_unwrap("TARGET");
    if target.contains("msvc") {
        None
    } else if target.contains("darwin") {
        Some("c++")
    } else {
        Some("stdc++")
    }
}

fn getenv(v: &str) -> Option<String> {
    let r = env::var(v).ok();
    println!("{} = {:?}", v, r);
    r
}

fn getenv_unwrap(v: &str) -> String {
    match getenv(v) {
        Some(s) => s,
        None => fail(&format!("environment variable `{}` not defined", v)),
    }
}

/// Compile a library from the given set of input C files.
///
/// This will simply compile all files into object files and then assemble them
/// into the output. This will read the standard environment variables to detect
/// cross compilations and such.
///
/// This function will also print all metadata on standard output for Cargo.
///
/// # Example
///
/// ```no_run
/// gcc::compile_library("libfoo.a", &["foo.c", "bar.c"]);
/// ```
pub fn compile_library(output: &str, files: &[&str]) {
    let mut c = Config::new();
    for f in files.iter() {
        c.file(*f);
    }
    c.compile(output)
}

impl Config {
    /// Construct a new instance of a blank set of configuration.
    ///
    /// This builder is finished with the `compile` function.
    pub fn new() -> Config {
        Config {
            include_directories: Vec::new(),
            definitions: Vec::new(),
            objects: Vec::new(),
            flags: Vec::new(),
            files: Vec::new(),
            cpp: false,
            cpp_stdlib: target_default_cpp_stdlib().map(|s| s.into()),
        }
    }

    /// Add a directory to the `-I` or include path for headers
    pub fn include<P: AsRef<Path>>(&mut self, dir: P) -> &mut Config {
        self.include_directories.push(dir.as_ref().to_path_buf());
        self
    }

    /// Specify a `-D` variable with an optional value.
    pub fn define(&mut self, var: &str, val: Option<&str>) -> &mut Config {
        self.definitions.push((var.to_string(), val.map(|s| s.to_string())));
        self
    }

    /// Add an arbitrary object file to link in
    pub fn object<P: AsRef<Path>>(&mut self, obj: P) -> &mut Config {
        self.objects.push(obj.as_ref().to_path_buf());
        self
    }

    /// Add an arbitrary flag to the invocation of the compiler
    pub fn flag(&mut self, flag: &str) -> &mut Config {
        self.flags.push(flag.to_string());
        self
    }

    /// Add a file which will be compiled
    pub fn file<P: AsRef<Path>>(&mut self, p: P) -> &mut Config {
        self.files.push(p.as_ref().to_path_buf());
        self
    }

    /// Set C++ support
    pub fn cpp(&mut self, cpp: bool) -> &mut Config {
        self.cpp = cpp;
        self
    }

    /// Set the standard library to use when compiling with C++ support.
    ///
    /// The default value of this property depends on the current target: On
    /// OS X `Some("c++")` is used and for other supported targets
    /// `Some("stdc++")` is used.
    ///
    /// The choosen library is specified when compiling using the `-stdlib` flag
    /// and Cargo is instructed to link to the given library.
    ///
    /// A value of `None` indicates that no special actions regarding the C++
    /// standard library should be take. This means that neither are linking
    /// instructions provided to Cargo nor is the compiler told to use a
    /// specific standard library.
    ///
    /// The given library name must not contain the `lib` prefix.
    pub fn cpp_stdlib(&mut self, cpp_stdlib: Option<&str>) -> &mut Config {
        self.cpp_stdlib = cpp_stdlib.map(|s| s.into());
        self
    }

    /// Run the compiler, generating the file `output`
    ///
    /// The name `output` must begin with `lib` and end with `.a`
    pub fn compile(&self, output: &str) {
        assert!(output.starts_with("lib"));
        assert!(output.ends_with(".a"));
        let lib_name = &output[3..output.len() - 2];

        let target = getenv_unwrap("TARGET");
        let src = PathBuf::from(getenv_unwrap("CARGO_MANIFEST_DIR"));
        let dst = PathBuf::from(getenv_unwrap("OUT_DIR"));
        let mut objects = Vec::new();
        for file in self.files.iter() {
            let mut cmd = self.compile_cmd(&target);
            cmd.arg(src.join(file));

            let obj = dst.join(file).with_extension("o");
            fs::create_dir_all(&obj.parent().unwrap()).unwrap();
            if target.contains("msvc") {
                let mut s = OsString::from("/Fo:");
                s.push(&obj);
                cmd.arg(s);
            } else {
                cmd.arg("-o").arg(&obj);
            }
            run(&mut cmd, &self.compiler(&target));
            objects.push(obj);
        }

        if target.contains("msvc") {
            let mut out = OsString::from("/OUT:");
            out.push(dst.join(output));
            run(Command::new("lib").arg(out).args(&objects).args(&self.objects),
                "lib");

            // The Rust compiler will look for libfoo.a and foo.lib, but the
            // MSVC linker will also be passed foo.lib, so be sure that both
            // exist for now.
            let lib_dst = dst.join(format!("{}.lib", lib_name));
            let _ = fs::remove_file(&lib_dst);
            fs::hard_link(dst.join(output), lib_dst).unwrap();
        } else {
            run(Command::new(&ar(&target)).arg("crus")
                                          .arg(&dst.join(output))
                                          .args(&objects)
                                          .args(&self.objects),
                &ar(&target));
        }
        println!("cargo:rustc-link-search=native={}", dst.display());
        println!("cargo:rustc-link-lib=static={}",
                 &output[3..output.len() - 2]);

        // Add specific C++ libraries, if enabled.
        if self.cpp {
            if let Some(ref stdlib) = self.cpp_stdlib {
                println!("cargo:rustc-link-lib={}", stdlib);
            }
        }
    }

    fn compiler(&self, target: &str) -> String {
        if self.cpp {
            gxx(target)
        } else {
            gcc(target)
        }
    }

    fn compile_flags(&self) -> Vec<String> {
        if self.cpp {
            cxxflags()
        } else {
            cflags()
        }
    }

    fn compile_cmd(&self, target: &str) -> Command {
        let opt_level = getenv_unwrap("OPT_LEVEL");
        let profile = getenv_unwrap("PROFILE");
        println!("{} {}", profile, opt_level);

        let mut cmd = Command::new(self.compiler(&target));

        if target.contains("msvc") {
            cmd.arg("/c");
            cmd.arg(format!("/O{}", opt_level));
        } else {
            cmd.arg(format!("-O{}", opt_level));
            cmd.arg("-c");
            cmd.arg("-ffunction-sections").arg("-fdata-sections");
        }
        cmd.args(&self.compile_flags());

        if target.contains("-ios") {
            cmd.args(&ios_flags(&target));
        } else if !target.contains("msvc") {
            if target.contains("windows") {
                cmd.arg("-mwin32");
            }

            if target.contains("i686") {
                cmd.arg("-m32");
            } else if target.contains("x86_64") {
                cmd.arg("-m64");
            }

            if !target.contains("i686") {
                cmd.arg("-fPIC");
            }
        }

        if self.cpp {
            if let Some(ref stdlib) = self.cpp_stdlib {
                cmd.arg(&format!("-stdlib=lib{}", stdlib));
            }
        }

        for directory in self.include_directories.iter() {
            cmd.arg(if target.contains("msvc") {"/I"} else {"-I"});
            cmd.arg(directory);
        }

        for flag in self.flags.iter() {
            cmd.arg(flag);
        }

        for &(ref key, ref value) in self.definitions.iter() {
            let lead = if target.contains("msvc") {"/"} else {"-"};
            if let &Some(ref value) = value {
                cmd.arg(&format!("{}D{}={}", lead, key, value));
            } else {
                cmd.arg(&format!("{}D{}", lead, key));
            }
        }
        return cmd;
    }
}

fn run(cmd: &mut Command, program: &str) {
    println!("running: {:?}", cmd);
    let status = match cmd.status() {
        Ok(status) => status,
        Err(ref e) if e.kind() == io::ErrorKind::NotFound => {
            let extra = if cfg!(windows) {
                " (see https://github.com/alexcrichton/gcc-rs#windows-notes \
                   for help)"
            } else {
                ""
            };
            fail(&format!("failed to execute command: {}\nIs `{}` \
                           not installed?{}", e, program, extra));
        }
        Err(e) => fail(&format!("failed to execute command: {}", e)),
    };
    if !status.success() {
        fail(&format!("command did not execute successfully, got: {}", status));
    }
}

fn get_var(var_base: &str) -> Result<String, String> {
    let target = getenv_unwrap("TARGET");
    let host = getenv_unwrap("HOST");
    let kind = if host == target {"HOST"} else {"TARGET"};
    let target_u = target.replace("-", "_");
    let res = getenv(&format!("{}_{}", var_base, target))
        .or_else(|| getenv(&format!("{}_{}", var_base, target_u)))
        .or_else(|| getenv(&format!("{}_{}", kind, var_base)))
        .or_else(|| getenv(var_base));

    match res {
        Some(res) => Ok(res),
        None => Err("Could not get environment variable".to_string()),
    }
}

fn gcc(target: &str) -> String {
    let is_android = target.find("android").is_some();

    get_var("CC").unwrap_or(if cfg!(windows) {
        if target.contains("msvc") {
            "cl".to_string()
        } else {
            "gcc".to_string()
        }
    } else if is_android {
        format!("{}-gcc", target)
    } else {
        "cc".to_string()
    })
}

fn gxx(target: &str) -> String {
    let is_android = target.find("android").is_some();

    get_var("CXX").unwrap_or(if cfg!(windows) {
        if target.contains("msvc") {
            "cl".to_string()
        } else {
            "g++".to_string()
        }
    } else if is_android {
        format!("{}-g++", target)
    } else {
        "c++".to_string()
    })
}

fn ar(target: &str) -> String {
    let is_android = target.find("android").is_some();

    get_var("AR").unwrap_or(if is_android {
        format!("{}-ar", target)
    } else {
        "ar".to_string()
    })
}

fn envflags(name: &str) -> Vec<String> {
    get_var(name).unwrap_or(String::new())
       .split(|c: char| c.is_whitespace()).filter(|s| !s.is_empty())
       .map(|s| s.to_string())
       .collect()
}

fn cflags() -> Vec<String> {
    envflags("CFLAGS")
}

fn cxxflags() -> Vec<String> {
    envflags("CXXFLAGS")
}

fn ios_flags(target: &str) -> Vec<String> {
    enum ArchSpec {
        Device(&'static str),
        Simulator(&'static str),
    }

    let mut res = Vec::new();

    let arch = target.split('-').nth(0).expect("expected target in format `arch-vendor-os`");
    let arch = match arch {
        "arm" | "armv7" | "thumbv7" => ArchSpec::Device("armv7"),
        "armv7s" | "thumbv7s" => ArchSpec::Device("armv7s"),
        "arm64" | "aarch64" => ArchSpec::Device("arm64"),
        "i386" | "i686" => ArchSpec::Simulator("-m32"),
        "x86_64" => ArchSpec::Simulator("-m64"),
        _ => unreachable!("Unknown arch for iOS target")
    };

    let sdk = match arch {
        ArchSpec::Device(arch) => {
            res.push("-arch".to_string());
            res.push(arch.to_string());
            "iphoneos"
        },
        ArchSpec::Simulator(arch) => {
            res.push(arch.to_string());
            "iphonesimulator"
        }
    };

    println!("Detecting iOS SDK path for {}", sdk);
    let sdk_path = Command::new("xcrun")
        .arg("--show-sdk-path")
        .arg("--sdk")
        .arg(sdk)
        .stderr(Stdio::inherit())
        .output()
        .unwrap()
        .stdout;

    let sdk_path = String::from_utf8(sdk_path).unwrap();

    res.push("-isysroot".to_string());
    res.push(sdk_path.trim().to_string());

    res
}

fn fail(s: &str) -> ! {
    println!("\n\n{}\n\n", s);
    panic!()
}
<|endoftext|>


<commit_before>pub mod ffi;
use std::ffi::{CStr, CString};
use std::fmt::Write;

#[repr(C)]
#[derive(Debug)]
pub struct Rect {
    x: f32,
    y: f32,
    w: f32,
    h: f32,
}

#[derive(Debug)]
pub struct Detection {
    rect: Rect,
    label: String,
    prob: f32
}

#[repr(C)]
#[derive(Debug)]
pub struct Detections {
    pub num: usize,
    pub detections: Vec<Detection>,
    pub proc_time_in_ms: f32,
}

impl Detection {
    fn csv(&self) -> String {
        format!("{}, {}, {}, {}, {}, {}",
                self.label, self.prob, self.rect.x, self.rect.y, self.rect.w, self.rect.h)
    }
}

impl Detections {
    pub fn csv(&self) -> String {
        let mut res = String::new();
        for i in 0..self.num {
            write!(&mut res, "{}, {}\n", self.proc_time_in_ms, self.detections[i].csv()).unwrap();
        }
        res
    }
}

#[repr(C)]
pub struct InputImage {
    inner: ffi::image,
}

impl InputImage {
    pub fn new(w: i32, h: i32, c: i32) -> InputImage {
        InputImage {
            inner: unsafe {
                make_image(w, h, c)
            }
        }
    }

    pub fn data_mut(&mut self) -> *mut f32 {
        self.inner.data
    }
}

pub struct Darknet {
    inner: *mut ffi::Darknet,
}

impl Darknet {
    pub fn new(data: &str, network: &str, weight: &str, label: &str) -> Self {
        let datacfg_cstring = CString::new(data).expect("cstring failed");
        let network_cstring = CString::new(network).expect("cstring failed");
        let weight_cstring = CString::new(weight).expect("cstring failed");
        let label_cstring = CString::new(label).expect("cstring failed");

        let config = ffi::DarknetConfig {
            datacfg: datacfg_cstring.as_ptr(),
            network_file: network_cstring.as_ptr(),
            weight_file: weight_cstring.as_ptr(),
            label_file: label_cstring.as_ptr(),
        };

        Darknet {
            inner: unsafe {
                darknet_new(config)
            },
        }
    }

    pub fn detect(&mut self, image: InputImage) -> Detections {
        let c_detections = unsafe { darknet_detect(self.inner, image.inner) };
        let num = c_detections.num;
        let mut detections = Vec::with_capacity(num as usize);
        for i in 0..(num as isize) {
            let label = unsafe {
                CStr::from_ptr(*c_detections.labels.offset(i)).to_string_lossy().into_owned()
            };
            let d = unsafe {
                Detection {
                    label: label,
                    prob:  *(c_detections.probs.offset(i)),
                    rect: Rect {
                        x: (*c_detections.rects.offset(i)).x,
                        y: (*c_detections.rects.offset(i)).y,
                        w: (*c_detections.rects.offset(i)).w,
                        h: (*c_detections.rects.offset(i)).h,
                    },
                }
            };
            detections.push(d);
        }
        unsafe { detections_drop(c_detections) }
        Detections {
            num: num as usize,
            detections: detections,
            proc_time_in_ms: c_detections.proc_time_in_ms,
        }
    }
}

impl Drop for Darknet {
    fn drop(&mut self) {
        unsafe {
            darknet_drop(self.inner);
        }
    }
}

extern "C" {
    fn darknet_new(config: ffi::DarknetConfig) -> *mut ffi::Darknet;
    fn darknet_drop(dn: *mut ffi::Darknet);
    fn darknet_detect(dn: *mut ffi::Darknet, image: ffi::image) -> ffi::Detections;
    fn detections_drop(dt: ffi::Detections);
    fn make_image(w: i32, h: i32, c: i32) -> ffi::image;
}
<commit_msg>fix: make csv pub<commit_after>pub mod ffi;
use std::ffi::{CStr, CString};
use std::fmt::Write;

#[repr(C)]
#[derive(Debug)]
pub struct Rect {
    x: f32,
    y: f32,
    w: f32,
    h: f32,
}

#[derive(Debug)]
pub struct Detection {
    rect: Rect,
    label: String,
    prob: f32
}

#[repr(C)]
#[derive(Debug)]
pub struct Detections {
    pub num: usize,
    pub detections: Vec<Detection>,
    pub proc_time_in_ms: f32,
}

impl Detection {
    pub fn csv(&self) -> String {
        format!("{}, {}, {}, {}, {}, {}",
                self.label, self.prob, self.rect.x, self.rect.y, self.rect.w, self.rect.h)
    }
}

impl Detections {
    pub fn csv(&self) -> String {
        let mut res = String::new();
        for i in 0..self.num {
            write!(&mut res, "{}, {}\n", self.proc_time_in_ms, self.detections[i].csv()).unwrap();
        }
        res
    }
}

#[repr(C)]
pub struct InputImage {
    inner: ffi::image,
}

impl InputImage {
    pub fn new(w: i32, h: i32, c: i32) -> InputImage {
        InputImage {
            inner: unsafe {
                make_image(w, h, c)
            }
        }
    }

    pub fn data_mut(&mut self) -> *mut f32 {
        self.inner.data
    }
}

pub struct Darknet {
    inner: *mut ffi::Darknet,
}

impl Darknet {
    pub fn new(data: &str, network: &str, weight: &str, label: &str) -> Self {
        let datacfg_cstring = CString::new(data).expect("cstring failed");
        let network_cstring = CString::new(network).expect("cstring failed");
        let weight_cstring = CString::new(weight).expect("cstring failed");
        let label_cstring = CString::new(label).expect("cstring failed");

        let config = ffi::DarknetConfig {
            datacfg: datacfg_cstring.as_ptr(),
            network_file: network_cstring.as_ptr(),
            weight_file: weight_cstring.as_ptr(),
            label_file: label_cstring.as_ptr(),
        };

        Darknet {
            inner: unsafe {
                darknet_new(config)
            },
        }
    }

    pub fn detect(&mut self, image: InputImage) -> Detections {
        let c_detections = unsafe { darknet_detect(self.inner, image.inner) };
        let num = c_detections.num;
        let mut detections = Vec::with_capacity(num as usize);
        for i in 0..(num as isize) {
            let label = unsafe {
                CStr::from_ptr(*c_detections.labels.offset(i)).to_string_lossy().into_owned()
            };
            let d = unsafe {
                Detection {
                    label: label,
                    prob:  *(c_detections.probs.offset(i)),
                    rect: Rect {
                        x: (*c_detections.rects.offset(i)).x,
                        y: (*c_detections.rects.offset(i)).y,
                        w: (*c_detections.rects.offset(i)).w,
                        h: (*c_detections.rects.offset(i)).h,
                    },
                }
            };
            detections.push(d);
        }
        unsafe { detections_drop(c_detections) }
        Detections {
            num: num as usize,
            detections: detections,
            proc_time_in_ms: c_detections.proc_time_in_ms,
        }
    }
}

impl Drop for Darknet {
    fn drop(&mut self) {
        unsafe {
            darknet_drop(self.inner);
        }
    }
}

extern "C" {
    fn darknet_new(config: ffi::DarknetConfig) -> *mut ffi::Darknet;
    fn darknet_drop(dn: *mut ffi::Darknet);
    fn darknet_detect(dn: *mut ffi::Darknet, image: ffi::image) -> ffi::Detections;
    fn detections_drop(dt: ffi::Detections);
    fn make_image(w: i32, h: i32, c: i32) -> ffi::image;
}
<|endoftext|>


<commit_before>//! Setup and control loop devices.
//!
//! Provides rust interface with similar functionality to the Linux utility `losetup`.
//!
//! # Examples
//!
//! ```rust
//! use loopdev::LoopControl;
//! let lc = LoopControl::open().unwrap();
//! let ld = lc.next_free().unwrap();
//!
//! println!("{}", ld.path().unwrap().display());
//!
//! ld.attach_file("test.img").unwrap();
//! // ...
//! ld.detach().unwrap();
//! ```

extern crate libc;

use std::fs::File;
use std::fs::OpenOptions;

use libc::{c_int, ioctl};
use std::default::Default;
use std::io;
use std::os::unix::prelude::*;
use std::path::{Path, PathBuf};

// TODO support missing operations
const LOOP_SET_FD: u16 = 0x4C00;
const LOOP_CLR_FD: u16 = 0x4C01;
const LOOP_SET_STATUS64: u16 = 0x4C04;
//const LOOP_GET_STATUS64: u16 = 0x4C05;
const LOOP_SET_CAPACITY: u16 = 0x4C07;
//const LOOP_SET_DIRECT_IO: u16 = 0x4C08;
//const LOOP_SET_BLOCK_SIZE: u16 = 0x4C09;

//const LOOP_CTL_ADD: u16 = 0x4C80;
//const LOOP_CTL_REMOVE: u16 = 0x4C81;
const LOOP_CTL_GET_FREE: u16 = 0x4C82;

const LOOP_CONTROL: &str = "/dev/loop-control";
const LOOP_PREFIX: &str = "/dev/loop";

/// Interface to the loop control device: `/dev/loop-control`.
#[derive(Debug)]
pub struct LoopControl {
    dev_file: File,
}

impl LoopControl {
    /// Opens the loop control device.
    pub fn open() -> io::Result<Self> {
        Ok(Self {
            dev_file: OpenOptions::new()
                .read(true)
                .write(true)
                .open(LOOP_CONTROL)?,
        })
    }

    /// Finds and opens the next available loop device.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use loopdev::LoopControl;
    /// let lc = LoopControl::open().unwrap();
    /// let ld = lc.next_free().unwrap();
    /// println!("{}", ld.path().unwrap().display());
    /// ```
    pub fn next_free(&self) -> io::Result<LoopDevice> {
        let result;
        unsafe {
            result = ioctl(self.dev_file.as_raw_fd() as c_int, LOOP_CTL_GET_FREE.into());
        }
        if result < 0 {
            Err(io::Error::last_os_error())
        } else {
            LoopDevice::open(&format!("{}{}", LOOP_PREFIX, result))
        }
    }
}

/// Interface to a loop device ie `/dev/loop0`.
#[derive(Debug)]
pub struct LoopDevice {
    device: File,
}

impl AsRawFd for LoopDevice {
    fn as_raw_fd(&self) -> RawFd {
        self.device.as_raw_fd()
    }
}

impl LoopDevice {
    /// Opens a loop device.
    pub fn open<P: AsRef<Path>>(dev: P) -> io::Result<Self> {
        // TODO create dev if it does not exist and begins with LOOP_PREFIX
        let f = OpenOptions::new().read(true).write(true).open(dev)?;
        Ok(Self { device: f })
    }

    /// Attach the loop device to a file starting at offset into the file.
    #[deprecated(
        since = "0.2.0",
        note = "use `attach_file` or `attach_with_offset` instead"
    )]
    pub fn attach<P: AsRef<Path>>(&self, backing_file: P, offset: u64) -> io::Result<()> {
        self.attach_with_sizelimit(backing_file, offset, 0)
    }

    /// Attach the loop device to a file that maps to the whole file.
    ///
    /// # Examples
    ///
    /// Attach the device to a file.
    ///
    /// ```rust
    /// use loopdev::LoopDevice;
    /// let ld = LoopDevice::open("/dev/loop4").unwrap();
    /// ld.attach_file("test.img").unwrap();
    /// # ld.detach().unwrap();
    /// ```
    pub fn attach_file<P: AsRef<Path>>(&self, backing_file: P) -> io::Result<()> {
        self.attach_with_sizelimit(backing_file, 0, 0)
    }

    /// Attach the loop device to a file starting at offset into the file.
    ///
    /// # Examples
    ///
    /// Attach the device to the start of a file.
    ///
    /// ```rust
    /// use loopdev::LoopDevice;
    /// let ld = LoopDevice::open("/dev/loop5").unwrap();
    /// ld.attach_with_offset("test.img", 0).unwrap();
    /// # ld.detach().unwrap();
    /// ```
    pub fn attach_with_offset<P: AsRef<Path>>(
        &self,
        backing_file: P,
        offset: u64,
    ) -> io::Result<()> {
        self.attach_with_sizelimit(backing_file, offset, 0)
    }

    /// Attach the loop device to a file starting at offset into the file and a the given sizelimit.
    ///
    /// # Examples
    ///
    /// Attach the device to the start of a file with a maximum size of 1024 bytes.
    ///
    /// ```rust
    /// use loopdev::LoopDevice;
    /// let ld = LoopDevice::open("/dev/loop6").unwrap();
    /// ld.attach_with_sizelimit("test.img", 0, 1024).unwrap();
    /// # ld.detach().unwrap();
    /// ```
    pub fn attach_with_sizelimit<P: AsRef<Path>>(
        &self,
        backing_file: P,
        offset: u64,
        sizelimit: u64,
    ) -> io::Result<()> {
        let bf = OpenOptions::new()
            .read(true)
            .write(true)
            .open(backing_file)?;

        // Attach the file
        unsafe {
            if ioctl(
                self.device.as_raw_fd() as c_int,
                LOOP_SET_FD.into(),
                bf.as_raw_fd() as c_int,
            ) < 0
            {
                return Err(io::Error::last_os_error());
            }
        }

        // Set offset for backing_file
        let mut info = loop_info64::default();
        info.lo_offset = offset;
        info.lo_sizelimit = sizelimit;
        unsafe {
            if ioctl(
                self.device.as_raw_fd() as c_int,
                LOOP_SET_STATUS64.into(),
                &mut info,
            ) < 0
            {
                self.detach()?;
                return Err(io::Error::last_os_error());
            }
        }
        Ok(())
    }

    /// Get the path of the loop device.
    #[deprecated(since = "0.2.0", note = "use `path` instead")]
    pub fn get_path(&self) -> Option<PathBuf> {
        self.path()
    }

    /// Get the path of the loop device.
    pub fn path(&self) -> Option<PathBuf> {
        let mut p = PathBuf::from("/proc/self/fd");
        p.push(self.device.as_raw_fd().to_string());
        std::fs::read_link(&p).ok()
    }

    /// Detach a loop device from its backing file.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use loopdev::LoopDevice;
    /// let ld = LoopDevice::open("/dev/loop7").unwrap();
    /// # ld.attach_file("test.img").unwrap();
    /// ld.detach().unwrap();
    /// ```
    pub fn detach(&self) -> io::Result<()> {
        unsafe {
            if ioctl(self.device.as_raw_fd() as c_int, LOOP_CLR_FD.into(), 0) < 0 {
                Err(io::Error::last_os_error())
            } else {
                Ok(())
            }
        }
    }

    /// Resize a live loop device. If the size of the backing file changes this can be called to
    /// inform the loop driver about the new size.
    pub fn set_capacity(&self) -> io::Result<()> {
        println!("running set_capacity");
        unsafe {
            if ioctl(
                self.device.as_raw_fd() as c_int,
                LOOP_SET_CAPACITY.into(),
                0,
            ) < 0
            {
                Err(io::Error::last_os_error())
            } else {
                println!("ok");
                Ok(())
            }
        }
    }
}

#[repr(C)]
struct loop_info64 {
    pub lo_device: u64,
    pub lo_inode: u64,
    pub lo_rdevice: u64,
    pub lo_offset: u64,
    pub lo_sizelimit: u64,
    pub lo_number: u32,
    pub lo_encrypt_type: u32,
    pub lo_encrypt_key_size: u32,
    pub lo_flags: u32,
    pub lo_file_name: [u8; 64],
    pub lo_crypt_name: [u8; 64],
    pub lo_encrypt_key: [u8; 32],
    pub lo_init: [u64; 2],
}

impl Default for loop_info64 {
    fn default() -> Self {
        Self {
            lo_device: 0,
            lo_inode: 0,
            lo_rdevice: 0,
            lo_offset: 0,
            lo_sizelimit: 0,
            lo_number: 0,
            lo_encrypt_type: 0,
            lo_encrypt_key_size: 0,
            lo_flags: 0,
            lo_file_name: [0; 64],
            lo_crypt_name: [0; 64],
            lo_encrypt_key: [0; 32],
            lo_init: [0; 2],
        }
    }
}
<commit_msg>Narrorws the scope of the unsafe blocks<commit_after>//! Setup and control loop devices.
//!
//! Provides rust interface with similar functionality to the Linux utility `losetup`.
//!
//! # Examples
//!
//! ```rust
//! use loopdev::LoopControl;
//! let lc = LoopControl::open().unwrap();
//! let ld = lc.next_free().unwrap();
//!
//! println!("{}", ld.path().unwrap().display());
//!
//! ld.attach_file("test.img").unwrap();
//! // ...
//! ld.detach().unwrap();
//! ```

extern crate libc;

use std::fs::File;
use std::fs::OpenOptions;

use libc::{c_int, ioctl};
use std::default::Default;
use std::io;
use std::os::unix::prelude::*;
use std::path::{Path, PathBuf};

// TODO support missing operations
const LOOP_SET_FD: u16 = 0x4C00;
const LOOP_CLR_FD: u16 = 0x4C01;
const LOOP_SET_STATUS64: u16 = 0x4C04;
//const LOOP_GET_STATUS64: u16 = 0x4C05;
const LOOP_SET_CAPACITY: u16 = 0x4C07;
//const LOOP_SET_DIRECT_IO: u16 = 0x4C08;
//const LOOP_SET_BLOCK_SIZE: u16 = 0x4C09;

//const LOOP_CTL_ADD: u16 = 0x4C80;
//const LOOP_CTL_REMOVE: u16 = 0x4C81;
const LOOP_CTL_GET_FREE: u16 = 0x4C82;

const LOOP_CONTROL: &str = "/dev/loop-control";
const LOOP_PREFIX: &str = "/dev/loop";

/// Interface to the loop control device: `/dev/loop-control`.
#[derive(Debug)]
pub struct LoopControl {
    dev_file: File,
}

impl LoopControl {
    /// Opens the loop control device.
    pub fn open() -> io::Result<Self> {
        Ok(Self {
            dev_file: OpenOptions::new()
                .read(true)
                .write(true)
                .open(LOOP_CONTROL)?,
        })
    }

    /// Finds and opens the next available loop device.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use loopdev::LoopControl;
    /// let lc = LoopControl::open().unwrap();
    /// let ld = lc.next_free().unwrap();
    /// println!("{}", ld.path().unwrap().display());
    /// ```
    pub fn next_free(&self) -> io::Result<LoopDevice> {
        let result = unsafe { ioctl(self.dev_file.as_raw_fd() as c_int, LOOP_CTL_GET_FREE.into()) };
        if result < 0 {
            Err(io::Error::last_os_error())
        } else {
            LoopDevice::open(&format!("{}{}", LOOP_PREFIX, result))
        }
    }
}

/// Interface to a loop device ie `/dev/loop0`.
#[derive(Debug)]
pub struct LoopDevice {
    device: File,
}

impl AsRawFd for LoopDevice {
    fn as_raw_fd(&self) -> RawFd {
        self.device.as_raw_fd()
    }
}

impl LoopDevice {
    /// Opens a loop device.
    pub fn open<P: AsRef<Path>>(dev: P) -> io::Result<Self> {
        // TODO create dev if it does not exist and begins with LOOP_PREFIX
        Ok(Self {
            device: OpenOptions::new().read(true).write(true).open(dev)?,
        })
    }

    /// Attach the loop device to a file starting at offset into the file.
    #[deprecated(
        since = "0.2.0",
        note = "use `attach_file` or `attach_with_offset` instead"
    )]
    pub fn attach<P: AsRef<Path>>(&self, backing_file: P, offset: u64) -> io::Result<()> {
        self.attach_with_sizelimit(backing_file, offset, 0)
    }

    /// Attach the loop device to a file that maps to the whole file.
    ///
    /// # Examples
    ///
    /// Attach the device to a file.
    ///
    /// ```rust
    /// use loopdev::LoopDevice;
    /// let ld = LoopDevice::open("/dev/loop4").unwrap();
    /// ld.attach_file("test.img").unwrap();
    /// # ld.detach().unwrap();
    /// ```
    pub fn attach_file<P: AsRef<Path>>(&self, backing_file: P) -> io::Result<()> {
        self.attach_with_sizelimit(backing_file, 0, 0)
    }

    /// Attach the loop device to a file starting at offset into the file.
    ///
    /// # Examples
    ///
    /// Attach the device to the start of a file.
    ///
    /// ```rust
    /// use loopdev::LoopDevice;
    /// let ld = LoopDevice::open("/dev/loop5").unwrap();
    /// ld.attach_with_offset("test.img", 0).unwrap();
    /// # ld.detach().unwrap();
    /// ```
    pub fn attach_with_offset<P: AsRef<Path>>(
        &self,
        backing_file: P,
        offset: u64,
    ) -> io::Result<()> {
        self.attach_with_sizelimit(backing_file, offset, 0)
    }

    /// Attach the loop device to a file starting at offset into the file and a the given sizelimit.
    ///
    /// # Examples
    ///
    /// Attach the device to the start of a file with a maximum size of 1024 bytes.
    ///
    /// ```rust
    /// use loopdev::LoopDevice;
    /// let ld = LoopDevice::open("/dev/loop6").unwrap();
    /// ld.attach_with_sizelimit("test.img", 0, 1024).unwrap();
    /// # ld.detach().unwrap();
    /// ```
    pub fn attach_with_sizelimit<P: AsRef<Path>>(
        &self,
        backing_file: P,
        offset: u64,
        sizelimit: u64,
    ) -> io::Result<()> {
        let bf = OpenOptions::new()
            .read(true)
            .write(true)
            .open(backing_file)?;

        // Attach the file

        if unsafe {
            ioctl(
                self.device.as_raw_fd() as c_int,
                LOOP_SET_FD.into(),
                bf.as_raw_fd() as c_int,
            )
        } < 0
        {
            return Err(io::Error::last_os_error());
        }

        // Set offset for backing_file
        let mut info = loop_info64::default();
        info.lo_offset = offset;
        info.lo_sizelimit = sizelimit;

        if unsafe {
            ioctl(
                self.device.as_raw_fd() as c_int,
                LOOP_SET_STATUS64.into(),
                &mut info,
            )
        } < 0
        {
            self.detach()?;
            return Err(io::Error::last_os_error());
        }
        Ok(())
    }

    /// Get the path of the loop device.
    #[deprecated(since = "0.2.0", note = "use `path` instead")]
    pub fn get_path(&self) -> Option<PathBuf> {
        self.path()
    }

    /// Get the path of the loop device.
    pub fn path(&self) -> Option<PathBuf> {
        let mut p = PathBuf::from("/proc/self/fd");
        p.push(self.device.as_raw_fd().to_string());
        std::fs::read_link(&p).ok()
    }

    /// Detach a loop device from its backing file.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use loopdev::LoopDevice;
    /// let ld = LoopDevice::open("/dev/loop7").unwrap();
    /// # ld.attach_file("test.img").unwrap();
    /// ld.detach().unwrap();
    /// ```
    pub fn detach(&self) -> io::Result<()> {
        if unsafe { ioctl(self.device.as_raw_fd() as c_int, LOOP_CLR_FD.into(), 0) } < 0 {
            Err(io::Error::last_os_error())
        } else {
            Ok(())
        }
    }

    /// Resize a live loop device. If the size of the backing file changes this can be called to
    /// inform the loop driver about the new size.
    pub fn set_capacity(&self) -> io::Result<()> {
        println!("running set_capacity");

        if unsafe {
            ioctl(
                self.device.as_raw_fd() as c_int,
                LOOP_SET_CAPACITY.into(),
                0,
            )
        } < 0
        {
            Err(io::Error::last_os_error())
        } else {
            println!("ok");
            Ok(())
        }
    }
}

#[repr(C)]
struct loop_info64 {
    pub lo_device: u64,
    pub lo_inode: u64,
    pub lo_rdevice: u64,
    pub lo_offset: u64,
    pub lo_sizelimit: u64,
    pub lo_number: u32,
    pub lo_encrypt_type: u32,
    pub lo_encrypt_key_size: u32,
    pub lo_flags: u32,
    pub lo_file_name: [u8; 64],
    pub lo_crypt_name: [u8; 64],
    pub lo_encrypt_key: [u8; 32],
    pub lo_init: [u64; 2],
}

impl Default for loop_info64 {
    fn default() -> Self {
        Self {
            lo_device: 0,
            lo_inode: 0,
            lo_rdevice: 0,
            lo_offset: 0,
            lo_sizelimit: 0,
            lo_number: 0,
            lo_encrypt_type: 0,
            lo_encrypt_key_size: 0,
            lo_flags: 0,
            lo_file_name: [0; 64],
            lo_crypt_name: [0; 64],
            lo_encrypt_key: [0; 32],
            lo_init: [0; 2],
        }
    }
}
<|endoftext|>


<commit_before>extern crate num;
extern crate rutorch;

use rutorch::*;
use std::ops::Index;
use num::Num;
use std::cmp::max;

pub trait ArgsArray<T> {
	fn is_leaf(&self) -> bool;
	fn child(&self) -> &T;
	fn dim(&self) -> usize;
}

impl <T> ArgsArray<T> for [T]  {
	fn is_leaf(&self) -> bool { false }
	fn child(&self) ->  &T { &self[0]}
	fn dim(&self) -> usize { self.len()}
}

impl <T:Num + Sized> ArgsArray<T> for T  {
	fn is_leaf(&self) -> bool { true }
	fn child(&self) ->  &T { &self}
	fn dim(&self) -> usize { 1 }
}

trait Storage {
	fn new() -> Self;
	fn sized(len: isize) -> Self;
}

struct FloatStorage {
	t: *mut THFloatStorage,
}

impl Storage for FloatStorage {
	fn new() -> Self {
		unsafe { FloatStorage  {t : THFloatStorage_new()} }
	}
	fn sized(size: isize) ->Self {
		unsafe { FloatStorage  {t : THFloatStorage_newWithSize(size)} }
	}
}

impl Index<isize> for FloatStorage {
	type Output = f32;

	fn index(&self, idx: isize) -> &f32 {
		unsafe {&*(*self.t).data.offset(idx)}
	}
}

//fn from(args: &ArgsArray<T>) -> Self;

trait Tensor<'a> : Index<&'a [isize]> {
	fn new() -> Self;
	fn sized(size: &'a [isize]) -> Self;
	fn randn(dims: &'a [isize]) -> Self;
}

struct FloatTensor {
	t: *mut THFloatTensor,
	storage: FloatStorage,
	dims: Vec<isize>,
}

impl <'a>Tensor<'a> for FloatTensor {
	fn new() -> Self {
		unsafe { FloatTensor { t : THFloatTensor_new(), storage: FloatStorage::new(), dims: Vec::new()  }}
	}
	fn sized(dims: &'a [isize]) -> Self {
		let size = dims.iter().product();
		let storage = FloatStorage::sized(size);
		let strides = vec![1; dims.len()];
		let mut t = THFloatTensor { size : dims.clone().as_ptr() as *mut ::std::os::raw::c_long,
			stride: strides.as_ptr() as *mut ::std::os::raw::c_long,
			nDimension : dims.len() as i32, storage: storage.t, storageOffset: 0, refcount: 1,
			flag: TH_TENSOR_REFCOUNTED as i8};
		FloatTensor {t : &mut t, storage: storage, dims: Vec::from(dims)}
	}
	fn randn(dims: &'a [isize]) -> Self {
		/* XXX */
		Tensor::sized(dims)
	}
}

impl <'a> Index<&'a [isize]> for FloatTensor {
	type Output = f32;

	fn index(&self, idx: &'a [isize]) -> &f32 {
		let mut index : isize = 0;
		let lastidx = max(0, idx.len() as isize - 1) as usize;
		if idx.len() != self.dims.len() { panic!("bad dimlen")}
		for i in 0..lastidx {
			if idx[i] >= self.dims[i] { panic!("bad dimlen")}
			index += idx[i] * self.dims[i]
		}
		if idx[lastidx] >= self.dims[lastidx] { panic!("bad dimlen")}
		index += idx[lastidx];
		&self.storage[index]
	}
}

#[cfg(test)]
mod tests {
	use super::*;
    #[test]
    fn args_array_works() {
    	let v = [[5, 2], [3, 4]];
    	let x = v.child();
    	let z = x.child();
    	println!("is_leaf = {} {} {:?}, {} {} {:?}, {} {} {:?}",
    		v.is_leaf(), v.dim(), v,
            x.is_leaf(), x.dim(), x,
            z.is_leaf(), z.dim(), z
            );
    }
    fn tensor_ops_work() {
		let v = [[5, 2], [3, 4]];

    }
}
<commit_msg>give up on ArgsArray<commit_after>extern crate num;
extern crate rutorch;

use rutorch::*;
use std::ops::Index;
//use std::convert::From;
//use num::Num;
use std::cmp::max;

trait Storage {
	fn new() -> Self;
	fn sized(len: isize) -> Self;
}

struct FloatStorage {
	t: *mut THFloatStorage,
}

impl Storage for FloatStorage {
	fn new() -> Self {
		unsafe { FloatStorage  {t : THFloatStorage_new()} }
	}
	fn sized(size: isize) ->Self {
		unsafe { FloatStorage  {t : THFloatStorage_newWithSize(size)} }
	}
}

impl Index<isize> for FloatStorage {
	type Output = f32;

	fn index(&self, idx: isize) -> &f32 {
		unsafe {&*(*self.t).data.offset(idx)}
	}
}

//fn from(args: &ArgsArray<T>) -> Self;

trait Tensor<'a> : Index<&'a [isize]> {
	fn new() -> Self;
	fn sized(size: &'a [isize]) -> Self;
	fn randn(dims: &'a [isize]) -> Self;
}

struct FloatTensor {
	t: *mut THFloatTensor,
	storage: FloatStorage,
	dims: Vec<isize>,
}

impl <'a>Tensor<'a> for FloatTensor {
	fn new() -> Self {
		unsafe { FloatTensor { t : THFloatTensor_new(), storage: FloatStorage::new(), dims: Vec::new()  }}
	}
	fn sized(dims: &'a [isize]) -> Self {
		let size = dims.iter().product();
		let storage = FloatStorage::sized(size);
		let strides = vec![1; dims.len()];
		let mut t = THFloatTensor { size : dims.clone().as_ptr() as *mut ::std::os::raw::c_long,
			stride: strides.as_ptr() as *mut ::std::os::raw::c_long,
			nDimension : dims.len() as i32, storage: storage.t, storageOffset: 0, refcount: 1,
			flag: TH_TENSOR_REFCOUNTED as i8};
		FloatTensor {t : &mut t, storage: storage, dims: Vec::from(dims)}
	}
	fn randn(dims: &'a [isize]) -> Self {
		/* XXX */
		Tensor::sized(dims)
	}
}

impl <'a> Index<&'a [isize]> for FloatTensor {
	type Output = f32;

	fn index(&self, idx: &'a [isize]) -> &f32 {
		let mut index : isize = 0;
		let lastidx = max(0, idx.len() as isize - 1) as usize;
		if idx.len() != self.dims.len() { panic!("bad dimlen")}
		for i in 0..lastidx {
			if idx[i] >= self.dims[i] { panic!("bad dimlen")}
			index += idx[i] * self.dims[i]
		}
		if idx[lastidx] >= self.dims[lastidx] { panic!("bad dimlen")}
		index += idx[lastidx];
		&self.storage[index]
	}
}

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

    }
}
<|endoftext|>


<commit_before>use std::io::{self, Write, Read, BufRead};

pub use Either::{Left, Right};

/// `Either` represents an alternative holding one value out of
/// either of the two possible values.
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub enum Either<L, R> {
    Left(L),
    Right(R),
}

macro_rules! either {
    ($value:expr, $inner:ident => $result:expr) => (
        match $value {
            Either::Left(ref $inner) => $result,
            Either::Right(ref $inner) => $result,
        }
    )
}

macro_rules! either_mut {
    ($value:expr, $inner:ident => $result:expr) => (
        match $value {
            Either::Left(ref mut $inner) => $result,
            Either::Right(ref mut $inner) => $result,
        }
    )
}

impl<L, R> Either<L, R> {
    pub fn is_left(&self) -> bool {
        match *self {
            Left(_) => true,
            Right(_) => false,
        }
    }

    pub fn is_right(&self) -> bool {
        !self.is_left()
    }

    pub fn left(self) -> Option<L> {
        match self {
            Left(l) => Some(l),
            Right(_) => None,
        }
    }

    pub fn right(self) -> Option<R> {
        match self {
            Left(_) => None,
            Right(r) => Some(r),
        }
    }

    pub fn as_ref(&self) -> Either<&L, &R> {
        match *self {
            Left(ref inner) => Left(inner),
            Right(ref inner) => Right(inner),
        }
    }

    pub fn as_mut(&mut self) -> Either<&mut L, &mut R> {
        match *self {
            Left(ref mut inner) => Left(inner),
            Right(ref mut inner) => Right(inner),
        }
    }
}

/// Convert from `Result` to `Either` with `Ok => Right` and `Err => Left`.
impl<L, R> From<Result<R, L>> for Either<L, R> {
    fn from(r: Result<R, L>) -> Self {
        match r {
            Err(e) => Left(e),
            Ok(o) => Right(o),
        }
    }
}

/// Convert from `Either` to `Result` with `Right => Ok` and `Left => Err`.
impl<L, R> Into<Result<R, L>> for Either<L, R> {
    fn into(self) -> Result<R, L> {
        match self {
            Left(l) => Err(l),
            Right(r) => Ok(r),
        }
    }
}

impl<L, R> Iterator for Either<L, R>
    where L: Iterator, R: Iterator<Item=L::Item>
{
    type Item = L::Item;

    fn next(&mut self) -> Option<L::Item> {
        either_mut!(*self, inner => inner.next())
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        either!(*self, inner => inner.size_hint())
    }
}

impl<L, R> DoubleEndedIterator for Either<L, R>
    where L: DoubleEndedIterator, R: DoubleEndedIterator<Item=L::Item>
{
    fn next_back(&mut self) -> Option<L::Item> {
        either_mut!(*self, inner => inner.next_back())
    }
}

impl<L, R> Read for Either<L, R>
    where L: Read, R: Read
{
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        either_mut!(*self, inner => inner.read(buf))
    }

    fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
        either_mut!(*self, inner => inner.read_to_end(buf))
    }
}

impl<L, R> BufRead for Either<L, R>
    where L: BufRead, R: BufRead
{
    fn fill_buf(&mut self) -> io::Result<&[u8]> {
        either_mut!(*self, inner => inner.fill_buf())
    }

    fn consume(&mut self, amt: usize) {
        either_mut!(*self, inner => inner.consume(amt))
    }
}

impl<L, R> Write for Either<L, R>
    where L: Write, R: Write
{
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        either_mut!(*self, inner => inner.write(buf))
    }

    fn flush(&mut self) -> io::Result<()> {
        either_mut!(*self, inner => inner.flush())
    }
}

#[test]
fn basic() {
    let mut e = Left(2);
    let r = Right(2);
    assert_eq!(e, Left(2));
    e = r;
    assert_eq!(e, Right(2));
    assert_eq!(e.left(), None);
    assert_eq!(e.right(), Some(2));
    assert_eq!(e.as_ref().right(), Some(&2));
    assert_eq!(e.as_mut().right(), Some(&mut 2));
}

#[test]
fn iter() {
    let x = 3;
    let mut iter = match x {
        1...3 => Left(0..10),
        _ => Right(17..),
    };

    assert_eq!(iter.next(), Some(0));
    assert_eq!(iter.count(), 9);
}
<commit_msg>Add simple Read / Write tests<commit_after>use std::io::{self, Write, Read, BufRead};

pub use Either::{Left, Right};

/// `Either` represents an alternative holding one value out of
/// either of the two possible values.
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub enum Either<L, R> {
    Left(L),
    Right(R),
}

macro_rules! either {
    ($value:expr, $inner:ident => $result:expr) => (
        match $value {
            Either::Left(ref $inner) => $result,
            Either::Right(ref $inner) => $result,
        }
    )
}

macro_rules! either_mut {
    ($value:expr, $inner:ident => $result:expr) => (
        match $value {
            Either::Left(ref mut $inner) => $result,
            Either::Right(ref mut $inner) => $result,
        }
    )
}

impl<L, R> Either<L, R> {
    pub fn is_left(&self) -> bool {
        match *self {
            Left(_) => true,
            Right(_) => false,
        }
    }

    pub fn is_right(&self) -> bool {
        !self.is_left()
    }

    pub fn left(self) -> Option<L> {
        match self {
            Left(l) => Some(l),
            Right(_) => None,
        }
    }

    pub fn right(self) -> Option<R> {
        match self {
            Left(_) => None,
            Right(r) => Some(r),
        }
    }

    pub fn as_ref(&self) -> Either<&L, &R> {
        match *self {
            Left(ref inner) => Left(inner),
            Right(ref inner) => Right(inner),
        }
    }

    pub fn as_mut(&mut self) -> Either<&mut L, &mut R> {
        match *self {
            Left(ref mut inner) => Left(inner),
            Right(ref mut inner) => Right(inner),
        }
    }
}

/// Convert from `Result` to `Either` with `Ok => Right` and `Err => Left`.
impl<L, R> From<Result<R, L>> for Either<L, R> {
    fn from(r: Result<R, L>) -> Self {
        match r {
            Err(e) => Left(e),
            Ok(o) => Right(o),
        }
    }
}

/// Convert from `Either` to `Result` with `Right => Ok` and `Left => Err`.
impl<L, R> Into<Result<R, L>> for Either<L, R> {
    fn into(self) -> Result<R, L> {
        match self {
            Left(l) => Err(l),
            Right(r) => Ok(r),
        }
    }
}

impl<L, R> Iterator for Either<L, R>
    where L: Iterator, R: Iterator<Item=L::Item>
{
    type Item = L::Item;

    fn next(&mut self) -> Option<L::Item> {
        either_mut!(*self, inner => inner.next())
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        either!(*self, inner => inner.size_hint())
    }
}

impl<L, R> DoubleEndedIterator for Either<L, R>
    where L: DoubleEndedIterator, R: DoubleEndedIterator<Item=L::Item>
{
    fn next_back(&mut self) -> Option<L::Item> {
        either_mut!(*self, inner => inner.next_back())
    }
}

impl<L, R> Read for Either<L, R>
    where L: Read, R: Read
{
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        either_mut!(*self, inner => inner.read(buf))
    }

    fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
        either_mut!(*self, inner => inner.read_to_end(buf))
    }
}

impl<L, R> BufRead for Either<L, R>
    where L: BufRead, R: BufRead
{
    fn fill_buf(&mut self) -> io::Result<&[u8]> {
        either_mut!(*self, inner => inner.fill_buf())
    }

    fn consume(&mut self, amt: usize) {
        either_mut!(*self, inner => inner.consume(amt))
    }
}

impl<L, R> Write for Either<L, R>
    where L: Write, R: Write
{
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        either_mut!(*self, inner => inner.write(buf))
    }

    fn flush(&mut self) -> io::Result<()> {
        either_mut!(*self, inner => inner.flush())
    }
}

#[test]
fn basic() {
    let mut e = Left(2);
    let r = Right(2);
    assert_eq!(e, Left(2));
    e = r;
    assert_eq!(e, Right(2));
    assert_eq!(e.left(), None);
    assert_eq!(e.right(), Some(2));
    assert_eq!(e.as_ref().right(), Some(&2));
    assert_eq!(e.as_mut().right(), Some(&mut 2));
}

#[test]
fn iter() {
    let x = 3;
    let mut iter = match x {
        1...3 => Left(0..10),
        _ => Right(17..),
    };

    assert_eq!(iter.next(), Some(0));
    assert_eq!(iter.count(), 9);
}

#[test]
fn read_write() {
    use std::io;

    let use_stdio = false;
    let mockdata = [0xff; 256];

    let mut reader = if use_stdio {
        Left(io::stdin())
    } else {
        Right(&mockdata[..])
    };

    let mut buf = [0u8; 16];
    assert_eq!(reader.read(&mut buf).unwrap(), buf.len());
    assert_eq!(&buf, &mockdata[..buf.len()]);

    let mut mockbuf = [0u8; 256];
    let mut writer = if use_stdio {
        Left(io::stdout())
    } else {
        Right(&mut mockbuf[..])
    };

    let buf = [1u8; 16];
    assert_eq!(writer.write(&buf).unwrap(), buf.len());
}
<|endoftext|>


<commit_before>//! holdem-rs contains more or less common types and functions used by other poker related libraries.

extern crate cards;
use std::cmp::{Ord};

/// All hand rank classes that a 5-card hand can be worth in Texas Hold'em.
#[derive(Debug, PartialEq)]
#[derive(Eq, Hash)]
#[derive(PartialOrd, Ord)]
pub enum HandRankClass {
    HighCard,
    OnePair,
    TwoPair,
    ThreeOfAKind,
    Straight,
    Flush,
    FullHouse,
    FourOfAKind,
    StraightFlush,
}

/// A card encoded using the bit pattern described in Cactus Kev's
/// [article](http://www.suffecool.net/poker/evaluator.html).
pub type CactusKevCard = u32;

/// A value representing the strength of a hand. The higheer, the better.
/// The numbers go from 0 to 7461 inclusive.
pub type HandRank = u16;
pub const HAND_RANK_COUNT : u16 = 7462;

/// Translates a hand rank to a rank class.
/// assumes there are HAND_RANK_COUNT distinct hand ranks, where the
/// largest are the most valuable. Numbers based on: http://www.suffecool.net/poker/evaluator.html
pub fn hand_rank_to_class(val: &HandRank) -> HandRankClass {
    match *val {
        0...1276 => HandRankClass::HighCard,
        1277...4136 => HandRankClass::OnePair,
        4137...4994 => HandRankClass::TwoPair,
        4995...5852 => HandRankClass::ThreeOfAKind,
        5853...5862 => HandRankClass::Straight,
        5863...7139 => HandRankClass::Flush,
        7140...7295 => HandRankClass::FullHouse,
        7296...7451 => HandRankClass::FourOfAKind,
        7452...7461 => HandRankClass::StraightFlush,
        _ => panic!("Unexpected hand rank value! '{}'", *val)
    }
}
<commit_msg>A tiny comment on a future improvement.<commit_after>//! holdem-rs contains more or less common types and functions used by other poker related libraries.

extern crate cards;
use std::cmp::{Ord};

/// All hand rank classes that a 5-card hand can be worth in Texas Hold'em.
#[derive(Debug, PartialEq)]
#[derive(Eq, Hash)]
#[derive(PartialOrd, Ord)]
pub enum HandRankClass {
    HighCard,
    OnePair,
    TwoPair,
    ThreeOfAKind,
    Straight,
    Flush,
    FullHouse,
    FourOfAKind,
    StraightFlush,
}

/// A card encoded using the bit pattern described in Cactus Kev's
/// [article](http://www.suffecool.net/poker/evaluator.html).
pub type CactusKevCard = u32;

/// A value representing the strength of a hand. The higheer, the better.
/// The numbers go from 0 to 7461 inclusive.
pub type HandRank = u16; //TODO: struct HandRank(u16); //TODO: pub?
pub const HAND_RANK_COUNT : u16 = 7462;

/// Translates a hand rank to a rank class.
/// assumes there are HAND_RANK_COUNT distinct hand ranks, where the
/// largest are the most valuable. Numbers based on: http://www.suffecool.net/poker/evaluator.html
pub fn hand_rank_to_class(val: &HandRank) -> HandRankClass {
    match *val {
        0...1276 => HandRankClass::HighCard,
        1277...4136 => HandRankClass::OnePair,
        4137...4994 => HandRankClass::TwoPair,
        4995...5852 => HandRankClass::ThreeOfAKind,
        5853...5862 => HandRankClass::Straight,
        5863...7139 => HandRankClass::Flush,
        7140...7295 => HandRankClass::FullHouse,
        7296...7451 => HandRankClass::FourOfAKind,
        7452...7461 => HandRankClass::StraightFlush,
        _ => panic!("Unexpected hand rank value! '{}'", *val)
    }
}
<|endoftext|>


<commit_before>//! A library for sending push notifications using the Pushover API.
//! Repository and command-line client documentation is
//! [here](https://github.com/unjordy/po).

extern crate curl;
extern crate url;
extern crate rustc_serialize;
extern crate regex;

use std::collections::BTreeMap;
use curl::http;
use url::form_urlencoded;
use rustc_serialize::json::{self, ToJson};
use self::Parameters::*;

pub mod config;

/// Optional parameters for Pushover API messages
#[derive(PartialEq, Clone)]
pub enum Parameters {
    /// A numeric priority from -2 (lowest priority) to 2 (emergency priority)
    /// (Default: 0)
    Priority(i8),
    /// A title for the push notification
    Title(String),
    /// A string identifying the device to send the notification to
    Device(String),
    /// The notification sound to play (out of the Pushover-supported sounds)
    Sound(String),
    /// A supplementary URL to send with the notification
    URL(String),
    /// A title to give the supplementary URL
    URLTitle(String),
    /// Gist the full message body and link it as a supplementary URL with
    /// title "Full Output (GitHub Gist)". This option supersedes the URL and
    /// URLTitle options if those are also provided.
    Gist,
    /// Enable debugging output
    Debug
}

#[derive(RustcDecodable)]
struct MessagesJson {
    status: isize,
    errors: Vec<String>
}

#[derive(RustcEncodable)]
struct GistPost {
    files: BTreeMap<String, json::Json>
}

#[derive(RustcDecodable)]
struct GistResponse {
    html_url: String
}

fn api_error(response_body: &str) -> Result<(), Vec<String>> {
    let response: MessagesJson = json::decode(response_body).unwrap();

    if response.status != 1 {
        return Err(response.errors);
    }

    Err(vec![format!("general API error")])
}

/// Post a message body with a given title to GitHub Gist and return the Gist's
/// URL.
pub fn gist(message: &str, title: String) -> Result<String, (u32, String)> {
    let mut content = BTreeMap::new();
    content.insert("content".to_string(), message.to_json());
    let mut gist_file = BTreeMap::new();
    gist_file.insert(title, content.to_json());
    let gist = GistPost {
        files: gist_file
    };

    if let Ok(json) = json::encode(&gist) {
        let mut handle = http::handle();
        let json_ref: &str = json.as_ref();
        let upload = handle
                        .post("https://api.github.com/gists", json_ref)
                        .header("Content-Type", "application/json")
                        .header("User-Agent", "po");
        if let Ok(res) = upload.exec() {
            if res.get_code() == 201 || res.get_code() == 200 {
                let body = std::str::from_utf8(res.get_body()).unwrap();
                let response: GistResponse = json::decode(body).unwrap();
                return Ok(response.html_url);
            }
        }
    }

    Err((0, format!("Generic: Couldn't post to Gist.")))
}
/// Pushes a message using the Pushover API, with the specified API token,
/// user key, message body, and array of optional Parameters.
pub fn push(token: &str, user: &str, message: &str,
                       parameters: &[Parameters]) -> Result<(), Vec<String>> {
    // Keep these here for now to satisfy the borrow checker:
    let msg = if message.len() > 1024 {
        message[0..1024].as_ref()
    }
    else {
        message
    };
    let mut title = "po".to_string();
    let mut debug = false;

    let mut notification = vec![
        ("token".to_string(), token.to_string()),
        ("user".to_string(), user.to_string()),
        ("message".to_string(), msg.to_string())];

    // Copy the parameters collection into a vector we own; slightly inefficent
    // but much more convenient for the caller.
    let mut para = Vec::with_capacity(parameters.len());
    for p in parameters.iter() {
        para.push(p.clone());
    }

    for parameter in para.into_iter() {
        match parameter {
            Priority(p)  => notification.push(("priority".to_string(), p.to_string())),
            Title(t)     => {
                notification.push(("title".to_string(), t.clone()));
                title = t;
            },
            Device(d)    => notification.push(("device".to_string(), d)),
            Sound(s)     => notification.push(("sound".to_string(), s)),
            URL(u)       => notification.push(("url".to_string(), u)),
            URLTitle(ut) => notification.push(("url_title".to_string(), ut)),
            Gist         => {
                if let Ok(gist_url) = gist(message, title.clone()) {
                    notification.push(("url".to_string(), gist_url));
                    notification.push(("url_title".to_string(),
                        "Full Output (GitHub Gist)".to_string()));
                }
            },
            Debug        => debug = true
        }
    }

    let body = form_urlencoded::serialize_owned(notification.as_ref());
    let body_ref: &str = body.as_ref();
    if debug {
        println!("push body:\n{}", body);
    }
    let mut handle = http::handle();
    let message = handle
                    .post("https://api.pushover.net/1/messages.json", body_ref)
                    .header("Content-Type", "application/x-www-form-urlencoded");
    match message.exec() {
        Ok(res) => {
            match res.get_code() {
                200 => Ok(()),
                400...499 => api_error(std::str::from_utf8(res.get_body()).unwrap()),
                n => Err(vec![format!("API error {}", n)])
            }
        },
        Err(code) => Err(vec![format!("curl error {}", code)])
    }
}

pub fn send_with_url(token: &str, user: &str, message: &str, priority: i8,
            title: Option<&str>, device: Option<&str>,
            sound: Option<&str>, url: Option<&str>,
            url_title: Option<&str>) -> Result<(), Vec<String>> {
    let mut parameters: Vec<Parameters> = vec![Parameters::Priority(priority)];

    if let Some(t) = title {
        parameters.push(Parameters::Title(t.to_string()));
    }
    if let Some(d) = device {
        parameters.push(Parameters::Device(d.to_string()));
    }
    if let Some(s) = sound {
        parameters.push(Parameters::Sound(s.to_string()));
    }
    if let Some(u) = url {
        parameters.push(Parameters::URL(u.to_string()));
    }
    if let Some(ut) = url_title {
        parameters.push(Parameters::URLTitle(ut.to_string()));
    }
    push(token, user, message, parameters.as_ref())
}

pub fn send(token: &str, user: &str, message: &str, priority: i8,
            title: Option<&str>, device: Option<&str>,
            sound: Option<&str>) -> Result<(), Vec<String>> {
    send_with_url(token, user, message, priority, title, device, sound, None, None)
}

pub fn send_gist(token: &str, user: &str, message: &str, priority: i8,
                 title: Option<&str>, device: Option<&str>,
                 sound: Option<&str>) -> Result<(), Vec<String>> {
    let mut parameters: Vec<Parameters> = vec![Parameters::Priority(priority)];

    if let Some(t) = title {
        parameters.push(Parameters::Title(t.to_string()));
    }
    if let Some(d) = device {
        parameters.push(Parameters::Device(d.to_string()));
    }
    if let Some(s) = sound {
        parameters.push(Parameters::Sound(s.to_string()));
    }
    parameters.push(Parameters::Gist);
    push(token, user, message, parameters.as_ref())
}

/// Send a basic push notification with just an API token, user key, and
/// message body.
pub fn send_basic(token: &str, user: &str,
                  message: &str) -> Result<(), Vec<String>> {
    return push(token, user, message, vec![].as_ref());
}
<commit_msg>Switched to url::form_urlencoded::serialize() for form encoding, since the owned variant no longer exists.<commit_after>//! A library for sending push notifications using the Pushover API.
//! Repository and command-line client documentation is
//! [here](https://github.com/unjordy/po).

extern crate curl;
extern crate url;
extern crate rustc_serialize;
extern crate regex;

use std::collections::BTreeMap;
use curl::http;
use url::form_urlencoded;
use rustc_serialize::json::{self, ToJson};
use self::Parameters::*;

pub mod config;

/// Optional parameters for Pushover API messages
#[derive(PartialEq, Clone)]
pub enum Parameters {
    /// A numeric priority from -2 (lowest priority) to 2 (emergency priority)
    /// (Default: 0)
    Priority(i8),
    /// A title for the push notification
    Title(String),
    /// A string identifying the device to send the notification to
    Device(String),
    /// The notification sound to play (out of the Pushover-supported sounds)
    Sound(String),
    /// A supplementary URL to send with the notification
    URL(String),
    /// A title to give the supplementary URL
    URLTitle(String),
    /// Gist the full message body and link it as a supplementary URL with
    /// title "Full Output (GitHub Gist)". This option supersedes the URL and
    /// URLTitle options if those are also provided.
    Gist,
    /// Enable debugging output
    Debug
}

#[derive(RustcDecodable)]
struct MessagesJson {
    status: isize,
    errors: Vec<String>
}

#[derive(RustcEncodable)]
struct GistPost {
    files: BTreeMap<String, json::Json>
}

#[derive(RustcDecodable)]
struct GistResponse {
    html_url: String
}

fn api_error(response_body: &str) -> Result<(), Vec<String>> {
    let response: MessagesJson = json::decode(response_body).unwrap();

    if response.status != 1 {
        return Err(response.errors);
    }

    Err(vec![format!("general API error")])
}

/// Post a message body with a given title to GitHub Gist and return the Gist's
/// URL.
pub fn gist(message: &str, title: String) -> Result<String, (u32, String)> {
    let mut content = BTreeMap::new();
    content.insert("content".to_string(), message.to_json());
    let mut gist_file = BTreeMap::new();
    gist_file.insert(title, content.to_json());
    let gist = GistPost {
        files: gist_file
    };

    if let Ok(json) = json::encode(&gist) {
        let mut handle = http::handle();
        let json_ref: &str = json.as_ref();
        let upload = handle
                        .post("https://api.github.com/gists", json_ref)
                        .header("Content-Type", "application/json")
                        .header("User-Agent", "po");
        if let Ok(res) = upload.exec() {
            if res.get_code() == 201 || res.get_code() == 200 {
                let body = std::str::from_utf8(res.get_body()).unwrap();
                let response: GistResponse = json::decode(body).unwrap();
                return Ok(response.html_url);
            }
        }
    }

    Err((0, format!("Generic: Couldn't post to Gist.")))
}
/// Pushes a message using the Pushover API, with the specified API token,
/// user key, message body, and array of optional Parameters.
pub fn push(token: &str, user: &str, message: &str,
                       parameters: &[Parameters]) -> Result<(), Vec<String>> {
    // Keep these here for now to satisfy the borrow checker:
    let msg = if message.len() > 1024 {
        message[0..1024].as_ref()
    }
    else {
        message
    };
    let mut title = "po".to_string();
    let mut debug = false;

    let mut notification = vec![
        ("token".to_string(), token.to_string()),
        ("user".to_string(), user.to_string()),
        ("message".to_string(), msg.to_string())];

    // Copy the parameters collection into a vector we own; slightly inefficent
    // but much more convenient for the caller.
    let mut para = Vec::with_capacity(parameters.len());
    for p in parameters.iter() {
        para.push(p.clone());
    }

    for parameter in para.into_iter() {
        match parameter {
            Priority(p)  => notification.push(("priority".to_string(), p.to_string())),
            Title(t)     => {
                notification.push(("title".to_string(), t.clone()));
                title = t;
            },
            Device(d)    => notification.push(("device".to_string(), d)),
            Sound(s)     => notification.push(("sound".to_string(), s)),
            URL(u)       => notification.push(("url".to_string(), u)),
            URLTitle(ut) => notification.push(("url_title".to_string(), ut)),
            Gist         => {
                if let Ok(gist_url) = gist(message, title.clone()) {
                    notification.push(("url".to_string(), gist_url));
                    notification.push(("url_title".to_string(),
                        "Full Output (GitHub Gist)".to_string()));
                }
            },
            Debug        => debug = true
        }
    }

    let body = form_urlencoded::serialize(notification.into_iter());
    let body_ref: &str = body.as_ref();
    if debug {
        println!("push body:\n{}", body);
    }
    let mut handle = http::handle();
    let message = handle
                    .post("https://api.pushover.net/1/messages.json", body_ref)
                    .header("Content-Type", "application/x-www-form-urlencoded");
    match message.exec() {
        Ok(res) => {
            match res.get_code() {
                200 => Ok(()),
                400...499 => api_error(std::str::from_utf8(res.get_body()).unwrap()),
                n => Err(vec![format!("API error {}", n)])
            }
        },
        Err(code) => Err(vec![format!("curl error {}", code)])
    }
}

pub fn send_with_url(token: &str, user: &str, message: &str, priority: i8,
            title: Option<&str>, device: Option<&str>,
            sound: Option<&str>, url: Option<&str>,
            url_title: Option<&str>) -> Result<(), Vec<String>> {
    let mut parameters: Vec<Parameters> = vec![Parameters::Priority(priority)];

    if let Some(t) = title {
        parameters.push(Parameters::Title(t.to_string()));
    }
    if let Some(d) = device {
        parameters.push(Parameters::Device(d.to_string()));
    }
    if let Some(s) = sound {
        parameters.push(Parameters::Sound(s.to_string()));
    }
    if let Some(u) = url {
        parameters.push(Parameters::URL(u.to_string()));
    }
    if let Some(ut) = url_title {
        parameters.push(Parameters::URLTitle(ut.to_string()));
    }
    push(token, user, message, parameters.as_ref())
}

pub fn send(token: &str, user: &str, message: &str, priority: i8,
            title: Option<&str>, device: Option<&str>,
            sound: Option<&str>) -> Result<(), Vec<String>> {
    send_with_url(token, user, message, priority, title, device, sound, None, None)
}

pub fn send_gist(token: &str, user: &str, message: &str, priority: i8,
                 title: Option<&str>, device: Option<&str>,
                 sound: Option<&str>) -> Result<(), Vec<String>> {
    let mut parameters: Vec<Parameters> = vec![Parameters::Priority(priority)];

    if let Some(t) = title {
        parameters.push(Parameters::Title(t.to_string()));
    }
    if let Some(d) = device {
        parameters.push(Parameters::Device(d.to_string()));
    }
    if let Some(s) = sound {
        parameters.push(Parameters::Sound(s.to_string()));
    }
    parameters.push(Parameters::Gist);
    push(token, user, message, parameters.as_ref())
}

/// Send a basic push notification with just an API token, user key, and
/// message body.
pub fn send_basic(token: &str, user: &str,
                  message: &str) -> Result<(), Vec<String>> {
    return push(token, user, message, vec![].as_ref());
}
<|endoftext|>


<commit_before>extern crate rustc_serialize;
extern crate chrono;
extern crate uuid;

use std::collections::HashMap;
use std::sync::{Arc, Mutex, RwLock};
use std::sync::mpsc::{channel, Receiver, Sender, SendError};
use std::thread::{Builder, JoinHandle};

use rustc_serialize::json::{Json, ToJson};
use uuid::Uuid;

pub struct Actor {
    join_handle: JoinHandle<()>,
    mailbox: Sender<Json>,
    name: String,
    uuid: Uuid,
}

impl Actor {
    fn new(
        join_handle: JoinHandle<()>,
        mailbox: Sender<Json>,
        name: String,
        uuid: Uuid,
    ) -> Actor {
        Actor {
            join_handle: join_handle,
            mailbox: mailbox,
            name: name,
            uuid: uuid,
        }
    }
}

pub struct Supervisor {
    actors: RwLock<HashMap<Uuid, Actor>>,
    name: String,
}

impl Supervisor {
    fn new(name: &str) -> Supervisor {
        Supervisor {
            actors: RwLock::new(HashMap::new()),
            name: name.to_string(),
        }
    }

    fn join(&self, pid : Uuid) -> Result<(), String> {
        match self.actors.write().unwrap().remove(&pid) {
            Some(actor) => {
                match actor.join_handle.join() {
                    Ok(_) => Ok(()),
                    Err(e) => {
                        if let Some(e) = e.downcast_ref::<&'static str>() {
                            Err(e.to_string())
                        } else {
                            Err(format!("Uknown error joining PID {}", pid.to_string()))
                        }
                    }
                }
            },
            None => Err(format!("PID {} does not map to a spawned actor", pid.to_string())),
        }
    }

    fn send_message(&self, pid: Uuid, message: Json) -> Result<(), String> {
        match self.actors.read().unwrap().get(&pid) {
            Some(actor) => {
                match actor.mailbox.send(message) {
                    Ok(_) => Ok(()),
                    Err(e) => { Err(e.to_string()) },
                }
            },
            None => Err(format!("PID {} does not map to a spawned actor", pid.to_string())),
        }
    }

    fn spawn<F>(&self, actor_name: &str, body: F) -> Result<Uuid, String>
        where F : 'static + Send + Fn(Receiver<Json>) -> () {
        let pid = Uuid::new_v4();
        let (mailbox_sender, mailbox_receiver) = channel();
        let arc_body = Arc::new(Mutex::new(body));

        match self.actors.write() {
            Ok(mut actors) => {
                let actor_handle_result = Builder::new().name(pid.to_string()).spawn(move || {
                    arc_body.lock().unwrap()(mailbox_receiver);
                });

                match actor_handle_result {
                    Ok(actor_handle) => {
                        actors.insert(pid, Actor::new(
                            actor_handle,
                            mailbox_sender,
                            actor_name.to_string(),
                            pid,
                        ));

                        Ok(pid)
                    },
                    Err(e) => Err(e.to_string()),
                }
            },
            Err(e) => Err(e.to_string())
        }
    }
}

#[cfg(test)]
mod tests {
    use std::fs::{remove_file, File};
    use std::io::prelude::*;
    use std::sync::mpsc::{channel, Receiver, Sender};
    use std::thread;
    use chrono::*;
    use rustc_serialize::json::{Json, ToJson};
    use uuid::Uuid;
    use super::*;

    // spawn tests
    #[test]
    fn the_body_actor_callback_is_executed() {
        let supervisor = Supervisor::new("folks");
        let json_msg = "{\"hi\": \"friend\"}".to_json();
        let json_clone = json_msg.clone();
        let pid_bob: Uuid = supervisor.spawn(
            "Bob",
            |receiver| {
                let mut message_output_file = File::create("/tmp/bob-test.json").unwrap();
                message_output_file.write_all(receiver.recv().unwrap().to_string().as_bytes()).unwrap();
                ()
            }
        ).unwrap();

        supervisor.send_message(pid_bob, json_msg).unwrap();

        thread::sleep_ms(1001);
        let mut message_output_file = File::open("/tmp/bob-test.json").unwrap();
        let mut message_output = String::new();
        message_output_file.read_to_string(&mut message_output).unwrap();
        remove_file("/tmp/bob-test.json").unwrap();

        let actual_json = Json::from_str(&message_output).unwrap();
        assert_eq!(json_clone, actual_json);
    }

    // send_message tests
    #[test]
    fn error_is_returned_if_message_sent_for_pid_that_does_not_exist() {
        let supervisor = Supervisor::new("folks");
        let pid = Uuid::new_v4();

        let send_result = supervisor.send_message(pid, "{}".to_json());

        match send_result {
            Ok(_) => panic!("Message sent to non-existent PID"),
            Err(e) => assert_eq!(e, format!("PID {} does not map to a spawned actor", pid.to_string())),
        }
    }

    #[test]
    fn pid_can_be_used_to_send_message() {
        let supervisor = Supervisor::new("folks");
        let pid: Uuid = supervisor.spawn(
            "Bob",
            |r| { r.recv().unwrap(); () }
        ).unwrap();

        supervisor.send_message(pid, "{}".to_json()).unwrap();
    }

    // join tests
    #[test]
    fn pid_can_be_used_to_join_actor() {
        let supervisor = Supervisor::new("folks");
        let start_time = UTC::now();
        let pid: Uuid = supervisor.spawn(
            "Bob",
            |r| { thread::sleep_ms(1000); () }
        ).unwrap();
        assert!((start_time + Duration::milliseconds(1000)) > UTC::now());

        supervisor.join(pid).unwrap();

        assert!((start_time + Duration::milliseconds(1000)) < UTC::now());
    }

    #[test]
    fn joining_actor_makes_it_unreachable() {
        let supervisor = Supervisor::new("folks");
        let pid: Uuid = supervisor.spawn(
            "Bob",
            |r| { () }
        ).unwrap();
        supervisor.join(pid).unwrap();

        let send_result = supervisor.send_message(pid, "{}".to_json());

        match send_result {
            Ok(_) => panic!("Message sent to non-existent PID"),
            Err(e) => assert_eq!(e, format!("PID {} does not map to a spawned actor", pid.to_string())),
        }
    }

    #[test]
    fn error_is_returned_if_joining_actor_that_does_not_exist() {
        let supervisor = Supervisor::new("folks");
        let pid = Uuid::new_v4();

        let join_result = supervisor.join(pid);

        match join_result {
            Ok(_) => panic!("Actor joined for non-existent PID"),
            Err(e) => assert_eq!(e, format!("PID {} does not map to a spawned actor", pid.to_string())),
        }
    }
}
<commit_msg>Clear some compilation warnings and make supervisor methods public<commit_after>extern crate rustc_serialize;
extern crate chrono;
extern crate uuid;

use std::collections::HashMap;
use std::sync::{Arc, Mutex, RwLock};
use std::sync::mpsc::{channel, Receiver, Sender};
use std::thread::{Builder, JoinHandle};

use rustc_serialize::json::{Json};
use uuid::Uuid;

pub struct Actor {
    join_handle: JoinHandle<()>,
    mailbox: Sender<Json>,
    name: String,
    uuid: Uuid,
}

impl Actor {
    fn new(
        join_handle: JoinHandle<()>,
        mailbox: Sender<Json>,
        name: String,
        uuid: Uuid,
    ) -> Actor {
        Actor {
            join_handle: join_handle,
            mailbox: mailbox,
            name: name,
            uuid: uuid,
        }
    }
}

pub struct Supervisor {
    actors: RwLock<HashMap<Uuid, Actor>>,
    pub name: String,
}

impl Supervisor {
    pub fn new(name: &str) -> Supervisor {
        Supervisor {
            actors: RwLock::new(HashMap::new()),
            name: name.to_string(),
        }
    }

    pub fn join(&self, pid : Uuid) -> Result<(), String> {
        match self.actors.write().unwrap().remove(&pid) {
            Some(actor) => {
                match actor.join_handle.join() {
                    Ok(_) => Ok(()),
                    Err(e) => {
                        if let Some(e) = e.downcast_ref::<&'static str>() {
                            Err(e.to_string())
                        } else {
                            Err(format!("Uknown error joining PID {}", pid.to_string()))
                        }
                    }
                }
            },
            None => Err(format!("PID {} does not map to a spawned actor", pid.to_string())),
        }
    }

    pub fn send_message(&self, pid: Uuid, message: Json) -> Result<(), String> {
        match self.actors.read().unwrap().get(&pid) {
            Some(actor) => {
                match actor.mailbox.send(message) {
                    Ok(_) => Ok(()),
                    Err(e) => { Err(e.to_string()) },
                }
            },
            None => Err(format!("PID {} does not map to a spawned actor", pid.to_string())),
        }
    }

    pub fn spawn<F>(&self, actor_name: &str, body: F) -> Result<Uuid, String>
        where F : 'static + Send + Fn(Receiver<Json>) -> () {
        let pid = Uuid::new_v4();
        let (mailbox_sender, mailbox_receiver) = channel();
        let arc_body = Arc::new(Mutex::new(body));

        match self.actors.write() {
            Ok(mut actors) => {
                let actor_handle_result = Builder::new().name(pid.to_string()).spawn(move || {
                    arc_body.lock().unwrap()(mailbox_receiver);
                });

                match actor_handle_result {
                    Ok(actor_handle) => {
                        actors.insert(pid, Actor::new(
                            actor_handle,
                            mailbox_sender,
                            actor_name.to_string(),
                            pid,
                        ));

                        Ok(pid)
                    },
                    Err(e) => Err(e.to_string()),
                }
            },
            Err(e) => Err(e.to_string())
        }
    }
}

#[cfg(test)]
mod tests {
    use std::fs::{remove_file, File};
    use std::io::prelude::*;
    use std::thread;
    use chrono::*;
    use rustc_serialize::json::{Json, ToJson};
    use uuid::Uuid;
    use super::*;

    // spawn tests
    #[test]
    fn the_body_actor_callback_is_executed() {
        let supervisor = Supervisor::new("folks");
        let json_msg = "{\"hi\": \"friend\"}".to_json();
        let json_clone = json_msg.clone();
        let pid_bob: Uuid = supervisor.spawn(
            "Bob",
            |receiver| {
                let mut message_output_file = File::create("/tmp/bob-test.json").unwrap();
                message_output_file.write_all(receiver.recv().unwrap().to_string().as_bytes()).unwrap();
                ()
            }
        ).unwrap();

        supervisor.send_message(pid_bob, json_msg).unwrap();

        thread::sleep_ms(1001);
        let mut message_output_file = File::open("/tmp/bob-test.json").unwrap();
        let mut message_output = String::new();
        message_output_file.read_to_string(&mut message_output).unwrap();
        remove_file("/tmp/bob-test.json").unwrap();

        let actual_json = Json::from_str(&message_output).unwrap();
        assert_eq!(json_clone, actual_json);
    }

    // send_message tests
    #[test]
    fn error_is_returned_if_message_sent_for_pid_that_does_not_exist() {
        let supervisor = Supervisor::new("folks");
        let pid = Uuid::new_v4();

        let send_result = supervisor.send_message(pid, "{}".to_json());

        match send_result {
            Ok(_) => panic!("Message sent to non-existent PID"),
            Err(e) => assert_eq!(e, format!("PID {} does not map to a spawned actor", pid.to_string())),
        }
    }

    #[test]
    fn pid_can_be_used_to_send_message() {
        let supervisor = Supervisor::new("folks");
        let pid: Uuid = supervisor.spawn(
            "Bob",
            |r| { r.recv().unwrap(); () }
        ).unwrap();

        supervisor.send_message(pid, "{}".to_json()).unwrap();
    }

    // join tests
    #[test]
    fn pid_can_be_used_to_join_actor() {
        let supervisor = Supervisor::new("folks");
        let start_time = UTC::now();
        let pid: Uuid = supervisor.spawn(
            "Bob",
            |_| { thread::sleep_ms(1000); () }
        ).unwrap();
        assert!((start_time + Duration::milliseconds(1000)) > UTC::now());

        supervisor.join(pid).unwrap();

        assert!((start_time + Duration::milliseconds(1000)) < UTC::now());
    }

    #[test]
    fn joining_actor_makes_it_unreachable() {
        let supervisor = Supervisor::new("folks");
        let pid: Uuid = supervisor.spawn(
            "Bob",
            |_| { () }
        ).unwrap();
        supervisor.join(pid).unwrap();

        let send_result = supervisor.send_message(pid, "{}".to_json());

        match send_result {
            Ok(_) => panic!("Message sent to non-existent PID"),
            Err(e) => assert_eq!(e, format!("PID {} does not map to a spawned actor", pid.to_string())),
        }
    }

    #[test]
    fn error_is_returned_if_joining_actor_that_does_not_exist() {
        let supervisor = Supervisor::new("folks");
        let pid = Uuid::new_v4();

        let join_result = supervisor.join(pid);

        match join_result {
            Ok(_) => panic!("Actor joined for non-existent PID"),
            Err(e) => assert_eq!(e, format!("PID {} does not map to a spawned actor", pid.to_string())),
        }
    }
}
<|endoftext|>