<commit_before>extern crate glob;
extern crate env_logger;

extern crate protoc;
extern crate protoc_rust;

use std::io::Read;
use std::io::Write;
use std::fs;
use std::path;

fn glob_simple(pattern: &str) -> Vec<String> {
    glob::glob(pattern).expect("glob")
        .map(|g| {
            g.expect("item").as_path().to_str().expect("utf-8").to_owned()
        })
        .collect()
}

fn clean_old_files() {
    for f in glob_simple("src/*/pb_*.rs") {
        fs::remove_file(f).expect("rm");
    }
    for f in glob_simple("src/*/pb_*.rs") {
        fs::remove_file(f).expect("rm");
    }
}

fn generate_v_from_common() {
    let v3 = protoc::Protoc::from_env_path().version().expect("version").is_3();

    let mut mod_v2 = fs::File::create("src/common/v2/mod.rs").expect("mod.rs");
    let mut mod_v3 = fs::File::create("src/common/v3/mod.rs").expect("mod.rs");

    write!(mod_v2, "// generated\n").expect("write");
    write!(mod_v3, "// generated\n").expect("write");

    for f in glob_simple("src/common/v2/*.rs") {
        let f = path::PathBuf::from(f);
        let base_name = f.as_path().file_name().expect("file_name").to_str().expect("to_str");

        let without_suffix = &base_name[.. base_name.len() - ".rs".len()];

        if without_suffix == "mod" {
            continue;
        }

        if without_suffix.ends_with("_pb") {
            continue;
        }

        let carllerche = without_suffix.contains("carllerche");

        for mod_v in &mut [&mut mod_v2, &mut mod_v3] {
            for suffix in &["", "_pb"] {
                if carllerche {
                    write!(mod_v, "#[cfg(feature = \"bytes\")]").expect("write");
                }
                write!(mod_v, "mod {}{};\n", without_suffix, suffix).expect("write");
            }
        }

        let mut p2f = fs::File::open(&format!("src/common/v2/{}_pb.proto", without_suffix))
            .expect("open v2 .proto");
        let mut proto = String::new();
        p2f.read_to_string(&mut proto).expect("read .proto");
        drop(p2f);

        let mut r2f = fs::File::open(&format!("src/common/v2/{}.rs", without_suffix))
            .expect("open v2 .rs");
        let mut rs = String::new();
        r2f.read_to_string(&mut rs).expect("read .rs");
        drop(r2f);

        let mut p3f = fs::File::create(&format!("src/common/v3/{}_pb.proto", without_suffix))
            .expect("create v3 .proto");
        let mut r3f = fs::File::create(&format!("src/common/v3/{}.rs", without_suffix))
            .expect("create v3 .rs");

        // convert proto2 to proto3
        let proto = proto.replace("optional ", "");
        let proto = proto.replace("required ", "");
        let proto = proto.replace("syntax = \"proto2\";", "syntax = \"proto3\";");
        write!(p3f, "// generated\n").expect("write");
        write!(p3f, "{}", proto).expect("write");
        p3f.flush().expect("flush");

        write!(r3f, "// generated\n").expect("write");
        write!(r3f, "{}", rs).expect("write");
        r3f.flush().expect("flush");

        for &v in &[2, 3] {
            if v == 3 && !v3 {
                continue;
            }

            protoc_rust::run(protoc_rust::Args {
                out_dir: &format!("src/common/v{}", v),
                includes: &[&format!("src/common/v{}", v), "../proto"],
                input: &[&format!("src/common/v{}/{}_pb.proto", v, without_suffix)],
            }).expect("protoc");
        }
    }

    mod_v2.flush().expect("flush");
    mod_v3.flush().expect("flush");
}

fn generate_pb_rs() {

    fn gen_v2_v3(dir: &str) {

        let protos = glob_simple(&format!("{}/*.proto", dir));

        protoc_rust::run(protoc_rust::Args {
            out_dir: dir,
            input: &protos.iter().map(|a| a.as_ref()).collect::<Vec<&str>>(),
            includes: &["../proto", dir],
        }).expect("protoc");
    }

    let p = protoc::Protoc::from_env_path();
    let v3 = p.version().expect("version").is_3();
    gen_v2_v3("src/v2");
    if v3 {
        gen_v2_v3("src/v3");

        let protos = glob_simple("src/google/protobuf/*.proto");
        protoc_rust::run(protoc_rust::Args {
            out_dir: "src/google/protobuf",
            input: &protos.iter().map(|a| a.as_ref()).collect::<Vec<&str>>(),
            includes: &["../proto", "src"],
        }).expect("protoc");
    } else {
        // Because `#[cfg(nonexistent)]` still requires module files to exist
        // https://github.com/rust-lang/rust/pull/36482

        let g1 = glob::glob("src/v3/*.proto").expect("g1");
        let g2 = glob::glob("src/google/protobuf/*.proto").expect("g2");

        for f in g1.chain(g2) {
            let mut f: String = f.expect("f").as_path().to_str().expect("utf-8").to_owned();
            let suffix = ".proto";
            let len = f.len();
            f.truncate(len - suffix.len());

            let mut f = fs::File::create(f).expect("create");
            let content = b"// generated\n// empty file because protobuf 3 is not available\n";
            f.write_all(content).expect("write");
            f.flush().expect("flush");
        }
    }

    if p.version().expect("version").is_3() {
        println!("cargo:rustc-cfg=proto3");
    }
}

fn main() {
    env_logger::init().expect("env_logger");

    clean_old_files();
    generate_v_from_common();
    generate_pb_rs();
}
<commit_msg>Cleaner<commit_after>extern crate glob;
extern crate env_logger;

extern crate protoc;
extern crate protoc_rust;

use std::io::Read;
use std::io::Write;
use std::fs;
use std::path;

fn glob_simple(pattern: &str) -> Vec<String> {
    glob::glob(pattern).expect("glob")
        .map(|g| {
            g.expect("item").as_path().to_str().expect("utf-8").to_owned()
        })
        .collect()
}

fn clean_old_files() {
    for f in glob_simple("src/*/pb_*.rs") {
        fs::remove_file(f).expect("rm");
    }
    for f in glob_simple("src/*/pb_*.rs") {
        fs::remove_file(f).expect("rm");
    }
}

fn generate_v_from_common() {
    let v3 = protoc::Protoc::from_env_path().version().expect("version").is_3();

    let mut mod_v2 = fs::File::create("src/common/v2/mod.rs").expect("mod.rs");
    let mut mod_v3 = fs::File::create("src/common/v3/mod.rs").expect("mod.rs");

    write!(mod_v2, "// generated\n").expect("write");
    write!(mod_v3, "// generated\n").expect("write");

    for f in glob_simple("src/common/v2/*.rs") {
        let f = path::PathBuf::from(f);
        let base_name = f.as_path().file_name().expect("file_name").to_str().expect("to_str");

        let without_suffix = &base_name[.. base_name.len() - ".rs".len()];

        if without_suffix == "mod" {
            continue;
        }

        if without_suffix.ends_with("_pb") {
            continue;
        }

        let carllerche = without_suffix.contains("carllerche");

        for mod_v in &mut [&mut mod_v2, &mut mod_v3] {
            for suffix in &["", "_pb"] {
                if carllerche {
                    write!(mod_v, "#[cfg(feature = \"bytes\")]").expect("write");
                }
                write!(mod_v, "mod {}{};\n", without_suffix, suffix).expect("write");
            }
        }

        let mut p2f = fs::File::open(&format!("src/common/v2/{}_pb.proto", without_suffix))
            .expect("open v2 .proto");
        let mut proto = String::new();
        p2f.read_to_string(&mut proto).expect("read .proto");
        drop(p2f);

        let mut r2f = fs::File::open(&format!("src/common/v2/{}.rs", without_suffix))
            .expect("open v2 .rs");
        let mut rs = String::new();
        r2f.read_to_string(&mut rs).expect("read .rs");
        drop(r2f);

        let mut p3f = fs::File::create(&format!("src/common/v3/{}_pb.proto", without_suffix))
            .expect("create v3 .proto");
        let mut r3f = fs::File::create(&format!("src/common/v3/{}.rs", without_suffix))
            .expect("create v3 .rs");

        // convert proto2 to proto3
        let proto = proto.replace("optional ", "");
        let proto = proto.replace("required ", "");
        let proto = proto.replace("syntax = \"proto2\";", "syntax = \"proto3\";");
        write!(p3f, "// generated\n").expect("write");
        write!(p3f, "{}", proto).expect("write");
        p3f.flush().expect("flush");

        write!(r3f, "// generated\n").expect("write");
        write!(r3f, "{}", rs).expect("write");
        r3f.flush().expect("flush");

        for &v in &[2, 3] {
            if v == 3 && !v3 {
                continue;
            }

            protoc_rust::run(protoc_rust::Args {
                out_dir: &format!("src/common/v{}", v),
                includes: &[&format!("src/common/v{}", v), "../proto"],
                input: &[&format!("src/common/v{}/{}_pb.proto", v, without_suffix)],
            }).expect("protoc");
        }
    }

    mod_v2.flush().expect("flush");
    mod_v3.flush().expect("flush");
}

fn generate_pb_rs() {

    fn gen_v2_v3(dir: &str) {

        let protos = glob_simple(&format!("{}/*.proto", dir));

        protoc_rust::run(protoc_rust::Args {
            out_dir: dir,
            input: &protos.iter().map(|a| a.as_ref()).collect::<Vec<&str>>(),
            includes: &["../proto", dir],
        }).expect("protoc");
    }

    gen_v2_v3("src/v2");
    if protoc::Protoc::from_env_path().version().expect("version").is_3() {
        gen_v2_v3("src/v3");

        let protos = glob_simple("src/google/protobuf/*.proto");
        protoc_rust::run(protoc_rust::Args {
            out_dir: "src/google/protobuf",
            input: &protos.iter().map(|a| a.as_ref()).collect::<Vec<&str>>(),
            includes: &["../proto", "src"],
        }).expect("protoc");
    } else {
        // Because `#[cfg(nonexistent)]` still requires module files to exist
        // https://github.com/rust-lang/rust/pull/36482

        let g1 = glob::glob("src/v3/*.proto").expect("g1");
        let g2 = glob::glob("src/google/protobuf/*.proto").expect("g2");

        for f in g1.chain(g2) {
            let mut f: String = f.expect("f").as_path().to_str().expect("utf-8").to_owned();
            let suffix = ".proto";
            let len = f.len();
            f.truncate(len - suffix.len());

            let mut f = fs::File::create(f).expect("create");
            let content = b"// generated\n// empty file because protobuf 3 is not available\n";
            f.write_all(content).expect("write");
            f.flush().expect("flush");
        }
    }
}

fn main() {
    env_logger::init().expect("env_logger");

    clean_old_files();
    generate_v_from_common();
    generate_pb_rs();

    if protoc::Protoc::from_env_path().version().expect("version").is_3() {
        println!("cargo:rustc-cfg=proto3");
    }
}
<|endoftext|>


