<commit_before>//! Complete commands within shells

/// Complete commands within bash
pub mod bash {
    use std::ffi::OsString;
    use std::io::Write;

    use unicode_xid::UnicodeXID;

    #[derive(clap::Subcommand)]
    #[clap(hide = true)]
    #[allow(missing_docs)]
    #[derive(Clone, Debug)]
    pub enum CompleteCommand {
        /// Register shell completions for this program
        Complete(CompleteArgs),
    }

    #[derive(clap::Args)]
    #[clap(group = clap::ArgGroup::new("complete").multiple(true).conflicts_with("register"))]
    #[allow(missing_docs)]
    #[derive(Clone, Debug)]
    pub struct CompleteArgs {
        /// Path to write completion-registration to
        #[clap(long, required = true, parse(from_os_str))]
        register: Option<std::path::PathBuf>,

        #[clap(
            long,
            required = true,
            value_name = "COMP_CWORD",
            hide_short_help = true,
            group = "complete"
        )]
        index: Option<usize>,

        #[clap(
            long,
            required = true,
            value_name = "COMP_CWORD",
            hide_short_help = true,
            group = "complete"
        )]
        ifs: Option<String>,

        #[clap(
            long = "type",
            required = true,
            arg_enum,
            hide_short_help = true,
            group = "complete"
        )]
        comp_type: Option<CompType>,

        #[clap(long, hide_short_help = true, group = "complete")]
        space: bool,

        #[clap(
            long,
            conflicts_with = "space",
            hide_short_help = true,
            group = "complete"
        )]
        no_space: bool,

        #[clap(raw = true, hide_short_help = true, group = "complete")]
        comp_words: Vec<OsString>,
    }

    impl CompleteCommand {
        /// Process the completion request
        pub fn complete(&self, cmd: &mut clap::Command) -> std::convert::Infallible {
            self.try_complete(cmd).unwrap_or_else(|e| e.exit());
            std::process::exit(0)
        }

        /// Process the completion request
        pub fn try_complete(&self, cmd: &mut clap::Command) -> clap::Result<()> {
            debug!("CompleteCommand::try_complete: {:?}", self);
            let CompleteCommand::Complete(args) = self;
            if let Some(out_path) = args.register.as_deref() {
                let mut buf = Vec::new();
                let name = cmd.get_name();
                let bin = cmd.get_bin_name().unwrap_or(cmd.get_name());
                register(name, [bin], bin, &Behavior::default(), &mut buf)?;
                if out_path == std::path::Path::new("-") {
                    std::io::stdout().write(&buf)?;
                } else {
                    if out_path.is_dir() {
                        let out_path = out_path.join(file_name(name));
                        std::fs::write(out_path, buf)?;
                    } else {
                        std::fs::write(out_path, buf)?;
                    }
                }
            } else {
                let index = args.index.unwrap_or_default();
                let comp_type = args.comp_type.unwrap_or_default();
                let space = match (args.space, args.no_space) {
                    (true, false) => Some(true),
                    (false, true) => Some(false),
                    (true, true) => {
                        unreachable!("`--space` and `--no-space` set, clap should prevent this")
                    }
                    (false, false) => None,
                }
                .unwrap();
                let current_dir = std::env::current_dir().ok();
                let completions = complete(
                    cmd,
                    args.comp_words.clone(),
                    index,
                    comp_type,
                    space,
                    current_dir.as_deref(),
                )?;

                let mut buf = Vec::new();
                for completion in &completions {
                    write!(&mut buf, "{}", completion.to_string_lossy())?;
                    write!(&mut buf, "{}", args.ifs.as_deref().unwrap_or("\n"))?;
                }
                std::io::stdout().write(&buf)?;
            }

            Ok(())
        }
    }

    /// The recommended file name for the registration code
    pub fn file_name(name: &str) -> String {
        format!("{}.bash", name)
    }

    /// Define the completion behavior
    pub enum Behavior {
        /// Bare bones behavior
        Minimal,
        /// Fallback to readline behavior when no matches are generated
        Readline,
        /// Customize bash's completion behavior
        Custom(String),
    }

    impl Default for Behavior {
        fn default() -> Self {
            Self::Readline
        }
    }

    /// Generate code to register the dynamic completion
    pub fn register(
        name: &str,
        executables: impl IntoIterator<Item = impl AsRef<str>>,
        completer: &str,
        behavior: &Behavior,
        buf: &mut dyn Write,
    ) -> Result<(), std::io::Error> {
        let escaped_name = name.replace("-", "_");
        debug_assert!(
            escaped_name.chars().all(|c| c.is_xid_continue()),
            "`name` must be an identifier, got `{}`",
            escaped_name
        );
        let mut upper_name = escaped_name.clone();
        upper_name.make_ascii_uppercase();

        let executables = executables
            .into_iter()
            .map(|s| shlex::quote(s.as_ref()).into_owned())
            .collect::<Vec<_>>()
            .join(" ");

        let options = match behavior {
            Behavior::Minimal => "-o nospace -o bashdefault",
            Behavior::Readline => "-o nospace -o default -o bashdefault",
            Behavior::Custom(c) => c.as_str(),
        };

        let completer = shlex::quote(completer);

        let script = r#"
_clap_complete_NAME() {
    local IFS=$'\013'
    local SUPPRESS_SPACE=0
    if compopt +o nospace 2> /dev/null; then
        SUPPRESS_SPACE=1
    fi
    if [[ ${SUPPRESS_SPACE} == 1 ]]; then
        SPACE_ARG="--no-space"
    else
        SPACE_ARG="--space"
    fi
    COMPREPLY=( $("COMPLETER" complete --index ${COMP_CWORD} --type ${COMP_TYPE} ${SPACE_ARG} --ifs="$IFS" -- "${COMP_WORDS[@]}") )
    if [[ $? != 0 ]]; then
        unset COMPREPLY
    elif [[ $SUPPRESS_SPACE == 1 ]] && [[ "${COMPREPLY-}" =~ [=/:]$ ]]; then
        compopt -o nospace
    fi
}
complete OPTIONS -F _clap_complete_NAME EXECUTABLES
"#
        .replace("NAME", &escaped_name)
        .replace("EXECUTABLES", &executables)
        .replace("OPTIONS", options)
        .replace("COMPLETER", &completer)
        .replace("UPPER", &upper_name);

        writeln!(buf, "{}", script)?;
        Ok(())
    }

    /// Type of completion attempted that caused a completion function to be called
    #[derive(Copy, Clone, Debug, PartialEq, Eq)]
    #[non_exhaustive]
    pub enum CompType {
        /// Normal completion
        Normal,
        /// List completions after successive tabs
        Successive,
        /// List alternatives on partial word completion
        Alternatives,
        /// List completions if the word is not unmodified
        Unmodified,
        /// Menu completion
        Menu,
    }

    impl clap::ArgEnum for CompType {
        fn value_variants<'a>() -> &'a [Self] {
            &[
                Self::Normal,
                Self::Successive,
                Self::Alternatives,
                Self::Unmodified,
                Self::Menu,
            ]
        }
        fn to_possible_value<'a>(&self) -> ::std::option::Option<clap::PossibleValue<'a>> {
            match self {
                Self::Normal => {
                    let value = "9";
                    debug_assert_eq!(b'\t'.to_string(), value);
                    Some(
                        clap::PossibleValue::new(value)
                            .alias("normal")
                            .help("Normal completion"),
                    )
                }
                Self::Successive => {
                    let value = "63";
                    debug_assert_eq!(b'?'.to_string(), value);
                    Some(
                        clap::PossibleValue::new(value)
                            .alias("successive")
                            .help("List completions after successive tabs"),
                    )
                }
                Self::Alternatives => {
                    let value = "33";
                    debug_assert_eq!(b'!'.to_string(), value);
                    Some(
                        clap::PossibleValue::new(value)
                            .alias("alternatives")
                            .help("List alternatives on partial word completion"),
                    )
                }
                Self::Unmodified => {
                    let value = "64";
                    debug_assert_eq!(b'@'.to_string(), value);
                    Some(
                        clap::PossibleValue::new(value)
                            .alias("unmodified")
                            .help("List completions if the word is not unmodified"),
                    )
                }
                Self::Menu => {
                    let value = "37";
                    debug_assert_eq!(b'%'.to_string(), value);
                    Some(
                        clap::PossibleValue::new(value)
                            .alias("menu")
                            .help("Menu completion"),
                    )
                }
            }
        }
    }

    impl Default for CompType {
        fn default() -> Self {
            Self::Normal
        }
    }

    /// Complete the command specified
    pub fn complete(
        cmd: &mut clap::Command,
        args: Vec<std::ffi::OsString>,
        arg_index: usize,
        _comp_type: CompType,
        _trailing_space: bool,
        current_dir: Option<&std::path::Path>,
    ) -> Result<Vec<std::ffi::OsString>, std::io::Error> {
        cmd.build();

        let raw_args = clap_lex::RawArgs::new(args.into_iter());
        let mut cursor = raw_args.cursor();
        let mut target_cursor = raw_args.cursor();
        raw_args.seek(
            &mut target_cursor,
            clap_lex::SeekFrom::Start(arg_index as u64),
        );

        // TODO: Multicall support
        if !cmd.is_no_binary_name_set() {
            raw_args.next_os(&mut cursor);
        }

        let mut current_cmd = &*cmd;
        while let Some(arg) = raw_args.next(&mut cursor) {
            if let Ok(value) = arg.to_value() {
                if let Some(next_cmd) = current_cmd.find_subcommand(value) {
                    current_cmd = next_cmd;
                }
            }
            if cursor == target_cursor {
                break;
            }
        }

        let completions = complete_new(current_cmd, current_dir)?;

        Ok(completions)
    }

    fn complete_new(
        cmd: &clap::Command,
        current_dir: Option<&std::path::Path>,
    ) -> Result<Vec<std::ffi::OsString>, std::io::Error> {
        let mut completions = Vec::new();

        completions.extend(
            crate::generator::utils::longs_and_visible_aliases(cmd)
                .into_iter()
                .map(|f| format!("--{}", f).into()),
        );
        completions.extend(
            crate::generator::utils::shorts_and_visible_aliases(cmd)
                .into_iter()
                .map(|f| format!("-{}", f).into()),
        );

        let mut positionals = Vec::new();
        positionals.extend(
            cmd.get_positionals()
                .flat_map(|p| p.get_possible_values())
                .flatten()
                .map(|p| p.get_name().into()),
        );
        let hints = cmd
            .get_positionals()
            .map(|p| p.get_value_hint())
            .map(|h| {
                if h == clap::ValueHint::Unknown {
                    clap::ValueHint::AnyPath
                } else {
                    h
                }
            })
            .collect::<std::collections::HashSet<_>>();
        for hint in hints {
            match hint {
                clap::ValueHint::Unknown => unreachable!("Filtered out"),
                clap::ValueHint::Other => {
                    // Should not complete
                }
                clap::ValueHint::AnyPath => {
                    positionals.extend(complete_path(current_dir, |_| true));
                }
                clap::ValueHint::FilePath => {
                    positionals.extend(complete_path(current_dir, |p| p.is_file()));
                }
                clap::ValueHint::DirPath => {
                    positionals.extend(complete_path(current_dir, |p| p.is_dir()));
                }
                clap::ValueHint::ExecutablePath => {
                    use is_executable::IsExecutable;
                    positionals.extend(complete_path(current_dir, |p| p.is_executable()));
                }
                clap::ValueHint::CommandName
                | clap::ValueHint::CommandString
                | clap::ValueHint::CommandWithArguments
                | clap::ValueHint::Username
                | clap::ValueHint::Hostname
                | clap::ValueHint::Url
                | clap::ValueHint::EmailAddress => {
                    // No completion implementation
                }
                _ => {
                    // Safe-ish fallback
                    positionals.extend(complete_path(current_dir, |_| true));
                }
            }
        }
        positionals.sort();
        positionals.dedup();
        completions.extend(positionals);

        completions.extend(all_subcommands(cmd).into_iter().map(|s| s));

        Ok(completions)
    }

    fn complete_path(
        current_dir: Option<&std::path::Path>,
        is_wanted: impl Fn(&std::path::Path) -> bool,
    ) -> Vec<OsString> {
        let mut completions = Vec::new();

        let current_dir = match current_dir {
            Some(current_dir) => current_dir,
            None => {
                return Vec::new();
            }
        };
        for entry in std::fs::read_dir(current_dir)
            .ok()
            .into_iter()
            .flatten()
            .filter_map(Result::ok)
        {
            let mut path = entry.path();
            if entry.metadata().map(|m| m.is_dir()).unwrap_or(false) {
                path.push(""); // Ensure trailing `/`
                let suggestion = path.strip_prefix(current_dir).unwrap();
                completions.push(suggestion.as_os_str().to_owned());
            } else {
                if is_wanted(&path) {
                    let suggestion = path.strip_prefix(current_dir).unwrap();
                    completions.push(suggestion.as_os_str().to_owned());
                }
            }
        }

        completions
    }

    fn all_subcommands(cmd: &clap::Command) -> Vec<OsString> {
        debug!("all_subcommands");

        let mut scs = crate::generator::utils::all_subcommands(cmd)
            .iter()
            .map(|x| OsString::from(&x.0))
            .collect::<Vec<_>>();
        scs.sort();
        scs.dedup();
        scs
    }
}
<commit_msg>fix(complete): Detect which argument is being completed<commit_after>//! Complete commands within shells

