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

//! Command-line interface of the rustbuild build system.
//!
//! This module implements the command-line parsing of the build system which
//! has various flags to configure how it's run.

use std::fs;
use std::path::PathBuf;
use std::process;

use getopts::Options;

use Build;
use config::Config;
use metadata;
use builder::Builder;

use cache::{Interned, INTERNER};

/// Deserialized version of all flags for this compile.
pub struct Flags {
    pub verbose: usize, // number of -v args; each extra -v after the first is passed to Cargo
    pub on_fail: Option<String>,
    pub stage: Option<u32>,
    pub keep_stage: Option<u32>,

    pub host: Vec<Interned<String>>,
    pub target: Vec<Interned<String>>,
    pub config: Option<PathBuf>,
    pub jobs: Option<u32>,
    pub cmd: Subcommand,
    pub incremental: bool,
    pub exclude: Vec<PathBuf>,
    pub rustc_error_format: Option<String>,
    pub dry_run: bool,
}

pub enum Subcommand {
    Build {
        paths: Vec<PathBuf>,
    },
    Check {
        paths: Vec<PathBuf>,
    },
    Doc {
        paths: Vec<PathBuf>,
    },
    Test {
        paths: Vec<PathBuf>,
        test_args: Vec<String>,
        rustc_args: Vec<String>,
        fail_fast: bool,
        doc_tests: bool,
    },
    Bench {
        paths: Vec<PathBuf>,
        test_args: Vec<String>,
    },
    Clean {
        all: bool,
    },
    Dist {
        paths: Vec<PathBuf>,
    },
    Install {
        paths: Vec<PathBuf>,
    },
}

impl Default for Subcommand {
    fn default() -> Subcommand {
        Subcommand::Build {
            paths: vec![PathBuf::from("nowhere")],
        }
    }
}