<commit_before>extern crate pb_rs;
extern crate prost_build;
extern crate protobuf_codegen_pure;

use pb_rs::types::{RpcService, Config, FileDescriptor};
use std::path::{Path, PathBuf};
use std::{env, fs};
use std::io::Write;

fn generate_rpc_add_1_test<W: Write + ?Sized>(rpc: &RpcService, w: &mut W) -> Result<(), pb_rs::errors::Error> {
    /* Example:
        trait <service> {
            fn <func>(&self, arg: &<arg>) -> Result<<ret>, failure::Error>;
        }
    */

    writeln!(w, "\npub trait {SERVICE} {{", SERVICE = rpc.service_name)?;
    for func in rpc.functions.iter() {
        writeln!(w, "   fn {FUNC}(&self, arg: &{ARG}) -> std::result::Result<{RET}, failure::Error>;",
            FUNC = func.name, ARG = func.arg, RET = func.ret)?;
    }
    writeln!(w, "}}\n")?;

    Ok(())
}

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

    // protobuf
    protobuf_codegen_pure::run(protobuf_codegen_pure::Args {
        out_dir: "src",
        input: &["src/perftest_data.proto"],
        includes: &["src"],
        customize: protobuf_codegen_pure::Customize {
            ..Default::default()
        },
    })
    .expect("protoc");

    // quick-protobuf
    let quick_dest = Path::new(&out_dir).join("perftest_data_quick.rs");
    let config = Config {
        in_file: PathBuf::from("src/perftest_data.proto"),
        out_file: quick_dest,
        single_module: true,
        import_search_path: vec![PathBuf::from("src")],
        no_output: false,
        error_cycle: false,
        headers: false,
        dont_use_cow: false,
        custom_struct_derive: vec![],
        custom_rpc_generator: Box::new(|rpc, writer| generate_rpc_add_1_test(rpc, writer))
    };
    FileDescriptor::write_proto(&config).unwrap();

    // prost
    let old = ::std::env::var("OUT_DIR");
    env::set_var("OUT_DIR", ".");
    prost_build::compile_protos(&["src/perftest_data.proto"], &["src"]).unwrap();
    let _ = old.map(|val| env::set_var("OUT_DIR", &val));
    fs::rename("perftest_data.rs", "src/perftest_data_prost.rs").unwrap();
}
<commit_msg>Added custom_includes to Config generation<commit_after>extern crate pb_rs;
extern crate prost_build;
extern crate protobuf_codegen_pure;

use pb_rs::types::{RpcService, Config, FileDescriptor};
use std::path::{Path, PathBuf};
use std::{env, fs};
use std::io::Write;

fn generate_rpc_test<W: Write + ?Sized>(rpc: &RpcService, w: &mut W) -> Result<(), pb_rs::errors::Error> {
    /* Example:
        trait <service> {
            fn <func>(&self, arg: &<arg>) -> Result<<ret>, failure::Error>;
        }
    */

    writeln!(w, "\npub trait {SERVICE} {{", SERVICE = rpc.service_name)?;
    for func in rpc.functions.iter() {
        writeln!(w, "   fn {FUNC}(&self, arg: &{ARG}) -> std::result::Result<{RET}, failure::Error>;",
            FUNC = func.name, ARG = func.arg, RET = func.ret)?;
    }
    writeln!(w, "}}\n")?;

    Ok(())
}

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

    // protobuf
    protobuf_codegen_pure::run(protobuf_codegen_pure::Args {
        out_dir: "src",
        input: &["src/perftest_data.proto"],
        includes: &["src"],
        customize: protobuf_codegen_pure::Customize {
            ..Default::default()
        },
    })
    .expect("protoc");

    // quick-protobuf
    let quick_dest = Path::new(&out_dir).join("perftest_data_quick.rs");
    let config = Config {
        in_file: PathBuf::from("src/perftest_data.proto"),
        out_file: quick_dest,
        single_module: true,
        import_search_path: vec![PathBuf::from("src")],
        no_output: false,
        error_cycle: false,
        headers: false,
        dont_use_cow: false,
        custom_struct_derive: vec![],
        custom_rpc_generator: Box::new(|rpc, writer| generate_rpc_test(rpc, writer)),
        custom_includes: Vec::new()
    };
    FileDescriptor::write_proto(&config).unwrap();

    // prost
    let old = ::std::env::var("OUT_DIR");
    env::set_var("OUT_DIR", ".");
    prost_build::compile_protos(&["src/perftest_data.proto"], &["src"]).unwrap();
    let _ = old.map(|val| env::set_var("OUT_DIR", &val));
    fs::rename("perftest_data.rs", "src/perftest_data_prost.rs").unwrap();
}
<|endoftext|>


<commit_before>//! Provides the Direction enum and utilities for working with it

/// Represents any of the four cardinal directions.
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
pub enum Direction {
    Up,
    Down,
    Left,
    Right,
}

impl Direction {
    /// Return the angle represented by the direction, in degrees, starting from Up, going
    /// clockwise
    pub fn to_angle(self) -> usize {
        match self {
            Up => 0,
            Down => 180,
            Left => 270,
            Right => 90,
        }
    }
}
<commit_msg>Fix naming for direction matching.<commit_after>//! Provides the Direction enum and utilities for working with it

/// Represents any of the four cardinal directions.
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
pub enum Direction {
    Up,
    Down,
    Left,
    Right,
}

impl Direction {
    /// Return the angle represented by the direction, in degrees, starting from Up, going
    /// clockwise
    pub fn to_angle(self) -> usize {
        match self {
            Direction::Up => 0,
            Direction::Down => 180,
            Direction::Left => 270,
            Direction::Right => 90,
        }
    }
}
<|endoftext|>


<commit_before>use std::default::Default;
use std::fmt::{Debug, Display, Formatter, Error};
use std::path::PathBuf;
use std::result::Result as RResult;

pub use config::types::Config;
pub use config::reader::from_file;

/**
 * Errors which are related to configuration-file loading
 */
pub mod error {
    use std::error::Error;
    use std::fmt::{Debug, Display, Formatter};
    use std::fmt::Error as FmtError;

    /**
     * The kind of an error
     */
    #[derive(Clone, Debug, PartialEq)]
    pub enum ConfigErrorKind {
        ConfigNotFound,
        ConfigParsingFailed,
        NoConfigFileFound,
    }

    /**
     * Configuration error type
     */
    #[derive(Debug)]
    pub struct ConfigError {
        kind: ConfigErrorKind,
        cause: Option<Box<Error>>,
    }

    impl ConfigError {

        /**
         * Instantiate a new ConfigError, optionally with cause
         */
        pub fn new(kind: ConfigErrorKind, cause: Option<Box<Error>>) -> ConfigError {
            ConfigError {
                kind: kind,
                cause: cause,
            }
        }

        /**
         * get the Kind of the Error
         */
        pub fn kind(&self) -> ConfigErrorKind {
            self.kind.clone()
        }

        /**
         * Get the string, the ConfigError can be described with
         */
        pub fn as_str(e: &ConfigError) -> &'static str {
            match e.kind() {
                ConfigErrorKind::ConfigNotFound      => "Config not found",
                ConfigErrorKind::ConfigParsingFailed => "Config parsing failed",
                ConfigErrorKind::NoConfigFileFound   => "No config file found",
            }
        }

    }

    impl Display for ConfigError {

        fn fmt(&self, fmt: &mut Formatter) -> Result<(), FmtError> {
            try!(write!(fmt, "{}", ConfigError::as_str(self)));
            Ok(())
        }

    }

    impl Error for ConfigError {

        fn description(&self) -> &str {
            ConfigError::as_str(self)
        }

        fn cause(&self) -> Option<&Error> {
            self.cause.as_ref().map(|e| &**e)
        }

    }

}

use self::error::{ConfigError, ConfigErrorKind};

/**
 * Result type of this module. Either T or ConfigError
 */
pub type Result<T> = RResult<T, ConfigError>;

/**
 * Configuration object
 *
 * Holds all config variables which are globally available plus the configuration object from the
 * config parser, which can be accessed.
 */
#[derive(Debug)]
pub struct Configuration {

    /**
     * The verbosity the program should run with
     */
    verbosity: bool,

    /**
     * The editor which should be used
     */
    editor: Option<String>,

    /**
     * The options the editor should get when opening some file
     */
    editor_opts: String,
}

impl Configuration {

    /**
     * Get a new configuration object.
     *
     * The passed runtimepath is used for searching the configuration file, whereas several file
     * names are tested. If that does not work, the home directory and the XDG basedir are tested
     * with all variants.
     *
     * If that doesn't work either, an error is returned.
     */
    pub fn new(rtp: &PathBuf) -> Result<Configuration> {
        fetch_config(&rtp).map(|cfg| {
            let verbosity   = cfg.lookup_boolean("verbosity").unwrap_or(false);
            let editor      = cfg.lookup_str("editor").map(String::from);
            let editor_opts = String::from(cfg.lookup_str("editor-opts").unwrap_or(""));

            debug!("Building configuration");
            debug!("  - verbosity  : {:?}", verbosity);
            debug!("  - editor     : {:?}", editor);
            debug!("  - editor-opts: {}", editor_opts);

            Configuration {
                verbosity: verbosity,
                editor: editor,
                editor_opts: editor_opts,
            }
        })
    }

}

/**
 * Helper to fetch the config file
 *
 * Tests several variants for the config file path and uses the first one which works.
 */
fn fetch_config(rtp: &PathBuf) -> Result<Config> {
    use std::process::exit;
    use std::env;

    use xdg_basedir;
    use itertools::Itertools;

    use libimagutil::variants::generate_variants as gen_vars;

    let variants = vec!["config", "config.toml", "imagrc", "imagrc.toml"];
    let modifier = |base: &PathBuf, v: &'static str| {
        let mut base = base.clone();
        base.push(format!("/{}", v));
        base
    };

    vec![
        gen_vars(rtp.clone(), variants.clone(), &modifier),

        env::var("HOME").map(|home| gen_vars(PathBuf::from(home), variants.clone(), &modifier))
                        .unwrap_or(vec![]),

        xdg_basedir::get_data_home().map(|data_dir| gen_vars(data_dir, variants.clone(), &modifier))
                                    .unwrap_or(vec![]),
    ].iter()
        .flatten()
        .filter(|path| path.exists())
        .map(|path| {
            from_file(&path)
                    .map_err(|e| {
                        ConfigError::new(ConfigErrorKind::ConfigParsingFailed, Some(Box::new(e)))
                    })
        })
        .filter(|loaded| loaded.is_ok())
        .nth(0)
        .map(|inner| inner.unwrap())
        .ok_or(ConfigError::new(ConfigErrorKind::NoConfigFileFound, None))
}

impl Default for Configuration {