/// Complete commands within bash
pub mod bash {
    use std::ffi::OsString;
    use std::io::Write;

    use unicode_xid::UnicodeXID;

    #[derive(clap::Subcommand)]
    #[clap(hide = true)]
    #[allow(missing_docs)]
    #[derive(Clone, Debug)]
    pub enum CompleteCommand {
        /// Register shell completions for this program
        Complete(CompleteArgs),
    }

    #[derive(clap::Args)]
    #[clap(group = clap::ArgGroup::new("complete").multiple(true).conflicts_with("register"))]
    #[allow(missing_docs)]
    #[derive(Clone, Debug)]
    pub struct CompleteArgs {
        /// Path to write completion-registration to
        #[clap(long, required = true, parse(from_os_str))]
        register: Option<std::path::PathBuf>,

        #[clap(
            long,
            required = true,
            value_name = "COMP_CWORD",
            hide_short_help = true,
            group = "complete"
        )]
        index: Option<usize>,

        #[clap(
            long,
            required = true,
            value_name = "COMP_CWORD",
            hide_short_help = true,
            group = "complete"
        )]
        ifs: Option<String>,

        #[clap(
            long = "type",
            required = true,
            arg_enum,
            hide_short_help = true,
            group = "complete"
        )]
        comp_type: Option<CompType>,

        #[clap(long, hide_short_help = true, group = "complete")]
        space: bool,

        #[clap(
            long,
            conflicts_with = "space",
            hide_short_help = true,
            group = "complete"
        )]
        no_space: bool,

        #[clap(raw = true, hide_short_help = true, group = "complete")]
        comp_words: Vec<OsString>,
    }

    impl CompleteCommand {
        /// Process the completion request
        pub fn complete(&self, cmd: &mut clap::Command) -> std::convert::Infallible {
            self.try_complete(cmd).unwrap_or_else(|e| e.exit());
            std::process::exit(0)
        }

        /// Process the completion request
        pub fn try_complete(&self, cmd: &mut clap::Command) -> clap::Result<()> {
            debug!("CompleteCommand::try_complete: {:?}", self);
            let CompleteCommand::Complete(args) = self;
            if let Some(out_path) = args.register.as_deref() {
                let mut buf = Vec::new();
                let name = cmd.get_name();
                let bin = cmd.get_bin_name().unwrap_or(cmd.get_name());
                register(name, [bin], bin, &Behavior::default(), &mut buf)?;
                if out_path == std::path::Path::new("-") {
                    std::io::stdout().write(&buf)?;
                } else {
                    if out_path.is_dir() {
                        let out_path = out_path.join(file_name(name));
                        std::fs::write(out_path, buf)?;
                    } else {
                        std::fs::write(out_path, buf)?;
                    }
                }
            } else {
                let index = args.index.unwrap_or_default();
                let comp_type = args.comp_type.unwrap_or_default();
                let space = match (args.space, args.no_space) {
                    (true, false) => Some(true),
                    (false, true) => Some(false),
                    (true, true) => {
                        unreachable!("`--space` and `--no-space` set, clap should prevent this")
                    }
                    (false, false) => None,
                }
                .unwrap();
                let current_dir = std::env::current_dir().ok();
                let completions = complete(
                    cmd,
                    args.comp_words.clone(),
                    index,
                    comp_type,
                    space,
                    current_dir.as_deref(),
                )?;

                let mut buf = Vec::new();
                for completion in &completions {
                    write!(&mut buf, "{}", completion.to_string_lossy())?;
                    write!(&mut buf, "{}", args.ifs.as_deref().unwrap_or("\n"))?;
                }
                std::io::stdout().write(&buf)?;
            }

            Ok(())
        }
    }

    /// The recommended file name for the registration code
    pub fn file_name(name: &str) -> String {
        format!("{}.bash", name)
    }

    /// Define the completion behavior
    pub enum Behavior {
        /// Bare bones behavior
        Minimal,
        /// Fallback to readline behavior when no matches are generated
        Readline,
        /// Customize bash's completion behavior
        Custom(String),
    }

    impl Default for Behavior {
        fn default() -> Self {
            Self::Readline
        }
    }

    /// Generate code to register the dynamic completion
    pub fn register(
        name: &str,
        executables: impl IntoIterator<Item = impl AsRef<str>>,
        completer: &str,
        behavior: &Behavior,
        buf: &mut dyn Write,
    ) -> Result<(), std::io::Error> {
        let escaped_name = name.replace("-", "_");
        debug_assert!(
            escaped_name.chars().all(|c| c.is_xid_continue()),
            "`name` must be an identifier, got `{}`",
            escaped_name
        );
        let mut upper_name = escaped_name.clone();
        upper_name.make_ascii_uppercase();

        let executables = executables
            .into_iter()
            .map(|s| shlex::quote(s.as_ref()).into_owned())
            .collect::<Vec<_>>()
            .join(" ");

        let options = match behavior {
            Behavior::Minimal => "-o nospace -o bashdefault",
            Behavior::Readline => "-o nospace -o default -o bashdefault",
            Behavior::Custom(c) => c.as_str(),
        };

        let completer = shlex::quote(completer);

        let script = r#"
_clap_complete_NAME() {
    local IFS=$'\013'
    local SUPPRESS_SPACE=0
    if compopt +o nospace 2> /dev/null; then
        SUPPRESS_SPACE=1
    fi
    if [[ ${SUPPRESS_SPACE} == 1 ]]; then
        SPACE_ARG="--no-space"
    else
        SPACE_ARG="--space"
    fi
    COMPREPLY=( $("COMPLETER" complete --index ${COMP_CWORD} --type ${COMP_TYPE} ${SPACE_ARG} --ifs="$IFS" -- "${COMP_WORDS[@]}") )
    if [[ $? != 0 ]]; then
        unset COMPREPLY
    elif [[ $SUPPRESS_SPACE == 1 ]] && [[ "${COMPREPLY-}" =~ [=/:]$ ]]; then
        compopt -o nospace
    fi
}
complete OPTIONS -F _clap_complete_NAME EXECUTABLES
"#
        .replace("NAME", &escaped_name)
        .replace("EXECUTABLES", &executables)
        .replace("OPTIONS", options)
        .replace("COMPLETER", &completer)
        .replace("UPPER", &upper_name);

        writeln!(buf, "{}", script)?;
        Ok(())
    }

    /// Type of completion attempted that caused a completion function to be called
    #[derive(Copy, Clone, Debug, PartialEq, Eq)]
    #[non_exhaustive]
    pub enum CompType {
        /// Normal completion
        Normal,
        /// List completions after successive tabs
        Successive,
        /// List alternatives on partial word completion
        Alternatives,
        /// List completions if the word is not unmodified
        Unmodified,
        /// Menu completion
        Menu,
    }

    impl clap::ArgEnum for CompType {
        fn value_variants<'a>() -> &'a [Self] {
            &[
                Self::Normal,
                Self::Successive,
                Self::Alternatives,
                Self::Unmodified,
                Self::Menu,
            ]
        }
        fn to_possible_value<'a>(&self) -> ::std::option::Option<clap::PossibleValue<'a>> {
            match self {
                Self::Normal => {
                    let value = "9";
                    debug_assert_eq!(b'\t'.to_string(), value);
                    Some(
                        clap::PossibleValue::new(value)
                            .alias("normal")
                            .help("Normal completion"),
                    )
                }
                Self::Successive => {
                    let value = "63";
                    debug_assert_eq!(b'?'.to_string(), value);
                    Some(
                        clap::PossibleValue::new(value)
                            .alias("successive")
                            .help("List completions after successive tabs"),
                    )
                }
                Self::Alternatives => {
                    let value = "33";
                    debug_assert_eq!(b'!'.to_string(), value);
                    Some(
                        clap::PossibleValue::new(value)
                            .alias("alternatives")
                            .help("List alternatives on partial word completion"),
                    )
                }
                Self::Unmodified => {
                    let value = "64";
                    debug_assert_eq!(b'@'.to_string(), value);
                    Some(
                        clap::PossibleValue::new(value)
                            .alias("unmodified")
                            .help("List completions if the word is not unmodified"),
                    )
                }
                Self::Menu => {
                    let value = "37";
                    debug_assert_eq!(b'%'.to_string(), value);
                    Some(
                        clap::PossibleValue::new(value)
                            .alias("menu")
                            .help("Menu completion"),
                    )
                }
            }
        }
    }

    impl Default for CompType {
        fn default() -> Self {
            Self::Normal
        }
    }

    /// Complete the command specified
    pub fn complete(
        cmd: &mut clap::Command,
        args: Vec<std::ffi::OsString>,
        arg_index: usize,
        _comp_type: CompType,
        _trailing_space: bool,
        current_dir: Option<&std::path::Path>,
    ) -> Result<Vec<std::ffi::OsString>, std::io::Error> {
        cmd.build();

        let raw_args = clap_lex::RawArgs::new(args.into_iter());
        let mut cursor = raw_args.cursor();
        let mut target_cursor = raw_args.cursor();
        raw_args.seek(
            &mut target_cursor,
            clap_lex::SeekFrom::Start(arg_index as u64),
        );
        // As we loop, `cursor` will always be pointing to the next item
        raw_args.next_os(&mut target_cursor);

        // TODO: Multicall support
        if !cmd.is_no_binary_name_set() {
            raw_args.next_os(&mut cursor);
        }

        let mut current_cmd = &*cmd;
        while let Some(arg) = raw_args.next(&mut cursor) {
            if cursor == target_cursor {
                return complete_new(current_cmd, current_dir);
            }
            if let Ok(value) = arg.to_value() {
                if let Some(next_cmd) = current_cmd.find_subcommand(value) {
                    current_cmd = next_cmd;
                }
            }
        }

        Err(std::io::Error::new(
            std::io::ErrorKind::Other,
            "No completion generated",
        ))
    }

    fn complete_new(
        cmd: &clap::Command,
        current_dir: Option<&std::path::Path>,
    ) -> Result<Vec<std::ffi::OsString>, std::io::Error> {
        let mut completions = Vec::new();

        completions.extend(
            crate::generator::utils::longs_and_visible_aliases(cmd)
                .into_iter()
                .map(|f| format!("--{}", f).into()),
        );
        completions.extend(
            crate::generator::utils::shorts_and_visible_aliases(cmd)
                .into_iter()
                .map(|f| format!("-{}", f).into()),
        );

        let mut positionals = Vec::new();
        positionals.extend(
            cmd.get_positionals()
                .flat_map(|p| p.get_possible_values())
                .flatten()
                .map(|p| p.get_name().into()),
        );
        let hints = cmd
            .get_positionals()
            .map(|p| p.get_value_hint())
            .map(|h| {
                if h == clap::ValueHint::Unknown {
                    clap::ValueHint::AnyPath
                } else {
                    h
                }
            })
            .collect::<std::collections::HashSet<_>>();
        for hint in hints {
            match hint {
                clap::ValueHint::Unknown => unreachable!("Filtered out"),
                clap::ValueHint::Other => {
                    // Should not complete
                }
                clap::ValueHint::AnyPath => {
                    positionals.extend(complete_path(current_dir, |_| true));
                }
                clap::ValueHint::FilePath => {
                    positionals.extend(complete_path(current_dir, |p| p.is_file()));
                }
                clap::ValueHint::DirPath => {
                    positionals.extend(complete_path(current_dir, |p| p.is_dir()));
                }
                clap::ValueHint::ExecutablePath => {
                    use is_executable::IsExecutable;
                    positionals.extend(complete_path(current_dir, |p| p.is_executable()));
                }
                clap::ValueHint::CommandName
                | clap::ValueHint::CommandString
                | clap::ValueHint::CommandWithArguments
                | clap::ValueHint::Username
                | clap::ValueHint::Hostname
                | clap::ValueHint::Url
                | clap::ValueHint::EmailAddress => {
                    // No completion implementation
                }
                _ => {
                    // Safe-ish fallback
                    positionals.extend(complete_path(current_dir, |_| true));
                }
            }
        }
        positionals.sort();
        positionals.dedup();
        completions.extend(positionals);

        completions.extend(all_subcommands(cmd).into_iter().map(|s| s));

        Ok(completions)
    }

    fn complete_path(
        current_dir: Option<&std::path::Path>,
        is_wanted: impl Fn(&std::path::Path) -> bool,
    ) -> Vec<OsString> {
        let mut completions = Vec::new();

        let current_dir = match current_dir {
            Some(current_dir) => current_dir,
            None => {
                return Vec::new();
            }
        };
        for entry in std::fs::read_dir(current_dir)
            .ok()
            .into_iter()
            .flatten()
            .filter_map(Result::ok)
        {
            let mut path = entry.path();
            if entry.metadata().map(|m| m.is_dir()).unwrap_or(false) {
                path.push(""); // Ensure trailing `/`
                let suggestion = path.strip_prefix(current_dir).unwrap();
                completions.push(suggestion.as_os_str().to_owned());
            } else {
                if is_wanted(&path) {
                    let suggestion = path.strip_prefix(current_dir).unwrap();
                    completions.push(suggestion.as_os_str().to_owned());
                }
            }
        }

        completions
    }

    fn all_subcommands(cmd: &clap::Command) -> Vec<OsString> {
        debug!("all_subcommands");

        let mut scs = crate::generator::utils::all_subcommands(cmd)
            .iter()
            .map(|x| OsString::from(&x.0))
            .collect::<Vec<_>>();
        scs.sort();
        scs.dedup();
        scs
    }
}
<|endoftext|>


