<commit_before>extern crate pkg_config;
use std::process::Command;
use std::path::PathBuf;
use std::env;
use std::fs;

fn lib_available(name: &str) -> bool {
    match pkg_config::find_library(name) {
        Ok(_) => true,
        Err(_) => {
            let res = Command::new("ldconfig").arg("--print-cache").output().unwrap();
            if res.status.success() {
                String::from_utf8(res.stdout).unwrap().contains(name)
            } else {
                false
            }
        }
    }
}

fn windows(_: String) {
    // TODO: Use precompiled binary!
    unimplemented!();
}

fn osx(_: String) {
    // TODO: I down't own any apple hardware to test this.
    unimplemented!();
}

fn linux(target: String) {
    if !lib_available("soundio") {
        build(target);
    } else {
        // TODO: remove else branch
        build(target);
    }
}

fn main() {
    let target = env::var("TARGET").unwrap();
    if target.contains("windows") {
        windows(target)
    } else if target.contains("apple") {
        osx(target)
    } else {
        // assume the rest is linux
        linux(target)
    }
}

const LIBSOUNDIO_TAR: &'static str = "http://libsound.io/release/libsoundio-1.1.0.tar.gz";
const LIBSOUNDIO_WIN: &'static str = "http://libsound.io/release/libsoundio-1.1.0.zip";

fn build(target: String) {
    let host = env::var("HOST").unwrap();
    let dst_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
    let lib_dir = dst_dir.join("lib");
    let include_dir = dst_dir.join("include");

    // set cargo flags
    println!("cargo:rustc-link-search=native={}", &lib_dir.display()); // -L
    println!("cargo:include={}", &include_dir.display());
    println!("cargo:root={}", &dst_dir.display());

    // download and extract libsoundio source
    Command::new("curl")
        .current_dir(&dst_dir)
        .args(&["--location", "--remote-name"])
        .arg(LIBSOUNDIO_TAR)
        .output()
        .unwrap();
    Command::new("tar")
        .current_dir(&dst_dir)
        .arg("-xvzf")
        .arg("libsoundio-1.1.0.tar.gz")
        .output()
        .unwrap();

    // create build dir
    let soundio_root = dst_dir.join("libsoundio-1.1.0");
    let build_dir = soundio_root.join("build");
    fs::create_dir(&build_dir).unwrap();

    // run cmake
    Command::new("cmake")
        .current_dir(&build_dir)
        .arg("-DCMAKE_BUILD_TYPE=Debug")
        .arg("-DCMAKE_INSTALL_LIBDIR:PATH=lib")
        .arg(format!("-DCMAKE_INSTALL_PREFIX:PATH={}", &dst_dir.display()))
        .arg("-DBUILD_EXAMPLE_PROGRAMS:BOOL=OFF")
        .arg("-DBUILD_TESTS:BOOL=OFF")
        .arg("-DBUILD_STATIC_LIBS:BOOL=OFF")
        .arg("-DBUILD_SHARED_LIBS:BOOL=ON")
        .arg("-DENABLE_JACK:BOOL=OFF")
        .arg("-DENABLE_PULSEAUDIO:BOOL=OFF")
        .arg("-DCMAKE_POSITION_INDEPENDENT_CODE:BOOL=ON")
        .arg("..")
        .output()
        .unwrap();

    // make install
    Command::new("make")
        .current_dir(&build_dir)
        .arg("install")
        .output()
        .unwrap();

    // remove builddir
    fs::remove_dir_all(build_dir).unwrap();
}
<commit_msg>Add `err_exists` macro to ignore `AlreadyExists` error<commit_after>extern crate pkg_config;
use std::process::Command;
use std::path::PathBuf;
use std::env;
use std::fs;

macro_rules! err_exists {
    ($expr:expr, $msg:expr) => {
        match $expr {
            Ok(val) => val,
            Err(err) => {
                match err.kind() {
                    ::std::io::ErrorKind::AlreadyExists => (),
                    _ => panic!(format!("{}: {}", $msg, err)),
                }
            }
        }
    }
}

fn lib_available(name: &str) -> bool {
    match pkg_config::find_library(name) {
        Ok(_) => true,
        Err(_) => {
            let res = Command::new("ldconfig").arg("--print-cache").output().unwrap();
            if res.status.success() {
                String::from_utf8(res.stdout).unwrap().contains(name)
            } else {
                false
            }
        }
    }
}

fn windows(_: String) {
    // TODO: Use precompiled binary!
    unimplemented!();
}

fn osx(_: String) {
    // TODO: I down't own any apple hardware to test this.
    unimplemented!();
}

fn linux(target: String) {
    if !lib_available("soundio") {
        build(target);
    } else {
        // TODO: remove else branch
        build(target);
    }
}

fn main() {
    let target = env::var("TARGET").unwrap();
    if target.contains("windows") {
        windows(target)
    } else if target.contains("apple") {
        osx(target)
    } else {
        // assume the rest is linux
        linux(target)
    }
}

const LIBSOUNDIO_TAR: &'static str = "http://libsound.io/release/libsoundio-1.1.0.tar.gz";
const LIBSOUNDIO_WIN: &'static str = "http://libsound.io/release/libsoundio-1.1.0.zip";

fn build(target: String) {
    let host = env::var("HOST").unwrap();
    let dst_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
    let lib_dir = dst_dir.join("lib");
    let include_dir = dst_dir.join("include");
    err_exists!(fs::create_dir(&lib_dir), &lib_dir.display());
    err_exists!(fs::create_dir(&include_dir), &include_dir.display());

    // set cargo flags
    println!("cargo:rustc-link-search=native={}", &lib_dir.display()); // -L
    println!("cargo:include={}", &include_dir.display());
    println!("cargo:root={}", &dst_dir.display());

    // download and extract libsoundio source
    Command::new("curl")
        .current_dir(&dst_dir)
        .args(&["--location", "--remote-name"])
        .arg(LIBSOUNDIO_TAR)
        .output()
        .unwrap();
    Command::new("tar")
        .current_dir(&dst_dir)
        .arg("-xvzf")
        .arg("libsoundio-1.1.0.tar.gz")
        .output()
        .unwrap();

    // create build dir
    let soundio_root = dst_dir.join("libsoundio-1.1.0");
    let build_dir = soundio_root.join("build");
    err_exists!(fs::create_dir(&build_dir), &build_dir.display());

    // run cmake
    Command::new("cmake")
        .current_dir(&build_dir)
        .arg("-DCMAKE_BUILD_TYPE=Debug")
        .arg("-DCMAKE_INSTALL_LIBDIR:PATH=lib")
        .arg(format!("-DCMAKE_INSTALL_PREFIX:PATH={}", &dst_dir.display()))
        .arg("-DBUILD_EXAMPLE_PROGRAMS:BOOL=OFF")
        .arg("-DBUILD_TESTS:BOOL=OFF")
        .arg("-DBUILD_STATIC_LIBS:BOOL=OFF")
        .arg("-DBUILD_SHARED_LIBS:BOOL=ON")
        .arg("-DENABLE_JACK:BOOL=OFF")
        .arg("-DENABLE_PULSEAUDIO:BOOL=OFF")
        .arg("-DCMAKE_POSITION_INDEPENDENT_CODE:BOOL=ON")
        .arg("..")
        .output()
        .unwrap();

    // make install
    Command::new("make")
        .current_dir(&build_dir)
        .arg("install")
        .output()
        .unwrap();

    // remove builddir
    fs::remove_dir_all(build_dir).unwrap();
}
<|endoftext|>


<commit_before>fn main() {
    // Re-export the TARGET environment variable so it can
    // be accessed by miri.
    let target = std::env::var("TARGET").unwrap();
    println!("cargo:rustc-env=TARGET={}", target);
}
<commit_msg>dont rerun build script unnecessarily<commit_after>fn main() {
    // Don't rebuild miri when nothing changed.
    println!("cargo:rerun-if-changed=build.rs");
    // Re-export the TARGET environment variable so it can
    // be accessed by miri.
    let target = std::env::var("TARGET").unwrap();
    println!("cargo:rustc-env=TARGET={}", target);
}
<|endoftext|>


<commit_before>
use std::process::Command;
use std::path::Path;
use std::env;

fn main() {
    let out = env::var( "OUT_DIR" ).unwrap();

    Command::new("clang").args( &[ "-arch", "x86_64", "-c", "src/curses_constants.c", "-o" ] )
                         .arg( &format!( "{}/curses_constants.o", out ) )
                         .status()
                         .unwrap();

    Command::new( "libtool" ).args( &[ "-static", "curses_constants.o", "-o", "libcurses_constants.a" ] )
                             .current_dir( &Path::new( &out ) )
                             .status()
                             .unwrap();

    println!( "cargo:rustc-link-search=native={}", out );
    println!( "cargo:rustc-link-lib=static=curses_constants" );

}
<commit_msg>changed curses wrapper to be dynamically linked<commit_after>
use std::process::Command;
use std::path::Path;
use std::env;

fn main() {
    let out = env::var( "OUT_DIR" ).unwrap();

    Command::new("clang").args( &[ "-arch", "x86_64", "-c", "src/curses_wrapper.c", "-o" ] )
                         .arg( &format!( "{}/curses_wrapper.o", out ) )
                         .status()
                         .unwrap();

    Command::new( "libtool" ).args( &[ "-dynamic", "-lncurses", "curses_wrapper.o", "-o", "libcurses_wrapper.dylib" ] )
                             .current_dir( &Path::new( &out ) )
                             .status()
                             .unwrap();

    println!( "cargo:rustc-link-search=native={}", out );
    println!( "cargo:rustc-link-lib=dylib=curses_constants" );

}
<|endoftext|>


<commit_before>use std::env;
use std::fs;
use std::io;
use std::path::{Path, PathBuf};
use std::process::Command;

#[cfg(target_os="windows")]
fn build_lua() -> io::Result<()> {
    run_command_in_dir(&["make", "mingw"], Some(&build_dir().join("lua-5.3.0")))
}

/// The comand to build lua, specialized for different OSes.
#[cfg(target_os="macos")]
fn build_lua() -> io::Result<()> {
    run_command_in_dir(&["make", "macosx"], Some(&build_dir().join("lua-5.3.0")))
}

#[cfg(target_os="linux")]
fn build_lua() -> io::Result<()> {
    let dir = build_dir().join("lua-5.3.0");
    try!(run_command_in_dir(&["sed", "-i", "s/^MYCFLAGS=.*/MYCFLAGS=-fPIC/g", "src/Makefile"],
                            Some(&dir)));
    run_command_in_dir(&["make", "linux"], Some(&dir))
}

#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
fn build_lua() -> io::Result<()> {
    let dir = build_dir().join("lua-5.3.0");
    try!(run_command_in_dir(&["sed", "-e", "s/^MYCFLAGS=.*/MYCFLAGS=-fPIC/g", "-i", "bak", "src/Makefile"],
                            Some(&dir)));
    if cfg!(target_os = "freebsd") {
        run_command_in_dir(&["make", "freebsd"], Some(&dir))
    } else {
        run_command_in_dir(&["make", "bsd"], Some(&dir))
    }
}

/// The command to fetch a URL (e.g. with wget) specialized for different
/// OSes.
#[cfg(not(any(target_os = "freebsd", target_os = "dragonfly", target_os = "macos")))]
fn fetch_in_dir(url: &str, cwd: Option<&Path>) -> io::Result<()> {
    run_command_in_dir(&["wget", url], cwd)
}

#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
fn fetch_in_dir(url: &str, cwd: Option<&Path>) -> io::Result<()> {
    run_command_in_dir(&["fetch", url], cwd)
}

#[cfg(target_os = "macos")]
fn fetch_in_dir(url: &str, cwd: Option<&Path>) -> io::Result<()> {
    run_command_in_dir(&["curl", "-O", url], cwd)
}

/// Runs the command 'all_args[0]' with the arguments 'all_args[1..]' in the
/// current directory.
fn run_command(all_args: &[&str]) -> io::Result<()> {
    run_command_in_dir(all_args, None)
}

/// Runs the command 'all_args[0]' with the arguments 'all_args[1..]' in the
/// directory 'cwd' or the current directory.
fn run_command_in_dir(all_args: &[&str], cwd: Option<&Path>) -> io::Result<()> {
    let command_name = all_args[0];
    let args = &all_args[1..];
    let mut command = Command::new(command_name);
    command.args(args);
    if let Some(cwd) = cwd {
        command.current_dir(cwd);
    }
    let status = try!(command.status());
    if !status.success() {
        return Err(io::Error::new(io::ErrorKind::Other, format!("The command\n\
        \t{}\n\
        did not run successfully.", all_args.connect(" "))));
    }
    Ok(())
}