impl Flags {
    pub fn parse(args: &[String]) -> Flags {
        let mut extra_help = String::new();
        let mut subcommand_help = format!("\
Usage: x.py <subcommand> [options] [<paths>...]

Subcommands:
    build       Compile either the compiler or libraries
    check       Compile either the compiler or libraries, using cargo check
    test        Build and run some test suites
    bench       Build and run some benchmarks
    doc         Build documentation
    clean       Clean out build directories
    dist        Build distribution artifacts
    install     Install distribution artifacts

To learn more about a subcommand, run `./x.py <subcommand> -h`");

        let mut opts = Options::new();
        // Options common to all subcommands
        opts.optflagmulti("v", "verbose", "use verbose output (-vv for very verbose)");
        opts.optflag("i", "incremental", "use incremental compilation");
        opts.optopt("", "config", "TOML configuration file for build", "FILE");
        opts.optopt("", "build", "build target of the stage0 compiler", "BUILD");
        opts.optmulti("", "host", "host targets to build", "HOST");
        opts.optmulti("", "target", "target targets to build", "TARGET");
        opts.optmulti("", "exclude", "build paths to exclude", "PATH");
        opts.optopt("", "on-fail", "command to run on failure", "CMD");
        opts.optflag("", "dry-run", "dry run; don't build anything");
        opts.optopt("", "stage", "stage to build", "N");
        opts.optopt("", "keep-stage", "stage to keep without recompiling", "N");
        opts.optopt("", "src", "path to the root of the rust checkout", "DIR");
        opts.optopt("j", "jobs", "number of jobs to run in parallel", "JOBS");
        opts.optflag("h", "help", "print this help message");
        opts.optopt("", "error-format", "rustc error format", "FORMAT");

        // fn usage()
        let usage = |exit_code: i32, opts: &Options, subcommand_help: &str, extra_help: &str| -> ! {
            println!("{}", opts.usage(subcommand_help));
            if !extra_help.is_empty() {
                println!("{}", extra_help);
            }
            process::exit(exit_code);
        };

        // We can't use getopt to parse the options until we have completed specifying which
        // options are valid, but under the current implementation, some options are conditional on
        // the subcommand. Therefore we must manually identify the subcommand first, so that we can
        // complete the definition of the options.  Then we can use the getopt::Matches object from
        // there on out.
        let subcommand = args.iter().find(|&s|
            (s == "build")
            || (s == "check")
            || (s == "test")
            || (s == "bench")
            || (s == "doc")
            || (s == "clean")
            || (s == "dist")
            || (s == "install"));
        let subcommand = match subcommand {
            Some(s) => s,
            None => {
                // No or an invalid subcommand -- show the general usage and subcommand help
                // An exit code will be 0 when no subcommand is given, and 1 in case of an invalid
                // subcommand.
                println!("{}\n", subcommand_help);
                let exit_code = if args.is_empty() { 0 } else { 1 };
                process::exit(exit_code);
            }
        };

        // Some subcommands get extra options
        match subcommand.as_str() {
            "test"  => {
                opts.optflag("", "no-fail-fast", "Run all tests regardless of failure");
                opts.optmulti("", "test-args", "extra arguments", "ARGS");
                opts.optmulti(
                    "",
                    "rustc-args",
                    "extra options to pass the compiler when running tests",
                    "ARGS",
                );
                opts.optflag("", "doc", "run doc tests");
            },
            "bench" => { opts.optmulti("", "test-args", "extra arguments", "ARGS"); },
            "clean" => { opts.optflag("", "all", "clean all build artifacts"); },
            _ => { },
        };

        // Done specifying what options are possible, so do the getopts parsing
        let matches = opts.parse(&args[..]).unwrap_or_else(|e| {
            // Invalid argument/option format
            println!("\n{}\n", e);
            usage(1, &opts, &subcommand_help, &extra_help);
        });
        // Extra sanity check to make sure we didn't hit this crazy corner case:
        //
        //     ./x.py --frobulate clean build
        //            ^-- option  ^     ^- actual subcommand
        //                        \_ arg to option could be mistaken as subcommand
        let mut pass_sanity_check = true;
        match matches.free.get(0) {
            Some(check_subcommand) => {
                if check_subcommand != subcommand {
                    pass_sanity_check = false;
                }
            },
            None => {
                pass_sanity_check = false;
            }
        }
        if !pass_sanity_check {
            println!("{}\n", subcommand_help);
            println!("Sorry, I couldn't figure out which subcommand you were trying to specify.\n\
                      You may need to move some options to after the subcommand.\n");
            process::exit(1);
        }
        // Extra help text for some commands
        match subcommand.as_str() {
            "build" => {
                subcommand_help.push_str("\n
Arguments:
    This subcommand accepts a number of paths to directories to the crates
    and/or artifacts to compile. For example:

        ./x.py build src/libcore
        ./x.py build src/libcore src/libproc_macro
        ./x.py build src/libstd --stage 1

    If no arguments are passed then the complete artifacts for that stage are
    also compiled.

        ./x.py build
        ./x.py build --stage 1

    For a quick build of a usable compiler, you can pass:

        ./x.py build --stage 1 src/libtest

    This will first build everything once (like --stage 0 without further
    arguments would), and then use the compiler built in stage 0 to build
    src/libtest and its dependencies.
    Once this is done, build/$ARCH/stage1 contains a usable compiler.");
            }
            "check" => {
                subcommand_help.push_str("\n
Arguments:
    This subcommand accepts a number of paths to directories to the crates
    and/or artifacts to compile. For example:

        ./x.py check src/libcore
        ./x.py check src/libcore src/libproc_macro

    If no arguments are passed then the complete artifacts are compiled: std, test, and rustc. Note
    also that since we use `cargo check`, by default this will automatically enable incremental
    compilation, so there's no need to pass it separately, though it won't hurt. We also completely
    ignore the stage passed, as there's no way to compile in non-stage 0 without actually building
    the compiler.");
            }
            "test" => {
                subcommand_help.push_str("\n
Arguments:
    This subcommand accepts a number of paths to directories to tests that
    should be compiled and run. For example:

        ./x.py test src/test/run-pass
        ./x.py test src/libstd --test-args hash_map
        ./x.py test src/libstd --stage 0

    If no arguments are passed then the complete artifacts for that stage are
    compiled and tested.

        ./x.py test
        ./x.py test --stage 1");
            }
            "doc" => {
                subcommand_help.push_str("\n
Arguments:
    This subcommand accepts a number of paths to directories of documentation
    to build. For example:

        ./x.py doc src/doc/book
        ./x.py doc src/doc/nomicon
        ./x.py doc src/doc/book src/libstd

    If no arguments are passed then everything is documented:

        ./x.py doc
        ./x.py doc --stage 1");
            }
            _ => { }
        };
        // Get any optional paths which occur after the subcommand
        let paths = matches.free[1..].iter().map(|p| p.into()).collect::<Vec<PathBuf>>();

        let cfg_file = matches.opt_str("config").map(PathBuf::from).or_else(|| {
            if fs::metadata("config.toml").is_ok() {
                Some(PathBuf::from("config.toml"))
            } else {
                None
            }
        });

        // All subcommands except `clean` can have an optional "Available paths" section
        if matches.opt_present("verbose") {
            let config = Config::parse(&["build".to_string()]);
            let mut build = Build::new(config);
            metadata::build(&mut build);

            let maybe_rules_help = Builder::get_help(&build, subcommand.as_str());
            extra_help.push_str(maybe_rules_help.unwrap_or_default().as_str());
        } else if subcommand.as_str() != "clean" {
            extra_help.push_str(format!(
                "Run `./x.py {} -h -v` to see a list of available paths.",
                subcommand).as_str());
        }

        // User passed in -h/--help?
        if matches.opt_present("help") {
            usage(0, &opts, &subcommand_help, &extra_help);
        }

        let cmd = match subcommand.as_str() {
            "build" => {
                Subcommand::Build { paths: paths }
            }
            "check" => {
                Subcommand::Check { paths: paths }
            }
            "test" => {
                Subcommand::Test {
                    paths,
                    test_args: matches.opt_strs("test-args"),
                    rustc_args: matches.opt_strs("rustc-args"),
                    fail_fast: !matches.opt_present("no-fail-fast"),
                    doc_tests: matches.opt_present("doc"),
                }
            }
            "bench" => {
                Subcommand::Bench {
                    paths,
                    test_args: matches.opt_strs("test-args"),
                }
            }
            "doc" => {
                Subcommand::Doc { paths: paths }
            }
            "clean" => {
                if paths.len() > 0 {
                    println!("\nclean does not take a path argument\n");
                    usage(1, &opts, &subcommand_help, &extra_help);
                }

                Subcommand::Clean {
                    all: matches.opt_present("all"),
                }
            }
            "dist" => {
                Subcommand::Dist {
                    paths,
                }
            }
            "install" => {
                Subcommand::Install {
                    paths,
                }
            }
            _ => {
                usage(1, &opts, &subcommand_help, &extra_help);
            }
        };

        let mut stage = matches.opt_str("stage").map(|j| j.parse().unwrap());

        if matches.opt_present("incremental") && stage.is_none() {
            stage = Some(1);
        }

        Flags {
            verbose: matches.opt_count("verbose"),
            stage,
            dry_run: matches.opt_present("dry-run"),
            on_fail: matches.opt_str("on-fail"),
            rustc_error_format: matches.opt_str("error-format"),
            keep_stage: matches.opt_str("keep-stage").map(|j| j.parse().unwrap()),
            host: split(matches.opt_strs("host"))
                .into_iter().map(|x| INTERNER.intern_string(x)).collect::<Vec<_>>(),
            target: split(matches.opt_strs("target"))
                .into_iter().map(|x| INTERNER.intern_string(x)).collect::<Vec<_>>(),
            config: cfg_file,
            jobs: matches.opt_str("jobs").map(|j| j.parse().unwrap()),
            cmd,
            incremental: matches.opt_present("incremental"),
            exclude: split(matches.opt_strs("exclude"))
                .into_iter().map(|p| p.into()).collect::<Vec<_>>(),
        }
    }
}

impl Subcommand {
    pub fn test_args(&self) -> Vec<&str> {
        match *self {
            Subcommand::Test { ref test_args, .. } |
            Subcommand::Bench { ref test_args, .. } => {
                test_args.iter().flat_map(|s| s.split_whitespace()).collect()
            }
            _ => Vec::new(),
        }
    }

    pub fn rustc_args(&self) -> Vec<&str> {
        match *self {
            Subcommand::Test { ref rustc_args, .. } => {
                rustc_args.iter().flat_map(|s| s.split_whitespace()).collect()
            }
            _ => Vec::new(),
        }
    }

    pub fn fail_fast(&self) -> bool {
        match *self {
            Subcommand::Test { fail_fast, .. } => fail_fast,
            _ => false,
        }
    }

    pub fn doc_tests(&self) -> bool {
        match *self {
            Subcommand::Test { doc_tests, .. } => doc_tests,
            _ => false,
        }
    }
}

fn split(s: Vec<String>) -> Vec<String> {
    s.iter().flat_map(|s| s.split(',')).map(|s| s.to_string()).collect()
}
<commit_msg>Don't default to stage 1 with incremental<commit_after>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Command-line interface of the rustbuild build system.
//!
//! This module implements the command-line parsing of the build system which
//! has various flags to configure how it's run.

use std::fs;
use std::path::PathBuf;
use std::process;

use getopts::Options;

use Build;
use config::Config;
use metadata;
use builder::Builder;

use cache::{Interned, INTERNER};

/// Deserialized version of all flags for this compile.
pub struct Flags {
    pub verbose: usize, // number of -v args; each extra -v after the first is passed to Cargo
    pub on_fail: Option<String>,
    pub stage: Option<u32>,
    pub keep_stage: Option<u32>,

    pub host: Vec<Interned<String>>,
    pub target: Vec<Interned<String>>,
    pub config: Option<PathBuf>,
    pub jobs: Option<u32>,
    pub cmd: Subcommand,
    pub incremental: bool,
    pub exclude: Vec<PathBuf>,
    pub rustc_error_format: Option<String>,
    pub dry_run: bool,
}

pub enum Subcommand {
    Build {
        paths: Vec<PathBuf>,
    },
    Check {
        paths: Vec<PathBuf>,
    },
    Doc {
        paths: Vec<PathBuf>,
    },
    Test {
        paths: Vec<PathBuf>,
        test_args: Vec<String>,
        rustc_args: Vec<String>,
        fail_fast: bool,
        doc_tests: bool,
    },
    Bench {
        paths: Vec<PathBuf>,
        test_args: Vec<String>,
    },
    Clean {
        all: bool,
    },
    Dist {
        paths: Vec<PathBuf>,
    },
    Install {
        paths: Vec<PathBuf>,
    },
}

impl Default for Subcommand {
    fn default() -> Subcommand {
        Subcommand::Build {
            paths: vec![PathBuf::from("nowhere")],
        }
    }
}

impl Flags {
    pub fn parse(args: &[String]) -> Flags {
        let mut extra_help = String::new();
        let mut subcommand_help = format!("\
Usage: x.py <subcommand> [options] [<paths>...]

Subcommands:
    build       Compile either the compiler or libraries
    check       Compile either the compiler or libraries, using cargo check
    test        Build and run some test suites
    bench       Build and run some benchmarks
    doc         Build documentation
    clean       Clean out build directories
    dist        Build distribution artifacts
    install     Install distribution artifacts

To learn more about a subcommand, run `./x.py <subcommand> -h`");

        let mut opts = Options::new();
        // Options common to all subcommands
        opts.optflagmulti("v", "verbose", "use verbose output (-vv for very verbose)");
        opts.optflag("i", "incremental", "use incremental compilation");
        opts.optopt("", "config", "TOML configuration file for build", "FILE");
        opts.optopt("", "build", "build target of the stage0 compiler", "BUILD");
        opts.optmulti("", "host", "host targets to build", "HOST");
        opts.optmulti("", "target", "target targets to build", "TARGET");
        opts.optmulti("", "exclude", "build paths to exclude", "PATH");
        opts.optopt("", "on-fail", "command to run on failure", "CMD");
        opts.optflag("", "dry-run", "dry run; don't build anything");
        opts.optopt("", "stage", "stage to build", "N");
        opts.optopt("", "keep-stage", "stage to keep without recompiling", "N");
        opts.optopt("", "src", "path to the root of the rust checkout", "DIR");
        opts.optopt("j", "jobs", "number of jobs to run in parallel", "JOBS");
        opts.optflag("h", "help", "print this help message");
        opts.optopt("", "error-format", "rustc error format", "FORMAT");

        // fn usage()
        let usage = |exit_code: i32, opts: &Options, subcommand_help: &str, extra_help: &str| -> ! {
            println!("{}", opts.usage(subcommand_help));
            if !extra_help.is_empty() {
                println!("{}", extra_help);
            }
            process::exit(exit_code);
        };

        // We can't use getopt to parse the options until we have completed specifying which
        // options are valid, but under the current implementation, some options are conditional on
        // the subcommand. Therefore we must manually identify the subcommand first, so that we can
        // complete the definition of the options.  Then we can use the getopt::Matches object from
        // there on out.
        let subcommand = args.iter().find(|&s|
            (s == "build")
            || (s == "check")
            || (s == "test")
            || (s == "bench")
            || (s == "doc")
            || (s == "clean")
            || (s == "dist")
            || (s == "install"));
        let subcommand = match subcommand {
            Some(s) => s,
            None => {
                // No or an invalid subcommand -- show the general usage and subcommand help
                // An exit code will be 0 when no subcommand is given, and 1 in case of an invalid
                // subcommand.
                println!("{}\n", subcommand_help);
                let exit_code = if args.is_empty() { 0 } else { 1 };
                process::exit(exit_code);
            }
        };

        // Some subcommands get extra options
        match subcommand.as_str() {
            "test"  => {
                opts.optflag("", "no-fail-fast", "Run all tests regardless of failure");
                opts.optmulti("", "test-args", "extra arguments", "ARGS");
                opts.optmulti(
                    "",
                    "rustc-args",
                    "extra options to pass the compiler when running tests",
                    "ARGS",
                );
                opts.optflag("", "doc", "run doc tests");
            },
            "bench" => { opts.optmulti("", "test-args", "extra arguments", "ARGS"); },
            "clean" => { opts.optflag("", "all", "clean all build artifacts"); },
            _ => { },
        };

        // Done specifying what options are possible, so do the getopts parsing
        let matches = opts.parse(&args[..]).unwrap_or_else(|e| {
            // Invalid argument/option format
            println!("\n{}\n", e);
            usage(1, &opts, &subcommand_help, &extra_help);
        });
        // Extra sanity check to make sure we didn't hit this crazy corner case:
        //
        //     ./x.py --frobulate clean build
        //            ^-- option  ^     ^- actual subcommand
        //                        \_ arg to option could be mistaken as subcommand
        let mut pass_sanity_check = true;
        match matches.free.get(0) {
            Some(check_subcommand) => {
                if check_subcommand != subcommand {
                    pass_sanity_check = false;
                }
            },
            None => {
                pass_sanity_check = false;
            }
        }
        if !pass_sanity_check {
            println!("{}\n", subcommand_help);
            println!("Sorry, I couldn't figure out which subcommand you were trying to specify.\n\
                      You may need to move some options to after the subcommand.\n");
            process::exit(1);
        }
        // Extra help text for some commands
        match subcommand.as_str() {
            "build" => {
                subcommand_help.push_str("\n
Arguments:
    This subcommand accepts a number of paths to directories to the crates
    and/or artifacts to compile. For example:

        ./x.py build src/libcore
        ./x.py build src/libcore src/libproc_macro
        ./x.py build src/libstd --stage 1

    If no arguments are passed then the complete artifacts for that stage are
    also compiled.

        ./x.py build
        ./x.py build --stage 1

    For a quick build of a usable compiler, you can pass:

        ./x.py build --stage 1 src/libtest

    This will first build everything once (like --stage 0 without further
    arguments would), and then use the compiler built in stage 0 to build
    src/libtest and its dependencies.
    Once this is done, build/$ARCH/stage1 contains a usable compiler.");
            }
            "check" => {
                subcommand_help.push_str("\n
Arguments:
    This subcommand accepts a number of paths to directories to the crates
    and/or artifacts to compile. For example:

        ./x.py check src/libcore
        ./x.py check src/libcore src/libproc_macro

    If no arguments are passed then the complete artifacts are compiled: std, test, and rustc. Note
    also that since we use `cargo check`, by default this will automatically enable incremental
    compilation, so there's no need to pass it separately, though it won't hurt. We also completely
    ignore the stage passed, as there's no way to compile in non-stage 0 without actually building
    the compiler.");
            }
            "test" => {
                subcommand_help.push_str("\n
Arguments:
    This subcommand accepts a number of paths to directories to tests that
    should be compiled and run. For example:

        ./x.py test src/test/run-pass
        ./x.py test src/libstd --test-args hash_map
        ./x.py test src/libstd --stage 0

    If no arguments are passed then the complete artifacts for that stage are
    compiled and tested.

        ./x.py test
        ./x.py test --stage 1");
            }
            "doc" => {
                subcommand_help.push_str("\n
Arguments:
    This subcommand accepts a number of paths to directories of documentation
    to build. For example:

        ./x.py doc src/doc/book
        ./x.py doc src/doc/nomicon
        ./x.py doc src/doc/book src/libstd

    If no arguments are passed then everything is documented:

        ./x.py doc
        ./x.py doc --stage 1");
            }
            _ => { }
        };
        // Get any optional paths which occur after the subcommand
        let paths = matches.free[1..].iter().map(|p| p.into()).collect::<Vec<PathBuf>>();

        let cfg_file = matches.opt_str("config").map(PathBuf::from).or_else(|| {
            if fs::metadata("config.toml").is_ok() {
                Some(PathBuf::from("config.toml"))
            } else {
                None
            }
        });

        // All subcommands except `clean` can have an optional "Available paths" section
        if matches.opt_present("verbose") {
            let config = Config::parse(&["build".to_string()]);
            let mut build = Build::new(config);
            metadata::build(&mut build);

            let maybe_rules_help = Builder::get_help(&build, subcommand.as_str());
            extra_help.push_str(maybe_rules_help.unwrap_or_default().as_str());
        } else if subcommand.as_str() != "clean" {
            extra_help.push_str(format!(
                "Run `./x.py {} -h -v` to see a list of available paths.",
                subcommand).as_str());
        }

        // User passed in -h/--help?
        if matches.opt_present("help") {
            usage(0, &opts, &subcommand_help, &extra_help);
        }

        let cmd = match subcommand.as_str() {
            "build" => {
                Subcommand::Build { paths: paths }
            }
            "check" => {
                Subcommand::Check { paths: paths }
            }
            "test" => {
                Subcommand::Test {
                    paths,
                    test_args: matches.opt_strs("test-args"),
                    rustc_args: matches.opt_strs("rustc-args"),
                    fail_fast: !matches.opt_present("no-fail-fast"),
                    doc_tests: matches.opt_present("doc"),
                }
            }
            "bench" => {
                Subcommand::Bench {
                    paths,
                    test_args: matches.opt_strs("test-args"),
                }
            }
            "doc" => {
                Subcommand::Doc { paths: paths }
            }
            "clean" => {
                if paths.len() > 0 {
                    println!("\nclean does not take a path argument\n");
                    usage(1, &opts, &subcommand_help, &extra_help);
                }

                Subcommand::Clean {
                    all: matches.opt_present("all"),
                }
            }
            "dist" => {
                Subcommand::Dist {
                    paths,
                }
            }
            "install" => {
                Subcommand::Install {
                    paths,
                }
            }
            _ => {
                usage(1, &opts, &subcommand_help, &extra_help);
            }
        };

        Flags {
            verbose: matches.opt_count("verbose"),
            stage: matches.opt_str("stage").map(|j| j.parse().unwrap()),
            dry_run: matches.opt_present("dry-run"),
            on_fail: matches.opt_str("on-fail"),
            rustc_error_format: matches.opt_str("error-format"),
            keep_stage: matches.opt_str("keep-stage").map(|j| j.parse().unwrap()),
            host: split(matches.opt_strs("host"))
                .into_iter().map(|x| INTERNER.intern_string(x)).collect::<Vec<_>>(),
            target: split(matches.opt_strs("target"))
                .into_iter().map(|x| INTERNER.intern_string(x)).collect::<Vec<_>>(),
            config: cfg_file,
            jobs: matches.opt_str("jobs").map(|j| j.parse().unwrap()),
            cmd,
            incremental: matches.opt_present("incremental"),
            exclude: split(matches.opt_strs("exclude"))
                .into_iter().map(|p| p.into()).collect::<Vec<_>>(),
        }
    }
}

impl Subcommand {
    pub fn test_args(&self) -> Vec<&str> {
        match *self {
            Subcommand::Test { ref test_args, .. } |
            Subcommand::Bench { ref test_args, .. } => {
                test_args.iter().flat_map(|s| s.split_whitespace()).collect()
            }
            _ => Vec::new(),
        }
    }

    pub fn rustc_args(&self) -> Vec<&str> {
        match *self {
            Subcommand::Test { ref rustc_args, .. } => {
                rustc_args.iter().flat_map(|s| s.split_whitespace()).collect()
            }
            _ => Vec::new(),
        }
    }

    pub fn fail_fast(&self) -> bool {
        match *self {
            Subcommand::Test { fail_fast, .. } => fail_fast,
            _ => false,
        }
    }

    pub fn doc_tests(&self) -> bool {
        match *self {
            Subcommand::Test { doc_tests, .. } => doc_tests,
            _ => false,
        }
    }
}

fn split(s: Vec<String>) -> Vec<String> {
    s.iter().flat_map(|s| s.split(',')).map(|s| s.to_string()).collect()
}
<|endoftext|>


<commit_before>use crate::t;
use std::path::{Path, PathBuf};
use std::str::FromStr;
use std::{
    env, fmt, fs,
    io::{self, Write},
};

#[derive(Clone, Copy, Eq, PartialEq)]
pub enum Profile {
    Compiler,
    Codegen,
    Library,
    User,
}

impl Profile {
    fn include_path(&self, src_path: &Path) -> PathBuf {
        PathBuf::from(format!("{}/src/bootstrap/defaults/config.{}.toml", src_path.display(), self))
    }

    pub fn all() -> impl Iterator<Item = Self> {
        [Profile::Compiler, Profile::Codegen, Profile::Library, Profile::User].iter().copied()
    }
}

impl FromStr for Profile {
    type Err = String;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "a" | "lib" | "library" => Ok(Profile::Library),
            "b" | "compiler" => Ok(Profile::Compiler),
            "c" | "llvm" | "codegen" => Ok(Profile::Codegen),
            "d" | "maintainer" | "user" => Ok(Profile::User),
            _ => Err(format!("unknown profile: '{}'", s)),
        }
    }
}

impl fmt::Display for Profile {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Profile::Compiler => write!(f, "compiler"),
            Profile::Codegen => write!(f, "codegen"),
            Profile::Library => write!(f, "library"),
            Profile::User => write!(f, "user"),
        }
    }
}

pub fn setup(src_path: &Path, profile: Profile) {
    let cfg_file = env::var_os("BOOTSTRAP_CONFIG").map(PathBuf::from);

    if cfg_file.as_ref().map_or(false, |f| f.exists()) {
        let file = cfg_file.unwrap();
        println!(
            "error: you asked `x.py` to setup a new config file, but one already exists at `{}`",
            file.display()
        );
        println!("help: try adding `profile = \"{}\"` at the top of {}", profile, file.display());
        println!(
            "note: this will use the configuration in {}",
            profile.include_path(src_path).display()
        );
        std::process::exit(1);
    }

    let path = cfg_file.unwrap_or_else(|| src_path.join("config.toml"));
    let settings = format!(
        "# Includes one of the default files in src/bootstrap/defaults\n\
    profile = \"{}\"\n",
        profile
    );
    t!(fs::write(path, settings));

    let include_path = profile.include_path(src_path);
    println!("`x.py` will now use the configuration at {}", include_path.display());

    let suggestions = match profile {
        Profile::Codegen | Profile::Compiler => &["check", "build", "test"][..],
        Profile::Library => &["check", "build", "test library/std", "doc"],
        Profile::User => &["dist", "build"],
    };

    println!();

    t!(install_git_hook_maybe(src_path));

    println!();

    println!("To get started, try one of the following commands:");
    for cmd in suggestions {
        println!("- `x.py {}`", cmd);
    }

    if profile != Profile::User {
        println!(
            "For more suggestions, see https://rustc-dev-guide.rust-lang.org/building/suggested.html"
        );
    }
}

// Used to get the path for `Subcommand::Setup`
pub fn interactive_path() -> io::Result<Profile> {
    let mut input = String::new();
    println!(
        "Welcome to the Rust project! What do you want to do with x.py?
a) Contribute to the standard library
b) Contribute to the compiler
c) Contribute to the compiler, and also modify LLVM or codegen
d) Install Rust from source"
    );
    let template = loop {
        print!("Please choose one (a/b/c/d): ");
        io::stdout().flush()?;
        io::stdin().read_line(&mut input)?;
        break match input.trim().to_lowercase().parse() {
            Ok(profile) => profile,
            Err(err) => {
                println!("error: {}", err);
                println!("note: press Ctrl+C to exit");
                continue;
            }
        };
    };
    Ok(template)
}

// install a git hook to automatically run tidy --bless, if they want
fn install_git_hook_maybe(src_path: &Path) -> io::Result<()> {
    let mut input = String::new();
    println!(
        "Rust's CI will automatically fail if it doesn't pass `tidy`, the internal tool for ensuring code quality.
If you'd like, x.py can install a git hook for you that will automatically run `tidy --bless` on each commit
to ensure your code is up to par. If you decide later that this behavior is undesirable,
simply delete the `pre-commit` file from .git/hooks."
    );

    let should_install = loop {
        print!("Would you like to install the git hook?: [y/N] ");
        io::stdout().flush()?;
        input.clear();
        io::stdin().read_line(&mut input)?;
        break match input.trim().to_lowercase().as_str() {
            "y" | "yes" => true,
            "n" | "no" | "" => false,
            _ => {
                println!("error: unrecognized option '{}'", input.trim());
                println!("note: press Ctrl+C to exit");
                continue;
            }
        };
    };

    Ok(if should_install {
        let src = src_path.join("src").join("etc").join("pre-commit.sh");
        let dst = src_path.join(".git").join("hooks").join("pre-commit");
        match fs::hard_link(src, dst) {
            Err(e) => println!(
                "x.py encountered an error -- do you already have the git hook installed?\n{}",
                e
            ),
            Ok(_) => println!("Linked `src/etc/pre-commit.sh` to `.git/hooks/pre-commit`"),
        };
    } else {
        println!("Ok, skipping installation!");
    })
}
<commit_msg>Allow setting up git hooks from other worktrees<commit_after>use crate::t;
use std::path::{Path, PathBuf};
use std::process::Command;
use std::str::FromStr;
use std::{
    env, fmt, fs,
    io::{self, Write},
};

#[derive(Clone, Copy, Eq, PartialEq)]
pub enum Profile {
    Compiler,
    Codegen,
    Library,
    User,
}

impl Profile {
    fn include_path(&self, src_path: &Path) -> PathBuf {
        PathBuf::from(format!("{}/src/bootstrap/defaults/config.{}.toml", src_path.display(), self))
    }

    pub fn all() -> impl Iterator<Item = Self> {
        [Profile::Compiler, Profile::Codegen, Profile::Library, Profile::User].iter().copied()
    }
}

impl FromStr for Profile {
    type Err = String;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "a" | "lib" | "library" => Ok(Profile::Library),
            "b" | "compiler" => Ok(Profile::Compiler),
            "c" | "llvm" | "codegen" => Ok(Profile::Codegen),
            "d" | "maintainer" | "user" => Ok(Profile::User),
            _ => Err(format!("unknown profile: '{}'", s)),
        }
    }
}