    /**
     * Get a default configuration object
     */
    fn default() -> Configuration {
        Configuration {
            verbosity: false,
            editor: Some(String::from("nano")),
            editor_opts: String::from(""),
        }
    }

}

<commit_msg>rt/configuration: Remove unused imports<commit_after>use std::default::Default;
use std::path::PathBuf;
use std::result::Result as RResult;

pub use config::types::Config;
pub use config::reader::from_file;

/**
 * Errors which are related to configuration-file loading
 */
pub mod error {
    use std::error::Error;
    use std::fmt::{Debug, Display, Formatter};
    use std::fmt::Error as FmtError;

    /**
     * The kind of an error
     */
    #[derive(Clone, Debug, PartialEq)]
    pub enum ConfigErrorKind {
        ConfigNotFound,
        ConfigParsingFailed,
        NoConfigFileFound,
    }

    /**
     * Configuration error type
     */
    #[derive(Debug)]
    pub struct ConfigError {
        kind: ConfigErrorKind,
        cause: Option<Box<Error>>,
    }

    impl ConfigError {

        /**
         * Instantiate a new ConfigError, optionally with cause
         */
        pub fn new(kind: ConfigErrorKind, cause: Option<Box<Error>>) -> ConfigError {
            ConfigError {
                kind: kind,
                cause: cause,
            }
        }

        /**
         * get the Kind of the Error
         */
        pub fn kind(&self) -> ConfigErrorKind {
            self.kind.clone()
        }

        /**
         * Get the string, the ConfigError can be described with
         */
        pub fn as_str(e: &ConfigError) -> &'static str {
            match e.kind() {
                ConfigErrorKind::ConfigNotFound      => "Config not found",
                ConfigErrorKind::ConfigParsingFailed => "Config parsing failed",
                ConfigErrorKind::NoConfigFileFound   => "No config file found",
            }
        }

    }

    impl Display for ConfigError {

        fn fmt(&self, fmt: &mut Formatter) -> Result<(), FmtError> {
            try!(write!(fmt, "{}", ConfigError::as_str(self)));
            Ok(())
        }

    }

    impl Error for ConfigError {

        fn description(&self) -> &str {
            ConfigError::as_str(self)
        }

        fn cause(&self) -> Option<&Error> {
            self.cause.as_ref().map(|e| &**e)
        }

    }

}

use self::error::{ConfigError, ConfigErrorKind};

/**
 * Result type of this module. Either T or ConfigError
 */
pub type Result<T> = RResult<T, ConfigError>;

/**
 * Configuration object
 *
 * Holds all config variables which are globally available plus the configuration object from the
 * config parser, which can be accessed.
 */
#[derive(Debug)]
pub struct Configuration {

    /**
     * The verbosity the program should run with
     */
    verbosity: bool,

    /**
     * The editor which should be used
     */
    editor: Option<String>,

    /**
     * The options the editor should get when opening some file
     */
    editor_opts: String,
}

impl Configuration {

    /**
     * Get a new configuration object.
     *
     * The passed runtimepath is used for searching the configuration file, whereas several file
     * names are tested. If that does not work, the home directory and the XDG basedir are tested
     * with all variants.
     *
     * If that doesn't work either, an error is returned.
     */
    pub fn new(rtp: &PathBuf) -> Result<Configuration> {
        fetch_config(&rtp).map(|cfg| {
            let verbosity   = cfg.lookup_boolean("verbosity").unwrap_or(false);
            let editor      = cfg.lookup_str("editor").map(String::from);
            let editor_opts = String::from(cfg.lookup_str("editor-opts").unwrap_or(""));

            debug!("Building configuration");
            debug!("  - verbosity  : {:?}", verbosity);
            debug!("  - editor     : {:?}", editor);
            debug!("  - editor-opts: {}", editor_opts);

            Configuration {
                verbosity: verbosity,
                editor: editor,
                editor_opts: editor_opts,
            }
        })
    }

}

/**
 * Helper to fetch the config file
 *
 * Tests several variants for the config file path and uses the first one which works.
 */
fn fetch_config(rtp: &PathBuf) -> Result<Config> {
    use std::env;

    use xdg_basedir;
    use itertools::Itertools;

    use libimagutil::variants::generate_variants as gen_vars;

    let variants = vec!["config", "config.toml", "imagrc", "imagrc.toml"];
    let modifier = |base: &PathBuf, v: &'static str| {
        let mut base = base.clone();
        base.push(format!("/{}", v));
        base
    };

    vec![
        gen_vars(rtp.clone(), variants.clone(), &modifier),

        env::var("HOME").map(|home| gen_vars(PathBuf::from(home), variants.clone(), &modifier))
                        .unwrap_or(vec![]),

        xdg_basedir::get_data_home().map(|data_dir| gen_vars(data_dir, variants.clone(), &modifier))
                                    .unwrap_or(vec![]),
    ].iter()
        .flatten()
        .filter(|path| path.exists())
        .map(|path| {
            from_file(&path)
                    .map_err(|e| {
                        ConfigError::new(ConfigErrorKind::ConfigParsingFailed, Some(Box::new(e)))
                    })
        })
        .filter(|loaded| loaded.is_ok())
        .nth(0)
        .map(|inner| inner.unwrap())
        .ok_or(ConfigError::new(ConfigErrorKind::NoConfigFileFound, None))
}

impl Default for Configuration {

    /**
     * Get a default configuration object
     */
    fn default() -> Configuration {
        Configuration {
            verbosity: false,
            editor: Some(String::from("nano")),
            editor_opts: String::from(""),
        }
    }

}

<|endoftext|>


<commit_before>use std::collections::HashSet;
use std::sync::Mutex;

use fall_tree::{File, Node, AstNode, Text};
use fall_tree::visitor::{Visitor, NodeVisitor};
use fall_tree::search::child_of_type;
use fall_tree::search::ast;

use {FallFile, SynRule, RefExpr, CallExpr, SYN_RULE};
use editor_api::{Diagnostic, Severity};

mod calls;
mod references;
mod diagnostics;
mod cache;

use self::diagnostics::DiagnosticSink;
use self::cache::{FileCache, NodeCache};
pub use self::calls::CallKind;
pub use self::references::RefKind;

pub struct Analysis<'f> {
    file: FallFile<'f>,

    diagnostics: Mutex<Vec<Diagnostic>>,

    used_rules: FileCache<HashSet<Node<'f>>>,
    contexts: FileCache<Vec<Text<'f>>>,

    calls: NodeCache<'f, Option<CallKind<'f>>>,
    refs: NodeCache<'f, Option<RefKind<'f>>>,
}

impl<'f> Analysis<'f> {
    pub fn new(file: FallFile) -> Analysis {
        Analysis {
            file,
            diagnostics: Default::default(),
            used_rules: Default::default(),
            contexts: Default::default(),
            calls: Default::default(),
            refs: Default::default(),
        }
    }

    pub fn file(&self) -> FallFile<'f> {
        self.file
    }

    pub fn is_unused(&self, rule: SynRule<'f>) -> bool {
        !self.used_rules().contains(&rule.node())
    }

    pub fn resolve_call(&self, call: CallExpr<'f>) -> Option<CallKind<'f>> {
        let mut diagnostics = Vec::new();
        let (value, committed) = {
            let mut sink = DiagnosticSink::new(&mut diagnostics);
            self.calls.get(call.node(), || {
                calls::resolve(self, &mut sink, call)
            })
        };
        if committed {
            self.diagnostics.lock().unwrap().extend(diagnostics)
        }
        value
    }

    pub fn resolve_reference(&self, ref_: RefExpr<'f>) -> Option<RefKind<'f>> {
        let mut diagnostics = Vec::new();
        let (value, committed) = {
            let mut sink = DiagnosticSink::new(&mut diagnostics);
            self.refs.get(ref_.node(), || {
                references::resolve(self, &mut sink, ref_)
            })
        };
        if committed {
            self.diagnostics.lock().unwrap().extend(diagnostics)
        }
        value
    }

    pub fn collect_all_diagnostics(&self) -> Vec<Diagnostic> {
        impl Diagnostic {
            fn warning(node: Node, message: String) -> Diagnostic {
                Diagnostic {
                    range: node.range(),
                    severity: Severity::Warning,
                    message,
                }
            }
        }

        let mut result = Visitor(Vec::new())
            .visit::<RefExpr, _>(|_, ref_| { self.resolve_reference(ref_); })
            .visit::<CallExpr, _>(|_, call| { self.resolve_call(call); })
            .visit::<SynRule, _>(|acc, rule| {
                if self.is_unused(rule) {
                    if let Some(rule_name) = rule.name_ident() {
                        acc.push(Diagnostic::warning(rule_name, "Unused rule".to_string()))
                    }
                }
            })
            .walk_recursively_children_first(self.file().node());

        result.extend(self.diagnostics());
        result.sort_by_key(|d| (d.range.start(), d.range.end()));
        result
    }

    #[allow(unused)] // for debugging
    pub fn debug_diagnostics(&self) -> String {
        diagnostics::debug_diagnostics(&self.diagnostics(), self.file().node().text())
    }

    fn diagnostics(&self) -> Vec<Diagnostic> {
        self.diagnostics.lock().unwrap().clone()
    }

    fn contexts(&self) -> &[Text<'f>] {
        self.contexts.get(|| calls::contexts(self)).as_ref()
    }

    fn rule_by_name(&self, reference_name: Text<'f>) -> Option<SynRule<'f>> {
        self.file().syn_rules().find(|r| r.name() == Some(reference_name))
    }

    fn used_rules(&self) -> &HashSet<Node<'f>> {
        self.used_rules.get(|| self.calculate_used_rules())
    }

    fn calculate_used_rules(&self) -> HashSet<Node<'f>> {
        ast::descendants_of_type::<RefExpr>(self.file.node())
            .into_iter()
            .filter_map(|ref_| self.resolve_reference(ref_))
            .filter_map(|r| match r {
                RefKind::RuleReference(rule) => Some(rule.node()),
                _ => None
            })
            .chain(
                ast::descendants_of_type::<CallExpr>(self.file.node())
                    .into_iter()
                    .filter_map(|call| self.resolve_call(call))
                    .filter_map(|kind| match kind {
                        CallKind::RuleCall(rule, ..) => Some(rule.node()),
                        _ => None,
                    })
            )
            .chain(child_of_type(self.file.node(), SYN_RULE).into_iter())
            .collect()
    }
}

pub struct FileWithAnalysis {
    rent: rent::R
}

impl FileWithAnalysis {
    pub fn new(text: String) -> FileWithAnalysis {
        FileWithAnalysis::from_file(::parse(text))
    }

    fn from_file(file: File) -> FileWithAnalysis {
        FileWithAnalysis {
            rent: rent::R::new(Box::new(file), |file| {
                Analysis::new(FallFile::new(file.root()))
            })
        }
    }