/// The cargo output directory for builds from this.
fn build_dir() -> PathBuf {
    PathBuf::from(env::var("OUT_DIR").unwrap())
}

/// If a static Lua is not yet available from a prior run of this script, this
/// will download Lua and build it. The cargo configuration text to link
/// statically against lua.a is then printed to stdout.
fn prebuild() ->io::Result<()> {
    if !fs::metadata(
        &build_dir().join("lua-5.3.0").join("src").join("liblua.a")
    ).is_ok() {
        try!(fs::create_dir_all(&build_dir()));

        // Compile Lua.
        try!(fetch_in_dir("http://www.lua.org/ftp/lua-5.3.0.tar.gz",
                          Some(&build_dir())));
        try!(run_command_in_dir(&["tar", "xzvf", "lua-5.3.0.tar.gz"],
                                Some(&build_dir())));

        try!(build_lua());

        // Compile and run glue.c.
        let glue = build_dir().join("glue").to_string_lossy().into_owned();
        try!(run_command(&["gcc",
                         "-I", &build_dir().join("lua-5.3.0").join("src").to_string_lossy(),
                         &PathBuf::from("src").join("glue").join("glue.c").to_string_lossy(),
                         "-o", &glue]));
        try!(run_command(&[&glue, "src/ffi/glue.rs"]));
    }

    let mut build_dir_absolute = try!(env::current_dir());
    build_dir_absolute.push(&build_dir());
    build_dir_absolute.push("lua-5.3.0");
    build_dir_absolute.push("src");

    println!("cargo:rustc-link-lib=static=lua");
    println!("cargo:rustc-link-search=native={}", build_dir_absolute.to_string_lossy());

    Ok(())
}

fn main() {
    match prebuild() {
        Err(e) => panic!("Error: {}", e),
        Ok(()) => (),
    }
}
<commit_msg>Pass -fPIC using MYCFLAGS<commit_after>use std::env;
use std::fs;
use std::io;
use std::path::{Path, PathBuf};
use std::process::Command;

#[cfg(target_os="windows")]
fn build_lua() -> io::Result<()> {
    run_command_in_dir(&["make", "mingw"], Some(&build_dir().join("lua-5.3.0")))
}

/// The comand to build lua, specialized for different OSes.
#[cfg(target_os="macos")]
fn build_lua() -> io::Result<()> {
    run_command_in_dir(&["make", "macosx"], Some(&build_dir().join("lua-5.3.0")))
}

#[cfg(target_os="linux")]
fn build_lua() -> io::Result<()> {
    let dir = build_dir().join("lua-5.3.0");
    run_command_in_dir(&["make", "linux", "MYCFLAGS=-fPIC"], Some(&dir))
}

#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
fn build_lua() -> io::Result<()> {
    let dir = build_dir().join("lua-5.3.0");
    try!(run_command_in_dir(&["sed", "-e", "s/^MYCFLAGS=.*/MYCFLAGS=-fPIC/g", "-i", "bak", "src/Makefile"],
                            Some(&dir)));
    if cfg!(target_os = "freebsd") {
        run_command_in_dir(&["make", "freebsd"], Some(&dir))
    } else {
        run_command_in_dir(&["make", "bsd"], Some(&dir))
    }
}

/// The command to fetch a URL (e.g. with wget) specialized for different
/// OSes.
#[cfg(not(any(target_os = "freebsd", target_os = "dragonfly", target_os = "macos")))]
fn fetch_in_dir(url: &str, cwd: Option<&Path>) -> io::Result<()> {
    run_command_in_dir(&["wget", url], cwd)
}

#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
fn fetch_in_dir(url: &str, cwd: Option<&Path>) -> io::Result<()> {
    run_command_in_dir(&["fetch", url], cwd)
}

#[cfg(target_os = "macos")]
fn fetch_in_dir(url: &str, cwd: Option<&Path>) -> io::Result<()> {
    run_command_in_dir(&["curl", "-O", url], cwd)
}

/// Runs the command 'all_args[0]' with the arguments 'all_args[1..]' in the
/// current directory.
fn run_command(all_args: &[&str]) -> io::Result<()> {
    run_command_in_dir(all_args, None)
}

/// Runs the command 'all_args[0]' with the arguments 'all_args[1..]' in the
/// directory 'cwd' or the current directory.
fn run_command_in_dir(all_args: &[&str], cwd: Option<&Path>) -> io::Result<()> {
    let command_name = all_args[0];
    let args = &all_args[1..];
    let mut command = Command::new(command_name);
    command.args(args);
    if let Some(cwd) = cwd {
        command.current_dir(cwd);
    }
    let status = try!(command.status());
    if !status.success() {
        return Err(io::Error::new(io::ErrorKind::Other, format!("The command\n\
        \t{}\n\
        did not run successfully.", all_args.connect(" "))));
    }
    Ok(())
}

/// The cargo output directory for builds from this.
fn build_dir() -> PathBuf {
    PathBuf::from(env::var("OUT_DIR").unwrap())
}

/// If a static Lua is not yet available from a prior run of this script, this
/// will download Lua and build it. The cargo configuration text to link
/// statically against lua.a is then printed to stdout.
fn prebuild() ->io::Result<()> {
    if !fs::metadata(
        &build_dir().join("lua-5.3.0").join("src").join("liblua.a")
    ).is_ok() {
        try!(fs::create_dir_all(&build_dir()));

        // Compile Lua.
        try!(fetch_in_dir("http://www.lua.org/ftp/lua-5.3.0.tar.gz",
                          Some(&build_dir())));
        try!(run_command_in_dir(&["tar", "xzvf", "lua-5.3.0.tar.gz"],
                                Some(&build_dir())));

        try!(build_lua());

        // Compile and run glue.c.
        let glue = build_dir().join("glue").to_string_lossy().into_owned();
        try!(run_command(&["gcc",
                         "-I", &build_dir().join("lua-5.3.0").join("src").to_string_lossy(),
                         &PathBuf::from("src").join("glue").join("glue.c").to_string_lossy(),
                         "-o", &glue]));
        try!(run_command(&[&glue, "src/ffi/glue.rs"]));
    }

    let mut build_dir_absolute = try!(env::current_dir());
    build_dir_absolute.push(&build_dir());
    build_dir_absolute.push("lua-5.3.0");
    build_dir_absolute.push("src");

    println!("cargo:rustc-link-lib=static=lua");
    println!("cargo:rustc-link-search=native={}", build_dir_absolute.to_string_lossy());

    Ok(())
}

fn main() {
    match prebuild() {
        Err(e) => panic!("Error: {}", e),
        Ok(()) => (),
    }
}
<|endoftext|>


<commit_before>#![feature(path_ext)]

use std::fs::{File, PathExt};
use std::io::{stderr, Write};
use std::process::{Command, Output};
use std::path::Path;
use std::env;

fn run_cmd(command: &mut Command) -> Output {
    let stream = stderr();
    let mut f = stream.lock();
    let res = command.output();
    match res {
        Ok(output) => if !output.status.success() {
            write!(&mut f, "Command `{:?}` failed", command).unwrap();
            panic!("{}", String::from_utf8_lossy(&output.stderr[..]))
        } else {
            output
        },
        Err(err) => {
            write!(&mut f, "Command `{:?}` failed", command).unwrap();
            panic!("{}", err)
        }
    }
}

fn main()
{
    let source_file = Path::new("src/codegen/type_size.c");
    if !source_file.exists() {
        panic!("Could not find file: {}", source_file.to_string_lossy());
    }

    let out_dir_var = match env::var("OUT_DIR") {
        Ok(dir) => dir,
        Err(e) => panic!("Could not get env value OUT_DIR: {}", e)
    };
    let out_dir = Path::new(&out_dir_var);
    if !out_dir.exists() {
        panic!("Could not find directory: {}", out_dir.to_string_lossy());
    }

    let out_file = out_dir.join("codegen");

    let mut gcc_cmd = Command::new("gcc");
    gcc_cmd.arg(source_file)
        .arg("-o").arg(&out_file);

    match env::var("LIBYAML_CFLAGS") {
        Ok(compile_flags) => { gcc_cmd.arg(&compile_flags); },
        Err(_) => ()
    }

    run_cmd(&mut gcc_cmd);

    let mut codegen_cmd = Command::new(&out_file);
    let output = run_cmd(&mut codegen_cmd);

    let mut f = match File::create("src/type_size.rs") {
        Ok(f) => f,
        Err(e) => panic!("Could not open file src/type_size.rs: {}", e)
    };

    match f.write_all(&output.stdout[..]) {
        Err(e) => panic!("Could not write to src/type_size.rs: {}", e),
        Ok(_) => ()
    }
}
<commit_msg>PathExt deprecated in nightly<commit_after>use std::fs::File;
use std::io::{stderr, Write};
use std::process::{Command, Output};
use std::path::Path;
use std::env;

fn run_cmd(command: &mut Command) -> Output {
    let stream = stderr();
    let mut f = stream.lock();
    let res = command.output();
    match res {
        Ok(output) => if !output.status.success() {
            write!(&mut f, "Command `{:?}` failed", command).unwrap();
            panic!("{}", String::from_utf8_lossy(&output.stderr[..]))
        } else {
            output
        },
        Err(err) => {
            write!(&mut f, "Command `{:?}` failed", command).unwrap();
            panic!("{}", err)
        }
    }
}

fn main()
{
    let source_file = Path::new("src/codegen/type_size.c");
    if !source_file.exists() {
        panic!("Could not find file: {}", source_file.to_string_lossy());
    }

    let out_dir_var = match env::var("OUT_DIR") {
        Ok(dir) => dir,
        Err(e) => panic!("Could not get env value OUT_DIR: {}", e)
    };
    let out_dir = Path::new(&out_dir_var);
    if !out_dir.exists() {
        panic!("Could not find directory: {}", out_dir.to_string_lossy());
    }

    let out_file = out_dir.join("codegen");

    let mut gcc_cmd = Command::new("gcc");
    gcc_cmd.arg(source_file)
        .arg("-o").arg(&out_file);

    match env::var("LIBYAML_CFLAGS") {
        Ok(compile_flags) => { gcc_cmd.arg(&compile_flags); },
        Err(_) => ()
    }

    run_cmd(&mut gcc_cmd);

    let mut codegen_cmd = Command::new(&out_file);
    let output = run_cmd(&mut codegen_cmd);

    let mut f = match File::create("src/type_size.rs") {
        Ok(f) => f,
        Err(e) => panic!("Could not open file src/type_size.rs: {}", e)
    };

    match f.write_all(&output.stdout[..]) {
        Err(e) => panic!("Could not write to src/type_size.rs: {}", e),
        Ok(_) => ()
    }
}
<|endoftext|>


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

extern crate pkg_config;

use std::process::Command;
use std::env;
use std::fs;
use std::fs::File;

// Needs the libev-dev package

// Return true if file1 doesn't exist or is older than file2.
fn doesnt_exists_or_older(file1: &str, file2: &str) -> bool {
    let f1 = File::open(file1);
    if f1.is_err() {
        return true;
    }

    let f2 = File::open(file2);
    if f2.is_err() {
        panic!(format!("The file {} should exist!", file2));
    }

    let time1 = f1.unwrap().metadata().unwrap().modified().unwrap();
    let time2 = f2.unwrap().metadata().unwrap().modified().unwrap();
    time1 < time2
}

macro_rules! doesnt_exists_or_older (
    ($f1:expr, $f2:expr) => (
        doesnt_exists_or_older(&format!("libpagekite/{}", $f1), &format!("libpagekite/{}", $f2))
    )
);

fn build(output: &str) {
    // If configure doesn't exist or is older than configure.ac run autogen.sh
    if doesnt_exists_or_older!("configure", "configure.ac") {
        let exit_code = Command::new("./autogen.sh")
            .current_dir("libpagekite")
            .status()
            .unwrap();

        if !exit_code.success() {
            panic!("Failed to run libpagekite/autogen.sh");
        }
    }

    // If Makefile doesn't exist or is older than configure run configure
    if doesnt_exists_or_older!("Makefile", "configure") {
        let exit_code = Command::new("./configure")
            .env("CFLAGS", "-fPIC") // Needed to build the static library as PIC.
            .arg(format!("--prefix={}", output))
            .arg(format!("--without-java"))
            //.arg(format!("--host={}", env::var("TARGET_TRIPLE").unwrap_or(env::var("TARGET").unwrap())))
            .current_dir("libpagekite")
            .status().unwrap();

        if !exit_code.success() {
            panic!("Failed to run libpagekite/configure");
        }
    }

    // We don't pass -j parameter to `make` because the build fails with this option.
    let exit_code = Command::new("make")
        .arg("install")
        .current_dir("libpagekite")
        .status()
        .unwrap();

    if !exit_code.success() {
        panic!(format!("Failure running `make -C libpagekite`"));
    }
}