<commit_before>use crate::cdsl::settings::{PredicateNode, SettingGroup, SettingGroupBuilder};

pub fn define(shared: &SettingGroup) -> SettingGroup {
    let mut settings = SettingGroupBuilder::new("x86");

    // CPUID.01H:ECX
    let has_sse3 = settings.add_bool("has_sse3", "SSE3: CPUID.01H:ECX.SSE3[bit 0]", false);
    let has_ssse3 = settings.add_bool("has_ssse3", "SSSE3: CPUID.01H:ECX.SSSE3[bit 9]", false);
    let has_sse41 = settings.add_bool("has_sse41", "SSE4.1: CPUID.01H:ECX.SSE4_1[bit 19]", false);
    let has_sse42 = settings.add_bool("has_sse42", "SSE4.2: CPUID.01H:ECX.SSE4_2[bit 20]", false);
    let has_popcnt = settings.add_bool("has_popcnt", "POPCNT: CPUID.01H:ECX.POPCNT[bit 23]", false);
    settings.add_bool("has_avx", "AVX: CPUID.01H:ECX.AVX[bit 28]", false);

    // CPUID.(EAX=07H, ECX=0H):EBX
    let has_bmi1 = settings.add_bool(
        "has_bmi1",
        "BMI1: CPUID.(EAX=07H, ECX=0H):EBX.BMI1[bit 3]",
        false,
    );
    let has_bmi2 = settings.add_bool(
        "has_bmi2",
        "BMI2: CPUID.(EAX=07H, ECX=0H):EBX.BMI2[bit 8]",
        false,
    );

    // CPUID.EAX=80000001H:ECX
    let has_lzcnt = settings.add_bool(
        "has_lzcnt",
        "LZCNT: CPUID.EAX=80000001H:ECX.LZCNT[bit 5]",
        false,
    );

    settings.add_predicate("use_sse41", predicate!(has_sse41));
    settings.add_predicate("use_sse42", predicate!(has_sse41 && has_sse42));
    settings.add_predicate("use_popcnt", predicate!(has_popcnt && has_sse42));
    settings.add_predicate("use_bmi1", predicate!(has_bmi1));
    settings.add_predicate("use_lzcnt", predicate!(has_lzcnt));

    // Some shared boolean values are used in x86 instruction predicates, so we need to group them
    // in the same TargetIsa, for compabitibity with code generated by meta-python.
    // TODO Once all the meta generation code has been migrated from Python to Rust, we can put it
    // back in the shared SettingGroup, and use it in x86 instruction predicates.

    let is_pic = shared.get_bool("is_pic");
    let allones_funcaddrs = shared.get_bool("allones_funcaddrs");
    settings.add_predicate("is_pic", predicate!(is_pic));
    settings.add_predicate("not_is_pic", predicate!(!is_pic));
    settings.add_predicate(
        "all_ones_funcaddrs_and_not_is_pic",
        predicate!(allones_funcaddrs && !is_pic),
    );
    settings.add_predicate(
        "not_all_ones_funcaddrs_and_not_is_pic",
        predicate!(!allones_funcaddrs && !is_pic),
    );

    // Presets corresponding to x86 CPUs.

    settings.add_preset("baseline", preset!());
    let nehalem = settings.add_preset(
        "nehalem",
        preset!(has_sse3 && has_ssse3 && has_sse41 && has_sse42 && has_popcnt),
    );
    let haswell = settings.add_preset(
        "haswell",
        preset!(nehalem && has_bmi1 && has_bmi2 && has_lzcnt),
    );
    let broadwell = settings.add_preset("broadwell", preset!(haswell));
    let skylake = settings.add_preset("skylake", preset!(broadwell));
    let cannonlake = settings.add_preset("cannonlake", preset!(skylake));
    settings.add_preset("icelake", preset!(cannonlake));
    settings.add_preset(
        "znver1",
        preset!(
            has_sse3
                && has_ssse3
                && has_sse41
                && has_sse42
                && has_popcnt
                && has_bmi1
                && has_bmi2
                && has_lzcnt
        ),
    );

    settings.build()
}
<commit_msg>Add x86-specific SIMD settings, e.g. SSE2<commit_after>use crate::cdsl::settings::{PredicateNode, SettingGroup, SettingGroupBuilder};