impl fmt::Display for Profile {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Profile::Compiler => write!(f, "compiler"),
            Profile::Codegen => write!(f, "codegen"),
            Profile::Library => write!(f, "library"),
            Profile::User => write!(f, "user"),
        }
    }
}

pub fn setup(src_path: &Path, profile: Profile) {
    let cfg_file = env::var_os("BOOTSTRAP_CONFIG").map(PathBuf::from);

    if cfg_file.as_ref().map_or(false, |f| f.exists()) {
        let file = cfg_file.unwrap();
        println!(
            "error: you asked `x.py` to setup a new config file, but one already exists at `{}`",
            file.display()
        );
        println!("help: try adding `profile = \"{}\"` at the top of {}", profile, file.display());
        println!(
            "note: this will use the configuration in {}",
            profile.include_path(src_path).display()
        );
        std::process::exit(1);
    }

    let path = cfg_file.unwrap_or_else(|| src_path.join("config.toml"));
    let settings = format!(
        "# Includes one of the default files in src/bootstrap/defaults\n\
    profile = \"{}\"\n",
        profile
    );
    t!(fs::write(path, settings));

    let include_path = profile.include_path(src_path);
    println!("`x.py` will now use the configuration at {}", include_path.display());

    let suggestions = match profile {
        Profile::Codegen | Profile::Compiler => &["check", "build", "test"][..],
        Profile::Library => &["check", "build", "test library/std", "doc"],
        Profile::User => &["dist", "build"],
    };

    println!();

    t!(install_git_hook_maybe(src_path));

    println!();

    println!("To get started, try one of the following commands:");
    for cmd in suggestions {
        println!("- `x.py {}`", cmd);
    }

    if profile != Profile::User {
        println!(
            "For more suggestions, see https://rustc-dev-guide.rust-lang.org/building/suggested.html"
        );
    }
}