#[allow(unused_must_use)]
fn main() {
    let libpagekite_build_dir = format!("{}/{}", env::var("OUT_DIR").unwrap(), "libpagekite");

    fs::create_dir(libpagekite_build_dir.clone());

    build(&libpagekite_build_dir);

    pkg_config::probe_library("libssl").unwrap();

    // No pkg-config support for libev unfortunately.
    println!("cargo:rustc-link-lib=ev");

    println!("cargo:rustc-link-search=native={}/lib", libpagekite_build_dir);
    println!("cargo:rustc-link-lib=dylib=pagekite");
}
<commit_msg>Re-enable cross compiling in build.rs<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at http://mozilla.org/MPL/2.0/.

extern crate pkg_config;

use std::process::Command;
use std::env;
use std::fs;
use std::fs::File;

// Needs the libev-dev package

// Return true if file1 doesn't exist or is older than file2.
fn doesnt_exists_or_older(file1: &str, file2: &str) -> bool {
    let f1 = File::open(file1);
    if f1.is_err() {
        return true;
    }

    let f2 = File::open(file2);
    if f2.is_err() {
        panic!(format!("The file {} should exist!", file2));
    }

    let time1 = f1.unwrap().metadata().unwrap().modified().unwrap();
    let time2 = f2.unwrap().metadata().unwrap().modified().unwrap();
    time1 < time2
}

macro_rules! doesnt_exists_or_older (
    ($f1:expr, $f2:expr) => (
        doesnt_exists_or_older(&format!("libpagekite/{}", $f1), &format!("libpagekite/{}", $f2))
    )
);

fn build(output: &str) {
    // If configure doesn't exist or is older than configure.ac run autogen.sh
    if doesnt_exists_or_older!("configure", "configure.ac") {
        let exit_code = Command::new("./autogen.sh")
            .current_dir("libpagekite")
            .status()
            .unwrap();

        if !exit_code.success() {
            panic!("Failed to run libpagekite/autogen.sh");
        }
    }

    // If Makefile doesn't exist or is older than configure run configure
    if doesnt_exists_or_older!("Makefile", "configure") {
        let exit_code = Command::new("./configure")
            .env("CFLAGS", "-fPIC") // Needed to build the static library as PIC.
            .arg(format!("--prefix={}", output))
            .arg(format!("--without-java"))
            .arg(format!("--host={}", env::var("TARGET_TRIPLE").unwrap_or(env::var("TARGET").unwrap())))
            .current_dir("libpagekite")
            .status().unwrap();

        if !exit_code.success() {
            panic!("Failed to run libpagekite/configure");
        }
    }

    // We don't pass -j parameter to `make` because the build fails with this option.
    let exit_code = Command::new("make")
        .arg("install")
        .current_dir("libpagekite")
        .status()
        .unwrap();

    if !exit_code.success() {
        panic!(format!("Failure running `make -C libpagekite`"));
    }
}

#[allow(unused_must_use)]
fn main() {
    let libpagekite_build_dir = format!("{}/{}", env::var("OUT_DIR").unwrap(), "libpagekite");

    fs::create_dir(libpagekite_build_dir.clone());

    build(&libpagekite_build_dir);

    pkg_config::probe_library("libssl").unwrap();

    // No pkg-config support for libev unfortunately.
    println!("cargo:rustc-link-lib=ev");

    println!("cargo:rustc-link-search=native={}/lib", libpagekite_build_dir);
    println!("cargo:rustc-link-lib=dylib=pagekite");
}
<|endoftext|>


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

use std::error::Error;
use std::fs::File;
use std::io::prelude::*;
use std::path::Path;
use std::process::Command;

static HEADER: &'static str = "#include <wand/MagickWand.h>\n";