pub fn define(shared: &SettingGroup) -> SettingGroup {
    let mut settings = SettingGroupBuilder::new("x86");

    // CPUID.01H:EDX
    let has_sse2 = settings.add_bool("has_sse2", "SSE2: CPUID.01H:EDX.SSE2[bit 26]", false);

    // CPUID.01H:ECX
    let has_sse3 = settings.add_bool("has_sse3", "SSE3: CPUID.01H:ECX.SSE3[bit 0]", false);
    let has_ssse3 = settings.add_bool("has_ssse3", "SSSE3: CPUID.01H:ECX.SSSE3[bit 9]", false);
    let has_sse41 = settings.add_bool("has_sse41", "SSE4.1: CPUID.01H:ECX.SSE4_1[bit 19]", false);
    let has_sse42 = settings.add_bool("has_sse42", "SSE4.2: CPUID.01H:ECX.SSE4_2[bit 20]", false);
    let has_popcnt = settings.add_bool("has_popcnt", "POPCNT: CPUID.01H:ECX.POPCNT[bit 23]", false);
    settings.add_bool("has_avx", "AVX: CPUID.01H:ECX.AVX[bit 28]", false);

    // CPUID.(EAX=07H, ECX=0H):EBX
    let has_bmi1 = settings.add_bool(
        "has_bmi1",
        "BMI1: CPUID.(EAX=07H, ECX=0H):EBX.BMI1[bit 3]",
        false,
    );
    let has_bmi2 = settings.add_bool(
        "has_bmi2",
        "BMI2: CPUID.(EAX=07H, ECX=0H):EBX.BMI2[bit 8]",
        false,
    );

    // CPUID.EAX=80000001H:ECX
    let has_lzcnt = settings.add_bool(
        "has_lzcnt",
        "LZCNT: CPUID.EAX=80000001H:ECX.LZCNT[bit 5]",
        false,
    );

    let shared_enable_simd = shared.get_bool("enable_simd");

    settings.add_predicate("use_sse2", predicate!(shared_enable_simd && has_sse2));
    settings.add_predicate("use_ssse3", predicate!(shared_enable_simd && has_ssse3));
    settings.add_predicate("use_sse41", predicate!(shared_enable_simd && has_sse41));
    settings.add_predicate(
        "use_sse42",
        predicate!(shared_enable_simd && has_sse41 && has_sse42),
    );
    settings.add_predicate("use_popcnt", predicate!(has_popcnt && has_sse42));
    settings.add_predicate("use_bmi1", predicate!(has_bmi1));
    settings.add_predicate("use_lzcnt", predicate!(has_lzcnt));

    // Some shared boolean values are used in x86 instruction predicates, so we need to group them
    // in the same TargetIsa, for compabitibity with code generated by meta-python.
    // TODO Once all the meta generation code has been migrated from Python to Rust, we can put it
    // back in the shared SettingGroup, and use it in x86 instruction predicates.

    let is_pic = shared.get_bool("is_pic");
    let allones_funcaddrs = shared.get_bool("allones_funcaddrs");
    settings.add_predicate("is_pic", predicate!(is_pic));
    settings.add_predicate("not_is_pic", predicate!(!is_pic));
    settings.add_predicate(
        "all_ones_funcaddrs_and_not_is_pic",
        predicate!(allones_funcaddrs && !is_pic),
    );
    settings.add_predicate(
        "not_all_ones_funcaddrs_and_not_is_pic",
        predicate!(!allones_funcaddrs && !is_pic),
    );

    // Presets corresponding to x86 CPUs.

    settings.add_preset("baseline", preset!());
    let nehalem = settings.add_preset(
        "nehalem",
        preset!(has_sse2 && has_sse3 && has_ssse3 && has_sse41 && has_sse42 && has_popcnt),
    );
    let haswell = settings.add_preset(
        "haswell",
        preset!(nehalem && has_bmi1 && has_bmi2 && has_lzcnt),
    );
    let broadwell = settings.add_preset("broadwell", preset!(haswell));
    let skylake = settings.add_preset("skylake", preset!(broadwell));
    let cannonlake = settings.add_preset("cannonlake", preset!(skylake));
    settings.add_preset("icelake", preset!(cannonlake));
    settings.add_preset(
        "znver1",
        preset!(
            has_sse2
                && has_sse3
                && has_ssse3
                && has_sse41
                && has_sse42
                && has_popcnt
                && has_bmi1
                && has_bmi2
                && has_lzcnt
        ),
    );

    settings.build()
}
<|endoftext|>


<commit_before>use std::collections::btree_map;
use std::collections::btree_map::{BTreeMap, Entry};
use std::iter::{FromIterator, IntoIterator};
use std::cmp::{Ordering};

#[derive(Clone, Debug, PartialOrd, PartialEq, Ord, Eq)]
pub struct Index<T> {
    items: BTreeMap<T, i64>,
}

// Keys does not implement Debug :(
pub struct Iter<'a, T> where T: 'a {
    iter: btree_map::Iter<'a, T, i64>,
}

#[derive(Clone, Debug)]
pub struct Changes<T> {
    pub inserted: Vec<T>,
    pub removed: Vec<T>,
}

impl<T: Ord + Clone> Index<T> {
    pub fn new() -> Self {
        Index{items: BTreeMap::new()}
    }

    pub fn insert(&mut self, item: T) {
        match self.items.entry(item) {
            Entry::Vacant(vacant) => {
                vacant.insert(1);
            }
            Entry::Occupied(mut occupied) => {
                let existing_count = *occupied.get();
                occupied.insert(existing_count + 1);
            }
        }
    }

    pub fn remove(&mut self, item: T) {
        match self.items.entry(item) {
            Entry::Vacant(vacant) => {
                vacant.insert(-1);
            }
            Entry::Occupied(mut occupied) => {
                let new_count = *occupied.get() - 1;
                if new_count != 0 {
                    occupied.insert(new_count);
                } else {
                    occupied.remove();
                }
            }
        }
    }

    pub fn iter(&self) -> Iter<T> {
        Iter{iter: self.items.iter()}
    }

    pub fn change(&mut self, changes: Changes<T>) {
        for item in changes.removed {
            self.remove(item);
        }
        for item in changes.inserted {
            self.insert(item);
        }
    }

    pub fn changes_since(&self, before: &Index<T>) -> Changes<T> {
            let mut before_keys = before.items.keys();
            let mut after_keys = self.items.keys();
            let mut before_key = before_keys.next();
            let mut after_key = after_keys.next();
            let mut inserted = Vec::new();
            let mut removed = Vec::new();
            loop {
                match (before_key, after_key) {
                    (None, None) => {
                        break;
                    }
                    (Some(before), None) => {
                        removed.push(before.clone());
                        before_key = before_keys.next();
                    }
                    (None, Some(after)) => {
                        inserted.push(after.clone());
                        after_key = after_keys.next();
                    }
                    (Some(before), Some(after)) => {
                        match before.cmp(after) {
                            Ordering::Less => {
                                removed.push(before.clone());
                                before_key = before_keys.next();
                            }
                            Ordering::Greater => {
                                inserted.push(after.clone());
                                after_key = after_keys.next();
                            }
                            Ordering::Equal => {
                                before_key = before_keys.next();
                                after_key = after_keys.next();
                            }
                        }
                    }
                }
            }
            Changes{inserted: inserted, removed: removed}
    }
}

impl<'a, T: Ord> Iterator for Iter<'a, T> {
    type Item = &'a T;

    fn next(&mut self) -> Option<&'a T> {
        loop {
            match self.iter.next() {
                None => return None,
                Some((item, count)) if *count > 0 => return Some(item),
                _ => continue,
            }
        }
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        self.iter.size_hint()
    }
}

impl<T: Ord> FromIterator<T> for Index<T> {
    fn from_iter<I: IntoIterator<Item=T>>(iterable: I) -> Self {
        Index{items: BTreeMap::from_iter(
            iterable.into_iter().map(|item| (item, 1))
            )}
    }
}

pub struct IntoIter<T> {
    items: btree_map::IntoIter<T, i64>
}

impl<T: Ord> Iterator for IntoIter<T> {
    type Item = T;
    fn next(&mut self) -> Option<T> {
        self.items.next().map(|(key, _)| key)
    }
}

impl<T: Ord> IntoIterator for Index<T> {
    type Item = T;
    type IntoIter = IntoIter<T>;
    fn into_iter(self) -> IntoIter<T> {
        IntoIter{items: self.items.into_iter()}
    }
}<commit_msg>Remove dead code<commit_after><|endoftext|>


<commit_before>//! `i386` intrinsics

/// Reads EFLAGS.
///
/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=__readeflags)
#[cfg(target_arch = "x86")]
#[inline(always)]
#[stable(feature = "simd_x86", since = "1.27.0")]
pub unsafe fn __readeflags() -> u32 {
    let eflags: u32;
    asm!("pushfd; popl $0" : "=r"(eflags) : : : "volatile");
    eflags
}

/// Reads EFLAGS.
///
/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=__readeflags)
#[cfg(target_arch = "x86_64")]
#[inline(always)]
#[stable(feature = "simd_x86", since = "1.27.0")]
pub unsafe fn __readeflags() -> u64 {
    let eflags: u64;
    asm!("pushfq; popq $0" : "=r"(eflags) : : : "volatile");
    eflags
}

/// Write EFLAGS.
///
/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=__writeeflags)
#[cfg(target_arch = "x86")]
#[inline(always)]
#[stable(feature = "simd_x86", since = "1.27.0")]
pub unsafe fn __writeeflags(eflags: u32) {
    asm!("pushl $0; popfd" : : "r"(eflags) : "cc", "flags" : "volatile");
}

/// Write EFLAGS.
///
/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=__writeeflags)
#[cfg(target_arch = "x86_64")]
#[inline(always)]
#[stable(feature = "simd_x86", since = "1.27.0")]
pub unsafe fn __writeeflags(eflags: u64) {
    asm!("pushq $0; popfq" : : "r"(eflags) : "cc", "flags" : "volatile");
}

#[cfg(test)]
mod tests {
    use coresimd::x86::*;

    #[test]
    fn test_eflags() {
        unsafe {
            // reads eflags, writes them back, reads them again,
            // and compare for equality:
            let v = __readeflags();
            __writeeflags(v);
            let u = __readeflags();
            assert_eq!(v, u);
        }
    }
}
<commit_msg>deprecate read/write eflags (#494)<commit_after>//! `i386` intrinsics

/// Reads EFLAGS.
///
/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=__readeflags)
#[cfg(target_arch = "x86")]
#[inline(always)]
#[stable(feature = "simd_x86", since = "1.27.0")]
#[rustc_deprecated(since = "1.29.0", reason = "See issue #51810 - use inline assembly instead")]
#[doc(hidden)]
pub unsafe fn __readeflags() -> u32 {
    let eflags: u32;
    asm!("pushfd; popl $0" : "=r"(eflags) : : : "volatile");
    eflags
}

/// Reads EFLAGS.
///
/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=__readeflags)
#[cfg(target_arch = "x86_64")]
#[inline(always)]
#[stable(feature = "simd_x86", since = "1.27.0")]
#[rustc_deprecated(since = "1.29.0", reason = "See issue #51810 - use inline assembly instead")]
#[doc(hidden)]
pub unsafe fn __readeflags() -> u64 {
    let eflags: u64;
    asm!("pushfq; popq $0" : "=r"(eflags) : : : "volatile");
    eflags
}

/// Write EFLAGS.
///
/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=__writeeflags)
#[cfg(target_arch = "x86")]
#[inline(always)]
#[stable(feature = "simd_x86", since = "1.27.0")]
#[rustc_deprecated(since = "1.29.0", reason = "See issue #51810 - use inline assembly instead")]
#[doc(hidden)]
pub unsafe fn __writeeflags(eflags: u32) {
    asm!("pushl $0; popfd" : : "r"(eflags) : "cc", "flags" : "volatile");
}

/// Write EFLAGS.
///
/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=__writeeflags)
#[cfg(target_arch = "x86_64")]
#[inline(always)]
#[stable(feature = "simd_x86", since = "1.27.0")]
#[rustc_deprecated(since = "1.29.0", reason = "See issue #51810 - use inline assembly instead")]
#[doc(hidden)]
pub unsafe fn __writeeflags(eflags: u64) {
    asm!("pushq $0; popfq" : : "r"(eflags) : "cc", "flags" : "volatile");
}

#[cfg(test)]
mod tests {
    use coresimd::x86::*;

    #[test]
    #[allow(deprecated)]
    fn test_eflags() {
        unsafe {
            // reads eflags, writes them back, reads them again,
            // and compare for equality:
            let v = __readeflags();
            __writeeflags(v);
            let u = __readeflags();
            assert_eq!(v, u);
        }
    }
}
<|endoftext|>


<commit_before>extern crate day_4;