// Used to get the path for `Subcommand::Setup`
pub fn interactive_path() -> io::Result<Profile> {
    let mut input = String::new();
    println!(
        "Welcome to the Rust project! What do you want to do with x.py?
a) Contribute to the standard library
b) Contribute to the compiler
c) Contribute to the compiler, and also modify LLVM or codegen
d) Install Rust from source"
    );
    let template = loop {
        print!("Please choose one (a/b/c/d): ");
        io::stdout().flush()?;
        io::stdin().read_line(&mut input)?;
        break match input.trim().to_lowercase().parse() {
            Ok(profile) => profile,
            Err(err) => {
                println!("error: {}", err);
                println!("note: press Ctrl+C to exit");
                continue;
            }
        };
    };
    Ok(template)
}

// install a git hook to automatically run tidy --bless, if they want
fn install_git_hook_maybe(src_path: &Path) -> io::Result<()> {
    let mut input = String::new();
    println!(
        "Rust's CI will automatically fail if it doesn't pass `tidy`, the internal tool for ensuring code quality.
If you'd like, x.py can install a git hook for you that will automatically run `tidy --bless` on each commit
to ensure your code is up to par. If you decide later that this behavior is undesirable,
simply delete the `pre-commit` file from .git/hooks."
    );

    let should_install = loop {
        print!("Would you like to install the git hook?: [y/N] ");
        io::stdout().flush()?;
        input.clear();
        io::stdin().read_line(&mut input)?;
        break match input.trim().to_lowercase().as_str() {
            "y" | "yes" => true,
            "n" | "no" | "" => false,
            _ => {
                println!("error: unrecognized option '{}'", input.trim());
                println!("note: press Ctrl+C to exit");
                continue;
            }
        };
    };

    Ok(if should_install {
        let src = src_path.join("src").join("etc").join("pre-commit.sh");
        let git = t!(Command::new("git").args(&["rev-parse", "--git-common-dir"]).output().map(
            |output| {
                assert!(output.status.success(), "failed to run `git`");
                PathBuf::from(t!(String::from_utf8(output.stdout)).trim())
            }
        ));
        let dst = git.join("hooks").join("pre-commit");
        match fs::hard_link(src, &dst) {
            Err(e) => println!(
                "error: could not create hook {}: do you already have the git hook installed?\n{}",
                dst.display(),
                e
            ),
            Ok(_) => println!("Linked `src/etc/pre-commit.sh` to `.git/hooks/pre-commit`"),
        };
    } else {
        println!("Ok, skipping installation!");
    })
}
<|endoftext|>


<commit_before>use crate::os::process::can_run_services_as_svc_user;
use nix::unistd::{getgrouplist,
                  setgid,
                  setgroups,
                  setuid,
                  Gid,
                  Uid,
                  User};
use std::{ffi::{CString,
                OsStr},
          io,
          os::unix::process::CommandExt,
          process::{Command,
                    Stdio},
          result};

/// Prepare a `Command` to execute a lifecycle hook.
// TODO (CM): Ideally, `ids` would not be an `Option`, but separate
// `Uid` and `Gid` inputs. However, the `Option` interface provides
// the least disruption to other existing code for the time being.
pub fn hook_command<X, I, K, V>(executable: X, env: I, ids: Option<(Uid, Gid)>) -> Command
    where X: AsRef<OsStr>,
          I: IntoIterator<Item = (K, V)>,
          K: AsRef<OsStr>,
          V: AsRef<OsStr>
{
    let mut cmd = Command::new(executable);

    // NOTE: CommandExt::uid and CommandExt::guid should *not* be
    // called here! They are set in `with_user_and_group_information`;
    // see there for further details.
    cmd.stdin(Stdio::null())
       .stdout(Stdio::piped())
       .stderr(Stdio::piped())
       .envs(env);

    with_own_process_group(&mut cmd);
    if let Some((uid, gid)) = ids {
        with_user_and_group_information(&mut cmd, uid, gid);
    }

    cmd
}

/// Ensures that the `Command` is executed within its own process
/// group, and not that of its parent process.
///
/// This should be used when spawning all hooks. This ensures that
/// they are not the same process group as the Launcher (for `run`
/// hooks) or the Supervisor (for all other hooks). Otherwise, if a
/// child process were to send `SIGTERM`, the Launcher could be
/// terminated. Similarly, it prevents a `^C` sent to a foregrounded
/// Supervisor from terminating any hooks prematurely.
///
/// This basically ensures that all hooks are properly isolated,
/// without signaling cross-talk between them and the Launcher /
/// Supervisor.
fn with_own_process_group(cmd: &mut Command) -> &mut Command {
    unsafe {
        cmd.pre_exec(set_own_process_group);
    }
    cmd
}