fn main() {
    //
    // If the MagickWand bindings are missing, generate them using
    // rust-bindgen.
    //
    let bindings_path = Path::new("src/bindings.rs");
    if !bindings_path.exists() {
        let bindgen_path = Path::new("rust-bindgen");
        if !bindgen_path.exists() {
            Command::new("git")
                    .arg("clone")
                    .arg("https://github.com/crabtw/rust-bindgen.git")
                    .status().unwrap();
            // Checkout a version of rust-bindgen that is known to work;
            // more recent versions produce code that does not compile (the
            // commit after 8a51860 changes the way enums are generated).
            Command::new("git")
                    .arg("checkout")
                    .arg("8a51860")
                    .current_dir("rust-bindgen")
                    .status().unwrap();
            Command::new("cargo")
                    .arg("build")
                    .current_dir("rust-bindgen")
                    .status().unwrap();
        }
        // Create the header file that rust-bindgen needs as input.
        let mut gen_h = match File::create("gen.h") {
            Err(why) => panic!("could not create gen.h file: {}", Error::description(&why)),
            Ok(file) => file
        };
        match gen_h.write_all(HEADER.as_bytes()) {
            Err(why) => panic!("could not write to gen.h: {}", Error::description(&why)),
            Ok(_)    => ()
        };
        // Get the compiler and linker flags for the MagickWand library.
        let mw_cflags_output = Command::new("MagickWand-config")
                .arg("--cflags")
                .output().unwrap();
        let mw_cflags = std::str::from_utf8(&mw_cflags_output.stdout).unwrap().trim();
        let mw_cflags_arr: Vec<&str> = mw_cflags.split_whitespace().collect();
        let mw_ldflags_output = Command::new("MagickWand-config")
                .arg("--ldflags")
                .output().unwrap();
        let mw_ldflags = std::str::from_utf8(&mw_ldflags_output.stdout).unwrap().trim();
        let mw_ldflags_arr: Vec<&str> = mw_ldflags.split_whitespace().collect();
        // Combine all of that in the invocation of rust-bindgen.
        Command::new("./rust-bindgen/target/debug/bindgen")
                // always include this even though it only makes sense on Mac to
                // work around https://github.com/crabtw/rust-bindgen/issues/89
                .env("DYLD_LIBRARY_PATH", "/Library/Developer/CommandLineTools/usr/lib")
                .args(&mw_cflags_arr[..])
                .arg("-builtins")
                .arg("-o")
                .arg("src/bindings.rs")
                .args(&mw_ldflags_arr[..])
                .arg("gen.h")
                .status().unwrap();
        // how to get the output of the command...
        // let output = Commad::new(...).output().unwrap();
        // let out = std::str::from_utf8(&output.stdout).unwrap();
        // println!("cargo:output={}", out);
        // let err = std::str::from_utf8(&output.stderr).unwrap();
        // println!("cargo:error={}", err);
        match std::fs::remove_file("gen.h") {
            Err(why) => panic!("could not remove gen.h: {}", Error::description(&why)),
            Ok(_)    => ()
        }
    }
    // For the sake of easily building and testing on Mac, include the path
    // to MagickWand. Chances are MagickWand is in /usr/local/lib, or
    // somewhere else that rustc can find it.
    println!("cargo:rustc-link-search=native=/usr/local/lib");
}
<commit_msg>Handle building on macos a little better<commit_after>/*
 * Copyright 2016 Nathan Fiedler
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

use std::error::Error;
use std::fs::File;
use std::io::prelude::*;
use std::path::Path;
use std::process::Command;

static HEADER: &'static str = "#include <wand/MagickWand.h>\n";
static LIBPATH: &'static str = "/Library/Developer/CommandLineTools/usr/lib";

fn main() {
    //
    // If the MagickWand bindings are missing, generate them using
    // rust-bindgen.
    //
    let bindings_path = Path::new("src/bindings.rs");
    if !bindings_path.exists() {
        let bindgen_path = Path::new("rust-bindgen");
        if !bindgen_path.exists() {
            Command::new("git")
                    .arg("clone")
                    .arg("https://github.com/crabtw/rust-bindgen.git")
                    .status().unwrap();
            // Checkout a version of rust-bindgen that is known to work;
            // more recent versions produce code that does not compile (the
            // commit after 8a51860 changes the way enums are generated).
            Command::new("git")
                    .arg("checkout")
                    .arg("8a51860")
                    .current_dir("rust-bindgen")
                    .status().unwrap();
            Command::new("cargo")
                    .arg("build")
                    .current_dir("rust-bindgen")
                    .status().unwrap();
        }
        // Create the header file that rust-bindgen needs as input.
        let mut gen_h = match File::create("gen.h") {
            Err(why) => panic!("could not create gen.h file: {}", Error::description(&why)),
            Ok(file) => file
        };
        match gen_h.write_all(HEADER.as_bytes()) {
            Err(why) => panic!("could not write to gen.h: {}", Error::description(&why)),
            Ok(_)    => ()
        };
        // Get the compiler and linker flags for the MagickWand library.
        let mw_cflags_output = Command::new("MagickWand-config")
                .arg("--cflags")
                .output().unwrap();
        let mw_cflags = std::str::from_utf8(&mw_cflags_output.stdout).unwrap().trim();
        let mw_cflags_arr: Vec<&str> = mw_cflags.split_whitespace().collect();
        let mw_ldflags_output = Command::new("MagickWand-config")
                .arg("--ldflags")
                .output().unwrap();
        let mw_ldflags = std::str::from_utf8(&mw_ldflags_output.stdout).unwrap().trim();
        let mw_ldflags_arr: Vec<&str> = mw_ldflags.split_whitespace().collect();
        // Combine all of that in the invocation of rust-bindgen.
        let mut cmd = &mut Command::new("./rust-bindgen/target/debug/bindgen");
        if cfg!(target_os = "macos") {
            // Mac requires that the xcode tools are installed so that
            // rustc can find the clang.dylib file. See also issue
            // https://github.com/crabtw/rust-bindgen/issues/89
            let lib_path = Path::new(LIBPATH);
            if !lib_path.exists() {
                panic!("missing {}, run xcode-select --install", LIBPATH);
            }
            cmd.env("DYLD_LIBRARY_PATH", LIBPATH);
        }
        cmd.args(&mw_cflags_arr[..])
           .arg("-builtins")
           .arg("-o")
           .arg("src/bindings.rs")
           .args(&mw_ldflags_arr[..])
           .arg("gen.h")
           .status().unwrap();
        // how to get the output of the command...
        // let output = Commad::new(...).output().unwrap();
        // let out = std::str::from_utf8(&output.stdout).unwrap();
        // println!("cargo:output={}", out);
        // let err = std::str::from_utf8(&output.stderr).unwrap();
        // println!("cargo:error={}", err);
        match std::fs::remove_file("gen.h") {
            Err(why) => panic!("could not remove gen.h: {}", Error::description(&why)),
            Ok(_)    => ()
        }
    }
    // For the sake of easily building and testing on Mac, include the path
    // to MagickWand. Chances are MagickWand is in /usr/local/lib, or
    // somewhere else that rustc can find it.
    println!("cargo:rustc-link-search=native=/usr/local/lib");
}
<|endoftext|>


<commit_before>use std::env;
use std::ffi::OsString;
use std::process::Command;

fn main() {
    let rustc = env::var_os("RUSTC").unwrap_or(OsString::from("rustc"));
    let output = Command::new(&rustc)
        .arg("--version")
        .output()
        .unwrap()
        .stdout;
    let version = String::from_utf8(output).unwrap();

    enable_simd_optimizations(&version);
}

fn enable_simd_optimizations(version: &str) {
    // We don't activate SIMD optimizations these if we've explicitly disabled
    // them. Disabling auto optimizations is intended for use in tests, so that
    // we can reliably test fallback implementations.
    if env::var_os("CARGO_CFG_REGEX_DISABLE_AUTO_OPTIMIZATIONS").is_some() {
        return;
    }
    let parsed = match Version::parse(&version) {
        Ok(parsed) => parsed,
        Err(err) => {
            eprintln!("failed to parse `rustc --version`: {}", err);
            return;
        }
    };
    let minimum = Version { major: 1, minor: 27, patch: 0 };
    if version.contains("nightly") || parsed >= minimum {
        println!("cargo:rustc-cfg=regex_runtime_teddy_ssse3");
        println!("cargo:rustc-cfg=regex_runtime_teddy_avx2");
    }
}

#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)]
struct Version {
    major: u32,
    minor: u32,
    patch: u32,
}

impl Version {
    fn parse(mut s: &str) -> Result<Version, String> {
        if !s.starts_with("rustc ") {
            return Err(format!("unrecognized version string: {}", s));
        }
        s = &s["rustc ".len()..];

        let parts: Vec<&str> = s.split(".").collect();
        if parts.len() < 3 {
            return Err(format!("not enough version parts: {:?}", parts));
        }

        let mut num = String::new();
        for c in parts[0].chars() {
            if !c.is_digit(10) {
                break;
            }
            num.push(c);
        }
        let major = num.parse::<u32>().map_err(|e| e.to_string())?;

        num.clear();
        for c in parts[1].chars() {
            if !c.is_digit(10) {
                break;
            }
            num.push(c);
        }
        let minor = num.parse::<u32>().map_err(|e| e.to_string())?;

        num.clear();
        for c in parts[2].chars() {
            if !c.is_digit(10) {
                break;
            }
            num.push(c);
        }
        let patch = num.parse::<u32>().map_err(|e| e.to_string())?;

        Ok(Version { major, minor, patch })
    }
}
<commit_msg>build: refactor build.rs slightly<commit_after>use std::env;
use std::ffi::OsString;
use std::process::Command;

fn main() {
    let version = match Version::read() {
        Ok(version) => version,
        Err(err) => {
            eprintln!("failed to parse `rustc --version`: {}", err);
            return;
        }
    };
    enable_simd_optimizations(version);
}

fn enable_simd_optimizations(version: Version) {
    // We don't activate SIMD optimizations these if we've explicitly disabled
    // them. Disabling auto optimizations is intended for use in tests, so that
    // we can reliably test fallback implementations.
    if env::var_os("CARGO_CFG_REGEX_DISABLE_AUTO_OPTIMIZATIONS").is_some() {
        return;
    }
    if version < (Version { major: 1, minor: 27, patch: 0 }) {
        return;
    }

    println!("cargo:rustc-cfg=regex_runtime_teddy_ssse3");
    println!("cargo:rustc-cfg=regex_runtime_teddy_avx2");
}

#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)]
struct Version {
    major: u32,
    minor: u32,
    patch: u32,
}

impl Version {
    fn read() -> Result<Version, String> {
        let rustc = env::var_os("RUSTC").unwrap_or(OsString::from("rustc"));
        let output = Command::new(&rustc)
            .arg("--version")
            .output()
            .unwrap()
            .stdout;
        Version::parse(&String::from_utf8(output).unwrap())
    }

    fn parse(mut s: &str) -> Result<Version, String> {
        if !s.starts_with("rustc ") {
            return Err(format!("unrecognized version string: {}", s));
        }
        s = &s["rustc ".len()..];

        let parts: Vec<&str> = s.split(".").collect();
        if parts.len() < 3 {
            return Err(format!("not enough version parts: {:?}", parts));
        }

        let mut num = String::new();
        for c in parts[0].chars() {
            if !c.is_digit(10) {
                break;
            }
            num.push(c);
        }
        let major = num.parse::<u32>().map_err(|e| e.to_string())?;

        num.clear();
        for c in parts[1].chars() {
            if !c.is_digit(10) {
                break;
            }
            num.push(c);
        }
        let minor = num.parse::<u32>().map_err(|e| e.to_string())?;

        num.clear();
        for c in parts[2].chars() {
            if !c.is_digit(10) {
                break;
            }
            num.push(c);
        }
        let patch = num.parse::<u32>().map_err(|e| e.to_string())?;

        Ok(Version { major, minor, patch })
    }
}
<|endoftext|>


<commit_before>use std::env;

fn main() {
    println!("cargo:rustc-env=TARGET={}", env::var("TARGET").unwrap());
    println!("cargo:rerun-if-changed=build.rs");
}
<commit_msg>Rerun build.rs when needed; more comments<commit_after>use std::env;

fn main() {
    // Cargo sets the host and target env vars for build scripts, but not crates:
    // https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-build-scripts
    // So we just re-export them to the crate code.
    println!("cargo:rustc-env=TARGET={}", env::var("TARGET").unwrap());
    // By default Cargo only runs the build script when a file changes.
    // This makes it re-run on target change
     println!("cargo:rerun-if-changed-env=TARGET")
}
<|endoftext|>


<commit_before>use std::borrow::Cow;
use std::char;
use std::collections::hash_map::Entry;
use std::collections::HashMap;
use std::convert::TryFrom;
use std::env;
use std::fs::File;
use std::io::{self, BufWriter, Write};
use std::path::Path;

const RAW_DATA: &str = include_str!(concat!(
    env!("CARGO_MANIFEST_DIR"),
    "/pinyin-data/pinyin.txt"
));

#[cfg(any(
    feature = "plain",
    feature = "with_tone_num",
    feature = "with_tone_num_end"
))]
const PHONETIC_SYMBOL_MAP: &[(char, char, u8)] = &[
    ('ā', 'a', 1),
    ('á', 'a', 2),
    ('ǎ', 'a', 3),
    ('à', 'a', 4),
    ('ē', 'e', 1),
    ('é', 'e', 2),
    ('ě', 'e', 3),
    ('è', 'e', 4),
    ('ế', 'ê', 2),
    ('ề', 'ê', 4),
    ('ō', 'o', 1),
    ('ó', 'o', 2),
    ('ǒ', 'o', 3),
    ('ò', 'o', 4),
    ('ī', 'i', 1),
    ('í', 'i', 2),
    ('ǐ', 'i', 3),
    ('ì', 'i', 4),
    ('ū', 'u', 1),
    ('ú', 'u', 2),
    ('ǔ', 'u', 3),
    ('ù', 'u', 4),
    ('ǘ', 'ü', 2),
    ('ǚ', 'ü', 3),
    ('ǜ', 'ü', 4),
    ('ń', 'n', 2),
    ('ň', 'n', 3),
    ('ǹ', 'n', 4),
    ('ḿ', 'm', 2),
];

#[rustfmt::skip]
const LETTER_TABLE: &[char] = &[
    'b', 'p', 'm', 'f', 'd',
    't', 'n', 'l', 'g', 'k',
    'h', 'j', 'q', 'x', 'r',
    'z', 'c', 's',
                   'w', 'y',
    // 因为数据源里面不会使用 `v` 以及其它的 简写字母，所以这里注释掉
    // 'v', 'ẑ', 'ĉ', 'ŝ', 'ŋ',

    '\u{0304}', '\u{030C}', '\u{0300}', // Unicode 声调连字符
    'a', 'ā', 'á', 'ǎ', 'à',
    'e', 'ē', 'é', 'ě', 'è',
    'i', 'ī', 'í', 'ǐ', 'ì',
    //   "m̄"       "m̌"  "m̀"
    'm',      'ḿ',
    //   "n̄"
    'n',      'ń', 'ň', 'ǹ',
    'o', 'ō', 'ó', 'ǒ', 'ò',
    'u', 'ū', 'ú', 'ǔ', 'ù',
    //   "ê̄"       "ê̌"
    'ê',      'ế',      'ề',
    //   'ǖ'
    'ü',      'ǘ', 'ǚ', 'ǜ',
];

#[cfg(any(feature = "with_tone_num", feature = "with_tone_num_end"))]
const TONE_NUMS: &[char] = &['0', '1', '2', '3', '4'];

type Style = (&'static str, fn(&str) -> Cow<'_, str>);
type InputData = Vec<(u32, Vec<&'static str>)>;
type PinyinDataIndex = HashMap<&'static str, usize>;
type HeteronymDataIndex = HashMap<u32, usize>;

fn main() -> io::Result<()> {
    let data = build_data();
    let pinyin_index = generate_pinyin_data(&data)?;
    let heteronym_index = generate_heteronym_table(&data, &pinyin_index)?;
    generate_char_table(&data, &pinyin_index, &heteronym_index)?;
    // 输出这行以保证改动项目的其他文件不会触发编译脚本重新执行
    println!("cargo:rerun-if-changed=build.rs");
    Ok(())
}

fn build_data() -> InputData {
    let mut input_data = RAW_DATA
        .lines()
        .enumerate()
        // 移除注释和空格
        .map(|(i, mut line)| {
            if let Some(hash_pos) = line.find('#') {
                line = &line[..hash_pos];
            }
            (i, line.trim())
        })
        // 移除空行
        .filter(|(_, line)| !line.is_empty())
        .map(|(i, line)| {
            // Split the line by colon
            let colon_pos = match line.find(':') {
                Some(pos) => pos,
                None => unreachable!("no colon found in line {}", i),
            };
            let code_point = line[..colon_pos].trim();
            let pinyin_list: Vec<_> = line[colon_pos + 1..].trim().split(',').collect();

            // 确保输入数据的字符全部在我们预料之中。
            // 同时也可以提前知道一些被遗弃的码位，如: U+E7C8 和 U+E7C7
            for pinyin in pinyin_list.iter() {
                for ch in pinyin.chars() {
                    let is_known = LETTER_TABLE.contains(&ch);
                    assert!(
                        is_known,
                        "unknown character {:?} at line {}: {}",
                        ch, i, line,
                    );
                }
            }

            // 解析码位
            const CODE_POINT_PREFIX: &str = "U+";
            assert!(code_point.starts_with(CODE_POINT_PREFIX));
            let code = &code_point[CODE_POINT_PREFIX.len()..];
            let code = match u32::from_str_radix(code, 16) {
                Ok(code) => code,
                Err(_) => unreachable!("invalid code point {} at line {}", code, i),
            };
            (code, pinyin_list)
        })
        .collect::<Vec<_>>();
    input_data.sort_by_key(|(code, _)| *code);
    input_data
}

const STYLES: &[Style] = &[
    #[cfg(feature = "plain")]
    ("plain", |input| {
        input.chars().filter_map(|c| get_char_info(c).0).collect()
    }),
    #[cfg(feature = "with_tone")]
    ("with_tone", |input| Cow::from(input)),
    #[cfg(feature = "with_tone_num")]
    ("with_tone_num", |input| {
        let mut result = String::new();
        for ch in input.chars() {
            let (ch, tone) = get_char_info(ch);
            if let Some(ch) = ch {
                result.push(ch);
            }
            if tone > 0 {
                result.push(TONE_NUMS[usize::try_from(tone).unwrap()]);
            }
        }
        result.into()
    }),
    #[cfg(feature = "with_tone_num_end")]
    ("with_tone_num_end", |input| {
        let mut result = String::new();
        let mut output_tone = None;
        for ch in input.chars() {
            let (ch, tone) = get_char_info(ch);
            if let Some(ch) = ch {
                result.push(ch);
            }
            if tone > 0 {
                assert!(output_tone.is_none());
                output_tone = Some(TONE_NUMS[usize::try_from(tone).unwrap()]);
            }
        }
        if let Some(tone) = output_tone {
            result.push(tone);
        }
        result.into()
    }),
];

fn generate_pinyin_data(data: &InputData) -> io::Result<PinyinDataIndex> {
    let mut output = create_out_file("pinyin_data.rs")?;
    let mut pinyin_data = HashMap::new();
    writeln!(output, "&[")?;
    let mut process_pinyin = |pinyin| {
        let index = pinyin_data.len();
        match pinyin_data.entry(pinyin) {
            Entry::Occupied(_) => return Ok(()),
            Entry::Vacant(entry) => {
                entry.insert(index);
            }
        }
        write!(output, "    PinyinData {{ ")?;
        for (field, converter) in STYLES.iter() {
            write!(output, r#"{}: "{}", "#, field, converter(pinyin))?;
        }
        #[cfg(feature = "compat")]
        {
            // 计算切分声母和韵母的位置
            const INITIALS: &[&str] = &[
                "b", "p", "m", "f", "d", "t", "n", "l", "g", "k", "h", "j", "q", "x", "r", "zh",
                "ch", "sh", "z", "c", "s",
            ];
            let split = INITIALS
                .iter()
                .find(|initial| pinyin.starts_with(*initial))
                .map_or(0, |initial| initial.len());
            write!(output, "split: {}, ", split)?;
        }
        writeln!(output, "}},")?;
        Ok(())
    };
    // 插入一个空的拼音数据作为零位
    process_pinyin("")?;
    data.iter()
        .flat_map(|(_, list)| list.iter().map(|s| *s))
        .map(process_pinyin)
        .collect::<io::Result<()>>()?;
    writeln!(output, "]")?;
    Ok(pinyin_data)
}

#[cfg(feature = "heteronym")]
fn generate_heteronym_table(
    data: &InputData,
    index: &PinyinDataIndex,
) -> io::Result<HeteronymDataIndex> {
    let mut heteronym_list_index = HashMap::new();
    let mut heteronym_index = HashMap::new();
    let mut output = create_out_file("heteronym_table.rs")?;
    writeln!(output, "&[")?;
    writeln!(output, "    &[],")?;
    heteronym_list_index.insert(vec![].into_boxed_slice(), 0);
    data.iter()
        .map(|(code, list)| {
            let list = list[1..]
                .iter()
                .map(|pinyin| *index.get(pinyin).unwrap())
                .collect::<Box<[_]>>();
            let new_idx = heteronym_list_index.len();
            let idx = match heteronym_list_index.entry(list) {
                Entry::Occupied(entry) => *entry.get(),
                Entry::Vacant(entry) => {
                    write!(output, "    &[")?;
                    for i in entry.key().iter() {
                        write!(output, "{}, ", i)?;
                    }
                    writeln!(output, "],")?;
                    entry.insert(new_idx);
                    new_idx
                }
            };
            heteronym_index.insert(*code, idx);
            Ok(())
        })
        .collect::<io::Result<()>>()?;
    writeln!(output, "]")?;
    Ok(heteronym_index)
}

#[cfg(not(feature = "heteronym"))]
fn generate_heteronym_table(
    _data: &InputData,
    _index: &PinyinDataIndex,
) -> io::Result<HeteronymDataIndex> {
    Ok(HashMap::new())
}

fn generate_char_table(
    data: &InputData,
    pinyin_index: &PinyinDataIndex,
    heteronym_index: &HeteronymDataIndex,
) -> io::Result<()> {
    // 将码位分入数据块
    const GAP_THRESHOLD: u32 = 2048;
    let mut block_ranges = vec![];
    data.iter()
        .for_each(|(code, _)| match block_ranges.last_mut() {
            Some((_, end)) if *end + GAP_THRESHOLD > *code => *end = *code + 1,
            _ => block_ranges.push((*code, *code + 1)),
        });
    // 当我们允许最大2048个空位时，我们目前会切出3个块。如果这个数字在未来增加了，我们也许会希望调整策略。
    assert_eq!(block_ranges.len(), 3);

    // 输出字符表
    let mut output = create_out_file("char_blocks.rs")?;
    writeln!(output, "&[")?;
    let mut data_iter = data.iter().peekable();
    for (start, end) in block_ranges {
        let len = usize::try_from(end - start).unwrap();
        let mut block = vec![0; len];
        let mut heteronym = vec![0; len];
        while let Some((code, list)) = data_iter.peek() {
            let idx = usize::try_from(*code - start).unwrap();
            if idx >= len {
                break;
            }
            block[idx] = *pinyin_index.get(list[0]).unwrap();
            if cfg!(feature = "heteronym") {
                heteronym[idx] = *heteronym_index.get(code).unwrap();
            }
            data_iter.next();
        }
        write!(output, "    CharBlock {{ start_code: {}, data: &[", start)?;
        for idx in block {
            write!(output, "{}, ", idx)?;
        }
        write!(output, "], ")?;
        if cfg!(feature = "heteronym") {
            write!(output, "heteronym: &[")?;
            for idx in heteronym {
                write!(output, "{}, ", idx)?;
            }
            write!(output, "], ")?;
        }
        writeln!(output, "}},")?;
    }
    writeln!(output, "]")?;
    Ok(())
}

fn create_out_file(name: &str) -> io::Result<impl Write> {
    let path = Path::new(&env::var("OUT_DIR").unwrap()).join(name);
    Ok(BufWriter::new(File::create(&path)?))
}

#[cfg(any(
    feature = "plain",
    feature = "with_tone_num",
    feature = "with_tone_num_end"
))]
fn get_char_info(ch: char) -> (Option<char>, u8) {
    if let Some((_, base, tone)) = PHONETIC_SYMBOL_MAP.iter().find(|(c, _, _)| *c == ch) {
        return (Some(*base), *tone);
    }
    const TONE_MAP: &[(char, u8)] = &[('\u{304}', 1), ('\u{30c}', 3), ('\u{300}', 4)];
    if let Some((_, tone)) = TONE_MAP.iter().find(|(c, _)| *c == ch) {
        return (None, *tone);
    }
    (Some(ch), 0)
}
<commit_msg>Fix build failed on stable version of rust<commit_after>use std::borrow::Cow;
use std::char;
use std::collections::hash_map::Entry;
use std::collections::HashMap;
use std::convert::TryFrom;
use std::env;
use std::fs::File;
use std::io::{self, BufWriter, Write};
use std::path::Path;

const RAW_DATA: &str = include_str!(concat!(
    env!("CARGO_MANIFEST_DIR"),
    "/pinyin-data/pinyin.txt"
));

#[cfg(any(
    feature = "plain",
    feature = "with_tone_num",
    feature = "with_tone_num_end"
))]
const PHONETIC_SYMBOL_MAP: &[(char, char, u8)] = &[
    ('ā', 'a', 1),
    ('á', 'a', 2),
    ('ǎ', 'a', 3),
    ('à', 'a', 4),
    ('ē', 'e', 1),
    ('é', 'e', 2),
    ('ě', 'e', 3),
    ('è', 'e', 4),
    ('ế', 'ê', 2),
    ('ề', 'ê', 4),
    ('ō', 'o', 1),
    ('ó', 'o', 2),
    ('ǒ', 'o', 3),
    ('ò', 'o', 4),
    ('ī', 'i', 1),
    ('í', 'i', 2),
    ('ǐ', 'i', 3),
    ('ì', 'i', 4),
    ('ū', 'u', 1),
    ('ú', 'u', 2),
    ('ǔ', 'u', 3),
    ('ù', 'u', 4),
    ('ǘ', 'ü', 2),
    ('ǚ', 'ü', 3),
    ('ǜ', 'ü', 4),
    ('ń', 'n', 2),
    ('ň', 'n', 3),
    ('ǹ', 'n', 4),
    ('ḿ', 'm', 2),
];

#[rustfmt::skip]
const LETTER_TABLE: &[char] = &[
    'b', 'p', 'm', 'f', 'd',
    't', 'n', 'l', 'g', 'k',
    'h', 'j', 'q', 'x', 'r',
    'z', 'c', 's',
                   'w', 'y',
    // 因为数据源里面不会使用 `v` 以及其它的 简写字母，所以这里注释掉
    // 'v', 'ẑ', 'ĉ', 'ŝ', 'ŋ',

    '\u{0304}', '\u{030C}', '\u{0300}', // Unicode 声调连字符
    'a', 'ā', 'á', 'ǎ', 'à',
    'e', 'ē', 'é', 'ě', 'è',
    'i', 'ī', 'í', 'ǐ', 'ì',
    //   "m̄"       "m̌"  "m̀"
    'm',      'ḿ',
    //   "n̄"
    'n',      'ń', 'ň', 'ǹ',
    'o', 'ō', 'ó', 'ǒ', 'ò',
    'u', 'ū', 'ú', 'ǔ', 'ù',
    //   "ê̄"       "ê̌"
    'ê',      'ế',      'ề',
    //   'ǖ'
    'ü',      'ǘ', 'ǚ', 'ǜ',
];

#[cfg(any(feature = "with_tone_num", feature = "with_tone_num_end"))]
const TONE_NUMS: &[char] = &['0', '1', '2', '3', '4'];

type Style = (&'static str, fn(&str) -> Cow<'_, str>);
type InputData = Vec<(u32, Vec<&'static str>)>;
type PinyinDataIndex = HashMap<&'static str, usize>;
type HeteronymDataIndex = HashMap<u32, usize>;

fn main() -> io::Result<()> {
    let data = build_data();
    let pinyin_index = generate_pinyin_data(&data)?;
    let heteronym_index = generate_heteronym_table(&data, &pinyin_index)?;
    generate_char_table(&data, &pinyin_index, &heteronym_index)?;
    // 输出这行以保证改动项目的其他文件不会触发编译脚本重新执行
    println!("cargo:rerun-if-changed=build.rs");
    Ok(())
}

fn build_data() -> InputData {
    let mut input_data = RAW_DATA
        .lines()
        .enumerate()
        // 移除注释和空格
        .map(|(i, mut line)| {
            if let Some(hash_pos) = line.find('#') {
                line = &line[..hash_pos];
            }
            (i, line.trim())
        })
        // 移除空行
        .filter(|(_, line)| !line.is_empty())
        .map(|(i, line)| {
            // Split the line by colon
            let colon_pos = match line.find(':') {
                Some(pos) => pos,
                None => unreachable!("no colon found in line {}", i),
            };
            let code_point = line[..colon_pos].trim();
            let pinyin_list: Vec<_> = line[colon_pos + 1..].trim().split(',').collect();

            // 确保输入数据的字符全部在我们预料之中。
            // 同时也可以提前知道一些被遗弃的码位，如: U+E7C8 和 U+E7C7
            for pinyin in pinyin_list.iter() {
                for ch in pinyin.chars() {
                    let is_known = LETTER_TABLE.contains(&ch);
                    assert!(
                        is_known,
                        "unknown character {:?} at line {}: {}",
                        ch, i, line,
                    );
                }
            }

            // 解析码位
            const CODE_POINT_PREFIX: &str = "U+";
            assert!(code_point.starts_with(CODE_POINT_PREFIX));
            let code = &code_point[CODE_POINT_PREFIX.len()..];
            let code = match u32::from_str_radix(code, 16) {
                Ok(code) => code,
                Err(_) => unreachable!("invalid code point {} at line {}", code, i),
            };
            (code, pinyin_list)
        })
        .collect::<Vec<_>>();
    input_data.sort_by_key(|(code, _)| *code);
    input_data
}

const STYLES: &[Style] = &[
    #[cfg(feature = "plain")]
    ("plain", |input| {
        input.chars().filter_map(|c| get_char_info(c).0).collect()
    }),
    #[cfg(feature = "with_tone")]
    ("with_tone", |input| Cow::from(input)),
    #[cfg(feature = "with_tone_num")]
    ("with_tone_num", |input| {
        let mut result = String::new();
        for ch in input.chars() {
            let (ch, tone) = get_char_info(ch);
            if let Some(ch) = ch {
                result.push(ch);
            }
            if tone > 0 {
                result.push(TONE_NUMS[usize::try_from(tone).unwrap()]);
            }
        }
        result.into()
    }),
    #[cfg(feature = "with_tone_num_end")]
    ("with_tone_num_end", |input| {
        let mut result = String::new();
        let mut output_tone = None;
        for ch in input.chars() {
            let (ch, tone) = get_char_info(ch);
            if let Some(ch) = ch {
                result.push(ch);
            }
            if tone > 0 {
                assert!(output_tone.is_none());
                output_tone = Some(TONE_NUMS[usize::try_from(tone).unwrap()]);
            }
        }
        if let Some(tone) = output_tone {
            result.push(tone);
        }
        result.into()
    }),
];

fn generate_pinyin_data(data: &InputData) -> io::Result<PinyinDataIndex> {
    let mut output = create_out_file("pinyin_data.rs")?;
    let mut pinyin_data = HashMap::new();
    writeln!(output, "&[")?;
    let mut process_pinyin = |pinyin| {
        let index = pinyin_data.len();
        match pinyin_data.entry(pinyin) {
            Entry::Occupied(_) => return Ok(()),
            Entry::Vacant(entry) => {
                entry.insert(index);
            }
        }
        write!(output, "    PinyinData {{ ")?;
        for (field, converter) in STYLES.iter() {
            write!(output, r#"{}: "{}", "#, field, converter(pinyin))?;
        }
        #[cfg(feature = "compat")]
        {
            // 计算切分声母和韵母的位置
            const INITIALS: &[&str] = &[
                "b", "p", "m", "f", "d", "t", "n", "l", "g", "k", "h", "j", "q", "x", "r", "zh",
                "ch", "sh", "z", "c", "s",
            ];
            let split = INITIALS
                .iter()
                .find(|initial| pinyin.starts_with(*initial))
                .map_or(0, |initial| initial.len());
            write!(output, "split: {}, ", split)?;
        }
        writeln!(output, "}},")?;
        Ok(())
    };
    // 插入一个空的拼音数据作为零位
    process_pinyin("")?;
    data.iter()
        .flat_map(|(_, list)| list.iter().map(|s| *s))
        .map(process_pinyin)
        .collect::<io::Result<()>>()?;
    writeln!(output, "]")?;
    Ok(pinyin_data)
}

#[cfg(feature = "heteronym")]
fn generate_heteronym_table(
    data: &InputData,
    index: &PinyinDataIndex,
) -> io::Result<HeteronymDataIndex> {
    let mut heteronym_list_index = HashMap::new();
    let mut heteronym_index = HashMap::new();
    let mut output = create_out_file("heteronym_table.rs")?;
    writeln!(output, "&[")?;
    writeln!(output, "    &[],")?;
    heteronym_list_index.insert(vec![].into_boxed_slice(), 0);
    data.iter()
        .map(|(code, list)| {
            let list = list[1..]
                .iter()
                .map(|pinyin| *index.get(pinyin).unwrap())
                .collect::<Box<[_]>>();
            let new_idx = heteronym_list_index.len();
            let idx = match heteronym_list_index.entry(list) {
                Entry::Occupied(entry) => *entry.get(),
                Entry::Vacant(entry) => {
                    write!(output, "    &[")?;
                    for i in entry.key().iter() {
                        write!(output, "{}, ", i)?;
                    }
                    writeln!(output, "],")?;
                    entry.insert(new_idx);
                    new_idx
                }
            };
            heteronym_index.insert(*code, idx);
            Ok(())
        })
        .collect::<io::Result<()>>()?;
    writeln!(output, "]")?;
    Ok(heteronym_index)
}

#[cfg(not(feature = "heteronym"))]
fn generate_heteronym_table(
    _data: &InputData,
    _index: &PinyinDataIndex,
) -> io::Result<HeteronymDataIndex> {
    Ok(HashMap::new())
}

fn generate_char_table(
    data: &InputData,
    pinyin_index: &PinyinDataIndex,
    heteronym_index: &HeteronymDataIndex,
) -> io::Result<()> {
    // 将码位分入数据块
    const GAP_THRESHOLD: u32 = 2048;
    let mut block_ranges = vec![];
    data.iter()
        .for_each(|(code, _)| match block_ranges.last_mut() {
            Some((_, end)) if *end + GAP_THRESHOLD > *code => *end = *code + 1,
            _ => block_ranges.push((*code, *code + 1)),
        });
    // 当我们允许最大2048个空位时，我们目前会切出3个块。如果这个数字在未来增加了，我们也许会希望调整策略。
    assert_eq!(block_ranges.len(), 4);

    // 输出字符表
    let mut output = create_out_file("char_blocks.rs")?;
    writeln!(output, "&[")?;
    let mut data_iter = data.iter().peekable();
    for (start, end) in block_ranges {
        let len = usize::try_from(end - start).unwrap();
        let mut block = vec![0; len];
        let mut heteronym = vec![0; len];
        while let Some((code, list)) = data_iter.peek() {
            let idx = usize::try_from(*code - start).unwrap();
            if idx >= len {
                break;
            }
            block[idx] = *pinyin_index.get(list[0]).unwrap();
            if cfg!(feature = "heteronym") {
                heteronym[idx] = *heteronym_index.get(code).unwrap();
            }
            data_iter.next();
        }
        write!(output, "    CharBlock {{ start_code: {}, data: &[", start)?;
        for idx in block {
            write!(output, "{}, ", idx)?;
        }
        write!(output, "], ")?;
        if cfg!(feature = "heteronym") {
            write!(output, "heteronym: &[")?;
            for idx in heteronym {
                write!(output, "{}, ", idx)?;
            }
            write!(output, "], ")?;
        }
        writeln!(output, "}},")?;
    }
    writeln!(output, "]")?;
    Ok(())
}

fn create_out_file(name: &str) -> io::Result<impl Write> {
    let path = Path::new(&env::var("OUT_DIR").unwrap()).join(name);
    Ok(BufWriter::new(File::create(&path)?))
}

#[cfg(any(
    feature = "plain",
    feature = "with_tone_num",
    feature = "with_tone_num_end"
))]
fn get_char_info(ch: char) -> (Option<char>, u8) {
    if let Some((_, base, tone)) = PHONETIC_SYMBOL_MAP.iter().find(|(c, _, _)| *c == ch) {
        return (Some(*base), *tone);
    }
    const TONE_MAP: &[(char, u8)] = &[('\u{304}', 1), ('\u{30c}', 3), ('\u{300}', 4)];
    if let Some((_, tone)) = TONE_MAP.iter().find(|(c, _)| *c == ch) {
        return (None, *tone);
    }
    (Some(ch), 0)
}
<|endoftext|>


<commit_before>use toml::Value;

/// Check whether the configuration is valid for the store
///
/// The passed `Value` _must be_ the `[store]` sub-tree of the configuration. Otherwise this will
/// fail.
///
/// It checks whether the configuration looks like the store wants it to be:
///
/// ```toml
/// [store]
/// pre-create-hook-aspects = [ "misc", "encryption", "version-control"]
///
/// [[aspects.misc]]
/// parallel = true
/// [[aspects.encryption]]
/// parallel = false
/// [[aspects.version-control]]
/// parallel = false
///
/// [[hooks.gnupg]]
/// aspect = "encryption"
/// key = "0x123456789"
///
/// [[hooks.git]]
/// aspect = "version-control"
/// ```
///
/// It checks:
///  * Whether all the maps are there (whether store, store.aspects, store.aspects.example are all
///  maps)
///  * Whether each aspect configuration has a "parallel = <Boolean>" setting
///  * Whether each hook congfiguration has a "aspect = <String>" setting
///
/// It does NOT check:
///  * Whether all aspects which are used in the hook configuration are also configured
///
/// No configuration is a valid configuration, as the store will use the most conservative settings
/// automatically. This has also performance impact, as all hooks run in no-parallel mode then.
/// You have been warned!
///
///
pub fn config_is_valid(config: &Option<Value>) -> bool {
    use std::collections::BTreeMap;
    use std::io::Write;
    use std::io::stderr;

    if config.is_none() {
        return true;
    }

    fn has_key_with_string_ary(v: &BTreeMap<String, Value>, key: &str) -> bool {
        v.get(key)
            .map_or_else(|| {
                write!(stderr(), "Required key '{}' is not in store config", key).ok();
                false
            }, |t| match *t {
                    Value::Array(ref a) => a.iter().all(|elem| {
                        match *elem {
                            Value::String(_) => true,
                            _ => false,
                        }
                    }),
                    _ => {
                        write!(stderr(), "Key '{}' in store config should contain an array", key)
                            .ok();
                        false
                    }
            })
    }

    /// Check that
    /// * the top-level configuration
    /// * is a table
    /// * where all entries of a key `section` (eg. "hooks" or "aspects")
    ///     * Are maps
    ///     * where each has a key `key` (eg. "aspect" or "parallel")
    ///     * which fullfills constraint `f` (typecheck)
    fn check_all_inner_maps_have_key_with<F>(store_config: &BTreeMap<String, Value>,
                                             section: &str,
                                             key: &str,
                                             f: F)
        -> bool
        where F: Fn(&Value) -> bool
    {
        store_config.get(section) // The store config has the section `section`
            .map_or_else(|| {
                write!(stderr(), "Store config expects section '{}' to be present, but isn't.",
                        section).ok();
                false
            }, |section_table| {
                match *section_table { // which is
                    Value::Table(ref section_table) => // a table
                        section_table
                            .iter() // which has values,
                            .all(|(inner_key, cfg)| { // and all of these values
                                match *cfg {
                                    Value::Table(ref hook_config) => { // are tables
                                        hook_config.get(key) // with a key
                                            // fullfilling this constraint
                                            .map_or(false, |hook_aspect| f(&hook_aspect))
                                    },
                                    _ => {
                                        write!(stderr(), "Store config expects '{}' to be in '{}.{}', but isn't.",
                                                 key, section, inner_key).ok();
                                        false
                                    }
                                }
                            }),
                    _ => {
                        write!(stderr(), "Store config expects '{}' to be a Table, but isn't.",
                               section).ok();
                        false
                    }
                }
            })
    }

    match *config {
        Some(Value::Table(ref t)) => {
            has_key_with_string_ary(t, "store-unload-hook-aspects")    &&

            has_key_with_string_ary(t, "pre-create-hook-aspects")      &&
            has_key_with_string_ary(t, "post-create-hook-aspects")     &&
            has_key_with_string_ary(t, "pre-retrieve-hook-aspects")    &&
            has_key_with_string_ary(t, "post-retrieve-hook-aspects")   &&
            has_key_with_string_ary(t, "pre-update-hook-aspects")      &&
            has_key_with_string_ary(t, "post-update-hook-aspects")     &&
            has_key_with_string_ary(t, "pre-delete-hook-aspects")      &&
            has_key_with_string_ary(t, "post-delete-hook-aspects")     &&

            // The section "hooks" has maps which have a key "aspect" which has a value of type
            // String
            check_all_inner_maps_have_key_with(t, "hooks", "aspect",
                                               |asp| is_match!(asp, &Value::String(_))) &&

            // The section "aspects" has maps which have a key "parllel" which has a value of type
            // Boolean
            check_all_inner_maps_have_key_with(t, "aspects", "parallel",
                                               |asp| is_match!(asp, &Value::Boolean(_)))
        }
        _ => {
            write!(stderr(), "Store config is no table").ok();
            false
        },
    }
}

pub fn get_store_unload_aspect_names(value: &Option<Value>) -> Vec<String> {
    get_aspect_names_for_aspect_position("store-unload-hook-aspects", value)
}

pub fn get_pre_create_aspect_names(value: &Option<Value>) -> Vec<String> {
    get_aspect_names_for_aspect_position("pre-create-hook-aspects", value)
}

pub fn get_post_create_aspect_names(value: &Option<Value>) -> Vec<String> {
    get_aspect_names_for_aspect_position("post-create-hook-aspects", value)
}

pub fn get_pre_retrieve_aspect_names(value: &Option<Value>) -> Vec<String> {
    get_aspect_names_for_aspect_position("pre-retrieve-hook-aspects", value)
}

pub fn get_post_retrieve_aspect_names(value: &Option<Value>) -> Vec<String> {
    get_aspect_names_for_aspect_position("post-retrieve-hook-aspects", value)
}

pub fn get_pre_update_aspect_names(value: &Option<Value>) -> Vec<String> {
    get_aspect_names_for_aspect_position("pre-update-hook-aspects", value)
}

pub fn get_post_update_aspect_names(value: &Option<Value>) -> Vec<String> {
    get_aspect_names_for_aspect_position("post-update-hook-aspects", value)
}

pub fn get_pre_delete_aspect_names(value: &Option<Value>) -> Vec<String> {
    get_aspect_names_for_aspect_position("pre-delete-hook-aspects", value)
}

pub fn get_post_delete_aspect_names(value: &Option<Value>) -> Vec<String> {
    get_aspect_names_for_aspect_position("post-delete-hook-aspects", value)
}

pub fn get_pre_move_aspect_names(value: &Option<Value>) -> Vec<String> {
    get_aspect_names_for_aspect_position("pre-move-hook-aspects", value)
}

pub fn get_post_move_aspect_names(value: &Option<Value>) -> Vec<String> {
    get_aspect_names_for_aspect_position("post-move-hook-aspects", value)
}

#[derive(Debug)]
pub struct AspectConfig {
    parallel: bool,
    config: Value,
}

impl AspectConfig {

    pub fn new(init: Value) -> AspectConfig {
        let parallel = AspectConfig::is_parallel(&init);
        AspectConfig {
            config: init,
            parallel: parallel,
        }
    }

    fn is_parallel(init: &Value) -> bool {
        match *init {
            Value::Table(ref t) =>
                t.get("parallel")
                    .map_or(false, |value| {
                        match *value {
                            Value::Boolean(b) => b,
                            _ => false,
                        }
                    }),
            _ => false,
        }
    }

    /// Get the aspect configuration for an aspect.
    ///
    /// Pass the store configuration object, this searches in `[aspects][<aspect_name>]`.
    ///
    /// Returns `None` if one of the keys in the chain is not available
    pub fn get_for(v: &Option<Value>, a_name: String) -> Option<AspectConfig> {
        match *v {
            Some(Value::Table(ref tabl)) => tabl.get(&a_name[..])
                .map(|asp| AspectConfig::new(asp.clone())),
            _ => None,
        }
    }

}

fn get_aspect_names_for_aspect_position(config_name: &'static str, value: &Option<Value>) -> Vec<String> {
    let mut v = vec![];

    match *value {
        Some(Value::Table(ref t)) => {
            match t.get(config_name) {
                Some(&Value::Array(ref a)) => {
                    for elem in a {
                        match *elem {
                            Value::String(ref s) => v.push(s.clone()),
                            _ => warn!("Non-String in configuration, inside '{}'", config_name),
                        }
                    }
                },
                _ => warn!("'{}' configuration key should contain Array, does not", config_name),
            };
        },
        None => warn!("No store configuration"),
        _ => warn!("Configuration is not a table"),
    }
    v
}

<commit_msg>Add configuration parsing/getter for denying of mutable hooks<commit_after>use toml::Value;

/// Check whether the configuration is valid for the store
///
/// The passed `Value` _must be_ the `[store]` sub-tree of the configuration. Otherwise this will
/// fail.
///
/// It checks whether the configuration looks like the store wants it to be:
///
/// ```toml
/// [store]
/// pre-create-hook-aspects = [ "misc", "encryption", "version-control"]
///
/// [[aspects.misc]]
/// parallel = true
/// [[aspects.encryption]]
/// parallel = false
/// [[aspects.version-control]]
/// parallel = false
///
/// [[hooks.gnupg]]
/// aspect = "encryption"
/// key = "0x123456789"
///
/// [[hooks.git]]
/// aspect = "version-control"
/// ```
///
/// It checks:
///  * Whether all the maps are there (whether store, store.aspects, store.aspects.example are all
///  maps)
///  * Whether each aspect configuration has a "parallel = <Boolean>" setting
///  * Whether each hook congfiguration has a "aspect = <String>" setting
///
/// It does NOT check:
///  * Whether all aspects which are used in the hook configuration are also configured
///
/// No configuration is a valid configuration, as the store will use the most conservative settings
/// automatically. This has also performance impact, as all hooks run in no-parallel mode then.
/// You have been warned!
///
///
pub fn config_is_valid(config: &Option<Value>) -> bool {
    use std::collections::BTreeMap;
    use std::io::Write;
    use std::io::stderr;

    if config.is_none() {
        return true;
    }

    fn has_key_with_string_ary(v: &BTreeMap<String, Value>, key: &str) -> bool {
        v.get(key)
            .map_or_else(|| {
                write!(stderr(), "Required key '{}' is not in store config", key).ok();
                false
            }, |t| match *t {
                    Value::Array(ref a) => a.iter().all(|elem| {
                        match *elem {
                            Value::String(_) => true,
                            _ => false,
                        }
                    }),
                    _ => {
                        write!(stderr(), "Key '{}' in store config should contain an array", key)
                            .ok();
                        false
                    }
            })
    }

    /// Check that
    /// * the top-level configuration
    /// * is a table
    /// * where all entries of a key `section` (eg. "hooks" or "aspects")
    ///     * Are maps
    ///     * where each has a key `key` (eg. "aspect" or "parallel")
    ///     * which fullfills constraint `f` (typecheck)
    fn check_all_inner_maps_have_key_with<F>(store_config: &BTreeMap<String, Value>,
                                             section: &str,
                                             key: &str,
                                             f: F)
        -> bool
        where F: Fn(&Value) -> bool
    {
        store_config.get(section) // The store config has the section `section`
            .map_or_else(|| {
                write!(stderr(), "Store config expects section '{}' to be present, but isn't.",
                        section).ok();
                false
            }, |section_table| {
                match *section_table { // which is
                    Value::Table(ref section_table) => // a table
                        section_table
                            .iter() // which has values,
                            .all(|(inner_key, cfg)| { // and all of these values
                                match *cfg {
                                    Value::Table(ref hook_config) => { // are tables
                                        hook_config.get(key) // with a key
                                            // fullfilling this constraint
                                            .map_or(false, |hook_aspect| f(&hook_aspect))
                                    },
                                    _ => {
                                        write!(stderr(), "Store config expects '{}' to be in '{}.{}', but isn't.",
                                                 key, section, inner_key).ok();
                                        false
                                    }
                                }
                            }),
                    _ => {
                        write!(stderr(), "Store config expects '{}' to be a Table, but isn't.",
                               section).ok();
                        false
                    }
                }
            })
    }

    match *config {
        Some(Value::Table(ref t)) => {
            has_key_with_string_ary(t, "store-unload-hook-aspects")    &&

            has_key_with_string_ary(t, "pre-create-hook-aspects")      &&
            has_key_with_string_ary(t, "post-create-hook-aspects")     &&
            has_key_with_string_ary(t, "pre-retrieve-hook-aspects")    &&
            has_key_with_string_ary(t, "post-retrieve-hook-aspects")   &&
            has_key_with_string_ary(t, "pre-update-hook-aspects")      &&
            has_key_with_string_ary(t, "post-update-hook-aspects")     &&
            has_key_with_string_ary(t, "pre-delete-hook-aspects")      &&
            has_key_with_string_ary(t, "post-delete-hook-aspects")     &&

            // The section "hooks" has maps which have a key "aspect" which has a value of type
            // String
            check_all_inner_maps_have_key_with(t, "hooks", "aspect",
                                               |asp| is_match!(asp, &Value::String(_))) &&

            // The section "aspects" has maps which have a key "parllel" which has a value of type
            // Boolean
            check_all_inner_maps_have_key_with(t, "aspects", "parallel",
                                               |asp| is_match!(asp, &Value::Boolean(_)))
        }
        _ => {
            write!(stderr(), "Store config is no table").ok();
            false
        },
    }
}

pub fn get_store_unload_aspect_names(value: &Option<Value>) -> Vec<String> {
    get_aspect_names_for_aspect_position("store-unload-hook-aspects", value)
}

pub fn get_pre_create_aspect_names(value: &Option<Value>) -> Vec<String> {
    get_aspect_names_for_aspect_position("pre-create-hook-aspects", value)
}

pub fn get_post_create_aspect_names(value: &Option<Value>) -> Vec<String> {
    get_aspect_names_for_aspect_position("post-create-hook-aspects", value)
}

pub fn get_pre_retrieve_aspect_names(value: &Option<Value>) -> Vec<String> {
    get_aspect_names_for_aspect_position("pre-retrieve-hook-aspects", value)
}

pub fn get_post_retrieve_aspect_names(value: &Option<Value>) -> Vec<String> {
    get_aspect_names_for_aspect_position("post-retrieve-hook-aspects", value)
}

pub fn get_pre_update_aspect_names(value: &Option<Value>) -> Vec<String> {
    get_aspect_names_for_aspect_position("pre-update-hook-aspects", value)
}

pub fn get_post_update_aspect_names(value: &Option<Value>) -> Vec<String> {
    get_aspect_names_for_aspect_position("post-update-hook-aspects", value)
}

pub fn get_pre_delete_aspect_names(value: &Option<Value>) -> Vec<String> {
    get_aspect_names_for_aspect_position("pre-delete-hook-aspects", value)
}

pub fn get_post_delete_aspect_names(value: &Option<Value>) -> Vec<String> {
    get_aspect_names_for_aspect_position("post-delete-hook-aspects", value)
}

pub fn get_pre_move_aspect_names(value: &Option<Value>) -> Vec<String> {
    get_aspect_names_for_aspect_position("pre-move-hook-aspects", value)
}

pub fn get_post_move_aspect_names(value: &Option<Value>) -> Vec<String> {
    get_aspect_names_for_aspect_position("post-move-hook-aspects", value)
}

#[derive(Debug)]
pub struct AspectConfig {
    parallel: bool,
    mutable_hooks: bool,
    config: Value,
}

impl AspectConfig {

    pub fn new(init: Value) -> AspectConfig {
        let parallel = AspectConfig::is_parallel(&init);
        let muthooks = AspectConfig::allows_mutable_hooks(&init);
        AspectConfig {
            config: init,
            mutable_hooks: muthooks,
            parallel: parallel,
        }
    }

    fn is_parallel(init: &Value) -> bool {
        match *init {
            Value::Table(ref t) =>
                t.get("parallel")
                    .map_or(false, |value| {
                        match *value {
                            Value::Boolean(b) => b,
                            _ => false,
                        }
                    }),
            _ => false,
        }
    }

    fn allows_mutable_hooks(init: &Value) -> bool {
        match *init {
            Value::Table(ref t) =>
                t.get("mutable_hooks")
                    .map_or(false, |value| {
                        match *value {
                            Value::Boolean(b) => b,
                            _ => false,
                        }
                    }),
            _ => false,
        }
    }

    pub fn allow_mutable_hooks(&self) -> bool {
        self.mutable_hooks
    }

    /// Get the aspect configuration for an aspect.
    ///
    /// Pass the store configuration object, this searches in `[aspects][<aspect_name>]`.
    ///
    /// Returns `None` if one of the keys in the chain is not available
    pub fn get_for(v: &Option<Value>, a_name: String) -> Option<AspectConfig> {
        match *v {
            Some(Value::Table(ref tabl)) => tabl.get(&a_name[..])
                .map(|asp| AspectConfig::new(asp.clone())),
            _ => None,
        }
    }

}

fn get_aspect_names_for_aspect_position(config_name: &'static str, value: &Option<Value>) -> Vec<String> {
    let mut v = vec![];

    match *value {
        Some(Value::Table(ref t)) => {
            match t.get(config_name) {
                Some(&Value::Array(ref a)) => {
                    for elem in a {
                        match *elem {
                            Value::String(ref s) => v.push(s.clone()),
                            _ => warn!("Non-String in configuration, inside '{}'", config_name),
                        }
                    }
                },
                _ => warn!("'{}' configuration key should contain Array, does not", config_name),
            };
        },
        None => warn!("No store configuration"),
        _ => warn!("Configuration is not a table"),
    }
    v
}

<|endoftext|>


<commit_before>
fn taylor_atan (x: f64) -> f64 {
    //let x: f64 = 0.3;
    let mut a: f64 = 0.0;
    let mut s: f64 = 1.0;
    let mut xpow: f64;

    for ii in range(1i, 16) {
        if ii % 2 == 0 {
            continue;
        }

        xpow = 1.0;
        for jj in range(0i, ii) {
            xpow *= x;
        }

        a += s * xpow / (ii as f64);

        s = -1.0*s;
    }

    println!("{}", a);
    return a;
}

fn generate_table () {

    let pi: f64 = 3.1415926536897932384626;
    let k1: f64 = 0.6072529350088812561694; // 1/k
    let num_bits: uint = 32;
    //let num_elms: uint = num_bits;
    let mul: uint = 1<<(num_bits-2);

    println!("Cordic sin in rust");
    println!("num bits {}", num_bits);
    println!("mul {}", mul);
    println!("1<<(num_bits-2) {}", ((1i << (num_bits-2)) as f64));
    println!("pi is {}", pi);
    println!("k1 is {}", k1);

    println!("let cordic_tab = [");

    let shift: f64 = 2.0;
    for ii in range(0, num_bits) {
        let ipow: f64 = 1f64/shift.powi(ii as i32);
        let cur: f64 = ipow.atan() * (mul as f64);

        if ii % 4 == 0 && ii > 0 {
            println!("");
        }
        if ii == 0 {
            print!("0x{}, ", std::f64::to_str_hex(cur.floor()));
        } else {
            print!("0x{}, ", std::f64::to_str_hex(cur.floor()));
        }
    }

    println!("];");
}

fn int_to_f64 (lhs: int) -> f64 {
    let rhs: f64 = unsafe {
        std::mem::transmute(lhs)
    };
    return rhs;
}

fn f64_to_int (lhs: f64) -> int {
    let rhs: int = unsafe {
        std::mem::transmute(lhs)
    };
    return rhs;
}

fn sin(theta: f64) -> f64 {

    let num_bits: uint = 32;

    let k1: f64 = 0.6072529350088812561694; // 1/k
    let muli: int = 1<<(num_bits-2);
    let mul: f64 = muli as f64;

    let cordic_k1: f64 = k1 * mul;
    //println!("k1 - {}, mul - {}, cordic_k1 - {:X}", k1, mul, cordic_k1);

    let mut x: int = cordic_k1.floor() as int; //0x26DD3B6A;
    let mut y: int = 0;
    let mut z: int = (mul * theta).floor() as int;
    let mut tx: int;
    let mut ty: int;
    let mut tz: int;

    //println!("z {}", z);

    let mut d: int;
    let cordic_tab = [
        0x3243f6a8, 0x1dac6705, 0xfadbafc, 0x7f56ea6,
        0x3feab76, 0x1ffd55b, 0xfffaaa, 0x7fff55,
        0x3fffea, 0x1ffffd, 0xfffff, 0x7ffff,
        0x3ffff, 0x1ffff, 0xffff, 0x7fff,
        0x3fff, 0x1fff, 0xfff, 0x7ff,
        0x3ff, 0x1ff, 0xff, 0x7f,
        0x3f, 0x1f, 0xf, 0x8,
        0x4, 0x2, 0x1, 0x0, ];

    for k in range(0, num_bits) {
        if z >= 0 {
            d = 0;
        } else {
            d = -1;
        }

        //println!("z {}", z);

        tx = x - (((y>>k) ^ d) - d);
        ty = y + (((x>>k) ^ d) - d);
        tz = z - ((cordic_tab[k] ^ d) - d);
        //println!("{} {} {} {} {} {}", k, x, y, tx, ty, tz);
        x = tx; y = ty; z = tz;
    }

    let s: f64 = (y as f64)/(mul as f64);

    return s;

}

fn main() {
    let i: int = 42882;
    let f: f64 = 3498043.4824;
    assert!(int_to_f64(f64_to_int(f)) == f);
    assert!(f64_to_int(int_to_f64(i)) == i);

    generate_table();

    //let theta: f64 = 33732594.261305;

    let frac_pi_2: f64 = std::f64::consts::FRAC_PI_2; //3.1415926536897932384626/2.0;

    for ii in range(0i, 50) {
        let cur: f64 = ii as f64;
        let theta: f64 = cur/50.0 * frac_pi_2;
        let s: f64 = sin(theta);
            println!("s {}", s);
    }

}
<commit_msg>cleanup<commit_after>fn taylor_atan (x: f64) -> f64 {
    let mut a: f64 = 0.0;
    let mut s: f64 = 1.0;
    let mut xpow: f64;

    for ii in range(1i, 16) {
        if ii % 2 == 0 {
            continue;
        }
        xpow = 1.0;
        for jj in range(0i, ii) {
            xpow *= x;
        }
        a += s * xpow / (ii as f64);
        s = -1.0*s;
    }

    println!("{}", a);
    return a;
}

fn generate_table () {

    let pi: f64 = 3.1415926536897932384626;
    let k1: f64 = 0.6072529350088812561694; // 1/k
    let num_bits: uint = 32;
    let mul: uint = 1<<(num_bits-2);

    println!("Cordic sin in rust");
    println!("num bits {}", num_bits);
    println!("mul {}", mul);
    println!("1<<(num_bits-2) {}", ((1i << (num_bits-2)) as f64));
    println!("pi is {}", pi);
    println!("k1 is {}", k1);

    println!("let cordic_tab = [");

    let shift: f64 = 2.0;
    for ii in range(0, num_bits) {
        let ipow: f64 = 1f64/shift.powi(ii as i32);
        let cur: f64 = ipow.atan() * (mul as f64);

        if ii % 4 == 0 && ii > 0 {
            println!("");
        }
        if ii == 0 {
            print!("0x{}, ", std::f64::to_str_hex(cur.floor()));
        } else {
            print!("0x{}, ", std::f64::to_str_hex(cur.floor()));
        }
    }

    println!("];");
}

fn int_to_f64 (lhs: int) -> f64 {
    let rhs: f64 = unsafe {
        std::mem::transmute(lhs)
    };
    return rhs;
}

fn f64_to_int (lhs: f64) -> int {
    let rhs: int = unsafe {
        std::mem::transmute(lhs)
    };
    return rhs;
}

fn sin(theta: f64) -> f64 {

    let num_bits: uint = 32;

    let k1: f64 = 0.6072529350088812561694; // 1/k
    let muli: int = 1<<(num_bits-2);
    let mul: f64 = muli as f64;
    let cordic_k1: f64 = k1 * mul;
    let mut x: int = cordic_k1.floor() as int; //0x26DD3B6A;
    let mut y: int = 0;
    let mut z: int = (mul * theta).floor() as int;
    let mut tx: int;
    let mut ty: int;
    let mut tz: int;

    //println!("z {}", z);

    let mut d: int;
    let cordic_tab = [
        0x3243f6a8, 0x1dac6705, 0xfadbafc, 0x7f56ea6,
        0x3feab76, 0x1ffd55b, 0xfffaaa, 0x7fff55,
        0x3fffea, 0x1ffffd, 0xfffff, 0x7ffff,
        0x3ffff, 0x1ffff, 0xffff, 0x7fff,
        0x3fff, 0x1fff, 0xfff, 0x7ff,
        0x3ff, 0x1ff, 0xff, 0x7f,
        0x3f, 0x1f, 0xf, 0x8,
        0x4, 0x2, 0x1, 0x0, ];

    for k in range(0, num_bits) {
        if z >= 0 {
            d = 0;
        } else {
            d = -1;
        }

        tx = x - (((y>>k) ^ d) - d);
        ty = y + (((x>>k) ^ d) - d);
        tz = z - ((cordic_tab[k] ^ d) - d);
        x = tx; y = ty; z = tz;
    }

    let s: f64 = (y as f64)/(mul as f64);

    return s;

}

fn main() {
    let i: int = 42882;
    let f: f64 = 3498043.4824;
    assert!(int_to_f64(f64_to_int(f)) == f);
    assert!(f64_to_int(int_to_f64(i)) == i);

    generate_table();

    let frac_pi_2: f64 = std::f64::consts::FRAC_PI_2; //3.1415926536897932384626/2.0;

    for ii in range(0i, 50) {
        let cur: f64 = ii as f64;
        let theta: f64 = cur/50.0 * frac_pi_2;
        let s: f64 = sin(theta);
            println!("s {}", s);
    }
}
<|endoftext|>


<commit_before>extern crate rev;
extern crate time;
extern crate libc;

use std::mem;
use std::io;
use std::io::prelude::*;
use std::os::unix::io::RawFd;

use rev::{Selector, EventSet};
use time::Duration;

struct Pipe {
    read: libc::c_int,
    write: libc::c_int,
}

impl Pipe {
    fn new() -> io::Result<Pipe> {
        let mut fds = [0 as libc::c_int; 2];
        unsafe {
            let ret = libc::pipe(mem::transmute(&mut fds));
            if ret != 0 {
                return Err(io::Error::last_os_error());
            }
        }
        Ok(Pipe {
            read: fds[0],
            write: fds[1],
        })
    }
}

impl Read for Pipe {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        let ret = unsafe {
            let ptr = mem::transmute(buf.as_ptr());
            libc::read(self.read, ptr, buf.len() as libc::size_t)
        };
        match ret {
            -1 => Err(io::Error::last_os_error()),
            n => Ok(n as usize),
        }
    }
}

impl Write for Pipe {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        let ret = unsafe {
            let ptr = mem::transmute(buf.as_ptr());
            libc::write(self.write, ptr, buf.len() as libc::size_t)
        };
        match ret {
            -1 => Err(io::Error::last_os_error()),
            n => Ok(n as usize),
        }
    }

    fn flush(&mut self) -> io::Result<()> {
        Ok(())
    }
}

impl Drop for Pipe {
    fn drop(&mut self) {
        unsafe {
            libc::close(self.read);
            libc::close(self.write);
        }
    }
}

#[test]
fn test_poll_timeout() {
    fn count_events(selector: &mut Selector) -> usize {
        selector.poll_timeout(Duration::milliseconds(100)).unwrap().count()
    }

    let mut pipe = Pipe::new().unwrap();
    let mut selector = Selector::new().unwrap();

    selector.register(pipe.read, EventSet::readable()).unwrap();

    assert_eq!(count_events(&mut selector), 0);
    pipe.write_all(b"hello world").unwrap();
    assert_eq!(count_events(&mut selector), 1);
}

#[test]
fn test_poll() {
    fn count_events(selector: &mut Selector) -> usize {
        selector.poll().unwrap().count()
    }

    let mut pipe1 = Pipe::new().unwrap();
    let mut pipe2 = Pipe::new().unwrap();
    let mut selector = Selector::new().unwrap();

    selector.register(pipe1.read, EventSet::readable()).unwrap();
    selector.register(pipe2.read, EventSet::readable()).unwrap();

    pipe1.write_all(b"twelve bytes").unwrap();
    assert_eq!(count_events(&mut selector), 1);
    pipe2.write_all(b"more data").unwrap();
    assert_eq!(count_events(&mut selector), 2);
    let mut buf = [0; 12];
    assert_eq!(pipe1.read(&mut buf).unwrap(), 12);
    assert_eq!(count_events(&mut selector), 1);
}

#[test]
fn test_deregister() {
    fn first_fd(selector: &mut Selector) -> RawFd {
        selector.poll().unwrap().next().unwrap().fd()
    }

    let mut pipe1 = Pipe::new().unwrap();
    let mut pipe2 = Pipe::new().unwrap();
    let mut selector = Selector::new().unwrap();

    selector.register(pipe1.read, EventSet::readable()).unwrap();
    selector.register(pipe2.read, EventSet::readable()).unwrap();
    pipe1.write_all(b"abc").unwrap();
    pipe2.write_all(b"def").unwrap();

    selector.deregister(pipe1.read).unwrap();
    assert_eq!(first_fd(&mut selector), pipe2.read);
    selector.register(pipe1.read, EventSet::readable()).unwrap();
    selector.deregister(pipe2.read).unwrap();
    assert_eq!(first_fd(&mut selector), pipe1.read);
}<commit_msg>name changing<commit_after>extern crate rivet;
extern crate time;
extern crate libc;

use std::mem;
use std::io;
use std::io::prelude::*;
use std::os::unix::io::RawFd;

use rivet::{Selector, EventSet};
use time::Duration;

struct Pipe {
    read: libc::c_int,
    write: libc::c_int,
}

impl Pipe {
    fn new() -> io::Result<Pipe> {
        let mut fds = [0 as libc::c_int; 2];
        unsafe {
            let ret = libc::pipe(mem::transmute(&mut fds));
            if ret != 0 {
                return Err(io::Error::last_os_error());
            }
        }
        Ok(Pipe {
            read: fds[0],
            write: fds[1],
        })
    }
}

impl Read for Pipe {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        let ret = unsafe {
            let ptr = mem::transmute(buf.as_ptr());
            libc::read(self.read, ptr, buf.len() as libc::size_t)
        };
        match ret {
            -1 => Err(io::Error::last_os_error()),
            n => Ok(n as usize),
        }
    }
}

impl Write for Pipe {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        let ret = unsafe {
            let ptr = mem::transmute(buf.as_ptr());
            libc::write(self.write, ptr, buf.len() as libc::size_t)
        };
        match ret {
            -1 => Err(io::Error::last_os_error()),
            n => Ok(n as usize),
        }
    }

    fn flush(&mut self) -> io::Result<()> {
        Ok(())
    }
}

impl Drop for Pipe {
    fn drop(&mut self) {
        unsafe {
            libc::close(self.read);
            libc::close(self.write);
        }
    }
}

#[test]
fn test_poll_timeout() {
    fn count_events(selector: &mut Selector) -> usize {
        selector.poll_timeout(Duration::milliseconds(100)).unwrap().count()
    }

    let mut pipe = Pipe::new().unwrap();
    let mut selector = Selector::new().unwrap();

    selector.register(pipe.read, EventSet::readable()).unwrap();

    assert_eq!(count_events(&mut selector), 0);
    pipe.write_all(b"hello world").unwrap();
    assert_eq!(count_events(&mut selector), 1);
}

#[test]
fn test_poll() {
    fn count_events(selector: &mut Selector) -> usize {
        selector.poll().unwrap().count()
    }

    let mut pipe1 = Pipe::new().unwrap();
    let mut pipe2 = Pipe::new().unwrap();
    let mut selector = Selector::new().unwrap();

    selector.register(pipe1.read, EventSet::readable()).unwrap();
    selector.register(pipe2.read, EventSet::readable()).unwrap();

    pipe1.write_all(b"twelve bytes").unwrap();
    assert_eq!(count_events(&mut selector), 1);
    pipe2.write_all(b"more data").unwrap();
    assert_eq!(count_events(&mut selector), 2);
    let mut buf = [0; 12];
    assert_eq!(pipe1.read(&mut buf).unwrap(), 12);
    assert_eq!(count_events(&mut selector), 1);
}

#[test]
fn test_deregister() {
    fn first_fd(selector: &mut Selector) -> RawFd {
        selector.poll().unwrap().next().unwrap().fd()
    }

    let mut pipe1 = Pipe::new().unwrap();
    let mut pipe2 = Pipe::new().unwrap();
    let mut selector = Selector::new().unwrap();

    selector.register(pipe1.read, EventSet::readable()).unwrap();
    selector.register(pipe2.read, EventSet::readable()).unwrap();
    pipe1.write_all(b"abc").unwrap();
    pipe2.write_all(b"def").unwrap();

    selector.deregister(pipe1.read).unwrap();
    assert_eq!(first_fd(&mut selector), pipe2.read);
    selector.register(pipe1.read, EventSet::readable()).unwrap();
    selector.deregister(pipe2.read).unwrap();
    assert_eq!(first_fd(&mut selector), pipe1.read);
}<|endoftext|>