<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::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,
}

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,
        }
    }

    /// 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
    }

    /// 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();
            let obj = dst.join(file).with_extension("o");
            fs::create_dir_all(&obj.parent().unwrap()).unwrap();
            run(cmd.arg(&src.join(file)).arg("-o").arg(&obj),
                &self.compiler(&target));
            objects.push(obj);
        }

        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 {
            println!("cargo:rustc-link-lib=stdc++");
        }
    }

    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) -> Command {
        let target = getenv_unwrap("TARGET");
        let opt_level = getenv_unwrap("OPT_LEVEL");
        let profile = getenv_unwrap("PROFILE");
        println!("{} {}", profile, opt_level);

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

        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("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");
            }
        }

        for directory in self.include_directories.iter() {
            cmd.arg("-I").arg(directory);
        }

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

        for &(ref key, ref value) in self.definitions.iter() {
            if let &Some(ref value) = value {
                cmd.arg(&format!("-D{}={}", key, value));
            } else {
                cmd.arg(&format!("-D{}", 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 => {
            fail(&format!("failed to execute command: {}\nIs `{}` not installed?{}",
                          e, program,
                          if cfg!(windows) {
                             " (see http://github.com/alexcrichton/gcc-rs#windows-notes for help)"
                          } else {""}
                          ));
        }
        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) {
        "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) {
        "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>Link to https<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::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,
}

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,
        }
    }

    /// 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
    }

    /// 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();
            let obj = dst.join(file).with_extension("o");
            fs::create_dir_all(&obj.parent().unwrap()).unwrap();
            run(cmd.arg(&src.join(file)).arg("-o").arg(&obj),
                &self.compiler(&target));
            objects.push(obj);
        }

        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 {
            println!("cargo:rustc-link-lib=stdc++");
        }
    }

    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) -> Command {
        let target = getenv_unwrap("TARGET");
        let opt_level = getenv_unwrap("OPT_LEVEL");
        let profile = getenv_unwrap("PROFILE");
        println!("{} {}", profile, opt_level);

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

        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("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");
            }
        }

        for directory in self.include_directories.iter() {
            cmd.arg("-I").arg(directory);
        }

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

        for &(ref key, ref value) in self.definitions.iter() {
            if let &Some(ref value) = value {
                cmd.arg(&format!("-D{}={}", key, value));
            } else {
                cmd.arg(&format!("-D{}", 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 => {
            fail(&format!("failed to execute command: {}\nIs `{}` not installed?{}",
                          e, program,
                          if cfg!(windows) {
                             " (see https://github.com/alexcrichton/gcc-rs#windows-notes for help)"
                          } else {""}
                          ));
        }
        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) {
        "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) {
        "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>extern crate libc;

#[cfg(feature = "dlopen")]
#[macro_export]
macro_rules! ffi_dispatch(
    ($handle: ident, $func: ident, $($arg: expr),*) => (
        ($handle.$func)($($arg),*)
    )
);

#[cfg(not(feature = "dlopen"))]
#[macro_export]
macro_rules! ffi_dispatch(
    ($handle: ident, $func: ident, $($arg: expr),*) => (
        $func($($arg),*)
    )
);

#[cfg(feature = "dlopen")]
#[macro_export]
macro_rules! ffi_dispatch_static(
    ($handle: ident, $name: ident) => (
        $handle.$name
    )
);

#[cfg(not(feature = "dlopen"))]
#[macro_export]
macro_rules! ffi_dispatch_static(
    ($handle: ident, $name: ident) => (
        &$name
    )
);

#[cfg(not(feature = "dlopen"))]
#[macro_export]
macro_rules! external_library(
    ($structname: ident, $link: expr,
        $(statics: $($sname: ident: $stype: ty),+)|*
        $(functions: $(fn $fname: ident($($farg: ty),*) -> $fret:ty),+)|*
        $(varargs: $(fn $vname: ident($($vargs: ty),+ ...) -> $vret: ty),+)|*
    ) => (
        #[link(name = $link)]
        extern {
            $($(
                pub static $sname: $stype;
            )+)*
            $($(
                pub fn $fname($(_: $farg),*) -> $fret;
            )+)*
            $($(
                pub fn $vname($(_: $vargs),+ , ...) -> $vret;
            )+)*
        }
    );
);

pub enum DlError {
    NotFound,
    MissingSymbol(&'static str)
}

#[cfg(feature = "dlopen")]
#[macro_export]
macro_rules! external_library(
    (__struct, $structname: ident,
        $(statics: $($sname: ident: $stype: ty),+)|*
        $(functions: $(fn $fname: ident($($farg: ty),*) -> $fret:ty),+)|*
        $(varargs: $(fn $vname: ident($($vargs: ty),+ ...) -> $vret: ty),+)|*
    ) => (
        pub struct $structname {
            $($(
                pub $sname: &'static $stype,
            )+)*
            $($(
                pub $fname: unsafe extern fn($($farg),*) -> $fret,
            )+)*
            $($(
                pub $vname: unsafe extern "C" fn($($vargs),+ , ...) -> $vret,
            )+)*
        }
    );
    (__impl, $structname: ident,
        $(statics: $($sname: ident: $stype: ty),+)|*
        $(functions: $(fn $fname: ident($($farg: ty),*) -> $fret:ty),+)|*
        $(varargs: $(fn $vname: ident($($vargs: ty),+ ...) -> $vret: ty),+)|*
    ) => (
    impl $structname {
        pub fn open(name: &str) -> Result<$structname, $crate::DlError> {
            let cname = match ::std::ffi::CString::new(name) {
                Ok(cs) => cs,
                Err(_) => Err($crate::DlError::NotFound)
            };
            unsafe {
                let dl = $crate::ffi::dlopen(cname.as_bytes_with_nul().as_ptr() as *const _, 1);
                if dl.is_null() {
                    return Err($crate::DlError::NotFound);
                }
                $crate::ffi::dlerror();
                let s = $structname {
                    $($($sname: {
                        let s_name = concat!(stringify!($sname), "\0");
                        let symbol = $crate::ffi::dlsym(dl, s_name.as_ptr() as *const _);
                        if !$crate::ffi::dlerror().is_null() {
                            return Err($crate::DlError::MissingSymbol(s_name))
                        }
                        ::std::mem::transmute(symbol)
                    },
                    )+)*
                    $($($fname: {
                        let s_name = concat!(stringify!($fname), "\0");
                        let symbol = $crate::ffi::dlsym(dl, s_name.as_ptr() as *const _);
                        if !$crate::ffi::dlerror().is_null() {
                            return Err($crate::DlError::MissingSymbol(s_name))
                        }
                        ::std::mem::transmute(symbol)
                    },
                    )+)*
                    $($($vname: {
                        let s_name = concat!(stringify!($vname), "\0");
                        let symbol = $crate::ffi::dlsym(dl, s_name.as_ptr() as *const _);
                        if !$crate::ffi::dlerror().is_null() {
                            return Err($crate::DlError::MissingSymbol(s_name))
                        }
                        ::std::mem::transmute(symbol)
                    },
                    )+)*
                };
                Ok(s)
            }
        }
    }
    );
    ($structname: ident, $link: expr,
        $(statics: $($sname: ident: $stype: ty),+)|*
        $(functions: $(fn $fname: ident($($farg: ty),*) -> $fret:ty),+)|*
        $(varargs: $(fn $vname: ident($($vargs: ty),+ ...) -> $vret: ty),+)|*
    ) => (
        external_library!(__struct,
            $structname, $(statics: $($sname: $stype),+)|*
            $(functions: $(fn $fname($($farg),*) -> $fret),+)|*
           $(varargs: $(fn $vname($($vargs),+ ...) -> $vret),+)|*
        );
        external_library!(__impl,
            $structname, $(statics: $($sname: $stype),+)|*
            $(functions: $(fn $fname($($farg),*) -> $fret),+)|*
           $(varargs: $(fn $vname($($vargs),+ ...) -> $vret),+)|*
        );
        unsafe impl Sync for $structname { }
    );
);

pub mod ffi {
    use libc::{c_char, c_int, c_void};
    extern {
        pub fn dlopen(filename: *const c_char, flag: c_int) -> *mut c_void;
        pub fn dlerror() -> *mut c_char;
        pub fn dlsym(handle: *mut c_void, symbol: *const c_char) -> *mut c_void;
        pub fn dlclose(handle: *mut c_void) -> c_int;
    }
}<commit_msg>Fix a missing return Err().<commit_after>extern crate libc;

#[cfg(feature = "dlopen")]
#[macro_export]
macro_rules! ffi_dispatch(
    ($handle: ident, $func: ident, $($arg: expr),*) => (
        ($handle.$func)($($arg),*)
    )
);

#[cfg(not(feature = "dlopen"))]
#[macro_export]
macro_rules! ffi_dispatch(
    ($handle: ident, $func: ident, $($arg: expr),*) => (
        $func($($arg),*)
    )
);

#[cfg(feature = "dlopen")]
#[macro_export]
macro_rules! ffi_dispatch_static(
    ($handle: ident, $name: ident) => (
        $handle.$name
    )
);

#[cfg(not(feature = "dlopen"))]
#[macro_export]
macro_rules! ffi_dispatch_static(
    ($handle: ident, $name: ident) => (
        &$name
    )
);

#[cfg(not(feature = "dlopen"))]
#[macro_export]
macro_rules! external_library(
    ($structname: ident, $link: expr,
        $(statics: $($sname: ident: $stype: ty),+)|*
        $(functions: $(fn $fname: ident($($farg: ty),*) -> $fret:ty),+)|*
        $(varargs: $(fn $vname: ident($($vargs: ty),+ ...) -> $vret: ty),+)|*
    ) => (
        #[link(name = $link)]
        extern {
            $($(
                pub static $sname: $stype;
            )+)*
            $($(
                pub fn $fname($(_: $farg),*) -> $fret;
            )+)*
            $($(
                pub fn $vname($(_: $vargs),+ , ...) -> $vret;
            )+)*
        }
    );
);

pub enum DlError {
    NotFound,
    MissingSymbol(&'static str)
}

#[cfg(feature = "dlopen")]
#[macro_export]
macro_rules! external_library(
    (__struct, $structname: ident,
        $(statics: $($sname: ident: $stype: ty),+)|*
        $(functions: $(fn $fname: ident($($farg: ty),*) -> $fret:ty),+)|*
        $(varargs: $(fn $vname: ident($($vargs: ty),+ ...) -> $vret: ty),+)|*
    ) => (
        pub struct $structname {
            $($(
                pub $sname: &'static $stype,
            )+)*
            $($(
                pub $fname: unsafe extern fn($($farg),*) -> $fret,
            )+)*
            $($(
                pub $vname: unsafe extern "C" fn($($vargs),+ , ...) -> $vret,
            )+)*
        }
    );
    (__impl, $structname: ident,
        $(statics: $($sname: ident: $stype: ty),+)|*
        $(functions: $(fn $fname: ident($($farg: ty),*) -> $fret:ty),+)|*
        $(varargs: $(fn $vname: ident($($vargs: ty),+ ...) -> $vret: ty),+)|*
    ) => (
    impl $structname {
        pub fn open(name: &str) -> Result<$structname, $crate::DlError> {
            let cname = match ::std::ffi::CString::new(name) {
                Ok(cs) => cs,
                Err(_) => return Err($crate::DlError::NotFound)
            };
            unsafe {
                let dl = $crate::ffi::dlopen(cname.as_bytes_with_nul().as_ptr() as *const _, 1);
                if dl.is_null() {
                    return Err($crate::DlError::NotFound);
                }
                $crate::ffi::dlerror();
                let s = $structname {
                    $($($sname: {
                        let s_name = concat!(stringify!($sname), "\0");
                        let symbol = $crate::ffi::dlsym(dl, s_name.as_ptr() as *const _);
                        if !$crate::ffi::dlerror().is_null() {
                            return Err($crate::DlError::MissingSymbol(s_name))
                        }
                        ::std::mem::transmute(symbol)
                    },
                    )+)*
                    $($($fname: {
                        let s_name = concat!(stringify!($fname), "\0");
                        let symbol = $crate::ffi::dlsym(dl, s_name.as_ptr() as *const _);
                        if !$crate::ffi::dlerror().is_null() {
                            return Err($crate::DlError::MissingSymbol(s_name))
                        }
                        ::std::mem::transmute(symbol)
                    },
                    )+)*
                    $($($vname: {
                        let s_name = concat!(stringify!($vname), "\0");
                        let symbol = $crate::ffi::dlsym(dl, s_name.as_ptr() as *const _);
                        if !$crate::ffi::dlerror().is_null() {
                            return Err($crate::DlError::MissingSymbol(s_name))
                        }
                        ::std::mem::transmute(symbol)
                    },
                    )+)*
                };
                Ok(s)
            }
        }
    }
    );
    ($structname: ident, $link: expr,
        $(statics: $($sname: ident: $stype: ty),+)|*
        $(functions: $(fn $fname: ident($($farg: ty),*) -> $fret:ty),+)|*
        $(varargs: $(fn $vname: ident($($vargs: ty),+ ...) -> $vret: ty),+)|*
    ) => (
        external_library!(__struct,
            $structname, $(statics: $($sname: $stype),+)|*
            $(functions: $(fn $fname($($farg),*) -> $fret),+)|*
           $(varargs: $(fn $vname($($vargs),+ ...) -> $vret),+)|*
        );
        external_library!(__impl,
            $structname, $(statics: $($sname: $stype),+)|*
            $(functions: $(fn $fname($($farg),*) -> $fret),+)|*
           $(varargs: $(fn $vname($($vargs),+ ...) -> $vret),+)|*
        );
        unsafe impl Sync for $structname { }
    );
);

pub mod ffi {
    use libc::{c_char, c_int, c_void};
    extern {
        pub fn dlopen(filename: *const c_char, flag: c_int) -> *mut c_void;
        pub fn dlerror() -> *mut c_char;
        pub fn dlsym(handle: *mut c_void, symbol: *const c_char) -> *mut c_void;
        pub fn dlclose(handle: *mut c_void) -> c_int;
    }
}<|endoftext|>


<commit_before>#[macro_use]
extern crate nom;

pub mod metadata;
pub mod frame;
mod utility;

use metadata::metadata_parser;
use frame::frame_parser;

pub struct Stream {
  pub info: metadata::StreamInfo,
  pub metadata: Vec<metadata::Block>,
  pub frames: Vec<frame::Frame>
}

named!(stream <&[u8], Stream>,
  chain!(
    blocks: metadata_parser ~
    frames: many1!(apply!(frame_parser, &blocks.0)),
    move|| {
      Stream {
        info: blocks.0,
        metadata: blocks.1,
        frames: frames,
      }
    }
  )
);
<commit_msg>Add module level description to top lib file<commit_after>//! An implementation of [FLAC](https://xiph.org/flac), free lossless audio
//! codec, written in Rust.

#[macro_use]
extern crate nom;

pub mod metadata;
pub mod frame;
mod utility;

use metadata::metadata_parser;
use frame::frame_parser;

pub struct Stream {
  pub info: metadata::StreamInfo,
  pub metadata: Vec<metadata::Block>,
  pub frames: Vec<frame::Frame>
}

named!(stream <&[u8], Stream>,
  chain!(
    blocks: metadata_parser ~
    frames: many1!(apply!(frame_parser, &blocks.0)),
    move|| {
      Stream {
        info: blocks.0,
        metadata: blocks.1,
        frames: frames,
      }
    }
  )
);
<|endoftext|>


<commit_before>extern crate hyper;
extern crate libc;
extern crate serde;

#[macro_use]
extern crate serde_derive;

extern crate serde_json;

#[macro_use]
extern crate lazy_static;

use hyper::Client;
use hyper::client::response::Response;
use hyper::header::{Headers, ContentType};
use hyper::mime::{Mime, TopLevel, SubLevel};
use hyper::status::StatusCode;
use libc::{uint8_t, size_t};
use serde::Serialize;
use std::collections::HashMap;
use std::env::var;
use std::io::Read;
use std::slice;
use std::sync::Mutex;

lazy_static! {
    static ref MAP: Mutex<HashMap<u16, u16>> = Mutex::new(HashMap::new());
}

static OK: i8 = 0;
static ERR_INVALID: i8 = -1;
static ERR_HOST: i8 = -127;

#[derive(Serialize)]
struct Empty();

macro_rules! post_request {
    ($path:expr) => (post_request($path, &Empty{}));
    ($path:expr, $data:expr) => (post_request($path, $data));
}

#[no_mangle]
#[allow(non_snake_case)]
pub extern fn CT_init(ctn: u16, pn: u16) -> i8 {
    // Do we know this CTN?
    if MAP.lock().unwrap().contains_key(&ctn) {
        return ERR_INVALID
    }

    // Build the request URL
    let endpoint = "ct_init".to_string();
    let path = endpoint + "/" + &ctn.to_string() + "/" + &pn.to_string();

    // Perform the request
    let mut response = post_request!(&path);

    match response.status {
        StatusCode::Ok => {
            // Cast server response
            let mut body = String::new();
            response.read_to_string(&mut body).unwrap();

            let status = body.parse::<i8>().unwrap();
            if status == OK {
                // Store CTN
                MAP.lock().unwrap().insert(ctn, pn);
            }

            status
        },
        _ => ERR_HOST
    }
}

#[derive(Serialize)]
struct RequestData {
    ctn: u16,
    dad: u8,
    sad: u8,
    lenc: usize,
    command: Vec<u8>,
    lenr: usize
}

#[allow(non_snake_case)]
#[derive(Deserialize)]
struct ResponseData {
    dad: u8,
    sad: u8,
    lenr: usize,
    response: Vec<u8>,
    responseCode: i8
}

#[no_mangle]
#[allow(non_snake_case)]
pub extern fn CT_data(ctn: u16, dad: *mut uint8_t, sad: *mut uint8_t, lenc: size_t, command: *const uint8_t, lenr: *mut size_t, response: *mut uint8_t) -> i8 {
    if !MAP.lock().unwrap().contains_key(&ctn) {
        return ERR_INVALID
    }

    let dad: &mut u8 = unsafe { &mut *dad };

    let sad: &mut u8 = unsafe { &mut *sad };

    let lenr: &mut size_t = unsafe { &mut *lenr };

    let command = unsafe { slice::from_raw_parts(command, lenc as usize) };

    let response = unsafe { slice::from_raw_parts_mut(response, *lenr) };

    let requestData = RequestData {
        dad: *dad,
        sad: *sad,
        lenc: lenc,
        command: command.to_vec(),
        lenr: *lenr
    };

    let pn = MAP.lock().unwrap();
    let pn = pn.get(&ctn).unwrap();
    let endpoint = "ct_data".to_string();
    let path = endpoint + "/" + &ctn.to_string() + "/" + &pn.to_string();

    let mut http_response = post_request(&path, &requestData);

    match http_response.status {
        StatusCode::Ok => {
            // decode server response
            let mut body = String::new();
            http_response.read_to_string(&mut body).unwrap();
            let responseData: ResponseData = serde_json::from_str(&body).unwrap();

            if responseData.responseCode == OK {

                *dad = responseData.dad;
                *sad = responseData.sad;
                *lenr = responseData.lenr;

                for (place, element) in response.iter_mut().zip(responseData.response.iter()) {
                    *place = *element;
                }
            }
            return responseData.responseCode;
        },
        _ => ERR_HOST
    }
}

#[no_mangle]
#[allow(non_snake_case)]
pub extern fn CT_close(ctn: u16) -> i8 {
    // Do we know this CTN?
    if !MAP.lock().unwrap().contains_key(&ctn) {
        return ERR_INVALID
    }

    // Build the request URL
    let pn = MAP.lock().unwrap().get(&ctn).unwrap().clone();
    let endpoint = "ct_close".to_string();
    let path = endpoint + "/" + &ctn.to_string() + "/" + &pn.to_string();

    // Perform the request
    let mut response = post_request!(&path);

    match response.status {
        StatusCode::Ok => {
            // Cast server response
            let mut body = String::new();
            response.read_to_string(&mut body).unwrap();

            let status = body.parse::<i8>().unwrap();
            if status == OK {
                // Remove CTN
                MAP.lock().unwrap().remove(&ctn);
            }

            status
        },
        _ => ERR_HOST
    }
}

fn env_or_default(var_name: &str, default: &str) -> String {
    match var(var_name) {
        Ok(s) => s,
        Err(_) => default.into(),
    }
}

fn post_request<T>(path: &str, payload: &T) -> Response
    where T: Serialize
{
    // untested
    let base_url = env_or_default("K2_BASE_URL", "http://localhost:8080/k2/ctapi/");
    let url = base_url + path;

    let client = Client::new();

    let mut headers = Headers::new();
    headers.set(
        ContentType(Mime(TopLevel::Application, SubLevel::Json, vec![]))
    );

    let body = serde_json::to_string(&payload).unwrap();

    return client.post(&url)
        .headers(headers)
        .body(&body[..])
        .send()
        .unwrap();
}
<commit_msg>adjust struct for requesting data<commit_after>extern crate hyper;
extern crate libc;
extern crate serde;

#[macro_use]
extern crate serde_derive;

extern crate serde_json;

#[macro_use]
extern crate lazy_static;

use hyper::Client;
use hyper::client::response::Response;
use hyper::header::{Headers, ContentType};
use hyper::mime::{Mime, TopLevel, SubLevel};
use hyper::status::StatusCode;
use libc::{uint8_t, size_t};
use serde::Serialize;
use std::collections::HashMap;
use std::env::var;
use std::io::Read;
use std::slice;
use std::sync::Mutex;

lazy_static! {
    static ref MAP: Mutex<HashMap<u16, u16>> = Mutex::new(HashMap::new());
}

static OK: i8 = 0;
static ERR_INVALID: i8 = -1;
static ERR_HOST: i8 = -127;

#[derive(Serialize)]
struct Empty();

macro_rules! post_request {
    ($path:expr) => (post_request($path, &Empty{}));
    ($path:expr, $data:expr) => (post_request($path, $data));
}

#[no_mangle]
#[allow(non_snake_case)]
pub extern fn CT_init(ctn: u16, pn: u16) -> i8 {
    // Do we know this CTN?
    if MAP.lock().unwrap().contains_key(&ctn) {
        return ERR_INVALID
    }

    // Build the request URL
    let endpoint = "ct_init".to_string();
    let path = endpoint + "/" + &ctn.to_string() + "/" + &pn.to_string();

    // Perform the request
    let mut response = post_request!(&path);

    match response.status {
        StatusCode::Ok => {
            // Cast server response
            let mut body = String::new();
            response.read_to_string(&mut body).unwrap();

            let status = body.parse::<i8>().unwrap();
            if status == OK {
                // Store CTN
                MAP.lock().unwrap().insert(ctn, pn);
            }

            status
        },
        _ => ERR_HOST
    }
}

#[derive(Serialize)]
struct RequestData {
    dad: u8,
    sad: u8,
    lenc: usize,
    command: Vec<u8>,
    lenr: usize
}

#[allow(non_snake_case)]
#[derive(Deserialize)]
struct ResponseData {
    dad: u8,
    sad: u8,
    lenr: usize,
    response: Vec<u8>,
    responseCode: i8
}

#[no_mangle]
#[allow(non_snake_case)]
pub extern fn CT_data(ctn: u16, dad: *mut uint8_t, sad: *mut uint8_t, lenc: size_t, command: *const uint8_t, lenr: *mut size_t, response: *mut uint8_t) -> i8 {
    if !MAP.lock().unwrap().contains_key(&ctn) {
        return ERR_INVALID
    }

    let dad: &mut u8 = unsafe { &mut *dad };

    let sad: &mut u8 = unsafe { &mut *sad };

    let lenr: &mut size_t = unsafe { &mut *lenr };

    let command = unsafe { slice::from_raw_parts(command, lenc as usize) };

    let response = unsafe { slice::from_raw_parts_mut(response, *lenr) };

    let requestData = RequestData {
        dad: *dad,
        sad: *sad,
        lenc: lenc,
        command: command.to_vec(),
        lenr: *lenr
    };

    let pn = MAP.lock().unwrap();
    let pn = pn.get(&ctn).unwrap();
    let endpoint = "ct_data".to_string();
    let path = endpoint + "/" + &ctn.to_string() + "/" + &pn.to_string();

    let mut http_response = post_request(&path, &requestData);

    match http_response.status {
        StatusCode::Ok => {
            // decode server response
            let mut body = String::new();
            http_response.read_to_string(&mut body).unwrap();
            let responseData: ResponseData = serde_json::from_str(&body).unwrap();

            if responseData.responseCode == OK {

                *dad = responseData.dad;
                *sad = responseData.sad;
                *lenr = responseData.lenr;

                for (place, element) in response.iter_mut().zip(responseData.response.iter()) {
                    *place = *element;
                }
            }
            return responseData.responseCode;
        },
        _ => ERR_HOST
    }
}

#[no_mangle]
#[allow(non_snake_case)]
pub extern fn CT_close(ctn: u16) -> i8 {
    // Do we know this CTN?
    if !MAP.lock().unwrap().contains_key(&ctn) {
        return ERR_INVALID
    }

    // Build the request URL
    let pn = MAP.lock().unwrap().get(&ctn).unwrap().clone();
    let endpoint = "ct_close".to_string();
    let path = endpoint + "/" + &ctn.to_string() + "/" + &pn.to_string();

    // Perform the request
    let mut response = post_request!(&path);

    match response.status {
        StatusCode::Ok => {
            // Cast server response
            let mut body = String::new();
            response.read_to_string(&mut body).unwrap();

            let status = body.parse::<i8>().unwrap();
            if status == OK {
                // Remove CTN
                MAP.lock().unwrap().remove(&ctn);
            }

            status
        },
        _ => ERR_HOST
    }
}

fn env_or_default(var_name: &str, default: &str) -> String {
    match var(var_name) {
        Ok(s) => s,
        Err(_) => default.into(),
    }
}

fn post_request<T>(path: &str, payload: &T) -> Response
    where T: Serialize
{
    // untested
    let base_url = env_or_default("K2_BASE_URL", "http://localhost:8080/k2/ctapi/");
    let url = base_url + path;

    let client = Client::new();

    let mut headers = Headers::new();
    headers.set(
        ContentType(Mime(TopLevel::Application, SubLevel::Json, vec![]))
    );

    let body = serde_json::to_string(&payload).unwrap();

    return client.post(&url)
        .headers(headers)
        .body(&body[..])
        .send()
        .unwrap();
}
<|endoftext|>


<commit_before>#![feature(box_patterns)]

extern crate chrono;
#[macro_use]
extern crate derive_new;
#[macro_use]
extern crate diesel;
#[macro_use]
extern crate diesel_codegen;
#[macro_use]
extern crate error_chain;
extern crate itertools;
#[macro_use]
extern crate lazy_static;
extern crate take_mut;

#[cfg(test)]
#[macro_use]
extern crate assert_matches;

use std::hash::{Hash, Hasher};

use chrono::prelude::*;
use chrono::Duration;
use diesel::prelude::*;

use configuration::Configuration;
use scheduling::Schedule;

pub use errors::{Error, ErrorKind, Result, ResultExt};

#[macro_use]
mod util;

pub mod configuration;
mod db;
mod scheduling;

#[allow(unused_doc_comment)]
mod errors {
    use scheduling;

    error_chain! {
        links {
            Schedule(scheduling::Error, scheduling::ErrorKind);
        }
        errors {
            Parse(what: String, how_it_should_be: String) {
                description("parse error")
                display("I could not parse the {}. {}", what, how_it_should_be)
            }
            Database(when: String) {
                description("database error")
                display("A database error occurred {}", when)
            }
            Internal(more_info: String) {
                description("internal error")
                display("An internal error occurred (This shouldn't happen.): {}", more_info)
            }
        }
    }
}

#[derive(Debug, Eq, new, Clone)]
pub struct Task {
    id: Option<u32>,
    content: String,
    deadline: DateTime<Local>,
    duration: Duration,
    importance: u32,
}

impl PartialEq for Task {
    fn eq(&self, other: &Self) -> bool {
        let equal_id = match (self.id, other.id) {
            (Some(id1), Some(id2)) => id1 == id2,
            _ => true,
        };
        equal_id &&
            self.content == other.content &&
            self.deadline == other.deadline &&
            self.duration == other.duration &&
            self.importance == other.importance
    }
}

// Hack because chrono::Duration, which is a re-export of std::time::Duration, does not re-export
// implementation of Hash trait.
impl Hash for Task {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.id.hash(state);
        self.content.hash(state);
        self.deadline.hash(state);
        self.duration.to_std()
            .expect(&format!("Internal error: duration of {} was negative", self))
            .hash(state);
        self.importance.hash(state);
    }
}

pub fn add(configuration: &Configuration,
           content: &str,
           deadline: &str,
           duration: &str,
           importance: u32)
    -> Result<()>
{
    use db::tasks::dsl::tasks;

    let connection = db::make_connection(configuration)?;

    let deadline = parse_datetime(deadline)?;
    let duration = parse_duration(duration)?;
    let new_task = Task {
        id: None,
        content: content.to_string(),
        deadline,
        duration,
        importance,
    };

    diesel::insert(&new_task)
        .into(tasks)
        .execute(&connection)
        .chain_err(|| ErrorKind::Database("while trying to add a task".to_owned()))?;

    Ok(())
}

pub fn remove(configuration: &Configuration, id: u32) -> Result<()> {
    use db::tasks::dsl::tasks;

    let connection = db::make_connection(configuration)?;

    let amount_deleted =
        diesel::delete(tasks.find(id as i32))
        .execute(&connection)
        .chain_err(|| ErrorKind::Database("while trying to remove a task".to_owned()))?;

    if amount_deleted == 0 {
        bail!(ErrorKind::Database(format!("while trying to find the task with id {}", id)));
    } else if amount_deleted > 1 {
        bail!(ErrorKind::Internal("multiple tasks got deleted".to_owned()));
    }

    Ok(())
}

pub fn set(configuration: &Configuration, field_name: &str, id: u32, value: &str) -> Result<()> {
    assert!(["content", "deadline", "duration", "importance"].contains(&field_name));

    use db::tasks::dsl::tasks;

    let connection = db::make_connection(configuration)?;

    let mut task: Task = tasks.find(id as i32)
        .first(&connection)
        .chain_err(|| ErrorKind::Database("while trying to retrieve a task".to_owned()))?;

    match field_name {
        "content" => task.content = value.to_string(),
        "deadline" => task.deadline = parse_datetime(value)?,
        "duration" => task.duration = parse_duration(value)?,
        "importance" => task.importance = parse_importance(value)?,
        _ => unreachable!(),
    }

    let amount_updated = diesel::update(&task)
        .set(task)
        .execute(&connection)
        .chain_err(|| ErrorKind::Database("while trying to update a task".to_owned()))?;

    if amount_updated == 0 {
        bail!(ErrorKind::Database("while trying to update a task".to_owned()));
    } else if amount_updated > 1 {
        bail!(ErrorKind::Internal("multiple tasks got deleted".to_owned()));
    }

    Ok(())
}

pub fn print_schedule(configuration: &Configuration, strategy: &str) -> Result<()> {
    assert!(["importance", "urgency"].contains(&strategy));

    use db::tasks::dsl::tasks;

    let connection = db::make_connection(configuration)?;

    let tasks_ = tasks
        .load::<Task>(&connection)
        .chain_err(|| ErrorKind::Database("while trying to retrieve tasks".to_owned()))?;

    println!("Tasks:");
    for task in &tasks_ {
        println!("  {}", task);
    }
    println!();

    let schedule = match strategy {
        "importance" => Schedule::schedule_according_to_importance(&tasks_),
        "urgency" => Schedule::schedule_according_to_myrjam(&tasks_),
        _ => unreachable!(),
    }?;
    println!("{}", schedule);

    Ok(())
}

fn parse_datetime(datetime: &str) -> Result<DateTime<Local>> {
    Local.datetime_from_str(datetime, "%-d %b %Y %-H:%M")
        .chain_err(|| {
            ErrorKind::Parse("deadline".to_owned(),
                             "Please provide something like '4 Jul 2017 6:05'".to_owned())
        })
}

fn parse_importance(importance_str: &str) -> Result<u32> {
    importance_str.parse()
        .chain_err(|| ErrorKind::Parse("importance".to_owned(),
                                       "Please supply a valid integer".to_owned()))
}

fn parse_duration(duration_hours: &str) -> Result<Duration> {
    let hours: f64 = duration_hours.parse()
        .chain_err(|| ErrorKind::Parse("duration".to_owned(),
                                       "Please supply a valid, real number".to_owned()))?;
    if hours <= 0.0 {
        bail!(ErrorKind::Parse("duration".to_owned(),
                               "Please supply a positive number".to_owned()));
    }
    Ok(Duration::minutes((60.0 * hours) as i64))
}
<commit_msg>New APIs<commit_after>#![feature(box_patterns)]

extern crate chrono;
#[macro_use]
extern crate derive_new;
#[macro_use]
extern crate diesel;
#[macro_use]
extern crate diesel_codegen;
#[macro_use]
extern crate error_chain;
extern crate itertools;
#[macro_use]
extern crate lazy_static;
extern crate take_mut;

#[cfg(test)]
#[macro_use]
extern crate assert_matches;

use std::hash::{Hash, Hasher};

use chrono::prelude::*;
use chrono::Duration;
use diesel::prelude::*;

use configuration::Configuration;
use scheduling::Schedule;

pub use errors::{Error, ErrorKind, Result, ResultExt};

#[macro_use]
mod util;

pub mod configuration;
mod db;
mod scheduling;

#[allow(unused_doc_comment)]
mod errors {
    use scheduling;

    error_chain! {
        links {
            Schedule(scheduling::Error, scheduling::ErrorKind);
        }
        errors {
            Parse(what: String, how_it_should_be: String) {
                description("parse error")
                display("I could not parse the {}. {}", what, how_it_should_be)
            }
            Database(when: String) {
                description("database error")
                display("A database error occurred {}", when)
            }
            Internal(more_info: String) {
                description("internal error")
                display("An internal error occurred (This shouldn't happen.): {}", more_info)
            }
        }
    }
}

#[derive(Debug, Eq, new, Clone)]
pub struct Task {
    pub id: Option<u32>,
    pub content: String,
    pub deadline: DateTime<Local>,
    pub duration: Duration,
    pub importance: u32,
}

impl PartialEq for Task {
    fn eq(&self, other: &Self) -> bool {
        let equal_id = match (self.id, other.id) {
            (Some(id1), Some(id2)) => id1 == id2,
            _ => true,
        };
        equal_id &&
            self.content == other.content &&
            self.deadline == other.deadline &&
            self.duration == other.duration &&
            self.importance == other.importance
    }
}

// Hack because chrono::Duration, which is a re-export of std::time::Duration, does not re-export
// implementation of Hash trait.
impl Hash for Task {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.id.hash(state);
        self.content.hash(state);
        self.deadline.hash(state);
        self.duration.to_std()
            .expect(&format!("Internal error: duration of {} was negative", self))
            .hash(state);
        self.importance.hash(state);
    }
}

pub fn add(configuration: &Configuration,
           content: &str,
           deadline: &str,
           duration: &str,
           importance: u32)
    -> Result<()>
{
    use db::tasks::dsl::tasks;

    let connection = db::make_connection(configuration)?;

    let deadline = parse_datetime(deadline)?;
    let duration = parse_duration(duration)?;
    let new_task = Task {
        id: None,
        content: content.to_string(),
        deadline,
        duration,
        importance,
    };

    diesel::insert(&new_task)
        .into(tasks)
        .execute(&connection)
        .chain_err(|| ErrorKind::Database("while trying to add a task".to_owned()))?;

    Ok(())
}

pub fn remove(configuration: &Configuration, id: u32) -> Result<()> {
    use db::tasks::dsl::tasks;

    let connection = db::make_connection(configuration)?;

    let amount_deleted =
        diesel::delete(tasks.find(id as i32))
        .execute(&connection)
        .chain_err(|| ErrorKind::Database("while trying to remove a task".to_owned()))?;

    if amount_deleted == 0 {
        bail!(ErrorKind::Database(format!("while trying to find the task with id {}", id)));
    } else if amount_deleted > 1 {
        bail!(ErrorKind::Internal("multiple tasks got deleted".to_owned()));
    }

    Ok(())
}

pub fn set(configuration: &Configuration, field_name: &str, id: u32, value: &str) -> Result<()> {
    assert!(["content", "deadline", "duration", "importance"].contains(&field_name));

    use db::tasks::dsl::tasks;

    let connection = db::make_connection(configuration)?;

    let mut task: Task = tasks.find(id as i32)
        .first(&connection)
        .chain_err(|| ErrorKind::Database("while trying to retrieve a task".to_owned()))?;

    match field_name {
        "content" => task.content = value.to_string(),
        "deadline" => task.deadline = parse_datetime(value)?,
        "duration" => task.duration = parse_duration(value)?,
        "importance" => task.importance = parse_importance(value)?,
        _ => unreachable!(),
    }

    let amount_updated = diesel::update(&task)
        .set(task)
        .execute(&connection)
        .chain_err(|| ErrorKind::Database("while trying to update a task".to_owned()))?;

    if amount_updated == 0 {
        bail!(ErrorKind::Database("while trying to update a task".to_owned()));
    } else if amount_updated > 1 {
        bail!(ErrorKind::Internal("multiple tasks got deleted".to_owned()));
    }

    Ok(())
}

pub fn list_tasks(configuration: &Configuration) -> Result<Vec<Task>> {
    use db::tasks::dsl::tasks;

    let connection = db::make_connection(configuration)?;

    Ok(tasks.load::<Task>(&connection)
        .chain_err(|| ErrorKind::Database("while trying to retrieve tasks".to_owned()))?)
}

pub fn print_schedule(configuration: &Configuration, strategy: &str) -> Result<()> {
    assert!(["importance", "urgency"].contains(&strategy));

    use db::tasks::dsl::tasks;

    let connection = db::make_connection(configuration)?;

    let tasks_ = tasks
        .load::<Task>(&connection)
        .chain_err(|| ErrorKind::Database("while trying to retrieve tasks".to_owned()))?;

    println!("Tasks:");
    for task in &tasks_ {
        println!("  {}", task);
    }
    println!();

    let schedule = match strategy {
        "importance" => Schedule::schedule_according_to_importance(&tasks_),
        "urgency" => Schedule::schedule_according_to_myrjam(&tasks_),
        _ => unreachable!(),
    }?;
    println!("{}", schedule);

    Ok(())
}

fn parse_datetime(datetime: &str) -> Result<DateTime<Local>> {
    Local.datetime_from_str(datetime, "%-d %b %Y %-H:%M")
        .chain_err(|| {
            ErrorKind::Parse("deadline".to_owned(),
                             "Please provide something like '4 Jul 2017 6:05'".to_owned())
        })
}

fn parse_importance(importance_str: &str) -> Result<u32> {
    importance_str.parse()
        .chain_err(|| ErrorKind::Parse("importance".to_owned(),
                                       "Please supply a valid integer".to_owned()))
}

fn parse_duration(duration_hours: &str) -> Result<Duration> {
    let hours: f64 = duration_hours.parse()
        .chain_err(|| ErrorKind::Parse("duration".to_owned(),
                                       "Please supply a valid, real number".to_owned()))?;
    if hours <= 0.0 {
        bail!(ErrorKind::Parse("duration".to_owned(),
                               "Please supply a positive number".to_owned()));
    }
    Ok(Duration::minutes((60.0 * hours) as i64))
}
<|endoftext|>


<commit_before>#![allow(non_snake_case)]
#![allow(unused_doc_comments)]

#[macro_use]
extern crate error_chain;
#[macro_use]
extern crate serde_derive;

pub mod agent;
pub mod catalog;
pub mod connect_ca;
pub mod errors;
pub mod health;
pub mod kv;
pub mod session;

mod request;

use std::env;

use std::time::Duration;

use reqwest::blocking::Client as HttpClient;
use reqwest::blocking::ClientBuilder;

use errors::{Result, ResultExt};

#[derive(Clone, Debug)]
pub struct Client {
    config: Config,
}

impl Client {
    pub fn new(config: Config) -> Self {
        Client { config }
    }
}

#[derive(Clone, Debug)]
pub struct Config {
    pub address: String,
    pub datacenter: Option<String>,
    pub http_client: HttpClient,
    pub token: Option<String>,
    pub wait_time: Option<Duration>,
}

impl Config {
    pub fn new() -> Result<Config> {
        ClientBuilder::new()
            .build()
            .chain_err(|| "Failed to build reqwest client")
            .map(|client| Config {
                address: String::from("http://localhost:8500"),
                datacenter: None,
                http_client: client,
                token: None,
                wait_time: None,
            })
    }

    pub fn new_from_env() -> Result<Config> {
        let consul_addr = match env::var("CONSUL_HTTP_ADDR") {
            Ok(val) => {
                if val.starts_with("http") {
                    val
                } else {
                    format!("http://{}", val)
                }
            }
            Err(_e) => String::from("http://127.0.0.1:8500"),
        };
        let consul_token = env::var("CONSUL_HTTP_TOKEN").ok();
        ClientBuilder::new()
            .build()
            .chain_err(|| "Failed to build reqwest client")
            .map(|client| Config {
                address: consul_addr,
                datacenter: None,
                http_client: client,
                token: consul_token,
                wait_time: None,
            })
    }
}

#[derive(Clone, Debug, Default)]
pub struct QueryOptions {
    pub datacenter: Option<String>,
    pub wait_index: Option<u64>,
    pub wait_time: Option<Duration>,
}

#[derive(Clone, Debug)]
pub struct QueryMeta {
    pub last_index: Option<u64>,
    pub request_time: Duration,
}

#[derive(Clone, Debug, Default)]
pub struct WriteOptions {
    pub datacenter: Option<String>,
}

#[derive(Clone, Debug)]
pub struct WriteMeta {
    pub request_time: Duration,
}
<commit_msg>Add Config::new_from_consul_host()<commit_after>#![allow(non_snake_case)]
#![allow(unused_doc_comments)]

#[macro_use]
extern crate error_chain;
#[macro_use]
extern crate serde_derive;

pub mod agent;
pub mod catalog;
pub mod connect_ca;
pub mod errors;
pub mod health;
pub mod kv;
pub mod session;

mod request;

use std::env;

use std::time::Duration;

use reqwest::blocking::Client as HttpClient;
use reqwest::blocking::ClientBuilder;

use errors::{Result, ResultExt};

#[derive(Clone, Debug)]
pub struct Client {
    config: Config,
}

impl Client {
    pub fn new(config: Config) -> Self {
        Client { config }
    }
}

#[derive(Clone, Debug)]
pub struct Config {
    pub address: String,
    pub datacenter: Option<String>,
    pub http_client: HttpClient,
    pub token: Option<String>,
    pub wait_time: Option<Duration>,
}

impl Config {
    pub fn new() -> Result<Config> {
        ClientBuilder::new()
            .build()
            .chain_err(|| "Failed to build reqwest client")
            .map(|client| Config {
                address: String::from("http://localhost:8500"),
                datacenter: None,
                http_client: client,
                token: None,
                wait_time: None,
            })
    }

    pub fn new_from_env() -> Result<Config> {
        let consul_addr = match env::var("CONSUL_HTTP_ADDR") {
            Ok(val) => {
                if val.starts_with("http") {
                    val
                } else {
                    format!("http://{}", val)
                }
            }
            Err(_e) => String::from("http://127.0.0.1:8500"),
        };
        let consul_token = env::var("CONSUL_HTTP_TOKEN").ok();
        ClientBuilder::new()
            .build()
            .chain_err(|| "Failed to build reqwest client")
            .map(|client| Config {
                address: consul_addr,
                datacenter: None,
                http_client: client,
                token: consul_token,
                wait_time: None,
            })
    }

    pub fn new_from_consul_host(
        host: &str,
        port: Option<u16>,
        token: Option<String>,
    ) -> Result<Config> {
        ClientBuilder::new()
            .build()
            .chain_err(|| "Failed to build reqwest client")
            .map(|client| Config {
                address: format!("{}:{}", host, port.unwrap_or(8500)),
                datacenter: None,
                http_client: client,
                token,
                wait_time: None,
            })
    }
}

#[derive(Clone, Debug, Default)]
pub struct QueryOptions {
    pub datacenter: Option<String>,
    pub wait_index: Option<u64>,
    pub wait_time: Option<Duration>,
}

#[derive(Clone, Debug)]
pub struct QueryMeta {
    pub last_index: Option<u64>,
    pub request_time: Duration,
}

#[derive(Clone, Debug, Default)]
pub struct WriteOptions {
    pub datacenter: Option<String>,
}

#[derive(Clone, Debug)]
pub struct WriteMeta {
    pub request_time: Duration,
}
<|endoftext|>


<commit_before>extern crate xml;

use xml::{Element, Xml};

fn elem_with_text(tag_name: &'static str, chars: &str) -> Element {
    let mut elem = Element::new(tag_name, None, &[]);
    elem.text(chars);
    elem
}

trait ViaXml {
    fn to_xml(&self) -> Element;
}

/// RSS version 2.0
#[derive(Default)]
pub struct Rss(pub Vec<Channel>);

impl ViaXml for Rss {
    fn to_xml(&self) -> Element {
        let mut rss = Element::new("rss", None, &[("version", None, "2.0")]);

        let &Rss(ref channels) = self;
        for channel in channels {
            rss.tag(channel.to_xml());
        }

        rss
    }
}

impl Rss {
    fn to_string(&self) -> String {
        let mut ret = format!("{}", Xml::PINode("xml version='1.0' encoding='UTF-8'".to_string()));
        ret.push_str(&format!("{}", self.to_xml()));
        ret
    }
}

#[derive(Default)]
pub struct Channel {
    pub title: String,
    pub link: String,
    pub description: String,
    pub items: Vec<Item>,
}

impl ViaXml for Channel {
    fn to_xml(&self) -> Element {
        let mut channel = Element::new("channel", None, &[]);

        channel.tag(elem_with_text("title", &self.title));
        channel.tag(elem_with_text("link", &self.link));
        channel.tag(elem_with_text("description", &self.description));

        for item in &self.items {
            channel.tag(item.to_xml());
        }

        channel
    }
}

#[derive(Default)]
pub struct Item {
    pub title: Option<String>,
    pub link: Option<String>,
    pub description: Option<String>,
}

impl ViaXml for Item {
    fn to_xml(&self) -> Element {
        let mut item = Element::new("item", None, &[]);

        if let Some(ref s) = self.title {
            item.tag(elem_with_text("title", s));
        }

        if let Some(ref s) = self.link {
            item.tag(elem_with_text("link", s));
        }

        if let Some(ref s) = self.description {
            item.tag(elem_with_text("description", s));
        }

        item
    }
}

#[test]
fn test_consruct() {
    #![feature(collections)]

    let item = Item {
        title: Some(String::from_str("My first post!")),
        link: Some(String::from_str("http://myblog.com/post1")),
        description: Some(String::from_str("This is my first post")),
    };

    let channel = Channel {
        title: String::from_str("My Blog"),
        link: String::from_str("http://myblog.com"),
        description: String::from_str("Where I write stuff"),
        items: vec![item],
    };

    let rss = Rss(vec![channel]);
    assert_eq!(rss.to_string(), "<?xml version=\'1.0\' encoding=\'UTF-8\'?><rss version=\'2.0\'><channel><title>My Blog</title><link>http://myblog.com</link><description>Where I write stuff</description><item><title>My first post!</title><link>http://myblog.com/post1</link><description>This is my first post</description></item></channel></rss>");
}
<commit_msg>Rename test to better reflect action<commit_after>extern crate xml;

use xml::{Element, Xml};

fn elem_with_text(tag_name: &'static str, chars: &str) -> Element {
    let mut elem = Element::new(tag_name, None, &[]);
    elem.text(chars);
    elem
}

trait ViaXml {
    fn to_xml(&self) -> Element;
}

/// RSS version 2.0
#[derive(Default)]
pub struct Rss(pub Vec<Channel>);

impl ViaXml for Rss {
    fn to_xml(&self) -> Element {
        let mut rss = Element::new("rss", None, &[("version", None, "2.0")]);

        let &Rss(ref channels) = self;
        for channel in channels {
            rss.tag(channel.to_xml());
        }

        rss
    }
}

impl Rss {
    fn to_string(&self) -> String {
        let mut ret = format!("{}", Xml::PINode("xml version='1.0' encoding='UTF-8'".to_string()));
        ret.push_str(&format!("{}", self.to_xml()));
        ret
    }
}

#[derive(Default)]
pub struct Channel {
    pub title: String,
    pub link: String,
    pub description: String,
    pub items: Vec<Item>,
}

impl ViaXml for Channel {
    fn to_xml(&self) -> Element {
        let mut channel = Element::new("channel", None, &[]);

        channel.tag(elem_with_text("title", &self.title));
        channel.tag(elem_with_text("link", &self.link));
        channel.tag(elem_with_text("description", &self.description));

        for item in &self.items {
            channel.tag(item.to_xml());
        }

        channel
    }
}

#[derive(Default)]
pub struct Item {
    pub title: Option<String>,
    pub link: Option<String>,
    pub description: Option<String>,
}

impl ViaXml for Item {
    fn to_xml(&self) -> Element {
        let mut item = Element::new("item", None, &[]);

        if let Some(ref s) = self.title {
            item.tag(elem_with_text("title", s));
        }

        if let Some(ref s) = self.link {
            item.tag(elem_with_text("link", s));
        }

        if let Some(ref s) = self.description {
            item.tag(elem_with_text("description", s));
        }

        item
    }
}

#[test]
fn test_basic_to_string() {
    #![feature(collections)]

    let item = Item {
        title: Some(String::from_str("My first post!")),
        link: Some(String::from_str("http://myblog.com/post1")),
        description: Some(String::from_str("This is my first post")),
    };

    let channel = Channel {
        title: String::from_str("My Blog"),
        link: String::from_str("http://myblog.com"),
        description: String::from_str("Where I write stuff"),
        items: vec![item],
    };

    let rss = Rss(vec![channel]);
    assert_eq!(rss.to_string(), "<?xml version=\'1.0\' encoding=\'UTF-8\'?><rss version=\'2.0\'><channel><title>My Blog</title><link>http://myblog.com</link><description>Where I write stuff</description><item><title>My first post!</title><link>http://myblog.com/post1</link><description>This is my first post</description></item></channel></rss>");
}
<|endoftext|>


<commit_before>#![feature(const_fn)]
#![feature(custom_derive, plugin)]
#![plugin(serde_macros)]

extern crate serde;
extern crate serde_json;

use serde::Deserialize;
use std::fmt;
use std::io::{self, Read};
use std::convert;
use std::collections::HashMap;
use std::net::{
    TcpListener,
    TcpStream,
    SocketAddr,
};
use std::sync::{
    self,
    Mutex,
    Arc,
};
use std::sync::mpsc::{
    channel,
    Sender,
    Receiver,
    TryRecvError,
};
use std::thread::{
    self,
    JoinHandle,
};

#[derive(Debug)]
pub enum Error {
    Io(io::Error),
    Json(serde_json::Error),
    Sender,
    Unimplemented,
    Impossible
}

impl convert::From<serde_json::Error> for Error {
    fn from(err: serde_json::Error) -> Error {
        match err {
            serde_json::Error::IoError(err) => Error::Io(err),
            err => Error::Json(err),
        }
    }
}

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

impl<T> convert::From<sync::mpsc::SendError<T>> for Error {
    fn from(_: sync::mpsc::SendError<T>) -> Error {
        Error::Sender
    }
}

pub type Result<T> = std::result::Result<T, Error>;

pub fn handle_conn<F, Request, Reply>(mut stream: TcpStream, f: Arc<F>) -> Result<()>
    where Request: fmt::Debug + serde::de::Deserialize + serde::ser::Serialize,
          Reply: fmt::Debug + serde::ser::Serialize,
          F: Serve<Request, Reply>
{
    let read_stream = try!(stream.try_clone());
    let mut de = serde_json::Deserializer::new(read_stream.bytes());
    loop {
        println!("read");
        let request_packet: Packet<Request> = try!(Packet::deserialize(&mut de));
        match request_packet {
            Packet::Shutdown => {
                println!("server shutting down");
                try!(serde_json::to_writer(&mut stream, &request_packet));
                break;
            },
            Packet::Message(id, message) => {
                let reply = try!(f.serve(&message));
                let reply_packet = Packet::Message(id, reply);
                try!(serde_json::to_writer(&mut stream, &reply_packet));
            },
        }
    }
    Ok(())
}

pub struct Shutdown {
    tx: Sender<()>,
    join_handle: JoinHandle<()>,
    addr: SocketAddr,
}

impl Shutdown {
    fn wait(self) {
        self.join_handle.join().unwrap();
    }

    fn shutdown(self) {
        self.tx.send(()).expect(&line!().to_string());
        TcpStream::connect(&self.addr).unwrap();
        self.join_handle.join().expect(&line!().to_string());
    }
}

pub fn serve_async<F, Request, Reply>(addr: &SocketAddr, f: Arc<F>) -> io::Result<Shutdown>
    where Request: fmt::Debug + serde::de::Deserialize + fmt::Debug + serde::ser::Serialize,
          Reply: fmt::Debug + serde::ser::Serialize,
          F: 'static + Serve<Request, Reply>,
{
    let listener = try!(TcpListener::bind(addr));
    let (die_tx, die_rx) = channel();
    let join_handle = thread::spawn(move || {
        for conn in listener.incoming() {
            match die_rx.try_recv() {
                Ok(_) => break,
                Err(TryRecvError::Disconnected) => {
                    println!("serve: sender disconnected ");
                    break;
                },
                _ => (),
            }
            let conn = match conn {
                Err(err) => {
                    println!("I couldn't unwrap the connection :( {:?}", err);
                    return;
                },
                Ok(c) => c,
            };
            let f = f.clone();
            thread::spawn(move || {
                if let Err(err) = handle_conn(conn, f) {
                    println!("error handling connection: {:?}", err);
                }
            });
        }
    });
    Ok(Shutdown{
        tx: die_tx,
        join_handle: join_handle,
        addr: addr.clone(),
    })
}

pub trait Serve<Request, Reply>: Send + Sync {
    fn serve(&self, request: &Request) -> io::Result<Reply>;
}

#[derive(Debug, Clone, Serialize, Deserialize)]
enum Packet<T> {
    Message(u64, T),
    Shutdown,
}

fn reader<Reply>(
    stream: TcpStream,
    requests: Arc<Mutex<HashMap<u64, Sender<Reply>>>>)
    where Reply: serde::Deserialize
{
    let mut de = serde_json::Deserializer::new(stream.bytes());
    loop {
        match Packet::deserialize(&mut de) {
            Ok(Packet::Message(id, reply)) => {
                let mut requests = requests.lock().unwrap();
                let reply_tx = requests.remove(&id).unwrap();
                reply_tx.send(reply).unwrap();
            },
            Ok(Packet::Shutdown) => {
                println!("reader shutting down");
                break;
            }
            // TODO: This shutdown logic is janky.. What's the right way to do this?
            Err(err) => panic!("unexpected error while parsing!: {:?}", err),
        }
    }
}

fn increment(cur_id: &mut u64) -> u64 {
    let id = *cur_id;
    *cur_id += 1;
    id
}

struct SyncedClientState {
    next_id: u64,
    stream: TcpStream,
}

pub struct Client<Reply> {
    synced_state: Mutex<SyncedClientState>,
    requests: Arc<Mutex<HashMap<u64, Sender<Reply>>>>,
    reader_guard: thread::JoinHandle<()>,
}

impl<Reply> Client<Reply>
    where Reply: serde::de::Deserialize + Send + 'static
{
    pub fn new(stream: TcpStream) -> Result<Self> {
        let requests = Arc::new(Mutex::new(HashMap::new()));
        let reader_stream = try!(stream.try_clone());
        let reader_requests = requests.clone();
        let reader_guard =
            thread::spawn(move || reader(reader_stream, reader_requests));
        Ok(Client{
            synced_state: Mutex::new(SyncedClientState{
                next_id: 0,
                stream: stream,
            }),
            requests: requests,
            reader_guard: reader_guard,
        })
    }

    pub fn rpc<Request>(&self, request: &Request) -> Result<Reply>
        where Request: serde::ser::Serialize + Clone + Send + 'static
    {
        let (tx, rx) = channel();
        let mut state = self.synced_state.lock().unwrap();
        let id = increment(&mut state.next_id);
        {
            let mut requests = self.requests.lock().unwrap();
            requests.insert(id, tx);
        }
        let packet = Packet::Message(id, request.clone());
        try!(serde_json::to_writer(&mut state.stream, &packet));
        Ok(rx.recv().unwrap())
    }

    pub fn disconnect<Request: serde::Serialize>(self) -> Result<()> {
        {
            let mut state = self.synced_state.lock().unwrap();
            let packet: Packet<Request> = Packet::Shutdown;
            try!(serde_json::to_writer(&mut state.stream, &packet));
        }
        self.reader_guard.join().unwrap();
        Ok(())
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use std::io;
    use std::net::{TcpStream, TcpListener, SocketAddr, ToSocketAddrs};
    use std::str::FromStr;
    use std::sync::{Arc, Mutex, Barrier};
    use std::sync::mpsc::channel;
    use std::sync::atomic::{AtomicUsize, Ordering};
    use std::thread;

    const port: AtomicUsize = AtomicUsize::new(10000);

    fn next_addr() -> SocketAddr {
        let addr = format!("127.0.0.1:{}", port.fetch_add(1, Ordering::SeqCst));
        addr.to_socket_addrs().unwrap().next().unwrap()
        //ToSocketAddrs::to_socket_addrs(addr.as_ref()).unwrap().next().unwrap()
    }

    fn pair() -> (TcpStream, TcpListener) {
        let addr = format!("127.0.0.1:{}", port.fetch_add(1, Ordering::SeqCst));
        println!("what the fuck {}", &addr);
        // Do this one first so that we don't get connection refused :)
        let listener = TcpListener::bind(&*addr).unwrap();
        (TcpStream::connect(&*addr).unwrap(), listener)
    }

    #[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
    enum Request {
        Increment
    }

    #[derive(Debug, PartialEq, Serialize, Deserialize)]
    enum Reply {
        Increment(u64)
    }

    struct Server {
        counter: Mutex<u64>,
    }

    impl Serve<Request, Reply> for Server {
        fn serve(&self, _: &Request) -> io::Result<Reply> {
            let mut counter = self.counter.lock().unwrap();
            let reply = Reply::Increment(*counter);
            *counter += 1;
            Ok(reply)
        }
    }

    impl Server {
        fn new() -> Server {
            Server{counter: Mutex::new(0)}
        }

        fn count(&self) -> u64 {
            *self.counter.lock().unwrap()
        }
    }

    #[test]
    fn test_handle() {
        let addr = next_addr();
        let server = Arc::new(Server::new());
        let srv_shutdown = serve_async(&addr, server).unwrap();
        let client_stream = TcpStream::connect(&addr).unwrap();
        let client: Client<Reply> = Client::new(client_stream).expect(&line!().to_string());
        client.disconnect::<Request>();
        srv_shutdown.shutdown();
    }

    /*
    #[test]
    fn test() {
        let (client_stream, server_streams) = pair();
        let server = Arc::new(Server::new());
        let thread_server = server.clone();
        let guard = thread::spawn(move || serve(server_streams, thread_server));
        let client = Client::new(client_stream).unwrap();
        assert_eq!(Reply::Increment(0), client.rpc(&Request::Increment).unwrap());
        assert_eq!(1, server.count());
        assert_eq!(Reply::Increment(1), client.rpc(&Request::Increment).unwrap());
        assert_eq!(2, server.count());
        println!("joining client");
        client.join::<Request>().unwrap();
        println!("joining server");
        guard.join();
    }

    struct BarrierServer {
        barrier: Barrier,
        inner: Server,
    }

    impl Serve<Request, Reply> for BarrierServer {
        fn serve(&self, request: &Request) -> io::Result<Reply> {
            self.barrier.wait();
            let reply = try!(self.inner.serve(request));
            Ok(reply)
        }
    }

    impl BarrierServer {
        fn new(n: usize) -> BarrierServer {
            BarrierServer{barrier: Barrier::new(n), inner: Server::new()}
        }

        fn count(&self) -> u64 {
            self.inner.count()
        }
    }

    #[test]
    fn test_concurrent() {
        let (client_stream, server_streams) = pair();
        let server = Arc::new(BarrierServer::new(10));
        let thread_server = server.clone();
        let guard = thread::spawn(move || serve(server_streams, thread_server));
        let client: Arc<Client<Reply>> = Arc::new(Client::new(client_stream).unwrap());
        let mut join_handles = vec![];
        for _ in 0..10 {
            let my_client = client.clone();
            join_handles.push(thread::spawn(move || my_client.rpc(&Request::Increment).unwrap()));
        }
        for handle in join_handles.into_iter() {
            handle.join();
        }
        assert_eq!(10, server.count());
        let client = match Arc::try_unwrap(client) {
            Err(_) => panic!("couldn't unwrap arc"),
            Ok(c) => c,
        };
        client.join::<Request>().unwrap();
        guard.join();
    }
    */
}
<commit_msg>Tests pass, hooray<commit_after>#![feature(const_fn)]
#![feature(custom_derive, plugin)]
#![plugin(serde_macros)]

extern crate serde;
extern crate serde_json;

use serde::Deserialize;
use std::fmt;
use std::io::{self, Read};
use std::convert;
use std::collections::HashMap;
use std::net::{
    TcpListener,
    TcpStream,
    SocketAddr,
};
use std::sync::{
    self,
    Mutex,
    Arc,
};
use std::sync::mpsc::{
    channel,
    Sender,
    Receiver,
    TryRecvError,
};
use std::thread::{
    self,
    JoinHandle,
};

#[derive(Debug)]
pub enum Error {
    Io(io::Error),
    Json(serde_json::Error),
    Sender,
    Unimplemented,
    Impossible
}

impl convert::From<serde_json::Error> for Error {
    fn from(err: serde_json::Error) -> Error {
        match err {
            serde_json::Error::IoError(err) => Error::Io(err),
            err => Error::Json(err),
        }
    }
}

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

impl<T> convert::From<sync::mpsc::SendError<T>> for Error {
    fn from(_: sync::mpsc::SendError<T>) -> Error {
        Error::Sender
    }
}

pub type Result<T> = std::result::Result<T, Error>;

pub fn handle_conn<F, Request, Reply>(mut stream: TcpStream, f: F) -> Result<()>
    where Request: fmt::Debug + serde::de::Deserialize + serde::ser::Serialize,
          Reply: fmt::Debug + serde::ser::Serialize,
          F: Serve<Request, Reply>
{
    let read_stream = try!(stream.try_clone());
    let mut de = serde_json::Deserializer::new(read_stream.bytes());
    loop {
        let request_packet: Packet<Request> = try!(Packet::deserialize(&mut de));
        match request_packet {
            Packet::Shutdown => {
                try!(serde_json::to_writer(&mut stream, &request_packet));
                break;
            },
            Packet::Message(id, message) => {
                let reply = try!(f.serve(&message));
                let reply_packet = Packet::Message(id, reply);
                try!(serde_json::to_writer(&mut stream, &reply_packet));
            },
        }
    }
    Ok(())
}

pub struct Shutdown {
    tx: Sender<()>,
    join_handle: JoinHandle<()>,
    addr: SocketAddr,
}

impl Shutdown {
    fn wait(self) {
        self.join_handle.join().unwrap();
    }

    fn shutdown(self) {
        self.tx.send(()).expect(&line!().to_string());
        TcpStream::connect(&self.addr).unwrap();
        self.join_handle.join().expect(&line!().to_string());
    }
}

pub fn serve_async<F, Request, Reply>(addr: &SocketAddr, f: F) -> io::Result<Shutdown>
    where Request: fmt::Debug + serde::de::Deserialize + fmt::Debug + serde::ser::Serialize,
          Reply: fmt::Debug + serde::ser::Serialize,
          F: 'static + Clone + Serve<Request, Reply>,
{
    let listener = try!(TcpListener::bind(addr));
    let (die_tx, die_rx) = channel();
    let join_handle = thread::spawn(move || {
        for conn in listener.incoming() {
            match die_rx.try_recv() {
                Ok(_) => break,
                Err(TryRecvError::Disconnected) => {
                    println!("serve: sender disconnected ");
                    break;
                },
                _ => (),
            }
            let conn = match conn {
                Err(err) => {
                    println!("I couldn't unwrap the connection :( {:?}", err);
                    return;
                },
                Ok(c) => c,
            };
            let f = f.clone();
            thread::spawn(move || {
                if let Err(err) = handle_conn(conn, f) {
                    println!("error handling connection: {:?}", err);
                }
            });
        }
    });
    Ok(Shutdown{
        tx: die_tx,
        join_handle: join_handle,
        addr: addr.clone(),
    })
}

pub trait Serve<Request, Reply>: Send + Sync {
    fn serve(&self, request: &Request) -> io::Result<Reply>;
}

impl<Request, Reply, S> Serve<Request, Reply> for Arc<S>
    where S: Serve<Request, Reply>
{
    fn serve(&self, request: &Request) -> io::Result<Reply> {
        S::serve(self, request)
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
enum Packet<T> {
    Message(u64, T),
    Shutdown,
}

fn reader<Reply>(
    stream: TcpStream,
    requests: Arc<Mutex<HashMap<u64, Sender<Reply>>>>)
    where Reply: serde::Deserialize
{
    let mut de = serde_json::Deserializer::new(stream.bytes());
    loop {
        match Packet::deserialize(&mut de) {
            Ok(Packet::Message(id, reply)) => {
                let mut requests = requests.lock().unwrap();
                let reply_tx = requests.remove(&id).unwrap();
                reply_tx.send(reply).unwrap();
            },
            Ok(Packet::Shutdown) => {
                break;
            }
            // TODO: This shutdown logic is janky.. What's the right way to do this?
            Err(err) => panic!("unexpected error while parsing!: {:?}", err),
        }
    }
}

fn increment(cur_id: &mut u64) -> u64 {
    let id = *cur_id;
    *cur_id += 1;
    id
}

struct SyncedClientState {
    next_id: u64,
    stream: TcpStream,
}

pub struct Client<Reply> {
    synced_state: Mutex<SyncedClientState>,
    requests: Arc<Mutex<HashMap<u64, Sender<Reply>>>>,
    reader_guard: thread::JoinHandle<()>,
}

impl<Reply> Client<Reply>
    where Reply: serde::de::Deserialize + Send + 'static
{
    pub fn new(stream: TcpStream) -> Result<Self> {
        let requests = Arc::new(Mutex::new(HashMap::new()));
        let reader_stream = try!(stream.try_clone());
        let reader_requests = requests.clone();
        let reader_guard =
            thread::spawn(move || reader(reader_stream, reader_requests));
        Ok(Client{
            synced_state: Mutex::new(SyncedClientState{
                next_id: 0,
                stream: stream,
            }),
            requests: requests,
            reader_guard: reader_guard,
        })
    }

    pub fn rpc<Request>(&self, request: &Request) -> Result<Reply>
        where Request: serde::ser::Serialize + Clone + Send + 'static
    {
        let (tx, rx) = channel();
        let mut state = self.synced_state.lock().unwrap();
        let id = increment(&mut state.next_id);
        {
            let mut requests = self.requests.lock().unwrap();
            requests.insert(id, tx);
        }
        let packet = Packet::Message(id, request.clone());
        try!(serde_json::to_writer(&mut state.stream, &packet));
        Ok(rx.recv().unwrap())
    }

    pub fn disconnect<Request: serde::Serialize>(self) -> Result<()> {
        {
            let mut state = self.synced_state.lock().unwrap();
            let packet: Packet<Request> = Packet::Shutdown;
            try!(serde_json::to_writer(&mut state.stream, &packet));
        }
        self.reader_guard.join().unwrap();
        Ok(())
    }
}

#[cfg(test)]
mod test {
    use serde;
    use super::*;
    use std::fmt;
    use std::io;
    use std::net::{TcpStream, TcpListener, SocketAddr, ToSocketAddrs};
    use std::str::FromStr;
    use std::sync::{Arc, Mutex, Barrier};
    use std::sync::mpsc::channel;
    use std::sync::atomic::{AtomicUsize, Ordering};
    use std::thread;

    const port: AtomicUsize = AtomicUsize::new(10000);

    fn next_addr() -> SocketAddr {
        let addr = format!("127.0.0.1:{}", port.fetch_add(1, Ordering::SeqCst));
        addr.to_socket_addrs().unwrap().next().unwrap()
        //ToSocketAddrs::to_socket_addrs(addr.as_ref()).unwrap().next().unwrap()
    }

    fn pair() -> (TcpStream, TcpListener) {
        let addr = format!("127.0.0.1:{}", port.fetch_add(1, Ordering::SeqCst));
        println!("what the fuck {}", &addr);
        // Do this one first so that we don't get connection refused :)
        let listener = TcpListener::bind(&*addr).unwrap();
        (TcpStream::connect(&*addr).unwrap(), listener)
    }

    #[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
    enum Request {
        Increment
    }

    #[derive(Debug, PartialEq, Serialize, Deserialize)]
    enum Reply {
        Increment(u64)
    }

    struct Server {
        counter: Mutex<u64>,
    }

    impl Serve<Request, Reply> for Server {
        fn serve(&self, _: &Request) -> io::Result<Reply> {
            let mut counter = self.counter.lock().unwrap();
            let reply = Reply::Increment(*counter);
            *counter += 1;
            Ok(reply)
        }
    }

    impl Server {
        fn new() -> Server {
            Server{counter: Mutex::new(0)}
        }

        fn count(&self) -> u64 {
            *self.counter.lock().unwrap()
        }
    }

    fn wtf<F, Request, Reply>(server: F) -> (SocketAddr, Shutdown)
        where Request: fmt::Debug + serde::de::Deserialize + fmt::Debug + serde::ser::Serialize,
              Reply: fmt::Debug + serde::ser::Serialize,
              F: 'static + Clone + Serve<Request, Reply>
    {
        let mut addr;
        let mut shutdown;
        while let &Err(_) = {shutdown = serve_async({addr = next_addr(); &addr}, server.clone()); &shutdown} { }
        (addr, shutdown.unwrap())
    }

    #[test]
    fn test_handle() {
        let server = Arc::new(Server::new());
        let (addr, shutdown) = wtf(server.clone());
        let client_stream = TcpStream::connect(&addr).unwrap();
        let client: Client<Reply> = Client::new(client_stream).expect(&line!().to_string());
        client.disconnect::<Request>();
        shutdown.shutdown();
    }

    #[test]
    fn test() {
        let server = Arc::new(Server::new());
        let (addr, shutdown) = wtf(server.clone());
        let client_stream = TcpStream::connect(&addr).unwrap();
        let client = Client::new(client_stream).unwrap();
        assert_eq!(Reply::Increment(0), client.rpc(&Request::Increment).unwrap());
        assert_eq!(1, server.count());
        assert_eq!(Reply::Increment(1), client.rpc(&Request::Increment).unwrap());
        assert_eq!(2, server.count());
        client.disconnect::<Request>().unwrap();
        shutdown.shutdown();
    }

    /*
    struct BarrierServer {
        barrier: Barrier,
        inner: Server,
    }

    impl Serve<Request, Reply> for BarrierServer {
        fn serve(&self, request: &Request) -> io::Result<Reply> {
            self.barrier.wait();
            let reply = try!(self.inner.serve(request));
            Ok(reply)
        }
    }

    impl BarrierServer {
        fn new(n: usize) -> BarrierServer {
            BarrierServer{barrier: Barrier::new(n), inner: Server::new()}
        }

        fn count(&self) -> u64 {
            self.inner.count()
        }
    }

    #[test]
    fn test_concurrent() {
        let (client_stream, server_streams) = pair();
        let server = Arc::new(BarrierServer::new(10));
        let thread_server = server.clone();
        let guard = thread::spawn(move || serve(server_streams, thread_server));
        let client: Arc<Client<Reply>> = Arc::new(Client::new(client_stream).unwrap());
        let mut join_handles = vec![];
        for _ in 0..10 {
            let my_client = client.clone();
            join_handles.push(thread::spawn(move || my_client.rpc(&Request::Increment).unwrap()));
        }
        for handle in join_handles.into_iter() {
            handle.join();
        }
        assert_eq!(10, server.count());
        let client = match Arc::try_unwrap(client) {
            Err(_) => panic!("couldn't unwrap arc"),
            Ok(c) => c,
        };
        client.join::<Request>().unwrap();
        guard.join();
    }
    */
}
<|endoftext|>