/// Set the process group of the calling process to be the same as its
/// PID.
///
/// Intended for use in a
/// `std::os::unix::process::CommandExt::pre_exec` callback.
fn set_own_process_group() -> result::Result<(), io::Error> {
    unsafe {
        if libc::setpgid(0, 0) == 0 {
            Ok(())
        } else {
            Err(io::Error::last_os_error())
        }
    }
}
/// Sets uid, gid, and supplementary groups on command.
///
/// DO NOT call `CommandExt#uid` or `CommandExt#gid` on this command,
/// either before or after calling this function, or it will probably
/// not work like you want it to.
fn with_user_and_group_information(cmd: &mut Command, uid: Uid, gid: Gid) -> &mut Command {
    unsafe {
        cmd.pre_exec(set_supplementary_groups(uid, gid));
    }
    cmd
}

/// Stupid little private helper macro to make mapping `Nix` errors to
/// IO errors for our `pre_exec` hooks.
///
/// The format string should have a single variable placeholder for
/// the actual error.
///
/// e.g. `result.map_err(io_err!("blah blah {:?}"))`
macro_rules! io_error {
    ($format_string:tt) => {
        move |e| io::Error::new(io::ErrorKind::Other, format!($format_string, e))
    };
}

/// Returns a function that sets the supplementary group IDs of the
/// process to those that `user_id` belongs to.
///
/// Also sets the uid and gid of the process. We must do that here,
/// rather than using the `CommandExt::uid` and `CommandExt::gid`
/// methods to ensure that all the IDs are set on the process in the
/// correct order (that is, supplementary groups, gid, and finally uid).
///
/// Once https://github.com/rust-lang/rust/pull/72160 merges, we can
/// use all `CommandExt` methods, and thus simplify things a (little)
/// bit.
fn set_supplementary_groups(user_id: Uid,
                            group_id: Gid)
                            -> impl Fn() -> result::Result<(), io::Error> {
    // Note: since this function will be run a separate process that doesn't
    // inherit RUST_LOG, none of the log! macros will work actually
    // work here.

    move || {
        // Note that if we *can't* run services as another user,
        // that's OK; not an error. We just won't set supplementary
        // groups, and run all hooks as the user we currently are.
        if can_run_services_as_svc_user() {
            if let Some(user) =
                User::from_uid(user_id).map_err(io_error!("Error resolving user from ID: {:?}"))?
            {
                let user = CString::new(user.name).map_err(io_error!("User name cannot convert \
                                                                      to CString!: {:?}"))?;
                let groups =
                    getgrouplist(&user, group_id).map_err(io_error!("getgrouplist failed!: {:?}"))?;
                setgroups(&groups).map_err(io_error!("setgroups failed! {:?}"))?; // CAP_SETGID
            } else {
                return Err(io::Error::new(io::ErrorKind::Other,
                                          "Could not find user from user ID"));
            }

            // These calls replace `CommandExt::uid` and `CommandExt::gid`
            setgid(group_id).map_err(io_error!("setgid failed! {:?}"))?; // CAP_SETGID
            setuid(user_id).map_err(io_error!("setuid failed! {:?}"))?; // CAP_SETUID
        }

        Ok(())
    }
}
<commit_msg>[PR FEEDBACK] Fix compilation for macOS<commit_after>use crate::os::process::can_run_services_as_svc_user;
use nix::unistd::{setgid,
                  setuid,
                  Gid,
                  Uid};
use std::{ffi::OsStr,
          io,
          os::unix::process::CommandExt,
          process::{Command,
                    Stdio},
          result};

/// Prepare a `Command` to execute a lifecycle hook.
// TODO (CM): Ideally, `ids` would not be an `Option`, but separate
// `Uid` and `Gid` inputs. However, the `Option` interface provides
// the least disruption to other existing code for the time being.
pub fn hook_command<X, I, K, V>(executable: X, env: I, ids: Option<(Uid, Gid)>) -> Command
    where X: AsRef<OsStr>,
          I: IntoIterator<Item = (K, V)>,
          K: AsRef<OsStr>,
          V: AsRef<OsStr>
{
    let mut cmd = Command::new(executable);

    // NOTE: CommandExt::uid and CommandExt::guid should *not* be
    // called here! They are set in `with_user_and_group_information`;
    // see there for further details.
    cmd.stdin(Stdio::null())
       .stdout(Stdio::piped())
       .stderr(Stdio::piped())
       .envs(env);

    with_own_process_group(&mut cmd);
    if let Some((uid, gid)) = ids {
        with_user_and_group_information(&mut cmd, uid, gid);
    }

    cmd
}

/// Ensures that the `Command` is executed within its own process
/// group, and not that of its parent process.
///
/// This should be used when spawning all hooks. This ensures that
/// they are not the same process group as the Launcher (for `run`
/// hooks) or the Supervisor (for all other hooks). Otherwise, if a
/// child process were to send `SIGTERM`, the Launcher could be
/// terminated. Similarly, it prevents a `^C` sent to a foregrounded
/// Supervisor from terminating any hooks prematurely.
///
/// This basically ensures that all hooks are properly isolated,
/// without signaling cross-talk between them and the Launcher /
/// Supervisor.
fn with_own_process_group(cmd: &mut Command) -> &mut Command {
    unsafe {
        cmd.pre_exec(set_own_process_group);
    }
    cmd
}

/// Set the process group of the calling process to be the same as its
/// PID.
///
/// Intended for use in a
/// `std::os::unix::process::CommandExt::pre_exec` callback.
fn set_own_process_group() -> result::Result<(), io::Error> {
    unsafe {
        if libc::setpgid(0, 0) == 0 {
            Ok(())
        } else {
            Err(io::Error::last_os_error())
        }
    }
}
/// Sets uid, gid, and supplementary groups on command.
///
/// DO NOT call `CommandExt#uid` or `CommandExt#gid` on this command,
/// either before or after calling this function, or it will probably
/// not work like you want it to.
fn with_user_and_group_information(cmd: &mut Command, uid: Uid, gid: Gid) -> &mut Command {
    unsafe {
        cmd.pre_exec(set_supplementary_groups(uid, gid));
    }
    cmd
}

/// Stupid little private helper macro to make mapping `Nix` errors to
/// IO errors for our `pre_exec` hooks.
///
/// The format string should have a single variable placeholder for
/// the actual error.
///
/// e.g. `result.map_err(io_err!("blah blah {:?}"))`
macro_rules! io_error {
    ($format_string:tt) => {
        move |e| io::Error::new(io::ErrorKind::Other, format!($format_string, e))
    };
}