use std::env;
use day_4::RoomKey;

fn main() {
    let arguments: Vec<String> = env::args().collect();
    let data: Vec<RoomKey> = day_4::load_room_keys(arguments.get(1).unwrap()).unwrap();

    println!("Sum of Real Room Sector IDs: {}",
             day_4::sum_real_roomkey_sector_ids(&data));

    println!("Room Sector ID Containing North Pole Objects: {}",
             data.iter()
                 .find(|&roomkey| roomkey.decrypted_name().to_lowercase().contains("north"))
                 .unwrap()
                 .sector_id);
}<commit_msg>Minor syntax fix.<commit_after>extern crate day_4;

use std::env;
use day_4::RoomKey;

fn main() {
    let arguments: Vec<String> = env::args().collect();
    let data: Vec<RoomKey> = day_4::load_room_keys(arguments.get(1).unwrap()).unwrap();

    println!("Sum of Real Room Sector IDs: {}",
             day_4::sum_real_roomkey_sector_ids(&data));

    println!("Room Sector ID Containing North Pole Objects: {}",
             data.iter()
                 .find(|roomkey| roomkey.decrypted_name().to_lowercase().contains("north"))
                 .unwrap()
                 .sector_id);
}<|endoftext|>


<commit_before>use eyre::{bail, Result};
use regex::Regex;
use schemars::{schema_for, JsonSchema};
use serde::{Deserialize, Serialize};
use serde_with::skip_serializing_none;
use std::{collections::{BTreeMap, HashMap}, str::FromStr};
use std::{
    fs,
    path::{Path, PathBuf},
};
use strum::{EnumString, EnumVariantNames, ToString, VariantNames};

/// # A file or directory within a project
#[skip_serializing_none]
#[derive(Debug, Default, Clone, JsonSchema, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
struct File {
    /// The relative path of the file within the project folder
    path: String,

    /// Whether the entry is a directory or not
    is_dir: bool,

    /// The media type (aka MIME type) of the file
    media_type: Option<String>,

    /// The SHA1 hash of the contents of the file
    sha1: Option<String>,
}

impl File {
    pub fn from_path(folder: &Path, path: &Path) -> (PathBuf, File) {
        let canonical_path = path.canonicalize().expect("Unable to canonicalize path");
        let relative_path = path
            .strip_prefix(folder)
            .expect("Unable to strip prefix")
            .display()
            .to_string();

        let media_type = mime_guess::from_path(path)
            .first()
            .map(|mime| mime.essence_str().to_string());

        let file = File {
            path: relative_path,
            is_dir: path.is_dir(),
            media_type,
            ..Default::default()
        };

        (canonical_path, file)
    }
}

/// # Details of a project
///
/// An implementation, and extension, of schema.org [`Project`](https://schema.org/Project).
/// Uses schema.org properties where possible but adds extension properties
/// where needed (e.g. `theme`).
#[skip_serializing_none]
#[derive(Debug, Default, Clone, JsonSchema, Deserialize, Serialize)]
#[serde(default, rename_all = "camelCase")]
pub struct Project {
    /// The name of the project
    name: Option<String>,

    /// A description of the project
    description: Option<String>,

    /// URL of the image to be used when displaying the project
    ///
    /// If not specified, will default to the most recently
    /// modified image in the project (if any).
    image: Option<String>,

    /// The path (within the project) of the project's main file
    ///
    /// If not specified, will default to the first file matching the
    /// the regular expression in the configuration settings.
    main: Option<String>,

    /// The default theme to use when viewing documents in this project
    ///
    /// If not specified, will default to the default theme in the
    /// configuration settings.
    theme: Option<String>,

    /// The files in the project directory
    files: BTreeMap<PathBuf, File>,
}

/// A format to display a plugin using
#[derive(Debug, EnumString, EnumVariantNames, PartialEq, ToString)]
#[strum(serialize_all = "lowercase")]
pub enum ShowFormat {
    Md,
    Toml,
    Json,
}

impl Project {
    /// The name of the project's manifest file within the project directory
    const FILE_NAME: &'static str = "project.json";

    /// Get the JSON Schema for a project
    pub fn schema() -> String {
        let schema = schema_for!(Project);
        serde_json::to_string_pretty(&schema).unwrap()
    }

    /// Get the path to a projects' manifest file
    fn file(folder: &str) -> PathBuf {
        Path::new(folder).join(Project::FILE_NAME)
    }

    /// Read a project's manifest file
    ///
    /// If there is no manifest file, then return a default project
    fn read(folder: &str) -> Result<Project> {
        if !Path::new(folder).exists() {
            bail!("Project folder does not exist: {}", folder)
        }

        let file = Project::file(folder);
        let project = if file.exists() {
            let json = fs::read_to_string(file)?;
            serde_json::from_str(&json)?
        } else {
            Project::default()
        };

        Ok(project)
    }

    /// Write a project's manifest file
    ///
    /// If the project folder does not exist yet then it will be created
    pub fn write(folder: &str, project: &Project) -> Result<()> {
        fs::create_dir_all(folder)?;

        let file = Project::file(folder);
        let json = serde_json::to_string_pretty(project)?;
        fs::write(file, json)?;

        Ok(())
    }

    /// Initialize a project in a new, or existing, folder
    ///
    /// If the project has already been initialized (i.e. has a manifest file)
    /// then this function will do nothing
    pub fn init(folder: &str) -> Result<Project> {
        if Project::file(folder).exists() {
            return Project::read(folder);
        }

        let project = Project::default();
        Project::write(folder, &project)?;

        Ok(project)
    }

    /// Load a project including creating default values for properties
    /// where necessary
    pub fn load(folder: &str, config: &config::ProjectsConfig) -> Result<Project> {
        let project = Project::read(folder)?;
        let Project {
            name, main, theme, ..
        } = project;

        // Name defaults to the name of the folder
        let name = name.or_else(|| match Path::new(folder).components().last() {
            Some(last) => Some(last.as_os_str().to_string_lossy().to_string()),
            None => Some("Unnamed".to_string()),
        });

        // Get all the files in the project (use `min_depth` to ignore the folder itself)
        let folder = Path::new(folder);
        let files: Vec<(PathBuf, File)> = walkdir::WalkDir::new(folder).min_depth(1)
            .into_iter()
            .filter_map(|entry| {
                let entry = match entry.ok() {
                    Some(entry) => entry,
                    None => return None,
                };
                Some(File::from_path(folder, entry.path()))
            })
            .collect();

        // Main defaults to the first file that matches configured patterns (if any)
        let main = main.or_else(|| {
            // See if there are any files matching patterns
            for pattern in &config.main_patterns {
                let re = match Regex::new(&pattern.to_lowercase()) {
                    Ok(re) => re,
                    Err(_) => {
                        tracing::warn!("Project main file pattern is invalid: {}", pattern);
                        continue;
                    }
                };
                for (_, file) in &files {
                    let File { is_dir, path, .. } = file;
                    if *is_dir {
                        continue;
                    }
                    if re.is_match(path) {
                        return Some(path.clone());
                    }
                }
            }

            None
        });

        let files = files.into_iter().collect();

        // Name defaults to the configured default
        let theme = theme.or_else(|| Some(config.theme.clone()));

        Ok(Project {
            name,
            main,
            theme,
            files,
            ..project
        })
    }

    /// Show a project in a format
    ///
    /// Used for displaying a project in CLI and possibly elsewhere.
    pub fn show(&self, format: ShowFormat) -> Result<(String, String)> {
        let content = match format {
            ShowFormat::Json => serde_json::to_string_pretty(self)?,
            ShowFormat::Toml => toml::to_string(self)?,
            ShowFormat::Md => {
                use handlebars::Handlebars;

                let template = r#"
# {{name}}

**Main**: {{ main }}
**Theme**: {{ theme }}

"#;
                let hb = Handlebars::new();
                hb.render_template(template.trim(), self)?
            }
        };

        Ok((format.to_string(), content))
    }
}

/// An in-memory store of projects and associated
/// data (e.g. file system watchers)
#[derive(Debug, Deserialize)]
#[serde(default)]
pub struct Projects {
    /// The projects, stored by absolute path
    pub projects: HashMap<String, Project>,
}

impl Default for Projects {
    fn default() -> Self {
        Projects {
            projects: HashMap::new(),
        }
    }
}

impl Projects {
    /// Get the canonical absolute path of a project folder
    fn path(folder: &str) -> Result<String> {
        Ok(Path::new(folder).canonicalize()?.display().to_string())
    }

    /// List projects that are open
    pub fn list(&self) -> Result<HashMap<String, Project>> {
        Ok(self.projects.clone())
    }

    /// Open a project
    ///
    /// This function `loads` a project, stores it, watches the project folder,
    /// updates the project on changes and publishes the updates on the "projects"
    /// pubsub topic channel.
    pub fn open(
        &mut self,
        folder: &str,
        config: &config::ProjectsConfig,
    ) -> Result<(String, Project)> {
        let path = Projects::path(folder)?;
        let project = Project::load(folder, config)?;
        self.projects.insert(path.clone(), project.clone());
        Ok((path, project))
    }

    /// Close a project
    pub fn close(&mut self, folder: &str) -> Result<()> {
        let path = Projects::path(folder)?;
        self.projects.remove(&path);
        Ok(())
    }
}

#[cfg(feature = "config")]
pub mod config {
    use super::*;
    use defaults::Defaults;
    use validator::Validate;

    /// # Projects
    ///
    /// Configuration settings for project defaults
    #[derive(Debug, Defaults, PartialEq, Clone, JsonSchema, Deserialize, Serialize, Validate)]
    #[serde(default, rename_all = "camelCase")]
    pub struct ProjectsConfig {
        /// Patterns used to infer the main file of projects
        ///
        /// For projects that do not specify a main file, each file is tested
        /// against these case insensitive patterns in order. The first
        /// file (alphabetically) that matches is the project's main file.
        #[def = r#"vec!["^main\\b".to_string(), "^index\\b".to_string(), "^readme\\b".to_string()]"#]
        pub main_patterns: Vec<String>,

        /// Default project theme
        ///
        /// Will be applied to all projects that do not specify a theme
        #[def = r#"String::from("stencila")"#]
        pub theme: String,
    }
}

#[cfg(feature = "cli")]
pub mod cli {
    use super::*;
    use structopt::StructOpt;