    pub fn file(&self) -> &File {
        self.rent.head()
    }

    pub fn analyse<T, F: FnOnce(&Analysis) -> T>(&self, f: F) -> T {
        self.rent.rent(|a: &Analysis| f(a))
    }
}

rental! {
    mod rent {
        use super::*;

        #[rental]
        pub struct R {
            file: Box<File>,
            analysis: Analysis<'file>
        }
    }
}

#[cfg(test)]
fn check_diagnostics(code: &str, expected_diagnostics: &str) {
    use fall_tree::test_util::report_diff;
    let file = ::editor_api::analyse(code.to_string());

    file.analyse(|a| {
        let d = a.collect_all_diagnostics();
        let actual = d.into_iter().map(|d| {
            let s = match d.severity {
                Severity::Error => 'E',
                Severity::Warning => 'W',
            };
            format!("{}: {} {}", a.file().node().text().slice(d.range), s, d.message)
        }).collect::<Vec<_>>().join("\n");

        report_diff(&actual, expected_diagnostics);
    })
}

#[test]
fn test_diagnostics() {
    check_diagnostics(r"
       pub rule foo { <eof x> }
       rule bar { foo <abracadabra>}
       rule baz { <prev_is foo> <prev_is bar> <prev_is {foo}>}
    ", "\
<eof x>: E Wrong number of arguments, expected 0, got 1
x: E Unresolved reference
abracadabra: E Unresolved reference
baz: W Unused rule
<prev_is bar>: E <prev_is> arguments must be public rules
<prev_is {foo}>: E <prev_is> arguments must be public rules");
}
<commit_msg>Simplify<commit_after>use std::collections::HashSet;
use std::sync::Mutex;

use fall_tree::{File, Node, AstNode, Text};
use fall_tree::visitor::{Visitor, NodeVisitor};
use fall_tree::search::child_of_type;
use fall_tree::search::ast;

use {FallFile, SynRule, RefExpr, CallExpr, SYN_RULE};
use editor_api::{Diagnostic, Severity};

mod calls;
mod references;
mod diagnostics;
mod cache;

use self::diagnostics::DiagnosticSink;
use self::cache::{FileCache, NodeCache};
pub use self::calls::CallKind;
pub use self::references::RefKind;

pub struct Analysis<'f> {
    file: FallFile<'f>,

    diagnostics: Mutex<Vec<Diagnostic>>,

    used_rules: FileCache<HashSet<Node<'f>>>,
    contexts: FileCache<Vec<Text<'f>>>,

    calls: NodeCache<'f, Option<CallKind<'f>>>,
    refs: NodeCache<'f, Option<RefKind<'f>>>,
}

impl<'f> Analysis<'f> {
    pub fn new(file: FallFile) -> Analysis {
        Analysis {
            file,
            diagnostics: Default::default(),
            used_rules: Default::default(),
            contexts: Default::default(),
            calls: Default::default(),
            refs: Default::default(),
        }
    }

    pub fn file(&self) -> FallFile<'f> {
        self.file
    }

    pub fn is_unused(&self, rule: SynRule<'f>) -> bool {
        !self.used_rules().contains(&rule.node())
    }

    pub fn resolve_call(&self, call: CallExpr<'f>) -> Option<CallKind<'f>> {
        let mut diagnostics = Vec::new();
        let (value, committed) = {
            let mut sink = DiagnosticSink::new(&mut diagnostics);
            self.calls.get(call.node(), || {
                calls::resolve(self, &mut sink, call)
            })
        };
        if committed {
            self.diagnostics.lock().unwrap().extend(diagnostics)
        }
        value
    }

    pub fn resolve_reference(&self, ref_: RefExpr<'f>) -> Option<RefKind<'f>> {
        let mut diagnostics = Vec::new();
        let (value, committed) = {
            let mut sink = DiagnosticSink::new(&mut diagnostics);
            self.refs.get(ref_.node(), || {
                references::resolve(self, &mut sink, ref_)
            })
        };
        if committed {
            self.diagnostics.lock().unwrap().extend(diagnostics)
        }
        value
    }

    pub fn collect_all_diagnostics(&self) -> Vec<Diagnostic> {
        let mut result = Visitor(Vec::new())
            .visit::<RefExpr, _>(|_, ref_| { self.resolve_reference(ref_); })
            .visit::<CallExpr, _>(|_, call| { self.resolve_call(call); })
            .visit::<SynRule, _>(|acc, rule| {
                if self.is_unused(rule) {
                    if let Some(rule_name) = rule.name_ident() {
                        acc.push(Diagnostic {
                            range: rule_name.range(),
                            severity: Severity::Warning,
                            message: "Unused rule".to_string(),
                        })
                    }
                }
            })
            .walk_recursively_children_first(self.file().node());

        result.extend(self.diagnostics());
        result.sort_by_key(|d| (d.range.start(), d.range.end()));
        result
    }

    #[allow(unused)] // for debugging
    pub fn debug_diagnostics(&self) -> String {
        diagnostics::debug_diagnostics(&self.diagnostics(), self.file().node().text())
    }

    fn diagnostics(&self) -> Vec<Diagnostic> {
        self.diagnostics.lock().unwrap().clone()
    }

    fn contexts(&self) -> &[Text<'f>] {
        self.contexts.get(|| calls::contexts(self)).as_ref()
    }

    fn rule_by_name(&self, reference_name: Text<'f>) -> Option<SynRule<'f>> {
        self.file().syn_rules().find(|r| r.name() == Some(reference_name))
    }

    fn used_rules(&self) -> &HashSet<Node<'f>> {
        self.used_rules.get(|| self.calculate_used_rules())
    }

    fn calculate_used_rules(&self) -> HashSet<Node<'f>> {
        ast::descendants_of_type::<RefExpr>(self.file.node())
            .into_iter()
            .filter_map(|ref_| self.resolve_reference(ref_))
            .filter_map(|r| match r {
                RefKind::RuleReference(rule) => Some(rule.node()),
                _ => None
            })
            .chain(
                ast::descendants_of_type::<CallExpr>(self.file.node())
                    .into_iter()
                    .filter_map(|call| self.resolve_call(call))
                    .filter_map(|kind| match kind {
                        CallKind::RuleCall(rule, ..) => Some(rule.node()),
                        _ => None,
                    })
            )
            .chain(child_of_type(self.file.node(), SYN_RULE).into_iter())
            .collect()
    }
}

pub struct FileWithAnalysis {
    rent: rent::R
}

impl FileWithAnalysis {
    pub fn new(text: String) -> FileWithAnalysis {
        FileWithAnalysis::from_file(::parse(text))
    }

    fn from_file(file: File) -> FileWithAnalysis {
        FileWithAnalysis {
            rent: rent::R::new(Box::new(file), |file| {
                Analysis::new(FallFile::new(file.root()))
            })
        }
    }

    pub fn file(&self) -> &File {
        self.rent.head()
    }

    pub fn analyse<T, F: FnOnce(&Analysis) -> T>(&self, f: F) -> T {
        self.rent.rent(|a: &Analysis| f(a))
    }
}

rental! {
    mod rent {
        use super::*;

        #[rental]
        pub struct R {
            file: Box<File>,
            analysis: Analysis<'file>
        }
    }
}

#[cfg(test)]
fn check_diagnostics(code: &str, expected_diagnostics: &str) {
    use fall_tree::test_util::report_diff;
    let file = ::editor_api::analyse(code.to_string());

    file.analyse(|a| {
        let d = a.collect_all_diagnostics();
        let actual = d.into_iter().map(|d| {
            let s = match d.severity {
                Severity::Error => 'E',
                Severity::Warning => 'W',
            };
            format!("{}: {} {}", a.file().node().text().slice(d.range), s, d.message)
        }).collect::<Vec<_>>().join("\n");

        report_diff(&actual, expected_diagnostics);
    })
}