/// Returns a function that sets the supplementary group IDs of the
/// process to those that `user_id` belongs to.
///
/// Also sets the uid and gid of the process. We must do that here,
/// rather than using the `CommandExt::uid` and `CommandExt::gid`
/// methods to ensure that all the IDs are set on the process in the
/// correct order (that is, supplementary groups, gid, and finally uid).
///
/// Once https://github.com/rust-lang/rust/pull/72160 merges, we can
/// use all `CommandExt` methods, and thus simplify things a (little)
/// bit.
fn set_supplementary_groups(user_id: Uid,
                            group_id: Gid)
                            -> impl Fn() -> result::Result<(), io::Error> {
    // Note: since this function will be run a separate process that doesn't
    // inherit RUST_LOG, none of the log! macros will work actually
    // work here.

    move || {
        // Note that if we *can't* run services as another user,
        // that's OK; not an error. We just won't set supplementary
        // groups, and run all hooks as the user we currently are.
        if can_run_services_as_svc_user() {
            // These calls don't have a macOS counterpart (well, not a
            // direct one in nix, at least) and we don't need to
            // execute hooks on macOS, so it's not important to
            // implement this. Our crates aren't really factored well
            // enough to let us cut out larger chunks of code on macOS
            // at the moment, so we just won't compile this particular
            // bit for now.
            #[cfg(not(target_os = "macos"))]
            {
                use nix::unistd::{getgrouplist,
                                  setgroups,
                                  User};
                use std::ffi::CString;

                if let Some(user) = User::from_uid(user_id).map_err(io_error!("Error resolving \
                                                                               user from ID: \
                                                                               {:?}"))?
                {
                    let user = CString::new(user.name).map_err(io_error!("User name cannot \
                                                                          convert to CString!: \
                                                                          {:?}"))?;
                    let groups = getgrouplist(&user, group_id).map_err(io_error!("getgrouplist \
                                                                                failed!: {:?}"))?;
                    setgroups(&groups).map_err(io_error!("setgroups failed! {:?}"))?; // CAP_SETGID
                } else {
                    return Err(io::Error::new(io::ErrorKind::Other,
                                              "Could not find user from user ID"));
                }
            }

            // These calls replace `CommandExt::uid` and `CommandExt::gid`
            setgid(group_id).map_err(io_error!("setgid failed! {:?}"))?; // CAP_SETGID
            setuid(user_id).map_err(io_error!("setuid failed! {:?}"))?; // CAP_SETUID
        }

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


<commit_before>extern crate raw_cpuid;

use core::fmt::{Result, Write};

use self::raw_cpuid::CpuId;

pub fn cpu_info<W: Write>(w: &mut W) -> Result {
    let cpuid = CpuId::new();

    if let Some(info) = cpuid.get_vendor_info() {
        write!(w, "Vendor: {}\n", info.as_string())?;
    }

    if let Some(info) = cpuid.get_extended_function_info() {
        if let Some(brand) = info.processor_brand_string() {
            write!(w, "Brand: {}\n", brand)?;
        }
    }

    write!(w, "Features:")?;

    if let Some(info) = cpuid.get_feature_info() {
        if info.has_fpu() { write!(w, " fpu")? };
        if info.has_vme() { write!(w, " vme")? };
        if info.has_de() { write!(w, " de")? };
        if info.has_pse() { write!(w, " pse")? };
        if info.has_tsc() { write!(w, " tsc")? };
        if info.has_msr() { write!(w, " msr")? };
        if info.has_pae() { write!(w, " pae")? };
        if info.has_mce() { write!(w, " mce")? };

        if info.has_cmpxchg8b() { write!(w, " cx8")? };
        if info.has_apic() { write!(w, " apic")? };
        if info.has_sysenter_sysexit() { write!(w, " sep")? };
        if info.has_mtrr() { write!(w, " mtrr")? };
        if info.has_pge() { write!(w, " pge")? };
        if info.has_mca() { write!(w, " mca")? };
        if info.has_cmov() { write!(w, " cmov")? };
        if info.has_pat() { write!(w, " pat")? };

        if info.has_pse36() { write!(w, " pse36")? };
        if info.has_psn() { write!(w, " psn")? };
        if info.has_clflush() { write!(w, " clflush")? };
        if info.has_ds() { write!(w, " ds")? };
        if info.has_acpi() { write!(w, " acpi")? };
        if info.has_mmx() { write!(w, " mmx")? };
        if info.has_fxsave_fxstor() { write!(w, " fxsr")? };
        if info.has_sse() { write!(w, " sse")? };

        if info.has_sse2() { write!(w, " sse2")? };
        if info.has_ss() { write!(w, " ss")? };
        if info.has_htt() { write!(w, " ht")? };
        if info.has_tm() { write!(w, " tm")? };
        if info.has_pbe() { write!(w, " pbe")? };

        if info.has_sse3() { write!(w, " sse3")? };
        if info.has_pclmulqdq() { write!(w, " pclmulqdq")? };
        if info.has_ds_area() { write!(w, " dtes64")? };
        if info.has_monitor_mwait() { write!(w, " monitor")? };
        if info.has_cpl() { write!(w, " ds_cpl")? };
        if info.has_vmx() { write!(w, " vmx")? };
        if info.has_smx() { write!(w, " smx")? };
        if info.has_eist() { write!(w, " est")? };

        if info.has_tm2() { write!(w, " tm2")? };
        if info.has_ssse3() { write!(w, " ssse3")? };
        if info.has_cnxtid() { write!(w, " cnxtid")? };
        if info.has_fma() { write!(w, " fma")? };
        if info.has_cmpxchg16b() { write!(w, " cx16")? };
        if info.has_pdcm() { write!(w, " pdcm")? };
        if info.has_pcid() { write!(w, " pcid")? };
        if info.has_dca() { write!(w, " dca")? };

        if info.has_sse41() { write!(w, " sse4_1")? };
        if info.has_sse42() { write!(w, " sse4_2")? };
        if info.has_x2apic() { write!(w, " x2apic")? };
        if info.has_movbe() { write!(w, " movbe")? };
        if info.has_popcnt() { write!(w, " popcnt")? };
        if info.has_tsc_deadline() { write!(w, " tsc_deadline_timer")? };
        if info.has_aesni() { write!(w, " aes")? };
        if info.has_xsave() { write!(w, " xsave")? };

        if info.has_oxsave() { write!(w, " oxsave")? };
        if info.has_avx() { write!(w, " avx")? };
        if info.has_f16c() { write!(w, " f16c")? };
        if info.has_rdrand() { write!(w, " rdrand")? };
    }

    if let Some(info) = cpuid.get_extended_function_info() {
        if info.has_64bit_mode() { write!(w, " lm")? };
        if info.has_rdtscp() { write!(w, " rdtscp")? };
        if info.has_1gib_pages() { write!(w, " pdpe1gb")? };
        if info.has_execute_disable() { write!(w, " nx")? };
        if info.has_syscall_sysret() { write!(w, " syscall")? };
        if info.has_prefetchw() { write!(w, " prefetchw")? };
        if info.has_lzcnt() { write!(w, " lzcnt")? };
        if info.has_lahf_sahf() { write!(w, " lahf_lm")? };
        if info.has_invariant_tsc() { write!(w, " constant_tsc")? };
    }

    if let Some(info) = cpuid.get_extended_feature_info() {
        if info.has_fsgsbase() { write!(w, " fsgsbase")? };
        if info.has_tsc_adjust_msr() { write!(w, " tsc_adjust")? };
        if info.has_bmi1() { write!(w, " bmi1")? };
        if info.has_hle() { write!(w, " hle")? };
        if info.has_avx2() { write!(w, " avx2")? };
        if info.has_smep() { write!(w, " smep")? };
        if info.has_bmi2() { write!(w, " bmi2")? };
        if info.has_rep_movsb_stosb() { write!(w, " erms")? };
        if info.has_invpcid() { write!(w, " invpcid")? };
        if info.has_rtm() { write!(w, " rtm")? };
        if info.has_qm() { write!(w, " qm")? };
        if info.has_fpu_cs_ds_deprecated() { write!(w, " fpu_seg")? };
        if info.has_mpx() { write!(w, " mpx")? };
    }

    write!(w, "\n")?;

    Ok(())
}
<commit_msg>Get frequency info<commit_after>extern crate raw_cpuid;

use core::fmt::{Result, Write};

use self::raw_cpuid::CpuId;

pub fn cpu_info<W: Write>(w: &mut W) -> Result {
    let cpuid = CpuId::new();

    if let Some(info) = cpuid.get_vendor_info() {
        write!(w, "Vendor: {}\n", info.as_string())?;
    }

    if let Some(info) = cpuid.get_extended_function_info() {
        if let Some(brand) = info.processor_brand_string() {
            write!(w, "Model: {}\n", brand)?;
        }
    }

    if let Some(info) = cpuid.get_processor_frequency_info() {
        write!(w, "CPU Base MHz: {}\n", info.processor_base_frequency())?;
        write!(w, "CPU Max MHz: {}\n", info.processor_max_frequency())?;
        write!(w, "Bus MHz: {}\n", info.bus_frequency())?;
    }

    write!(w, "Features:")?;

    if let Some(info) = cpuid.get_feature_info() {
        if info.has_fpu() { write!(w, " fpu")? };
        if info.has_vme() { write!(w, " vme")? };
        if info.has_de() { write!(w, " de")? };
        if info.has_pse() { write!(w, " pse")? };
        if info.has_tsc() { write!(w, " tsc")? };
        if info.has_msr() { write!(w, " msr")? };
        if info.has_pae() { write!(w, " pae")? };
        if info.has_mce() { write!(w, " mce")? };

        if info.has_cmpxchg8b() { write!(w, " cx8")? };
        if info.has_apic() { write!(w, " apic")? };
        if info.has_sysenter_sysexit() { write!(w, " sep")? };
        if info.has_mtrr() { write!(w, " mtrr")? };
        if info.has_pge() { write!(w, " pge")? };
        if info.has_mca() { write!(w, " mca")? };
        if info.has_cmov() { write!(w, " cmov")? };
        if info.has_pat() { write!(w, " pat")? };

        if info.has_pse36() { write!(w, " pse36")? };
        if info.has_psn() { write!(w, " psn")? };
        if info.has_clflush() { write!(w, " clflush")? };
        if info.has_ds() { write!(w, " ds")? };
        if info.has_acpi() { write!(w, " acpi")? };
        if info.has_mmx() { write!(w, " mmx")? };
        if info.has_fxsave_fxstor() { write!(w, " fxsr")? };
        if info.has_sse() { write!(w, " sse")? };

        if info.has_sse2() { write!(w, " sse2")? };
        if info.has_ss() { write!(w, " ss")? };
        if info.has_htt() { write!(w, " ht")? };
        if info.has_tm() { write!(w, " tm")? };
        if info.has_pbe() { write!(w, " pbe")? };

        if info.has_sse3() { write!(w, " sse3")? };
        if info.has_pclmulqdq() { write!(w, " pclmulqdq")? };
        if info.has_ds_area() { write!(w, " dtes64")? };
        if info.has_monitor_mwait() { write!(w, " monitor")? };
        if info.has_cpl() { write!(w, " ds_cpl")? };
        if info.has_vmx() { write!(w, " vmx")? };
        if info.has_smx() { write!(w, " smx")? };
        if info.has_eist() { write!(w, " est")? };

        if info.has_tm2() { write!(w, " tm2")? };
        if info.has_ssse3() { write!(w, " ssse3")? };
        if info.has_cnxtid() { write!(w, " cnxtid")? };
        if info.has_fma() { write!(w, " fma")? };
        if info.has_cmpxchg16b() { write!(w, " cx16")? };
        if info.has_pdcm() { write!(w, " pdcm")? };
        if info.has_pcid() { write!(w, " pcid")? };
        if info.has_dca() { write!(w, " dca")? };

        if info.has_sse41() { write!(w, " sse4_1")? };
        if info.has_sse42() { write!(w, " sse4_2")? };
        if info.has_x2apic() { write!(w, " x2apic")? };
        if info.has_movbe() { write!(w, " movbe")? };
        if info.has_popcnt() { write!(w, " popcnt")? };
        if info.has_tsc_deadline() { write!(w, " tsc_deadline_timer")? };
        if info.has_aesni() { write!(w, " aes")? };
        if info.has_xsave() { write!(w, " xsave")? };

        if info.has_oxsave() { write!(w, " xsaveopt")? };
        if info.has_avx() { write!(w, " avx")? };
        if info.has_f16c() { write!(w, " f16c")? };
        if info.has_rdrand() { write!(w, " rdrand")? };
    }

    if let Some(info) = cpuid.get_extended_function_info() {
        if info.has_64bit_mode() { write!(w, " lm")? };
        if info.has_rdtscp() { write!(w, " rdtscp")? };
        if info.has_1gib_pages() { write!(w, " pdpe1gb")? };
        if info.has_execute_disable() { write!(w, " nx")? };
        if info.has_syscall_sysret() { write!(w, " syscall")? };
        if info.has_prefetchw() { write!(w, " prefetchw")? };
        if info.has_lzcnt() { write!(w, " lzcnt")? };
        if info.has_lahf_sahf() { write!(w, " lahf_lm")? };
        if info.has_invariant_tsc() { write!(w, " constant_tsc")? };
    }

    if let Some(info) = cpuid.get_extended_feature_info() {
        if info.has_fsgsbase() { write!(w, " fsgsbase")? };
        if info.has_tsc_adjust_msr() { write!(w, " tsc_adjust")? };
        if info.has_bmi1() { write!(w, " bmi1")? };
        if info.has_hle() { write!(w, " hle")? };
        if info.has_avx2() { write!(w, " avx2")? };
        if info.has_smep() { write!(w, " smep")? };
        if info.has_bmi2() { write!(w, " bmi2")? };
        if info.has_rep_movsb_stosb() { write!(w, " erms")? };
        if info.has_invpcid() { write!(w, " invpcid")? };
        if info.has_rtm() { write!(w, " rtm")? };
        if info.has_qm() { write!(w, " qm")? };
        if info.has_fpu_cs_ds_deprecated() { write!(w, " fpu_seg")? };
        if info.has_mpx() { write!(w, " mpx")? };
    }

    write!(w, "\n")?;

    Ok(())
}
<|endoftext|>


<commit_before>use super::internal_prelude::*;
use imageflow_riapi::sizing::{Layout, AspectRatio, LayoutError};
use imageflow_types::ConstraintMode;

pub static CONSTRAIN: ConstrainDef = ConstrainDef{};
pub static COMMAND_STRING: CommandStringDef = CommandStringDef{};

pub static EXPANDING_COMMAND_STRING: CommandStringPartiallyExpandedDef = CommandStringPartiallyExpandedDef{};

fn get_expand(ctx: &mut OpCtxMut, ix: NodeIndex) -> Result<::imageflow_riapi::ir4::Ir4Expand>{
    let input = ctx.first_parent_frame_info_some(ix).ok_or_else(|| nerror!(crate::ErrorKind::InvalidNodeConnections, "CommandString node requires that its parent nodes be perfectly estimable"))?;
    let params = &ctx.weight(ix).params;
    if let NodeParams::Json(s::Node::CommandString{ref kind, ref value, ref decode, ref encode}) =
    *params {
        match *kind {
            s::CommandStringKind::ImageResizer4 => {
                Ok(::imageflow_riapi::ir4::Ir4Expand {
                    i: ::imageflow_riapi::ir4::Ir4Command::QueryString(value.to_owned()),
                    encode_id: *encode,
                    source: ::imageflow_riapi::ir4::Ir4SourceFrameInfo {
                        w: input.w,
                        h: input.h,
                        fmt: input.fmt,
                        original_mime: None
                    }
                })
            }
        }
    }else{
        Err(nerror!(crate::ErrorKind::NodeParamsMismatch, "Need CommandString, got {:?}", params))
    }
}

#[derive(Debug,Clone)]
pub struct CommandStringPartiallyExpandedDef;

impl NodeDef for CommandStringPartiallyExpandedDef{
    fn fqn(&self) -> &'static str{
        "imazen.expanding_command_string"
    }
    fn edges_required(&self, p: &NodeParams) -> Result<(EdgesIn, EdgesOut)>{
        Ok((EdgesIn::OneInput, EdgesOut::Any))
    }
    fn validate_params(&self, p: &NodeParams) -> Result<()>{
        Ok(()) //TODO: need way to provide warnings
    }
    fn estimate(&self, ctx: &mut OpCtxMut, ix: NodeIndex) -> Result<FrameEstimate>{
        let old_estimate = ctx.weight(ix).frame_est;

        if old_estimate == FrameEstimate::InvalidateGraph{
            Ok(FrameEstimate::Impossible)
        } else {
            let e = get_expand(ctx, ix).map_err(|e| e.at(here!()))?;

            if let Some(commands) = e.get_decode_commands().map_err(|e|FlowError::from_layout(e).at(here!()))? {
                for command in commands {
                    //Send command to codec
                    for (io_id, decoder_ix) in ctx.get_decoder_io_ids_and_indexes(ix) {
                        ctx.job.tell_decoder(io_id, command.clone()).map_err(|e| e.at(here!()))?;
                    }
                }
            }

            Ok(FrameEstimate::InvalidateGraph)
        }
    }
    fn can_expand(&self) -> bool{
        true
    }

    fn expand(&self, ctx: &mut OpCtxMut, ix: NodeIndex) -> Result<()> {

        let e = get_expand(ctx, ix).map_err(|e| e.at(here!()))?;

        match e.expand_steps().map_err(|e| FlowError::from_layout(e).at(here!())) {
            Ok(r) => {
                //TODO: Find a way to expose warnings
                ctx.replace_node(ix, r.steps.unwrap().into_iter().map( Node::from).collect::<>());
                Ok(())
            }
            Err(e) => {
                //TODO: reparse to get warnings
                Err(e)
            }
        }
    }
}

#[derive(Debug,Clone)]
pub struct ConstrainDef;
impl NodeDef for ConstrainDef{
    fn as_one_input_expand(&self) -> Option<&dyn NodeDefOneInputExpand>{
        Some(self)
    }
}
impl NodeDefOneInputExpand for ConstrainDef{
    fn fqn(&self) -> &'static str{
        "imazen.constrain"
    }
    fn estimate(&self, params: &NodeParams, input: FrameEstimate) -> Result<FrameEstimate>{
        if let NodeParams::Json(s::Node::Constrain(ref constraint)) = *params {
            input.map_frame(|input| {
                let constraint_results = imageflow_riapi::ir4::process_constraint(input.w, input.h, constraint).unwrap(); //TODO: fix unwrap
                Ok(FrameInfo {
                    w: constraint_results.final_canvas.width() as i32,
                    h: constraint_results.final_canvas.height() as i32,
                    fmt: ffi::PixelFormat::from(input.fmt),
                })
            })
        }else{
            Err(nerror!(crate::ErrorKind::NodeParamsMismatch, "Need Constrain, got {:?}", params))
        }
    }

    fn expand(&self, ctx: &mut OpCtxMut, ix: NodeIndex, params: NodeParams, input: FrameInfo) -> Result<()> {
        if let NodeParams::Json(s::Node::Constrain(constraint)) = params {
            let constraint_results = imageflow_riapi::ir4::process_constraint(input.w, input.h, &constraint).unwrap(); //TODO: fix unwrap

            let mut b  = Vec::new();
            if let Some(c) = constraint_results.crop{
                b.push(Node::from(s::Node::Crop { x1: c[0], y1: c[1], x2: c[2], y2: c[3] }));
            }
            b.push(Node::from(
                imageflow_types::Node::Resample2D {
                    w: constraint_results.scale_to.width() as u32,
                    h: constraint_results.scale_to.height() as u32,
                    hints: constraint.hints,
                })
            );

            if let Some(pad) = constraint_results.pad{
                b.push(Node::from(
                imageflow_types::Node::ExpandCanvas {
                    left: pad[0],
                    top: pad[1],
                    right: pad[2],
                    bottom: pad[3],
                    color: constraint.canvas_color.unwrap_or(imageflow_types::Color::Transparent)
                }));
            }

            ctx.replace_node(ix, b);

            Ok(())
        } else {
            Err(nerror!(crate::ErrorKind::NodeParamsMismatch, "Need Constrain, got {:?}", params))
        }
    }
}