    #[derive(Debug, StructOpt)]
    #[structopt(
        about = "Initialize a project in a new, or existing, folder",
        setting = structopt::clap::AppSettings::DeriveDisplayOrder,
        setting = structopt::clap::AppSettings::ColoredHelp
    )]
    pub struct Init {
        /// The path of the new, or existing, folder to initialize
        ///
        /// If no folder exists at the path, then one will be created.
        /// If no `project.json` file exists in the folder then a new one
        /// will be created.
        #[structopt(default_value = ".")]
        pub folder: String,
    }

    impl Init {
        pub fn run(&self) -> Result<()> {
            let Self { folder } = self;
            Project::init(folder)?;
            Ok(())
        }
    }

    #[derive(Debug, StructOpt)]
    #[structopt(
        about = "Show a project details",
        setting = structopt::clap::AppSettings::DeriveDisplayOrder,
        setting = structopt::clap::AppSettings::ColoredHelp
    )]
    pub struct Show {
        /// The path of the project folder
        #[structopt(default_value = ".")]
        pub folder: String,

        /// The format to display the project in
        #[structopt(short, long, default_value = "md", possible_values = ShowFormat::VARIANTS, case_insensitive = true)]
        pub format: String,
    }

    impl Show {
        pub fn run(&self, config: &config::ProjectsConfig) -> Result<(String, String)> {
            let Self { folder, format } = self;
            let format = ShowFormat::from_str(&format)?;
            Project::load(folder, config)?.show(format)
        }
    }
}
<commit_msg>feat(Files): Add modified and size properties<commit_after>use eyre::{bail, Result};
use regex::Regex;
use schemars::{schema_for, JsonSchema};
use serde::{Deserialize, Serialize};
use serde_with::skip_serializing_none;
use std::{
    collections::{BTreeMap, HashMap},
    str::FromStr,
    time::{UNIX_EPOCH},
};
use std::{
    fs,
    path::{Path, PathBuf},
};
use strum::{EnumString, EnumVariantNames, ToString, VariantNames};

/// # A file or directory within a project
#[skip_serializing_none]
#[derive(Debug, Default, Clone, JsonSchema, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
struct File {
    /// The relative path of the file within the project folder
    path: String,

    /// Whether the entry is a directory or not
    is_dir: bool,

    /// Time that the file was last modified (seconds since Unix Epoch)
    modified: Option<u64>,

    /// Size of the file in bytes
    size: Option<u64>,

    /// The media type (aka MIME type) of the file
    media_type: Option<String>,

    /// The SHA1 hash of the contents of the file
    sha1: Option<String>,
}

impl File {
    pub fn from_path(folder: &Path, path: &Path) -> (PathBuf, File) {
        let canonical_path = path.canonicalize().expect("Unable to canonicalize path");
        let relative_path = path
            .strip_prefix(folder)
            .expect("Unable to strip prefix")
            .display()
            .to_string();

        let (modified, size) = match path.metadata() {
            Ok(metadata) => {
                let modified = metadata
                    .modified()
                    .ok()
                    .and_then(|time| time.duration_since(UNIX_EPOCH).ok())
                    .and_then(|duration| Some(duration.as_secs()));
                let size = Some(metadata.len());
                (modified, size)
            }
            Err(_) => (None, None),
        };

        let media_type = mime_guess::from_path(path)
            .first()
            .map(|mime| mime.essence_str().to_string());

        let file = File {
            path: relative_path,
            is_dir: path.is_dir(),
            modified,
            size,
            media_type,
            ..Default::default()
        };

        (canonical_path, file)
    }
}

/// # Details of a project
///
/// An implementation, and extension, of schema.org [`Project`](https://schema.org/Project).
/// Uses schema.org properties where possible but adds extension properties
/// where needed (e.g. `theme`).
#[skip_serializing_none]
#[derive(Debug, Default, Clone, JsonSchema, Deserialize, Serialize)]
#[serde(default, rename_all = "camelCase")]
pub struct Project {
    /// The name of the project
    name: Option<String>,

    /// A description of the project
    description: Option<String>,

    /// URL of the image to be used when displaying the project
    ///
    /// If not specified, will default to the most recently
    /// modified image in the project (if any).
    image: Option<String>,

    /// The path (within the project) of the project's main file
    ///
    /// If not specified, will default to the first file matching the
    /// the regular expression in the configuration settings.
    main: Option<String>,

    /// The default theme to use when viewing documents in this project
    ///
    /// If not specified, will default to the default theme in the
    /// configuration settings.
    theme: Option<String>,

    /// The files in the project directory
    files: BTreeMap<PathBuf, File>,
}

/// A format to display a plugin using
#[derive(Debug, EnumString, EnumVariantNames, PartialEq, ToString)]
#[strum(serialize_all = "lowercase")]
pub enum ShowFormat {
    Md,
    Toml,
    Json,
}

impl Project {
    /// The name of the project's manifest file within the project directory
    const FILE_NAME: &'static str = "project.json";

    /// Get the JSON Schema for a project
    pub fn schema() -> String {
        let schema = schema_for!(Project);
        serde_json::to_string_pretty(&schema).unwrap()
    }

    /// Get the path to a projects' manifest file
    fn file(folder: &str) -> PathBuf {
        Path::new(folder).join(Project::FILE_NAME)
    }

    /// Read a project's manifest file
    ///
    /// If there is no manifest file, then return a default project
    fn read(folder: &str) -> Result<Project> {
        if !Path::new(folder).exists() {
            bail!("Project folder does not exist: {}", folder)
        }

        let file = Project::file(folder);
        let project = if file.exists() {
            let json = fs::read_to_string(file)?;
            serde_json::from_str(&json)?
        } else {
            Project::default()
        };

        Ok(project)
    }

    /// Write a project's manifest file
    ///
    /// If the project folder does not exist yet then it will be created
    pub fn write(folder: &str, project: &Project) -> Result<()> {
        fs::create_dir_all(folder)?;

        let file = Project::file(folder);
        let json = serde_json::to_string_pretty(project)?;
        fs::write(file, json)?;

        Ok(())
    }

    /// Initialize a project in a new, or existing, folder
    ///
    /// If the project has already been initialized (i.e. has a manifest file)
    /// then this function will do nothing
    pub fn init(folder: &str) -> Result<Project> {
        if Project::file(folder).exists() {
            return Project::read(folder);
        }

        let project = Project::default();
        Project::write(folder, &project)?;

        Ok(project)
    }

    /// Load a project including creating default values for properties
    /// where necessary
    pub fn load(folder: &str, config: &config::ProjectsConfig) -> Result<Project> {
        let project = Project::read(folder)?;
        let Project {
            name, main, theme, ..
        } = project;

        // Name defaults to the name of the folder
        let name = name.or_else(|| match Path::new(folder).components().last() {
            Some(last) => Some(last.as_os_str().to_string_lossy().to_string()),
            None => Some("Unnamed".to_string()),
        });

        // Get all the files in the project (use `min_depth` to ignore the folder itself)
        let folder = Path::new(folder);
        let files: Vec<(PathBuf, File)> = walkdir::WalkDir::new(folder)
            .min_depth(1)
            .into_iter()
            .filter_map(|entry| {
                let entry = match entry.ok() {
                    Some(entry) => entry,
                    None => return None,
                };
                Some(File::from_path(folder, entry.path()))
            })
            .collect();

        // Main defaults to the first file that matches configured patterns (if any)
        let main = main.or_else(|| {
            // See if there are any files matching patterns
            for pattern in &config.main_patterns {
                let re = match Regex::new(&pattern.to_lowercase()) {
                    Ok(re) => re,
                    Err(_) => {
                        tracing::warn!("Project main file pattern is invalid: {}", pattern);
                        continue;
                    }
                };
                for (_, file) in &files {
                    let File { is_dir, path, .. } = file;
                    if *is_dir {
                        continue;
                    }
                    if re.is_match(path) {
                        return Some(path.clone());
                    }
                }
            }

            None
        });

        let files = files.into_iter().collect();

        // Name defaults to the configured default
        let theme = theme.or_else(|| Some(config.theme.clone()));

        Ok(Project {
            name,
            main,
            theme,
            files,
            ..project
        })
    }

    /// Show a project in a format
    ///
    /// Used for displaying a project in CLI and possibly elsewhere.
    pub fn show(&self, format: ShowFormat) -> Result<(String, String)> {
        let content = match format {
            ShowFormat::Json => serde_json::to_string_pretty(self)?,
            ShowFormat::Toml => toml::to_string(self)?,
            ShowFormat::Md => {
                use handlebars::Handlebars;

                let template = r#"
# {{name}}

**Main**: {{ main }}
**Theme**: {{ theme }}

"#;
                let hb = Handlebars::new();
                hb.render_template(template.trim(), self)?
            }
        };

        Ok((format.to_string(), content))
    }
}

/// An in-memory store of projects and associated
/// data (e.g. file system watchers)
#[derive(Debug, Deserialize)]
#[serde(default)]
pub struct Projects {
    /// The projects, stored by absolute path
    pub projects: HashMap<String, Project>,
}

impl Default for Projects {
    fn default() -> Self {
        Projects {
            projects: HashMap::new(),
        }
    }
}

impl Projects {
    /// Get the canonical absolute path of a project folder
    fn path(folder: &str) -> Result<String> {
        Ok(Path::new(folder).canonicalize()?.display().to_string())
    }

    /// List projects that are open
    pub fn list(&self) -> Result<HashMap<String, Project>> {
        Ok(self.projects.clone())
    }

    /// Open a project
    ///
    /// This function `loads` a project, stores it, watches the project folder,
    /// updates the project on changes and publishes the updates on the "projects"
    /// pubsub topic channel.
    pub fn open(
        &mut self,
        folder: &str,
        config: &config::ProjectsConfig,
    ) -> Result<(String, Project)> {
        let path = Projects::path(folder)?;
        let project = Project::load(folder, config)?;
        self.projects.insert(path.clone(), project.clone());
        Ok((path, project))
    }

    /// Close a project
    pub fn close(&mut self, folder: &str) -> Result<()> {
        let path = Projects::path(folder)?;
        self.projects.remove(&path);
        Ok(())
    }
}

#[cfg(feature = "config")]
pub mod config {
    use super::*;
    use defaults::Defaults;
    use validator::Validate;

    /// # Projects
    ///
    /// Configuration settings for project defaults
    #[derive(Debug, Defaults, PartialEq, Clone, JsonSchema, Deserialize, Serialize, Validate)]
    #[serde(default, rename_all = "camelCase")]
    pub struct ProjectsConfig {
        /// Patterns used to infer the main file of projects
        ///
        /// For projects that do not specify a main file, each file is tested
        /// against these case insensitive patterns in order. The first
        /// file (alphabetically) that matches is the project's main file.
        #[def = r#"vec!["^main\\b".to_string(), "^index\\b".to_string(), "^readme\\b".to_string()]"#]
        pub main_patterns: Vec<String>,

        /// Default project theme
        ///
        /// Will be applied to all projects that do not specify a theme
        #[def = r#"String::from("stencila")"#]
        pub theme: String,
    }
}

#[cfg(feature = "cli")]
pub mod cli {
    use super::*;
    use structopt::StructOpt;