#[test]
fn test_diagnostics() {
    check_diagnostics(r"
       pub rule foo { <eof x> }
       rule bar { foo <abracadabra>}
       rule baz { <prev_is foo> <prev_is bar> <prev_is {foo}>}
    ", "\
<eof x>: E Wrong number of arguments, expected 0, got 1
x: E Unresolved reference
abracadabra: E Unresolved reference
baz: W Unused rule
<prev_is bar>: E <prev_is> arguments must be public rules
<prev_is {foo}>: E <prev_is> arguments must be public rules");
}
<|endoftext|>


<commit_before>use parking_lot::Mutex;

use std::sync::Arc;

use crate::driver::cmd::Args;
use crate::gc::bump::BumpAllocator;
use crate::gc::root::iterate_strong_roots;
use crate::gc::tlab;
use crate::gc::{
    formatted_size, iterate_weak_roots, Address, CollectionStats, Collector, GcReason, Region,
};
use crate::mem;
use crate::object::Obj;
use crate::os::{self, MemoryPermission};
use crate::safepoint;
use crate::threads::DoraThread;
use crate::timer::Timer;
use crate::vm::VM;

pub struct CopyCollector {
    total: Region,
    separator: Address,

    alloc: BumpAllocator,
    stats: Mutex<CollectionStats>,
}

impl CopyCollector {
    pub fn new(args: &Args) -> CopyCollector {
        let alignment = 2 * os::page_size();
        let heap_size = mem::align_usize(args.max_heap_size(), alignment);
        let heap_start = os::commit(heap_size, false);

        if heap_start.is_null() {
            panic!("could not allocate semi space of size {} bytes", heap_size);
        }

        let heap = heap_start.region_start(heap_size);

        let semi_size = heap_size / 2;
        let separator = heap_start.offset(semi_size);

        if args.flag_gc_verbose {
            println!("GC: {}; semi size: {}", heap, formatted_size(semi_size),);
        }

        CopyCollector {
            total: heap,
            separator,
            alloc: BumpAllocator::new(heap_start, separator),
            stats: Mutex::new(CollectionStats::new()),
        }
    }
}

impl Collector for CopyCollector {
    fn supports_tlab(&self) -> bool {
        true
    }

    fn alloc_tlab_area(&self, vm: &VM, size: usize) -> Option<Region> {
        let ptr = self.alloc.bump_alloc(size);

        if ptr.is_non_null() {
            return Some(ptr.region_start(size));
        }

        self.collect(vm, GcReason::AllocationFailure);

        let ptr = self.alloc.bump_alloc(size);

        return if ptr.is_null() {
            None
        } else {
            Some(ptr.region_start(size))
        };
    }

    fn alloc(&self, vm: &VM, size: usize, _array_ref: bool) -> Address {
        let ptr = self.alloc.bump_alloc(size);

        if ptr.is_non_null() {
            return ptr;
        }

        self.collect(vm, GcReason::AllocationFailure);
        self.alloc.bump_alloc(size)
    }

    fn collect(&self, vm: &VM, reason: GcReason) {
        let mut timer = Timer::new(vm.args.flag_gc_stats);

        safepoint::stop_the_world(vm, |threads| {
            tlab::make_iterable_all(vm, &*threads);
            self.copy_collect(vm, &*threads, reason);
        });

        if vm.args.flag_gc_stats {
            let duration = timer.stop();
            let mut stats = self.stats.lock();
            stats.add(duration);
        }
    }

    fn minor_collect(&self, vm: &VM, reason: GcReason) {
        self.collect(vm, reason);
    }

    fn dump_summary(&self, runtime: f32) {
        let stats = self.stats.lock();
        let (mutator, gc) = stats.percentage(runtime);

        println!("GC stats: total={:.1}", runtime);
        println!("GC stats: mutator={:.1}", stats.mutator(runtime));
        println!("GC stats: collection={:.1}", stats.pause());

        println!("");
        println!("GC stats: collection-count={}", stats.collections());
        println!("GC stats: collection-pauses={}", stats.pauses());

        println!(
            "GC summary: {:.1}ms collection ({}), {:.1}ms mutator, {:.1}ms total ({}% mutator, {}% GC)",
            stats.pause(),
            stats.collections(),
            stats.mutator(runtime),
            runtime,
            mutator,
            gc,
        );
    }
}

impl Drop for CopyCollector {
    fn drop(&mut self) {
        os::free(self.total.start, self.total.size());
    }
}

impl CopyCollector {
    fn copy_collect(&self, vm: &VM, threads: &[Arc<DoraThread>], reason: GcReason) {
        let timer = Timer::new(vm.args.flag_gc_verbose);

        // enable writing into to-space again (for debug builds)
        if cfg!(debug_assertions) {
            let to_space = self.to_space();
            os::protect(to_space.start, to_space.size(), MemoryPermission::ReadWrite);
        }

        // empty to-space
        let to_space = self.to_space();
        let from_space = self.from_space();

        // determine size of heap before collection
        let old_size = self.alloc.top().offset_from(from_space.start);

        let mut top = to_space.start;
        let mut scan = top;

        iterate_strong_roots(vm, threads, |root| {
            let root_ptr = root.get();

            if from_space.contains(root_ptr) {
                root.set(self.copy(root_ptr, &mut top));
            }
        });

        while scan < top {
            let object: &mut Obj = scan.to_mut_obj();

            object.visit_reference_fields(|field| {
                let field_ptr = field.get();

                if from_space.contains(field_ptr) {
                    field.set(self.copy(field_ptr, &mut top));
                }
            });

            scan = scan.offset(object.size());
        }

        self.iterate_weak_roots(vm);

        // disable access in current from-space
        // makes sure that no pointer into from-space is left (in debug-builds)
        if cfg!(debug_assertions) {
            os::protect(from_space.start, from_space.size(), MemoryPermission::None);
        }

        self.alloc.reset(top, to_space.end);

        timer.stop_with(|time_pause| {
            let new_size = top.offset_from(to_space.start);
            let garbage = old_size - new_size;
            let garbage_ratio = if old_size == 0 {
                0f64
            } else {
                (garbage as f64 / old_size as f64) * 100f64
            };

            println!(
                "Copy GC: {:.1} ms, {}->{} size, {}/{:.0}% garbage, ({})",
                time_pause,
                formatted_size(old_size),
                formatted_size(new_size),
                formatted_size(garbage),
                garbage_ratio,
                reason
            );
        });
    }

    fn iterate_weak_roots(&self, vm: &VM) {
        iterate_weak_roots(vm, |current_address| {
            debug_assert!(self.from_space().contains(current_address));
            let obj = current_address.to_mut_obj();

            if let Some(new_address) = obj.header().vtblptr_forwarded() {
                debug_assert!(self.to_space().contains(new_address));
                Some(new_address)
            } else {
                None
            }
        })
    }

    fn copy(&self, obj_addr: Address, top: &mut Address) -> Address {
        let obj = obj_addr.to_mut_obj();

        if let Some(fwd) = obj.header().vtblptr_forwarded() {
            return fwd;
        }

        let addr = *top;
        let obj_size = obj.size();

        obj.copy_to(addr, obj_size);
        *top = top.offset(obj_size);

        obj.header_mut().vtblptr_forward(addr);

        addr
    }

    pub fn from_space(&self) -> Region {
        if self.alloc.limit() == self.separator {
            Region::new(self.total.start, self.separator)
        } else {
            Region::new(self.separator, self.total.end)
        }
    }

    pub fn to_space(&self) -> Region {
        if self.alloc.limit() == self.separator {
            Region::new(self.separator, self.total.end)
        } else {
            Region::new(self.total.start, self.separator)
        }
    }
}
<commit_msg>simplify copy collector code a bit<commit_after>use parking_lot::Mutex;

use std::sync::Arc;

use crate::driver::cmd::Args;
use crate::gc::bump::BumpAllocator;
use crate::gc::root::iterate_strong_roots;
use crate::gc::tlab;
use crate::gc::{
    formatted_size, iterate_weak_roots, Address, CollectionStats, Collector, GcReason, Region,
};
use crate::mem;
use crate::object::Obj;
use crate::os::{self, MemoryPermission};
use crate::safepoint;
use crate::threads::DoraThread;
use crate::timer::Timer;
use crate::vm::VM;

pub struct CopyCollector {
    total: Region,
    separator: Address,

    alloc: BumpAllocator,
    stats: Mutex<CollectionStats>,
}

impl CopyCollector {
    pub fn new(args: &Args) -> CopyCollector {
        let alignment = 2 * os::page_size();
        let heap_size = mem::align_usize(args.max_heap_size(), alignment);
        let heap_start = os::commit(heap_size, false);

        if heap_start.is_null() {
            panic!("could not allocate semi space of size {} bytes", heap_size);
        }

        let heap = heap_start.region_start(heap_size);

        let semi_size = heap_size / 2;
        let separator = heap_start.offset(semi_size);

        if args.flag_gc_verbose {
            println!("GC: {}; semi size: {}", heap, formatted_size(semi_size),);
        }

        CopyCollector {
            total: heap,
            separator,
            alloc: BumpAllocator::new(heap_start, separator),
            stats: Mutex::new(CollectionStats::new()),
        }
    }
}

impl Collector for CopyCollector {
    fn supports_tlab(&self) -> bool {
        true
    }

    fn alloc_tlab_area(&self, vm: &VM, size: usize) -> Option<Region> {
        let ptr = self.alloc.bump_alloc(size);

        if ptr.is_non_null() {
            return Some(ptr.region_start(size));
        }

        self.collect(vm, GcReason::AllocationFailure);

        let ptr = self.alloc.bump_alloc(size);

        return if ptr.is_null() {
            None
        } else {
            Some(ptr.region_start(size))
        };
    }

    fn alloc(&self, vm: &VM, size: usize, _array_ref: bool) -> Address {
        let ptr = self.alloc.bump_alloc(size);

        if ptr.is_non_null() {
            return ptr;
        }

        self.collect(vm, GcReason::AllocationFailure);
        self.alloc.bump_alloc(size)
    }

    fn collect(&self, vm: &VM, reason: GcReason) {
        let mut timer = Timer::new(vm.args.flag_gc_stats);

        safepoint::stop_the_world(vm, |threads| {
            tlab::make_iterable_all(vm, threads);
            self.copy_collect(vm, threads, reason);
        });

        if vm.args.flag_gc_stats {
            let duration = timer.stop();
            let mut stats = self.stats.lock();
            stats.add(duration);
        }
    }

    fn minor_collect(&self, vm: &VM, reason: GcReason) {
        self.collect(vm, reason);
    }

    fn dump_summary(&self, runtime: f32) {
        let stats = self.stats.lock();
        let (mutator, gc) = stats.percentage(runtime);

        println!("GC stats: total={:.1}", runtime);
        println!("GC stats: mutator={:.1}", stats.mutator(runtime));
        println!("GC stats: collection={:.1}", stats.pause());

        println!("");
        println!("GC stats: collection-count={}", stats.collections());
        println!("GC stats: collection-pauses={}", stats.pauses());

        println!(
            "GC summary: {:.1}ms collection ({}), {:.1}ms mutator, {:.1}ms total ({}% mutator, {}% GC)",
            stats.pause(),
            stats.collections(),
            stats.mutator(runtime),
            runtime,
            mutator,
            gc,
        );
    }
}

impl Drop for CopyCollector {
    fn drop(&mut self) {
        os::free(self.total.start, self.total.size());
    }
}

impl CopyCollector {
    fn copy_collect(&self, vm: &VM, threads: &[Arc<DoraThread>], reason: GcReason) {
        let timer = Timer::new(vm.args.flag_gc_verbose);

        // enable writing into to-space again (for debug builds)
        if cfg!(debug_assertions) {
            let to_space = self.to_space();
            os::protect(to_space.start, to_space.size(), MemoryPermission::ReadWrite);
        }

        // empty to-space
        let to_space = self.to_space();
        let from_space = self.from_space();

        // determine size of heap before collection
        let old_size = self.alloc.top().offset_from(from_space.start);

        let mut top = to_space.start;
        let mut scan = top;

        iterate_strong_roots(vm, threads, |root| {
            let root_ptr = root.get();

            if from_space.contains(root_ptr) {
                root.set(self.copy(root_ptr, &mut top));
            }
        });

        while scan < top {
            let object: &mut Obj = scan.to_mut_obj();

            object.visit_reference_fields(|field| {
                let field_ptr = field.get();

                if from_space.contains(field_ptr) {
                    field.set(self.copy(field_ptr, &mut top));
                }
            });

            scan = scan.offset(object.size());
        }

        self.iterate_weak_roots(vm);

        // disable access in current from-space
        // makes sure that no pointer into from-space is left (in debug-builds)
        if cfg!(debug_assertions) {
            os::protect(from_space.start, from_space.size(), MemoryPermission::None);
        }

        self.alloc.reset(top, to_space.end);

        timer.stop_with(|time_pause| {
            let new_size = top.offset_from(to_space.start);
            let garbage = old_size - new_size;
            let garbage_ratio = if old_size == 0 {
                0f64
            } else {
                (garbage as f64 / old_size as f64) * 100f64
            };

            println!(
                "Copy GC: {:.1} ms, {}->{} size, {}/{:.0}% garbage, ({})",
                time_pause,
                formatted_size(old_size),
                formatted_size(new_size),
                formatted_size(garbage),
                garbage_ratio,
                reason
            );
        });
    }

    fn iterate_weak_roots(&self, vm: &VM) {
        iterate_weak_roots(vm, |current_address| {
            debug_assert!(self.from_space().contains(current_address));
            let obj = current_address.to_mut_obj();

            if let Some(new_address) = obj.header().vtblptr_forwarded() {
                debug_assert!(self.to_space().contains(new_address));
                Some(new_address)
            } else {
                None
            }
        })
    }

    fn copy(&self, obj_addr: Address, top: &mut Address) -> Address {
        let obj = obj_addr.to_mut_obj();

        if let Some(fwd) = obj.header().vtblptr_forwarded() {
            return fwd;
        }

        let addr = *top;
        let obj_size = obj.size();

        obj.copy_to(addr, obj_size);
        *top = top.offset(obj_size);

        obj.header_mut().vtblptr_forward(addr);

        addr
    }

    pub fn from_space(&self) -> Region {
        if self.alloc.limit() == self.separator {
            Region::new(self.total.start, self.separator)
        } else {
            Region::new(self.separator, self.total.end)
        }
    }

    pub fn to_space(&self) -> Region {
        if self.alloc.limit() == self.separator {
            Region::new(self.separator, self.total.end)
        } else {
            Region::new(self.total.start, self.separator)
        }
    }
}
<|endoftext|>


<commit_before>use core::cmp::{Eq, Ord};

pub trait PersistentMap<K: Copy Eq Ord, V: Copy> {
  pure fn get(k: K) -> @Option<V>;
  pure fn put(k: K, v: V) -> self;
  pure fn delete(k: K) -> self;
  pure fn traverse(f: fn((&K), (@Option<V>)));
}

enum RBColor {
  Red,
  Black
}

pub enum RBMap<K: Copy Eq Ord, V: Copy> {
  //   color    left         k  value       right
  Tree(RBColor, @RBMap<K,V>, K, @Option<V>, @RBMap<K,V>),
  Leaf
}

pure fn RBMap<K: Copy Eq Ord, V: Copy>(key: K, value: V) -> @RBMap<K, V> {
  @Tree(Red, @Leaf, key, @Some(value), @Leaf)
}

/**
 * A purely functional Red-Black tree.
 *
 * A port of Matt Might's Scala port of Okasaki's RB Trees: http://matt.might.net/articles/implementation-of-immutable-purely-functional-okasaki-red-black-tree-maps-in-scala/RBMap.scala
 * which was itself a port of http://www.ccs.neu.edu/course/cs3500wc/jfp99redblack.pdf
 */
impl<K: Copy Eq Ord, V: Copy> @RBMap<K, V> : PersistentMap<K, V> {
  pure fn get(k: K) -> @Option<V> {
    match self {
      @Leaf => @None,
      @Tree(_, left, key, maybe_value, right) => {
        if (k.lt(&key)) {
          left.get(k)
        } else if (k.gt(&key)) {
          right.get(k)
        } else {
          maybe_value
        }
      }
    }
  }

  /// Visit all pairs in the map in order.
  pure fn traverse(f: fn((&K), (@Option<V>))) {
    match *self {
      Leaf => (),
      Tree(_, left, key, maybe_value, right) => {
        left.traverse(f);
        f(&key, maybe_value);
        right.traverse(f);
      }
    }
  }

  pure fn put(k : K, v : V) -> @RBMap<K, V> {
    self.modifiedWith(k, @Some(v))
  }

  pure fn delete(k: K) -> @RBMap<K, V> {
    self.modifiedWith(k, @None)
  }

  priv pure fn blacken(n : @RBMap<K, V>) -> @RBMap<K, V> {
    match n {
      @Leaf => n,
      @Tree(_,l,k,v,r) => @Tree(Black,l,k,v,r)
    }
  }

  priv pure fn modifiedWith (k : K, new_value: @Option<V>) -> @RBMap<K, V> {
    self.blacken(self.modWith(k, new_value))
  }

  priv pure fn balance (c : RBColor, l : @RBMap<K,V>, k : K, v : @Option<V>, r : @RBMap<K,V>) -> @RBMap<K,V> {
    match (c,l,k,v,r) {
      (Black,@Tree(Red,@Tree(Red,a,xK,xV,b),yK,yV,c),zK,zV,d) =>
        @Tree(Red,@Tree(Black,a,xK,xV,b),yK,yV,@Tree(Black,c,zK,zV,d)),
      (Black,@Tree(Red,a,xK,xV,@Tree(Red,b,yK,yV,c)),zK,zV,d) =>
        @Tree(Red,@Tree(Black,a,xK,xV,b),yK,yV,@Tree(Black,c,zK,zV,d)),
      (Black,a,xK,xV,@Tree(Red,@Tree(Red,b,yK,yV,c),zK,zV,d)) =>
        @Tree(Red,@Tree(Black,a,xK,xV,b),yK,yV,@Tree(Black,c,zK,zV,d)),
      (Black,a,xK,xV,@Tree(Red,b,yK,yV,@Tree(Red,c,zK,zV,d))) =>
        @Tree(Red,@Tree(Black,a,xK,xV,b),yK,yV,@Tree(Black,c,zK,zV,d)),
      (c,a,xK,xV,b) =>
        @Tree(c,a,xK,xV,b)
    }
  }

  priv pure fn modWith (k : K, new_value: @Option<V>) -> @RBMap<K, V> {
    match self {
      @Leaf => @Tree(Red, self, k, new_value, self),
      @Tree(color, left, key, original_value, right) => {
        if (k.lt(&key)) {
          self.balance(color, left.modWith(k, new_value), key, original_value, right)
        } else if (k == key) {
          @Tree(color, left, k, new_value, right)
        } else {
          self.balance(color, left, key, original_value, right.modWith(k, new_value))
        }
      }
    }
  }
}

#[test]
fn test_rb_tree_create() {
  let v1 = RBMap("stevej", 150);
  let v2 = v1.put("thatstacy", 167);

  assert(v2.get("stevej") == @Some(150));
  assert(v2.get("thatstacy") == @Some(167));

  let v3 = v2.delete("stevej");
  assert(v3.get("stevej") == @None);
  assert(v3.get("thatstacy") == @Some(167));
}

#[test]
fn test_traverse() {
  let v1 = RBMap(1, 0);
  let v2 = v1.put(4, 0);
  let v3 = v2.put(3, 0);
  let v4 = v3.put(5, 0);
  let v5 = v4.put(2, 0);

  let n = @mut 1;

  fn t(n: @mut int, k: int, _v: Option<int>) {
    assert (*n == k); *n += 1;
  }

  v5.traverse(|x,y| t(n, *x, *y));
}
<commit_msg>added test for both sides of tree<commit_after>use core::cmp::{Eq, Ord};

pub trait PersistentMap<K: Copy Eq Ord, V: Copy> {
  pure fn get(k: K) -> @Option<V>;
  pure fn put(k: K, v: V) -> self;
  pure fn delete(k: K) -> self;
  pure fn traverse(f: fn((&K), (@Option<V>)));
}

enum RBColor {
  Red,
  Black
}

pub enum RBMap<K: Copy Eq Ord, V: Copy> {
  //   color    left         k  value       right
  Tree(RBColor, @RBMap<K,V>, K, @Option<V>, @RBMap<K,V>),
  Leaf
}

pure fn RBMap<K: Copy Eq Ord, V: Copy>(key: K, value: V) -> @RBMap<K, V> {
  @Tree(Red, @Leaf, key, @Some(value), @Leaf)
}

/**
 * A purely functional Red-Black tree.
 *
 * A port of Matt Might's Scala port of Okasaki's RB Trees: http://matt.might.net/articles/implementation-of-immutable-purely-functional-okasaki-red-black-tree-maps-in-scala/RBMap.scala
 * which was itself a port of http://www.ccs.neu.edu/course/cs3500wc/jfp99redblack.pdf
 */
impl<K: Copy Eq Ord, V: Copy> @RBMap<K, V> : PersistentMap<K, V> {
  pure fn get(k: K) -> @Option<V> {
    match self {
      @Leaf => @None,
      @Tree(_, left, key, maybe_value, right) => {
        if (k.lt(&key)) {
          left.get(k)
        } else if (k.gt(&key)) {
          right.get(k)
        } else {
          maybe_value
        }
      }
    }
  }

  /// Visit all pairs in the map in order.
  pure fn traverse(f: fn((&K), (@Option<V>))) {
    match *self {
      Leaf => (),
      Tree(_, left, key, maybe_value, right) => {
        left.traverse(f);
        f(&key, maybe_value);
        right.traverse(f);
      }
    }
  }

  pure fn put(k : K, v : V) -> @RBMap<K, V> {
    self.modifiedWith(k, @Some(v))
  }

  pure fn delete(k: K) -> @RBMap<K, V> {
    self.modifiedWith(k, @None)
  }

  priv pure fn blacken(n : @RBMap<K, V>) -> @RBMap<K, V> {
    match n {
      @Leaf => n,
      @Tree(_,l,k,v,r) => @Tree(Black,l,k,v,r)
    }
  }

  priv pure fn modifiedWith (k : K, new_value: @Option<V>) -> @RBMap<K, V> {
    self.blacken(self.modWith(k, new_value))
  }

  priv pure fn balance (c : RBColor, l : @RBMap<K,V>, k : K, v : @Option<V>, r : @RBMap<K,V>) -> @RBMap<K,V> {
    match (c,l,k,v,r) {
      (Black,@Tree(Red,@Tree(Red,a,xK,xV,b),yK,yV,c),zK,zV,d) =>
        @Tree(Red,@Tree(Black,a,xK,xV,b),yK,yV,@Tree(Black,c,zK,zV,d)),

      (Black,@Tree(Red,a,xK,xV,@Tree(Red,b,yK,yV,c)),zK,zV,d) =>
        @Tree(Red,@Tree(Black,a,xK,xV,b),yK,yV,@Tree(Black,c,zK,zV,d)),

      (Black,a,xK,xV,@Tree(Red,@Tree(Red,b,yK,yV,c),zK,zV,d)) =>
        @Tree(Red,@Tree(Black,a,xK,xV,b),yK,yV,@Tree(Black,c,zK,zV,d)),

      (Black,a,xK,xV,@Tree(Red,b,yK,yV,@Tree(Red,c,zK,zV,d))) =>
        @Tree(Red,@Tree(Black,a,xK,xV,b),yK,yV,@Tree(Black,c,zK,zV,d)),

      (c,a,xK,xV,b) =>
        @Tree(c,a,xK,xV,b)
    }
  }

  priv pure fn modWith (k : K, new_value: @Option<V>) -> @RBMap<K, V> {
    match self {
      @Leaf => @Tree(Red, self, k, new_value, self),
      @Tree(color, left, key, original_value, right) => {
        if (k.lt(&key)) {
          self.balance(color, left.modWith(k, new_value), key, original_value, right)
        } else if (k == key) {
          @Tree(color, left, k, new_value, right)
        } else {
          self.balance(color, left, key, original_value, right.modWith(k, new_value))
        }
      }
    }
  }
}

#[test]
fn test_rb_tree_create() {
  let v1 = RBMap("stevej", 150);
  let v2 = v1.put("thatstacy", 187);

  assert(v2.get("stevej") == @Some(150));
  assert(v2.get("thatstacy") == @Some(187));

  let v3 = v2.delete("stevej");
  assert(v3.get("stevej") == @None);
  assert(v3.get("thatstacy") == @Some(187));

  let v4 = v2.put("jeremy", 16);
  assert(v4.get("jeremy") == @Some(16));
  assert(v4.get("stevej") == @Some(150));
  assert(v4.get("thatstacy") == @Some(187));
}

#[test]
fn test_traverse() {
  let v1 = RBMap(1, 0);
  let v2 = v1.put(4, 0);
  let v3 = v2.put(3, 0);
  let v4 = v3.put(5, 0);
  let v5 = v4.put(2, 0);

  let n = @mut 1;

  fn t(n: @mut int, k: int, _v: Option<int>) {
    assert (*n == k); *n += 1;
  }

  v5.traverse(|x,y| t(n, *x, *y));
}
<|endoftext|>


<commit_before>//! The `style` element.

use cssparser::Parser;
use markup5ever::{expanded_name, local_name, namespace_url, ns};

use crate::error::*;
use crate::node::{NodeResult, NodeTrait, RsvgNode};
use crate::parsers::{Parse, ParseValue};
use crate::property_bag::PropertyBag;

/// Represents the syntax used in the <style> node.
///
/// Currently only "text/css" is supported.
///
/// https://www.w3.org/TR/SVG11/styling.html#StyleElementTypeAttribute
/// https://www.w3.org/TR/SVG11/styling.html#ContentStyleTypeAttribute
#[derive(Copy, Clone, PartialEq)]
pub enum StyleType {
    TextCss,
}

impl Parse for StyleType {
    fn parse(parser: &mut Parser<'_, '_>) -> Result<StyleType, ValueErrorKind> {
        parser
            .expect_ident_matching("text/css")
            .and_then(|_| Ok(StyleType::TextCss))
            .map_err(|_| {
                ValueErrorKind::parse_error("only the \"text/css\" style type is supported")
            })
    }
}

/// Represents a <style> node.
///
/// It does not render itself, and just holds CSS stylesheet information for the rest of
/// the code to use.
#[derive(Default)]
pub struct Style {
    type_: Option<StyleType>,
}

impl Style {
    pub fn style_type(&self) -> Option<StyleType> {
        self.type_
    }
}

impl NodeTrait for Style {
    fn set_atts(&mut self, _: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult {
        for (attr, value) in pbag.iter() {
            if attr.expanded() == expanded_name!(svg "type") {
                self.type_ = Some(attr.parse(value)?);
            }
        }

        Ok(())
    }
}
<commit_msg>Convert StyleType to CssParseError<commit_after>//! The `style` element.

use cssparser::Parser;
use markup5ever::{expanded_name, local_name, namespace_url, ns};

use crate::error::*;
use crate::node::{NodeResult, NodeTrait, RsvgNode};
use crate::parsers::{ParseToParseError, ParseValueToParseError};
use crate::property_bag::PropertyBag;

/// Represents the syntax used in the <style> node.
///
/// Currently only "text/css" is supported.
///
/// https://www.w3.org/TR/SVG11/styling.html#StyleElementTypeAttribute
/// https://www.w3.org/TR/SVG11/styling.html#ContentStyleTypeAttribute
#[derive(Copy, Clone, PartialEq)]
pub enum StyleType {
    TextCss,
}

impl ParseToParseError for StyleType {
    fn parse_to_parse_error<'i>(parser: &mut Parser<'i, '_>) -> Result<StyleType, CssParseError<'i>> {
        parser.expect_ident_matching("text/css")?;
        Ok(StyleType::TextCss)
    }
}

/// Represents a <style> node.
///
/// It does not render itself, and just holds CSS stylesheet information for the rest of
/// the code to use.
#[derive(Default)]
pub struct Style {
    type_: Option<StyleType>,
}

impl Style {
    pub fn style_type(&self) -> Option<StyleType> {
        self.type_
    }
}

impl NodeTrait for Style {
    fn set_atts(&mut self, _: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult {
        for (attr, value) in pbag.iter() {
            if attr.expanded() == expanded_name!(svg "type") {
                self.type_ = Some(attr.parse_to_parse_error(value)?);
            }
        }

        Ok(())
    }
}
<|endoftext|>


<commit_before>//  Copyright (C) 2020 Éloïs SANCHEZ.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

use anyhow::Result;
use std::process::{Command, Output};
use structopt::StructOpt;

const MIN_RUST_VERTION: &str = "1.50.0";
const NODE_VERSION: &str = "10.22.1";
const NVM_VERSION: &str = "0.35.3";

#[derive(StructOpt)]
struct DuniterXTask {
    #[structopt(long)]
    skip_npm: bool,
    #[structopt(subcommand)]
    command: DuniterXTaskCommand,
}

#[derive(StructOpt)]
enum DuniterXTaskCommand {
    Build {
        #[structopt(long)]
        production: bool,
    },
    Test,
}

fn main() -> Result<()> {
    let args = DuniterXTask::from_args();

    if !version_check::is_min_version(MIN_RUST_VERTION).unwrap_or(false)
        && exec_should_success(Command::new("rustup").args(&["update", "stable"])).is_err()
    {
        eprintln!(
                "Duniter requires Rust {} or higher. If you installed the Rust toolchain via rustup, please execute the command `rustup update stable`.",
                MIN_RUST_VERTION
            );
        std::process::exit(1);
    }
    Command::new("rustc").arg("--version").status()?;
    Command::new("cargo").arg("--version").status()?;

    if !args.skip_npm {
        println!("Check nvm …");
        if exec_should_success(Command::new("nvm").arg("--version")).is_err() {
            println!("Duniter requires nvm, but nvm is not found, please install it or add it to your PATH. Nvm installation link: https://raw.githubusercontent.com/nvm-sh/nvm/v{}/install.sh", NVM_VERSION);
            std::process::abort();
        }
        println!("Check node version …");
        if exec_and_get_stdout(Command::new("node").arg("-v"))
            .unwrap_or_default()
            .trim_end()
            != format!("v{}", NODE_VERSION)
        {
            println!("Install node v{} …", NODE_VERSION);
            install_and_use_node_version()?;
        } else {
            println!("Node v{} already installed ✔", NODE_VERSION);
        }
    }
    match args.command {
        DuniterXTaskCommand::Build { production } => build(args.skip_npm, production),
        DuniterXTaskCommand::Test => test(args.skip_npm),
    }
}

fn install_and_use_node_version() -> Result<()> {
    exec_should_success(Command::new("nvm").args(&["install", NODE_VERSION]))?;
    exec_should_success(Command::new("nvm").args(&["use", NODE_VERSION]))
}

fn exec_and_get_stdout(command: &mut Command) -> Result<String> {
    let Output {
        status,
        stdout,
        stderr,
    } = command.output()?;

    if status.success() {
        Ok(String::from_utf8(stdout)
            .unwrap_or_else(|_| "Output is not a valid utf8 string".to_owned()))
    } else {
        eprintln!(
            "{}",
            String::from_utf8(stderr)
                .unwrap_or_else(|_| "Error message is not a valid utf8 string".to_owned())
        );
        std::process::exit(1);
    }
}

fn exec_should_success(command: &mut Command) -> Result<()> {
    if !command.status()?.success() {
        std::process::exit(1);
    } else {
        Ok(())
    }
}

fn build(skip_npm: bool, production: bool) -> Result<()> {
    if !skip_npm {
        exec_should_success(Command::new("npm").args(&["add", "duniter-ui"]))?;
        exec_should_success(
            Command::new("npm")
                .env("NEON_BUILD_RELEASE", "true")
                .arg("install"),
        )?;
        if production {
            exec_should_success(Command::new("npm").args(&["prune", "--production"]))?;
        }
    }
    println!("Build duniter-launcher …");
    exec_should_success(Command::new("cargo").args(&["build", "--release"]))?;
    std::fs::copy("target/release/duniter", "bin/duniter")?;
    Ok(())
}

fn test(skip_npm: bool) -> Result<()> {
    exec_should_success(Command::new("cargo").args(&["test", "--all"]))?;
    if !skip_npm {
        exec_should_success(Command::new("npm").arg("test"))?;
    }
    Ok(())
}
<commit_msg>[build] xtask: not use nvm<commit_after>//  Copyright (C) 2020 Éloïs SANCHEZ.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

use anyhow::Result;
use std::process::{Command, Output};
use structopt::StructOpt;

const MIN_RUST_VERTION: &str = "1.50.0";
const NODE_VERSION: &str = "10.22.1";

#[derive(StructOpt)]
struct DuniterXTask {
    #[structopt(long)]
    skip_npm: bool,
    #[structopt(subcommand)]
    command: DuniterXTaskCommand,
}

#[derive(StructOpt)]
enum DuniterXTaskCommand {
    Build {
        #[structopt(long)]
        production: bool,
    },
    Test,
}

fn main() -> Result<()> {
    let args = DuniterXTask::from_args();

    if !version_check::is_min_version(MIN_RUST_VERTION).unwrap_or(false)
        && exec_should_success(Command::new("rustup").args(&["update", "stable"])).is_err()
    {
        eprintln!(
                "Duniter requires Rust {} or higher. If you installed the Rust toolchain via rustup, please execute the command `rustup update stable`.",
                MIN_RUST_VERTION
            );
        std::process::exit(1);
    }
    Command::new("rustc").arg("--version").status()?;
    Command::new("cargo").arg("--version").status()?;

    if !args.skip_npm {
        println!("Check node version …");
        if exec_and_get_stdout(Command::new("node").arg("-v"))
            .unwrap_or_default()
            .trim_end()
            != format!("v{}", NODE_VERSION)
        {
            eprintln!(
                "Duniter requires node v{} exactly. Please install node v{} (you can use nvm).",
                NODE_VERSION, NODE_VERSION
            );
            std::process::exit(1);
        } else {
            println!("Node v{} already installed ✔", NODE_VERSION);
        }
    }
    match args.command {
        DuniterXTaskCommand::Build { production } => build(args.skip_npm, production),
        DuniterXTaskCommand::Test => test(args.skip_npm),
    }
}

fn exec_and_get_stdout(command: &mut Command) -> Result<String> {
    let Output {
        status,
        stdout,
        stderr,
    } = command.output()?;

    if status.success() {
        Ok(String::from_utf8(stdout)
            .unwrap_or_else(|_| "Output is not a valid utf8 string".to_owned()))
    } else {
        eprintln!(
            "{}",
            String::from_utf8(stderr)
                .unwrap_or_else(|_| "Error message is not a valid utf8 string".to_owned())
        );
        std::process::exit(1);
    }
}

fn exec_should_success(command: &mut Command) -> Result<()> {
    if !command.status()?.success() {
        std::process::exit(1);
    } else {
        Ok(())
    }
}

fn build(skip_npm: bool, production: bool) -> Result<()> {
    if !skip_npm {
        exec_should_success(Command::new("npm").args(&["add", "duniter-ui"]))?;
        exec_should_success(
            Command::new("npm")
                .env("NEON_BUILD_RELEASE", "true")
                .arg("install"),
        )?;
        if production {
            exec_should_success(Command::new("npm").args(&["prune", "--production"]))?;
        }
    }
    println!("Build duniter-launcher …");
    exec_should_success(Command::new("cargo").args(&["build", "--release"]))?;
    std::fs::copy("target/release/duniter", "bin/duniter")?;
    Ok(())
}

fn test(skip_npm: bool) -> Result<()> {
    exec_should_success(Command::new("cargo").args(&["test", "--all"]))?;
    if !skip_npm {
        exec_should_success(Command::new("npm").arg("test"))?;
    }
    Ok(())
}
<|endoftext|>


<commit_before>#![ warn( missing_docs ) ]

//!
//! Tools for writing procedural macroses.
//!

/// Macro for diagnostics purpose to print both syntax tree and source code behind it.

#[ macro_export ]
macro_rules! tree_print
{
  ( $src : expr ) =>
  {{
    println!( "{}", tree_export_str!( $src ) );
  }};
  ( $( $src : expr ),+ $(,)? ) =>
  {{
    $( tree_print!( $src ) );+;
  }};
}

/// Macro for diagnostics purpose to export both syntax tree and source code behind it into string.

#[ macro_export ]
macro_rules! tree_export_str
{
  ( $src : expr ) =>
  {{
    let src2 = &$src;
    format!( "{} : {} :\n{:#?}", stringify!( $src ), quote!{ #src2 }, $src )
  }};
}

/// Kind of container.

/* qqq : for rust : add HashSet */
#[derive( Debug, PartialEq, Copy, Clone )]
pub enum ContainerKind
{
  /// Not a container.
  No,
  /// Vector-like.
  Vector,
  /// Hash map-like.
  HashMap,
}

/// Return kind of container specified by type.
/// Good to verify `alloc::vec::Vec< i32 >` is vector.
/// Good to verify `std::collections::HashMap< i32, i32 >` is hash map.

pub fn container_kind( ty : &syn::Type ) -> ContainerKind
{
  if let syn::Type::Path( path ) = ty
  {
    let last = &path.path.segments.last();
    if last.is_none()
    {
      return ContainerKind::No
    }
    match last.unwrap().ident.to_string().as_ref()
    {
      "Vec" => { return ContainerKind::Vector }
      "HashMap" => { return ContainerKind::HashMap }
      _ => { return ContainerKind::No }
    }
  }
  ContainerKind::No
}

/// Check is the rightmost item of path refering a type is specified type.
/// Good to verify `core::option::Option< i32 >` is optional.
/// Good to verify `alloc::vec::Vec< i32 >` is vector.

pub fn rightmost_is< R : AsRef< str > >( ty : &syn::Type, rightmost : R ) -> bool
{
  if let syn::Type::Path( path ) = ty
  {
    let last = &path.path.segments.last();
    if last.is_none()
    {
      return false;
    }
    return last.unwrap().ident == rightmost.as_ref();
  }
  false
}

/// Return the specified number of parameters of the type.
/// Good to getting `i32` from `core::option::Option< i32 >` or `alloc::vec::Vec< i32 >`

pub fn parameters_internal( ty : &syn::Type, r : ::core::ops::RangeInclusive< usize > ) -> Vec< &syn::Type >
{
  if let syn::Type::Path( syn::TypePath{ path : syn::Path { ref segments, .. }, .. } ) = ty
  {
    let last = &segments.last();
    if last.is_none()
    {
      return vec![ &ty ]
    }
    let args = &last.unwrap().arguments;
    if let syn::PathArguments::AngleBracketed( ref args2 ) = args
    {
      let args3 = &args2.args;
      let selected : Vec< &syn::Type > = args3
      .iter()
      .skip_while( | e | if let syn::GenericArgument::Type( _ ) = e { false } else { true } )
      .skip( *r.start() )
      .take( *r.end() - *r.start() + 1 )
      .map( | e | if let syn::GenericArgument::Type( ty ) = e { ty } else { unreachable!( "Expects Type" ) } )
      .collect();
      return selected;
    }
  }
  vec![ &ty ]
}

// /// Wrapper to implement trait Spanned for those structures for which it's not implemented.
//
// pub struct DataWrapped( pub syn::Data );
// impl syn::spanned::Spanned for DataWrapped
// {
//   fn span( &self ) -> proc_macro2::Span
//   {
//     let DataWrapped( ref data ) = self;
//     match data
//     {
//       syn::Data::Struct( syn::DataStruct { ref fields, .. } ) => fields.span(),
//       syn::Data::Enum( syn::DataEnum { ref variants, .. } ) => variants.span(),
//       syn::Data::Union( syn::DataUnion { ref fields, .. } ) => fields.span(),
//       // _ => proc_macro2::Span::call_site(),
//     }
//   }
// }
//
// pub use syn::spanned::Spanned;

/// Trait to implement method span() for those structures which do not have it implemented.

pub trait Spanned2
{
  /// Returns a Span covering the complete contents of this syntax tree node, or Span::call_site() if this node is empty.
  fn span2( &self ) -> proc_macro2::Span;
}

//

impl Spanned2 for syn::Data
{
  fn span2( &self ) -> proc_macro2::Span
  {
    match self
    {
      syn::Data::Struct( syn::DataStruct { ref fields, .. } ) => fields.span(),
      syn::Data::Enum( syn::DataEnum { ref variants, .. } ) => variants.span(),
      syn::Data::Union( syn::DataUnion { ref fields, .. } ) => fields.span(),
    }
  }
}

/// Returns a Span covering the complete contents of this syntax tree node, or Span::call_site() if this node is empty.

pub fn span_of< Src : Spanned2 >( src : &Src ) -> proc_macro2::Span
{
  src.span2()
}
<commit_msg>publish rust module::wproc_macro<commit_after>#![ warn( missing_docs ) ]

//!
//! Tools for writing procedural macroses.
//!

/// Macro for diagnostics purpose to print both syntax tree and source code behind it.

#[ macro_export ]
macro_rules! tree_print
{
  ( $src : expr ) =>
  {{
    println!( "{}", tree_export_str!( $src ) );
  }};
  ( $( $src : expr ),+ $(,)? ) =>
  {{
    $( tree_print!( $src ) );+;
  }};
}

/// Macro for diagnostics purpose to export both syntax tree and source code behind it into string.

#[ macro_export ]
macro_rules! tree_export_str
{
  ( $src : expr ) =>
  {{
    let src2 = &$src;
    format!( "{} : {} :\n{:#?}", stringify!( $src ), quote!{ #src2 }, $src )
  }};
}

/// Kind of container.

/* qqq : for rust : add HashSet */
#[derive( Debug, PartialEq, Copy, Clone )]
pub enum ContainerKind
{
  /// Not a container.
  No,
  /// Vector-like.
  Vector,
  /// Hash map-like.
  HashMap,
}

/// Return kind of container specified by type.
/// Good to verify `alloc::vec::Vec< i32 >` is vector.
/// Good to verify `std::collections::HashMap< i32, i32 >` is hash map.

pub fn container_kind( ty : &syn::Type ) -> ContainerKind
{
  if let syn::Type::Path( path ) = ty
  {
    let last = &path.path.segments.last();
    if last.is_none()
    {
      return ContainerKind::No
    }
    match last.unwrap().ident.to_string().as_ref()
    {
      "Vec" => { return ContainerKind::Vector }
      "HashMap" => { return ContainerKind::HashMap }
      _ => { return ContainerKind::No }
    }
  }
  ContainerKind::No
}

/// Check is the rightmost item of path refering a type is specified type.
/// Good to verify `core::option::Option< i32 >` is optional.
/// Good to verify `alloc::vec::Vec< i32 >` is vector.

pub fn rightmost_is< R : AsRef< str > >( ty : &syn::Type, rightmost : R ) -> bool
{
  if let syn::Type::Path( path ) = ty
  {
    let last = &path.path.segments.last();
    if last.is_none()
    {
      return false;
    }
    return last.unwrap().ident == rightmost.as_ref();
  }
  false
}

/// Return the specified number of parameters of the type.
/// Good to getting `i32` from `core::option::Option< i32 >` or `alloc::vec::Vec< i32 >`

pub fn parameters_internal( ty : &syn::Type, r : ::core::ops::RangeInclusive< usize > ) -> Vec< &syn::Type >
{
  if let syn::Type::Path( syn::TypePath{ path : syn::Path { ref segments, .. }, .. } ) = ty
  {
    let last = &segments.last();
    if last.is_none()
    {
      return vec![ &ty ]
    }
    let args = &last.unwrap().arguments;
    if let syn::PathArguments::AngleBracketed( ref args2 ) = args
    {
      let args3 = &args2.args;
      let selected : Vec< &syn::Type > = args3
      .iter()
      .skip_while( | e | if let syn::GenericArgument::Type( _ ) = e { false } else { true } )
      .skip( *r.start() )
      .take( *r.end() - *r.start() + 1 )
      .map( | e | if let syn::GenericArgument::Type( ty ) = e { ty } else { unreachable!( "Expects Type" ) } )
      .collect();
      return selected;
    }
  }
  vec![ &ty ]
}

// /// Wrapper to implement trait Spanned for those structures for which it's not implemented.
//
// pub struct DataWrapped( pub syn::Data );
// impl syn::spanned::Spanned for DataWrapped
// {
//   fn span( &self ) -> proc_macro2::Span
//   {
//     let DataWrapped( ref data ) = self;
//     match data
//     {
//       syn::Data::Struct( syn::DataStruct { ref fields, .. } ) => fields.span(),
//       syn::Data::Enum( syn::DataEnum { ref variants, .. } ) => variants.span(),
//       syn::Data::Union( syn::DataUnion { ref fields, .. } ) => fields.span(),
//       // _ => proc_macro2::Span::call_site(),
//     }
//   }
// }

pub use syn::spanned::Spanned;

/// Trait to implement method span() for those structures which do not have it implemented.

pub trait Spanned2
{
  /// Returns a Span covering the complete contents of this syntax tree node, or Span::call_site() if this node is empty.
  fn span2( &self ) -> proc_macro2::Span;
}

//

impl Spanned2 for syn::Data
{
  fn span2( &self ) -> proc_macro2::Span
  {
    match self
    {
      syn::Data::Struct( syn::DataStruct { ref fields, .. } ) => fields.span(),
      syn::Data::Enum( syn::DataEnum { ref variants, .. } ) => variants.span(),
      syn::Data::Union( syn::DataUnion { ref fields, .. } ) => fields.span(),
    }
  }
}

/// Returns a Span covering the complete contents of this syntax tree node, or Span::call_site() if this node is empty.

pub fn span_of< Src : Spanned2 >( src : &Src ) -> proc_macro2::Span
{
  src.span2()
}
<|endoftext|>