#[derive(Debug,Clone)]
pub struct CommandStringDef;
impl NodeDef for CommandStringDef{

    fn fqn(&self) -> &'static str{
        "imazen.command_string"
    }
    fn estimate(&self, ctx: &mut OpCtxMut, ix: NodeIndex) -> Result<FrameEstimate>{
        Ok(FrameEstimate::Impossible)
    }
    fn edges_required(&self, p: &NodeParams) -> Result<(EdgesIn, EdgesOut)> {
        Ok((EdgesIn::OneInput, EdgesOut::Any))
    }
    fn validate_params(&self, p: &NodeParams) -> Result<()>{
        Ok(())
    }
    fn can_expand(&self) -> bool{
        true
    }
    fn expand(&self, ctx: &mut OpCtxMut, ix: NodeIndex) -> Result<()> {
        let has_parent = ctx.first_parent_of_kind(ix, EdgeKind::Input).is_some();
        let params = ctx.weight(ix).params.clone();
        let params_copy = ctx.weight(ix).params.clone();

        if let NodeParams::Json(s::Node::CommandString { kind, value, decode, encode }) = params_copy {
            if let Some(d_id) = decode {
                if has_parent {
                    return Err(nerror!(crate::ErrorKind::InvalidNodeParams, "CommandString must either have decode: null or have no parent nodes. Specifying a value for decode creates a new decoder node."));
                }
                let decode_node = ::imageflow_riapi::ir4::Ir4Translate {
                    i: ::imageflow_riapi::ir4::Ir4Command::QueryString(value.to_owned()),
                    decode_id: Some(d_id),
                    encode_id: None,
                }.get_decode_node().unwrap();
                ctx.replace_node(ix, vec![
                    Node::from(decode_node),
                    Node::n(&EXPANDING_COMMAND_STRING, params)
                ]);
            } else {
                if !has_parent {
                    return Err(nerror!(crate::ErrorKind::InvalidNodeParams,"CommandString must have a parent node unless 'decode' has a numeric value. Otherwise it has no image source. "));
                }
                ctx.replace_node(ix, vec![
                    Node::n(&EXPANDING_COMMAND_STRING, params)
                ]);
            }
            Ok(())
        } else {
            Err(nerror!(crate::ErrorKind::NodeParamsMismatch, "Need Constrain, got {:?}", params))
        }
    }
}