    #[derive(Debug, StructOpt)]
    #[structopt(
        about = "Initialize a project in a new, or existing, folder",
        setting = structopt::clap::AppSettings::DeriveDisplayOrder,
        setting = structopt::clap::AppSettings::ColoredHelp
    )]
    pub struct Init {
        /// The path of the new, or existing, folder to initialize
        ///
        /// If no folder exists at the path, then one will be created.
        /// If no `project.json` file exists in the folder then a new one
        /// will be created.
        #[structopt(default_value = ".")]
        pub folder: String,
    }

    impl Init {
        pub fn run(&self) -> Result<()> {
            let Self { folder } = self;
            Project::init(folder)?;
            Ok(())
        }
    }

    #[derive(Debug, StructOpt)]
    #[structopt(
        about = "Show a project details",
        setting = structopt::clap::AppSettings::DeriveDisplayOrder,
        setting = structopt::clap::AppSettings::ColoredHelp
    )]
    pub struct Show {
        /// The path of the project folder
        #[structopt(default_value = ".")]
        pub folder: String,

        /// The format to display the project in
        #[structopt(short, long, default_value = "md", possible_values = ShowFormat::VARIANTS, case_insensitive = true)]
        pub format: String,
    }

    impl Show {
        pub fn run(&self, config: &config::ProjectsConfig) -> Result<(String, String)> {
            let Self { folder, format } = self;
            let format = ShowFormat::from_str(&format)?;
            Project::load(folder, config)?.show(format)
        }
    }
}
<|endoftext|>


<commit_before>pub struct Item {
    pub name: String,
    pub sell_in: i32,
    pub quality: i32,
}

impl Item {
    pub fn new(name: String, sell_in: i32, quality: i32) -> Item {
        Item {
            name: name,
            sell_in: sell_in,
            quality: quality,
        }
    }
}

pub struct GildedRose {
    pub items: Vec<Item>,
}

impl GildedRose {
    pub fn new(items: Vec<Item>) -> GildedRose {
        GildedRose { items: items }
    }

    pub fn update_quality(&mut self) {
        for item in &mut self.items {
            if item.name != "Aged Brie" && item.name != "Backstage passes to a TAFKAL80ETC concert"
            {
                if item.quality > 0 {
                    if item.name != "Sulfuras, Hand of Ragnaros" {
                        item.quality = item.quality - 1;
                    }
                }
            } else {
                if item.quality < 50 {
                    item.quality = item.quality + 1;

                    if item.name == "Backstage passes to a TAFKAL80ETC concert" {
                        if item.sell_in < 11 {
                            if item.quality < 50 {
                                item.quality = item.quality + 1;
                            }
                        }

                        if item.sell_in < 6 {
                            if item.quality < 50 {
                                item.quality = item.quality + 1;
                            }
                        }
                    }
                }
            }

            if item.name != "Sulfuras, Hand of Ragnaros" {
                item.sell_in = item.sell_in - 1;
            }

            if item.sell_in < 0 {
                if item.name != "Aged Brie" {
                    if item.name != "Backstage passes to a TAFKAL80ETC concert" {
                        if item.quality > 0 {
                            if item.name != "Sulfuras, Hand of Ragnaros" {
                                item.quality = item.quality - 1;
                            }
                        }
                    } else {
                        item.quality = item.quality - item.quality;
                    }
                } else {
                    if item.quality < 50 {
                        item.quality = item.quality + 1;
                    }
                }
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use super::{GildedRose, Item};

    #[test]
    pub fn foo() {
        let items = vec![Item::new(String::from("foo"), 0, 0)];
        let mut rose = GildedRose::new(items);
        rose.update_quality();

        assert_eq!("fixme", rose.items[0].name);
    }
}
<commit_msg>Use 2018-edition field init shorthand<commit_after>pub struct Item {
    pub name: String,
    pub sell_in: i32,
    pub quality: i32,
}

impl Item {
    pub fn new(name: String, sell_in: i32, quality: i32) -> Item {
        Item {
            name,
            sell_in,
            quality,
        }
    }
}

pub struct GildedRose {
    pub items: Vec<Item>,
}

impl GildedRose {
    pub fn new(items: Vec<Item>) -> GildedRose {
        GildedRose { items }
    }

    pub fn update_quality(&mut self) {
        for item in &mut self.items {
            if item.name != "Aged Brie" && item.name != "Backstage passes to a TAFKAL80ETC concert"
            {
                if item.quality > 0 {
                    if item.name != "Sulfuras, Hand of Ragnaros" {
                        item.quality = item.quality - 1;
                    }
                }
            } else {
                if item.quality < 50 {
                    item.quality = item.quality + 1;

                    if item.name == "Backstage passes to a TAFKAL80ETC concert" {
                        if item.sell_in < 11 {
                            if item.quality < 50 {
                                item.quality = item.quality + 1;
                            }
                        }

                        if item.sell_in < 6 {
                            if item.quality < 50 {
                                item.quality = item.quality + 1;
                            }
                        }
                    }
                }
            }

            if item.name != "Sulfuras, Hand of Ragnaros" {
                item.sell_in = item.sell_in - 1;
            }

            if item.sell_in < 0 {
                if item.name != "Aged Brie" {
                    if item.name != "Backstage passes to a TAFKAL80ETC concert" {
                        if item.quality > 0 {
                            if item.name != "Sulfuras, Hand of Ragnaros" {
                                item.quality = item.quality - 1;
                            }
                        }
                    } else {
                        item.quality = item.quality - item.quality;
                    }
                } else {
                    if item.quality < 50 {
                        item.quality = item.quality + 1;
                    }
                }
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use super::{GildedRose, Item};

    #[test]
    pub fn foo() {
        let items = vec![Item::new(String::from("foo"), 0, 0)];
        let mut rose = GildedRose::new(items);
        rose.update_quality();

        assert_eq!("fixme", rose.items[0].name);
    }
}
<|endoftext|>


<commit_before>extern crate piston;
extern crate sdl2_window;
extern crate turbine_scene3d;
extern crate vecmath;
extern crate camera_controllers;

use piston::{Event, Input, ButtonArgs, Button, ButtonState};
use piston::{keyboard::Key};
use piston::window::*;
use piston::event_loop::*;
use piston::input::RenderEvent;
use turbine_scene3d::*;
use turbine_scene3d::Command::*;
use vecmath::*;
use camera_controllers::*;

fn main() {
    #[cfg(not(any(feature = "dx12", feature = "metal", feature = "vulkan")))]
    let (mut window, mut scene, vertex_shader, fragment_shader) = {
        use sdl2_window::Sdl2Window;
        let settings = WindowSettings::new("colored cube", [512; 2])
            .samples(4)
            .exit_on_esc(true);
        let mut window: Sdl2Window = settings.build().unwrap();
        window.set_capture_cursor(true);
        let mut scene = Scene::new(SceneSettings::new());
        let vertex_shader = scene.vertex_shader(include_str!("../assets/colored_cube.glslv"))
            .unwrap();
        let fragment_shader = scene.fragment_shader(include_str!("../assets/colored_cube.glslf"))
            .unwrap();
        (window, scene, vertex_shader, fragment_shader)
    };

    #[cfg(any(feature = "dx12", feature = "metal", feature = "vulkan"))]
    let (mut scene, vertex_shader, fragment_shader) = {
        let settings = WindowSettings::new("colored cube", [512; 2])
            .samples(4)
            .exit_on_esc(true);
        let mut scene = Scene::new(SceneSettings::new(), settings);
        scene.window().set_capture_cursor(true);
        let vertex_shader = scene.vertex_shader(include_str!("../assets/colored_cube_rendy.glslv"))
            .unwrap();
        let fragment_shader = scene.fragment_shader(include_str!("../assets/colored_cube_rendy.glslf"))
            .unwrap();
        (scene, vertex_shader, fragment_shader)
    };

    #[cfg(not(any(feature = "dx12", feature = "metal", feature = "vulkan")))]
    macro_rules! window {
        () => { &mut window };
    }

    #[cfg(any(feature = "dx12", feature = "metal", feature = "vulkan"))]
    macro_rules! window {
        () => { scene.window() };
    }

    let mut events = Events::new(EventSettings::new());
    let mut frame_graph = FrameGraph::new();

    let (cube, cube2) = {
        let program = scene.program_from_vertex_fragment(vertex_shader, fragment_shader);
        let mvp = scene.matrix4_uniform(program, "MVP").unwrap();

        let vertex_array = scene.vertex_array();
        let vertex_buffer = scene.vertex_buffer3(vertex_array, 0, &vertex_buffer_data());
        let _ = scene.color_buffer(vertex_array, 1, &color_buffer_data());

        let c1 = frame_graph.command_list(vec![
            EnableCullFace,
            CullFaceBack,
            UseProgram(program),
            SetModelViewProjection(mvp),
            DrawTriangles(vertex_array, vertex_buffer.len()),
        ]);

        let c2 = frame_graph.command_list(vec![
            EnableCullFace,
            CullFaceBack,
            UseProgram(program),
            SetModelViewProjection(mvp),
            DrawLines(vertex_array, vertex_buffer.len()),
        ]);

        (c1, c2)
    };

    let cubes = frame_graph.command_list(vec![
            Scale([1.0, 1.0, 1.0]),
            Draw(cube),
            Translate([2.5, 0.0, 0.0]),
            RotateAxisDeg(vec3_normalized([1.0, 0.0, 1.0]), 45.0),
            Draw(cube)
        ]);

    let cubes2 = frame_graph.command_list(vec![
            Scale([1.0, 1.0, 1.0]),
            Draw(cube2),
            Translate([2.5, 0.0, 0.0]),
            RotateAxisDeg(vec3_normalized([1.0, 0.0, 1.0]), 45.0),
            Draw(cube2)
        ]);

    let mut first_person = FirstPerson::new(
        [0.5, 0.5, 4.0],
        FirstPersonSettings::keyboard_wasd()
    );

    let mut switch = false;
    while let Some(e) = events.next(window!()) {
        first_person.event(&e);

        match e {
            Event::Input(Input::Button(ButtonArgs {button: Button::Keyboard(Key::T), state: ButtonState::Release, ..}), _) => {
                switch = !switch;
            },
            _ => ()
        };

        if let Some(args) = e.render_args() {
            let proj = get_projection(window!());
            scene.projection(proj);
            scene.camera(first_person.camera(args.ext_dt).orthogonal());
            scene.model(mat4_id());
            scene.clear([0.0, 0.0, 0.0, 1.0]);
            scene.scale([1.0, 1.0, 1.0]);

            if !switch {
                scene.draw(cubes, &frame_graph);
            }
            else {
                scene.draw(cubes2, &frame_graph);
            }
        }
    }
}

fn get_projection<W: Window>(w: &W) -> Matrix4<f32> {
    let draw_size = w.draw_size();
    CameraPerspective {
        fov: 90.0, near_clip: 0.1, far_clip: 1000.0,
        aspect_ratio: (draw_size.width as f32) / (draw_size.height as f32)
    }.projection()
}

fn vertex_buffer_data() -> Vec<f32> {
    vec![
        -1.0,   -1.0,   -1.0, // triangle 1 : begin
        -1.0,   -1.0,   1.0,
        -1.0,   1.0,    1.0, // triangle 1 : end

        1.0,    1.0,    -1.0, // triangle 2 : begin
        -1.0,   -1.0,   -1.0,
        -1.0,   1.0,    -1.0, // triangle 2 : end

        1.0,    -1.0,   1.0,
        -1.0,   -1.0,   -1.0,
        1.0,    -1.0,   -1.0,

        1.0,    1.0,    -1.0,
        1.0,    -1.0,   -1.0,
        -1.0,   -1.0,   -1.0,

        -1.0,   -1.0,   -1.0,
        -1.0,   1.0,    1.0,
        -1.0,   1.0,    -1.0,

        1.0,    -1.0,   1.0,
        -1.0,   -1.0,   1.0,
        -1.0,   -1.0,   -1.0,

        -1.0,   1.0,    1.0,
        -1.0,   -1.0,   1.0,
        1.0,    -1.0,   1.0,

        1.0,    1.0,    1.0,
        1.0,    -1.0,   -1.0,
        1.0,    1.0,    -1.0,

        1.0,    -1.0,   -1.0,
        1.0,    1.0,    1.0,
        1.0,    -1.0,   1.0,

        1.0,    1.0,    1.0,
        1.0,    1.0,    -1.0,
        -1.0,   1.0,    -1.0,

        1.0,    1.0,    1.0,
        -1.0,   1.0,    -1.0,
        -1.0,   1.0,    1.0,

        1.0,    1.0,    1.0,
        -1.0,   1.0,    1.0,
        1.0,    -1.0,   1.0
    ]
}

fn color_buffer_data() -> Vec<f32> {
    vec![
        0.583,  0.771,  0.014,  1.0,
        0.609,  0.115,  0.436,  1.0,
        0.327,  0.483,  0.844,  1.0,
        0.822,  0.569,  0.201,  1.0,
        0.435,  0.602,  0.223,  1.0,
        0.310,  0.747,  0.185,  1.0,
        0.597,  0.770,  0.761,  1.0,
        0.559,  0.436,  0.730,  1.0,
        0.359,  0.583,  0.152,  1.0,
        0.483,  0.596,  0.789,  1.0,
        0.559,  0.861,  0.639,  1.0,
        0.195,  0.548,  0.859,  1.0,
        0.014,  0.184,  0.576,  1.0,
        0.771,  0.328,  0.970,  1.0,
        0.406,  0.615,  0.116,  1.0,
        0.676,  0.977,  0.133,  1.0,
        0.971,  0.572,  0.833,  1.0,
        0.140,  0.616,  0.489,  1.0,
        0.997,  0.513,  0.064,  1.0,
        0.945,  0.719,  0.592,  1.0,
        0.543,  0.021,  0.978,  1.0,
        0.279,  0.317,  0.505,  1.0,
        0.167,  0.620,  0.077,  1.0,
        0.347,  0.857,  0.137,  1.0,
        0.055,  0.953,  0.042,  1.0,
        0.714,  0.505,  0.345,  1.0,
        0.783,  0.290,  0.734,  1.0,
        0.722,  0.645,  0.174,  1.0,
        0.302,  0.455,  0.848,  1.0,
        0.225,  0.587,  0.040,  1.0,
        0.517,  0.713,  0.338,  1.0,
        0.053,  0.959,  0.120,  1.0,
        0.393,  0.621,  0.362,  1.0,
        0.673,  0.211,  0.457,  1.0,
        0.820,  0.883,  0.371,  1.0,
        0.982,  0.099,  0.879,  1.0,
    ]
}
<commit_msg>Revert colored_cube example<commit_after>extern crate piston;
extern crate sdl2_window;
extern crate turbine_scene3d;
extern crate vecmath;
extern crate camera_controllers;

use piston::window::*;
use piston::event_loop::*;
use piston::input::RenderEvent;
use turbine_scene3d::*;
use turbine_scene3d::Command::*;
use vecmath::*;
use camera_controllers::*;

fn main() {
    #[cfg(not(any(feature = "dx12", feature = "metal", feature = "vulkan")))]
    let (mut window, mut scene, vertex_shader, fragment_shader) = {
        use sdl2_window::Sdl2Window;
        let settings = WindowSettings::new("colored cube", [512; 2])
            .samples(4)
            .exit_on_esc(true);
        let mut window: Sdl2Window = settings.build().unwrap();
        window.set_capture_cursor(true);
        let mut scene = Scene::new(SceneSettings::new());
        let vertex_shader = scene.vertex_shader(include_str!("../assets/colored_cube.glslv"))
            .unwrap();
        let fragment_shader = scene.fragment_shader(include_str!("../assets/colored_cube.glslf"))
            .unwrap();
        (window, scene, vertex_shader, fragment_shader)
    };

    #[cfg(any(feature = "dx12", feature = "metal", feature = "vulkan"))]
    let (mut scene, vertex_shader, fragment_shader) = {
        let settings = WindowSettings::new("colored cube", [512; 2])
            .samples(4)
            .exit_on_esc(true);
        let mut scene = Scene::new(SceneSettings::new(), settings);
        scene.window().set_capture_cursor(true);
        let vertex_shader = scene.vertex_shader(include_str!("../assets/colored_cube_rendy.glslv"))
            .unwrap();
        let fragment_shader = scene.fragment_shader(include_str!("../assets/colored_cube_rendy.glslf"))
            .unwrap();
        (scene, vertex_shader, fragment_shader)
    };

    #[cfg(not(any(feature = "dx12", feature = "metal", feature = "vulkan")))]
    macro_rules! window {
        () => { &mut window };
    }

    #[cfg(any(feature = "dx12", feature = "metal", feature = "vulkan"))]
    macro_rules! window {
        () => { scene.window() };
    }

    let mut events = Events::new(EventSettings::new());
    let mut frame_graph = FrameGraph::new();

    let cube = {
        let program = scene.program_from_vertex_fragment(vertex_shader, fragment_shader);
        let mvp = scene.matrix4_uniform(program, "MVP").unwrap();

        let vertex_array = scene.vertex_array();
        let vertex_buffer = scene.vertex_buffer3(vertex_array, 0, &vertex_buffer_data());
        let _ = scene.color_buffer(vertex_array, 1, &color_buffer_data());

        frame_graph.command_list(vec![
            EnableCullFace,
            CullFaceBack,
            UseProgram(program),
            SetModelViewProjection(mvp),
            DrawTriangles(vertex_array, vertex_buffer.len()),
        ])
    };

    let cubes = frame_graph.command_list(vec![
            Scale([1.0, 1.0, 1.0]),
            Draw(cube),
            Translate([2.5, 0.0, 0.0]),
            RotateAxisDeg(vec3_normalized([1.0, 0.0, 1.0]), 45.0),
            Draw(cube)
        ]);

    let mut first_person = FirstPerson::new(
        [0.5, 0.5, 4.0],
        FirstPersonSettings::keyboard_wasd()
    );

    while let Some(e) = events.next(window!()) {
        first_person.event(&e);

        if let Some(args) = e.render_args() {
            let proj = get_projection(window!());
            scene.projection(proj);
            scene.camera(first_person.camera(args.ext_dt).orthogonal());
            scene.model(mat4_id());
            scene.clear([0.0, 0.0, 0.0, 1.0]);
            scene.scale([1.0, 1.0, 1.0]);

            scene.draw(cubes, &frame_graph);
        }
    }
}

fn get_projection<W: Window>(w: &W) -> Matrix4<f32> {
    let draw_size = w.draw_size();
    CameraPerspective {
        fov: 90.0, near_clip: 0.1, far_clip: 1000.0,
        aspect_ratio: (draw_size.width as f32) / (draw_size.height as f32)
    }.projection()
}

fn vertex_buffer_data() -> Vec<f32> {
    vec![
        -1.0,   -1.0,   -1.0, // triangle 1 : begin
        -1.0,   -1.0,   1.0,
        -1.0,   1.0,    1.0, // triangle 1 : end

        1.0,    1.0,    -1.0, // triangle 2 : begin
        -1.0,   -1.0,   -1.0,
        -1.0,   1.0,    -1.0, // triangle 2 : end

        1.0,    -1.0,   1.0,
        -1.0,   -1.0,   -1.0,
        1.0,    -1.0,   -1.0,

        1.0,    1.0,    -1.0,
        1.0,    -1.0,   -1.0,
        -1.0,   -1.0,   -1.0,

        -1.0,   -1.0,   -1.0,
        -1.0,   1.0,    1.0,
        -1.0,   1.0,    -1.0,

        1.0,    -1.0,   1.0,
        -1.0,   -1.0,   1.0,
        -1.0,   -1.0,   -1.0,

        -1.0,   1.0,    1.0,
        -1.0,   -1.0,   1.0,
        1.0,    -1.0,   1.0,

        1.0,    1.0,    1.0,
        1.0,    -1.0,   -1.0,
        1.0,    1.0,    -1.0,

        1.0,    -1.0,   -1.0,
        1.0,    1.0,    1.0,
        1.0,    -1.0,   1.0,

        1.0,    1.0,    1.0,
        1.0,    1.0,    -1.0,
        -1.0,   1.0,    -1.0,

        1.0,    1.0,    1.0,
        -1.0,   1.0,    -1.0,
        -1.0,   1.0,    1.0,

        1.0,    1.0,    1.0,
        -1.0,   1.0,    1.0,
        1.0,    -1.0,   1.0
    ]
}

fn color_buffer_data() -> Vec<f32> {
    vec![
        0.583,  0.771,  0.014,  1.0,
        0.609,  0.115,  0.436,  1.0,
        0.327,  0.483,  0.844,  1.0,
        0.822,  0.569,  0.201,  1.0,
        0.435,  0.602,  0.223,  1.0,
        0.310,  0.747,  0.185,  1.0,
        0.597,  0.770,  0.761,  1.0,
        0.559,  0.436,  0.730,  1.0,
        0.359,  0.583,  0.152,  1.0,
        0.483,  0.596,  0.789,  1.0,
        0.559,  0.861,  0.639,  1.0,
        0.195,  0.548,  0.859,  1.0,
        0.014,  0.184,  0.576,  1.0,
        0.771,  0.328,  0.970,  1.0,
        0.406,  0.615,  0.116,  1.0,
        0.676,  0.977,  0.133,  1.0,
        0.971,  0.572,  0.833,  1.0,
        0.140,  0.616,  0.489,  1.0,
        0.997,  0.513,  0.064,  1.0,
        0.945,  0.719,  0.592,  1.0,
        0.543,  0.021,  0.978,  1.0,
        0.279,  0.317,  0.505,  1.0,
        0.167,  0.620,  0.077,  1.0,
        0.347,  0.857,  0.137,  1.0,
        0.055,  0.953,  0.042,  1.0,
        0.714,  0.505,  0.345,  1.0,
        0.783,  0.290,  0.734,  1.0,
        0.722,  0.645,  0.174,  1.0,
        0.302,  0.455,  0.848,  1.0,
        0.225,  0.587,  0.040,  1.0,
        0.517,  0.713,  0.338,  1.0,
        0.053,  0.959,  0.120,  1.0,
        0.393,  0.621,  0.362,  1.0,
        0.673,  0.211,  0.457,  1.0,
        0.820,  0.883,  0.371,  1.0,
        0.982,  0.099,  0.879,  1.0,
    ]
}
<|endoftext|>