<commit_msg>Provide original mime type to RIAPI module so it can preserve the file type by default<commit_after>use super::internal_prelude::*;
use imageflow_riapi::sizing::{Layout, AspectRatio, LayoutError};
use imageflow_types::ConstraintMode;

pub static CONSTRAIN: ConstrainDef = ConstrainDef{};
pub static COMMAND_STRING: CommandStringDef = CommandStringDef{};

pub static EXPANDING_COMMAND_STRING: CommandStringPartiallyExpandedDef = CommandStringPartiallyExpandedDef{};

fn get_decoder_mime(ctx: &mut OpCtxMut, ix: NodeIndex) -> Result<Option<String>>{
    let decoders = ctx.get_decoder_io_ids_and_indexes(ix);
    if let Some((io_id, _)) = decoders.first(){
        Ok(Some(ctx.c.get_codec(*io_id)?.get_decoder()?.get_image_info(ctx.c)?.preferred_mime_type))
    }
    else{
        Ok(None)
    }

}

fn get_expand(ctx: &mut OpCtxMut, ix: NodeIndex) -> Result<::imageflow_riapi::ir4::Ir4Expand>{
    let input = ctx.first_parent_frame_info_some(ix).ok_or_else(|| nerror!(crate::ErrorKind::InvalidNodeConnections, "CommandString node requires that its parent nodes be perfectly estimable"))?;
    let params = &ctx.weight(ix).params;
    if let NodeParams::Json(s::Node::CommandString{ref kind, ref value, ref decode, ref encode}) =
    *params {
        match *kind {
            s::CommandStringKind::ImageResizer4 => {
                Ok(::imageflow_riapi::ir4::Ir4Expand {
                    i: ::imageflow_riapi::ir4::Ir4Command::QueryString(value.to_owned()),
                    encode_id: *encode,
                    source: ::imageflow_riapi::ir4::Ir4SourceFrameInfo {
                        w: input.w,
                        h: input.h,
                        fmt: input.fmt,
                        original_mime: get_decoder_mime(ctx,ix)?
                    }
                })
            }
        }
    }else{
        Err(nerror!(crate::ErrorKind::NodeParamsMismatch, "Need CommandString, got {:?}", params))
    }
}

#[derive(Debug,Clone)]
pub struct CommandStringPartiallyExpandedDef;

impl NodeDef for CommandStringPartiallyExpandedDef{
    fn fqn(&self) -> &'static str{
        "imazen.expanding_command_string"
    }
    fn edges_required(&self, p: &NodeParams) -> Result<(EdgesIn, EdgesOut)>{
        Ok((EdgesIn::OneInput, EdgesOut::Any))
    }
    fn validate_params(&self, p: &NodeParams) -> Result<()>{
        Ok(()) //TODO: need way to provide warnings
    }
    fn estimate(&self, ctx: &mut OpCtxMut, ix: NodeIndex) -> Result<FrameEstimate>{
        let old_estimate = ctx.weight(ix).frame_est;

        if old_estimate == FrameEstimate::InvalidateGraph{
            Ok(FrameEstimate::Impossible)
        } else {
            let e = get_expand(ctx, ix).map_err(|e| e.at(here!()))?;

            if let Some(commands) = e.get_decode_commands().map_err(|e|FlowError::from_layout(e).at(here!()))? {
                for command in commands {
                    //Send command to codec
                    for (io_id, decoder_ix) in ctx.get_decoder_io_ids_and_indexes(ix) {
                        ctx.job.tell_decoder(io_id, command.clone()).map_err(|e| e.at(here!()))?;
                    }
                }
            }

            Ok(FrameEstimate::InvalidateGraph)
        }
    }
    fn can_expand(&self) -> bool{
        true
    }

    fn expand(&self, ctx: &mut OpCtxMut, ix: NodeIndex) -> Result<()> {

        let e = get_expand(ctx, ix).map_err(|e| e.at(here!()))?;

        match e.expand_steps().map_err(|e| FlowError::from_layout(e).at(here!())) {
            Ok(r) => {
                //TODO: Find a way to expose warnings
                ctx.replace_node(ix, r.steps.unwrap().into_iter().map( Node::from).collect::<>());
                Ok(())
            }
            Err(e) => {
                //TODO: reparse to get warnings
                Err(e)
            }
        }
    }
}

#[derive(Debug,Clone)]
pub struct ConstrainDef;
impl NodeDef for ConstrainDef{
    fn as_one_input_expand(&self) -> Option<&dyn NodeDefOneInputExpand>{
        Some(self)
    }
}
impl NodeDefOneInputExpand for ConstrainDef{
    fn fqn(&self) -> &'static str{
        "imazen.constrain"
    }
    fn estimate(&self, params: &NodeParams, input: FrameEstimate) -> Result<FrameEstimate>{
        if let NodeParams::Json(s::Node::Constrain(ref constraint)) = *params {
            input.map_frame(|input| {
                let constraint_results = imageflow_riapi::ir4::process_constraint(input.w, input.h, constraint).unwrap(); //TODO: fix unwrap
                Ok(FrameInfo {
                    w: constraint_results.final_canvas.width() as i32,
                    h: constraint_results.final_canvas.height() as i32,
                    fmt: ffi::PixelFormat::from(input.fmt),
                })
            })
        }else{
            Err(nerror!(crate::ErrorKind::NodeParamsMismatch, "Need Constrain, got {:?}", params))
        }
    }

    fn expand(&self, ctx: &mut OpCtxMut, ix: NodeIndex, params: NodeParams, input: FrameInfo) -> Result<()> {
        if let NodeParams::Json(s::Node::Constrain(constraint)) = params {
            let constraint_results = imageflow_riapi::ir4::process_constraint(input.w, input.h, &constraint).unwrap(); //TODO: fix unwrap

            let mut b  = Vec::new();
            if let Some(c) = constraint_results.crop{
                b.push(Node::from(s::Node::Crop { x1: c[0], y1: c[1], x2: c[2], y2: c[3] }));
            }
            b.push(Node::from(
                imageflow_types::Node::Resample2D {
                    w: constraint_results.scale_to.width() as u32,
                    h: constraint_results.scale_to.height() as u32,
                    hints: constraint.hints,
                })
            );

            if let Some(pad) = constraint_results.pad{
                b.push(Node::from(
                imageflow_types::Node::ExpandCanvas {
                    left: pad[0],
                    top: pad[1],
                    right: pad[2],
                    bottom: pad[3],
                    color: constraint.canvas_color.unwrap_or(imageflow_types::Color::Transparent)
                }));
            }

            ctx.replace_node(ix, b);

            Ok(())
        } else {
            Err(nerror!(crate::ErrorKind::NodeParamsMismatch, "Need Constrain, got {:?}", params))
        }
    }
}

#[derive(Debug,Clone)]
pub struct CommandStringDef;
impl NodeDef for CommandStringDef{

    fn fqn(&self) -> &'static str{
        "imazen.command_string"
    }
    fn estimate(&self, ctx: &mut OpCtxMut, ix: NodeIndex) -> Result<FrameEstimate>{
        Ok(FrameEstimate::Impossible)
    }
    fn edges_required(&self, p: &NodeParams) -> Result<(EdgesIn, EdgesOut)> {
        Ok((EdgesIn::OneInput, EdgesOut::Any))
    }
    fn validate_params(&self, p: &NodeParams) -> Result<()>{
        Ok(())
    }
    fn can_expand(&self) -> bool{
        true
    }
    fn expand(&self, ctx: &mut OpCtxMut, ix: NodeIndex) -> Result<()> {
        let has_parent = ctx.first_parent_of_kind(ix, EdgeKind::Input).is_some();
        let params = ctx.weight(ix).params.clone();
        let params_copy = ctx.weight(ix).params.clone();

        if let NodeParams::Json(s::Node::CommandString { kind, value, decode, encode }) = params_copy {
            if let Some(d_id) = decode {
                if has_parent {
                    return Err(nerror!(crate::ErrorKind::InvalidNodeParams, "CommandString must either have decode: null or have no parent nodes. Specifying a value for decode creates a new decoder node."));
                }
                let decode_node = ::imageflow_riapi::ir4::Ir4Translate {
                    i: ::imageflow_riapi::ir4::Ir4Command::QueryString(value.to_owned()),
                    decode_id: Some(d_id),
                    encode_id: None,
                }.get_decode_node().unwrap();
                ctx.replace_node(ix, vec![
                    Node::from(decode_node),
                    Node::n(&EXPANDING_COMMAND_STRING, params)
                ]);
            } else {
                if !has_parent {
                    return Err(nerror!(crate::ErrorKind::InvalidNodeParams,"CommandString must have a parent node unless 'decode' has a numeric value. Otherwise it has no image source. "));
                }
                ctx.replace_node(ix, vec![
                    Node::n(&EXPANDING_COMMAND_STRING, params)
                ]);
            }
            Ok(())
        } else {
            Err(nerror!(crate::ErrorKind::NodeParamsMismatch, "Need Constrain, got {:?}", params))
        }
    }
}

<|endoftext|>