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

/// Delivery Prototype
///
/// This module is responsible for handling the .delivery/project.toml file
/// that is currently a prototype for local phases executioin. This file can
/// be configurable and it doesn't conflict with the existing config.json

use errors::{DeliveryError, Kind};
use hyper::Client as HyperClient;
use project;
use std::default::Default;
use std::io::Read;
use std::path::PathBuf;
use std::fmt::{Display, Formatter, Error};
use toml;
use types::DeliveryResult;
use utils;
use utils::path_join_many::PathJoinMany;

#[derive(Deserialize, Clone, Debug)]
pub struct ProjectToml {
    pub remote_file: Option<String>,
    pub local_phases: Option<LocalPhases>
}

#[derive(Deserialize, Clone, Debug)]
pub struct LocalPhases {
    pub unit: Option<String>,
    pub lint: Option<String>,
    pub syntax: Option<String>,
    pub provision: Option<String>,
    pub deploy: Option<String>,
    pub smoke: Option<String>,
    pub functional: Option<String>,
    pub cleanup: Option<String>,
}

#[derive(Clone, Debug, PartialEq)]
pub enum Phase {
    Unit,
    Lint,
    Syntax,
    Provision,
    Deploy,
    Smoke,
    Functional,
    Cleanup,
}

#[derive(Clone, Debug)]
pub enum Stage {
    Verify,
    Acceptance,
    All,
}

// Modify how we display this enum so we can print the phases
// with lowercases instead of capital letter, see command/local.rs
impl Display for Phase {
    fn fmt(&self, f:&mut Formatter) -> Result<(), Error> {
        match *self {
            Phase::Unit => write!(f, "unit"),
            Phase::Lint => write!(f, "lint"),
            Phase::Syntax => write!(f, "syntax"),
            Phase::Provision => write!(f, "provision"),
            Phase::Deploy => write!(f, "deploy"),
            Phase::Smoke => write!(f, "smoke"),
            Phase::Functional => write!(f, "funtional"),
            Phase::Cleanup => write!(f, "cleanup"),
        }
    }
}

impl Display for Stage {
    fn fmt(&self, f:&mut Formatter) -> Result<(), Error> {
        match *self {
            Stage::Verify => write!(f, "Verify"),
            Stage::Acceptance => write!(f, "Acceptance"),
            Stage::All => write!(f, "Every"),
        }
    }
}

impl Stage {
    pub fn phases(&self) -> Vec<Phase> {
        match *self {
            Stage::Verify => vec![
                Phase::Lint,
                Phase::Syntax,
                Phase::Unit,
            ],
            Stage::Acceptance => vec![
                Phase::Provision,
                Phase::Deploy,
                Phase::Smoke,
                Phase::Functional,
                Phase::Cleanup,
            ],
            Stage::All => vec![
                Phase::Lint,
                Phase::Syntax,
                Phase::Unit,
                Phase::Provision,
                Phase::Deploy,
                Phase::Smoke,
                Phase::Functional,
                Phase::Cleanup,
            ],
        }
    }
}

impl Default for ProjectToml {
    fn default() -> Self {
        ProjectToml {
            remote_file: None,
            local_phases: Some(LocalPhases {
                unit: None,
                lint: None,
                syntax: None,
                provision: None,
                deploy: None,
                smoke: None,
                functional: None,
                cleanup: None
            })
        }
    }
}

impl ProjectToml {
    pub fn load_toml(remote_toml: Option<&str>) -> DeliveryResult<ProjectToml> {
        if remote_toml.is_some() {
            let url = remote_toml.unwrap().clone();
            return ProjectToml::load_toml_remote(url)
        }

        let path = ProjectToml::toml_file_path(project::project_path()?);
        let project_toml = ProjectToml::load_toml_file(path)?;

        match project_toml.remote_file {
            Some(url) => ProjectToml::load_toml_remote(&url),
            None => Ok(project_toml)
        }
    }

    fn load_toml_file(toml_path: PathBuf) -> DeliveryResult<ProjectToml> {
        debug!("Loading local project.toml from {:?}", toml_path);
        ProjectToml::validate_file(&toml_path)?;
        let toml = utils::read_file(&toml_path)?;
        ProjectToml::parse_config(&toml)
    }

    fn load_toml_remote(toml_url: &str) -> DeliveryResult<ProjectToml> {
        debug!("Loading remote project.toml from {:?}", toml_url);
        let client = HyperClient::new();
        match client.get(toml_url).send() {
            Ok(mut resp) => {
                let mut toml = String::new();
                resp.read_to_string(&mut toml)?;
                debug!("Content Remote project.toml: {:?}", toml);
                ProjectToml::parse_config(&toml)
            },
            Err(e) => {
                Err(DeliveryError{
                    kind: Kind::HttpError(e),
                    detail: None
                })
            }
        }
    }

    pub fn local_phase(&self, phase: Option<Phase>) -> DeliveryResult<Option<String>> {
        if let Some(p) = phase {
            match self.local_phases {
                Some(ref phases) => {
                    match p {
                        Phase::Unit       => Ok(phases.unit.clone()),
                        Phase::Lint       => Ok(phases.lint.clone()),
                        Phase::Syntax     => Ok(phases.syntax.clone()),
                        Phase::Provision  => Ok(phases.provision.clone()),
                        Phase::Deploy     => Ok(phases.deploy.clone()),
                        Phase::Smoke      => Ok(phases.smoke.clone()),
                        Phase::Functional => Ok(phases.functional.clone()),
                        Phase::Cleanup    => Ok(phases.cleanup.clone()),
                    }
                },
                None => Err(DeliveryError{ kind: Kind::LocalPhasesNotFound, detail: None })
            }
        } else {
            Err(DeliveryError{ kind: Kind::PhaseNotFound, detail: None })
       }
    }

    fn toml_file_path(proj_path: PathBuf) -> PathBuf {
        proj_path.join_many(&[".delivery", "project.toml"])
    }

    fn parse_config(toml: &str) -> DeliveryResult<ProjectToml> {
        debug!("Parsing toml: {}", toml);
        Ok(toml::from_str::<ProjectToml>(toml)?)
    }

    fn validate_file(toml_path: &PathBuf) -> DeliveryResult<()> {
        if toml_path.exists() {
            Ok(())
        } else {
            Err(DeliveryError{
                kind: Kind::MissingConfigFile,
                detail: Some(
                    format!("The .delivery/project.toml file was not found.\n\n\
                            You can generate this file using the command:\n\
                            \tchef generate build-cookbook [NAME]")
                )
            })
        }
    }
}

#[cfg(test)]
mod tests {
    pub use super::{ProjectToml, Phase, Stage};

    #[test]
    fn test_project_toml_with_defaults_plus_overrides() {
        let p_toml= ProjectToml::default();
        let unit = "mvn test".to_string();
        p_toml.local_phases.map(|mut phases| {
            // default is empty phases
            assert_eq!(None, phases.unit);
            // But if we fill them in
            phases.unit = Some(unit.clone());
            assert_eq!(unit, phases.unit.unwrap());
        });
    }

    #[test]
    fn test_parse_config_that_could_be_empty() {
        let  toml = r#"
# Now everything is optional so we can have an empty file
"#;
        let project_toml = ProjectToml::parse_config(toml);
        assert!(project_toml.is_ok());
    }

    #[test]
    fn test_local_phase_accessor() {
        let p_toml= ProjectToml::default();
        // Test failure - When None it must throw an Err()
        assert!(p_toml.local_phase(None).is_err());
        // If one works all of them does :)
        assert_eq!(p_toml.local_phase(Some(Phase::Unit)).unwrap(),
                   p_toml.local_phases.unwrap().unit);
    }

    #[test]
    fn test_stages_phases() {
        let verify = Stage::Verify;
        assert!(verify.phases().contains(&Phase::Syntax));
        assert!(verify.phases().contains(&Phase::Unit));
        assert!(verify.phases().contains(&Phase::Lint));

        let acceptance = Stage::Acceptance;
        assert!(acceptance.phases().contains(&Phase::Smoke));
        assert!(acceptance.phases().contains(&Phase::Deploy));
        assert!(acceptance.phases().contains(&Phase::Provision));
        assert!(acceptance.phases().contains(&Phase::Functional));
        assert!(acceptance.phases().contains(&Phase::Cleanup));

        let all = Stage::All;
        assert!(all.phases().contains(&Phase::Syntax));
        assert!(all.phases().contains(&Phase::Unit));
        assert!(all.phases().contains(&Phase::Lint));
        assert!(all.phases().contains(&Phase::Smoke));
        assert!(all.phases().contains(&Phase::Deploy));
        assert!(all.phases().contains(&Phase::Provision));
        assert!(all.phases().contains(&Phase::Functional));
        assert!(all.phases().contains(&Phase::Cleanup));
    }

    mod when_project_toml {
        pub use super::{ProjectToml, Phase};
        mod is_well_configured {
            fn toml<'a>() -> &'a str {
                r#"
                # This file is coming from chefdk (chef generate build-cookbook)
                [local_phases]
                unit = "rspec spec/"
                lint = "cookstyle"
                syntax = "foodcritic . --exclude spec -f any"
                provision = "chef exec kitchen create"
                deploy = "chef exec kitchen converge"
                smoke = "chef exec kitchen verify"
                cleanup = "chef exec kitchen destroy"
                "#
            }

            #[test]
            fn parse_project_config() {
                let project_toml = super::ProjectToml::parse_config(toml());
                match project_toml {
                    Ok(p_toml) => {
                        p_toml.local_phases.map(|phases| {
                            assert_eq!("rspec spec/".to_string(), phases.unit.unwrap());
                            assert_eq!("cookstyle".to_string(), phases.lint.unwrap());
                            assert_eq!("foodcritic . --exclude spec -f any".to_string(),
                                        phases.syntax.unwrap());
                            assert_eq!("chef exec kitchen create".to_string(),
                                        phases.provision.unwrap());
                            assert_eq!("chef exec kitchen converge".to_string(),
                                        phases.deploy.unwrap());
                            assert_eq!("chef exec kitchen verify".to_string(),
                                        phases.smoke.unwrap());
                            assert_eq!(None, phases.functional);
                            assert_eq!("chef exec kitchen destroy".to_string(),
                                        phases.cleanup.unwrap());
                        });
                    },
                    Err(e) => {
                        panic!("Failed to parse: {:?}", e.detail)
                    }
                }
            }
        }

        mod is_partially_configured {
            fn toml<'a>() -> &'a str {
                 r#"
                # Here we just define three phases
                [local_phases]
                unit = "rspec spec/"
                lint = "cookstyle"
                syntax = "something"
                "#
            }

            #[test]
            fn parse_project_config() {
                let project_toml = super::ProjectToml::parse_config(toml());
                match project_toml {
                    Ok(p_toml) => {
                        p_toml.local_phases.map(|phases| {
                            assert_eq!("rspec spec/".to_string(), phases.unit.unwrap());
                            assert_eq!("cookstyle".to_string(), phases.lint.unwrap());
                            assert_eq!("something".to_string(), phases.syntax.unwrap());
                            // The rest should be defined as None
                            // but we shouldn't fail parsing the file
                            assert!(phases.provision.is_none());
                            assert!(phases.deploy.is_none());
                            assert!(phases.smoke.is_none());
                            assert!(phases.functional.is_none());
                            assert!(phases.cleanup.is_none());
                        });
                    },
                    Err(e) => {
                        panic!("Failed to parse: {:?}", e.detail)
                    }
                }
            }
        }

        mod points_to_a_remote_file {
            fn toml<'a>() -> &'a str {
                r#"
                # No [local_phases]
                # Just point to a remote_file
                remote_file = "url"
                "#
            }

            #[test]
            fn parse_project_config() {
                let project_toml = super::ProjectToml::parse_config(toml());
                match project_toml {
                    Ok(p_toml) => {
                        assert_eq!("url".to_string(), p_toml.remote_file.unwrap());
                        // local_phases should be defined as None
                        assert!(p_toml.local_phases.is_none());
                    },
                    Err(e) => {
                        panic!("Failed to parse: {:?}", e.detail)
                    }
                }
            }
        }

        mod is_misconfigured {
        }
    }

    mod toml_file_path {
        pub use super::{ProjectToml};
        use std::path::{PathBuf};

        #[test]
        fn returns_path_to_toml() {
            let project_path = PathBuf::from("/tmp");
            let expected = PathBuf::from("/tmp/.delivery/project.toml");
            let actual = ProjectToml::toml_file_path(project_path);
            assert_eq!(expected, actual);
        }
    }

    mod load_toml_file {
        pub use super::{ProjectToml};
        use std::path::{PathBuf};
        use std::fs::File;
        use std::io::Write;

        #[test]
        fn returns_toml_from_path() {
            let path = PathBuf::from("/tmp/local.toml");
            let toml = r#"
[local_phases]
unit = "echo local-unit"
lint = "echo local-lint"
syntax = "echo local-syntax"
provision = "echo local-provision"
deploy = "echo local-deploy"
smoke = "echo local-smoke"
functional = "echo local-functional"
cleanup = "echo local-cleanup"
"#;

            let mut file = File::create(&path).expect("Unable to create local toml file");
            file.write_all(toml.as_bytes()).expect("Unable to write local toml file");

            let local_toml = ProjectToml::load_toml_file(path).unwrap();
            assert_eq!("echo local-unit".to_string(), local_toml.local_phases.unwrap().unit.unwrap());
        }
    }

    mod load_toml_remote {
        pub use super::{ProjectToml};
        use mockito::mock;

        #[test]
        fn returns_toml_from_url() {
            let url = "http://0.0.0.0:1234/toml-url";
            let toml = r#"
[local_phases]
unit = "echo remote-unit"
lint = "echo remote-lint"
syntax = "echo remote-syntax"
provision = "echo remote-provision"
deploy = "echo remote-deploy"
smoke = "echo remote-smoke"
functional = "echo remote-functional"
cleanup = "echo remote-cleanup"
"#;

            mock("GET", "/toml-url")
                .with_status(200)
                .with_header("content-type", "text/plain")
                .with_body(toml)
                .create();

            let remote_toml = ProjectToml::load_toml_remote(url).unwrap();
            assert_eq!("echo remote-unit".to_string(),
                       remote_toml.local_phases.unwrap().unit.unwrap());
        }
    }
}
<commit_msg>Fix typo in functional phase<commit_after>//
// Copyright:: Copyright (c) 2016 Chef Software, Inc.
// License:: Apache License, Version 2.0
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

/// Delivery Prototype
///
/// This module is responsible for handling the .delivery/project.toml file
/// that is currently a prototype for local phases executioin. This file can
/// be configurable and it doesn't conflict with the existing config.json

use errors::{DeliveryError, Kind};
use hyper::Client as HyperClient;
use project;
use std::default::Default;
use std::io::Read;
use std::path::PathBuf;
use std::fmt::{Display, Formatter, Error};
use toml;
use types::DeliveryResult;
use utils;
use utils::path_join_many::PathJoinMany;

#[derive(Deserialize, Clone, Debug)]
pub struct ProjectToml {
    pub remote_file: Option<String>,
    pub local_phases: Option<LocalPhases>
}

#[derive(Deserialize, Clone, Debug)]
pub struct LocalPhases {
    pub unit: Option<String>,
    pub lint: Option<String>,
    pub syntax: Option<String>,
    pub provision: Option<String>,
    pub deploy: Option<String>,
    pub smoke: Option<String>,
    pub functional: Option<String>,
    pub cleanup: Option<String>,
}

#[derive(Clone, Debug, PartialEq)]
pub enum Phase {
    Unit,
    Lint,
    Syntax,
    Provision,
    Deploy,
    Smoke,
    Functional,
    Cleanup,
}

#[derive(Clone, Debug)]
pub enum Stage {
    Verify,
    Acceptance,
    All,
}

// Modify how we display this enum so we can print the phases
// with lowercases instead of capital letter, see command/local.rs
impl Display for Phase {
    fn fmt(&self, f:&mut Formatter) -> Result<(), Error> {
        match *self {
            Phase::Unit => write!(f, "unit"),
            Phase::Lint => write!(f, "lint"),
            Phase::Syntax => write!(f, "syntax"),
            Phase::Provision => write!(f, "provision"),
            Phase::Deploy => write!(f, "deploy"),
            Phase::Smoke => write!(f, "smoke"),
            Phase::Functional => write!(f, "functional"),
            Phase::Cleanup => write!(f, "cleanup"),
        }
    }
}

impl Display for Stage {
    fn fmt(&self, f:&mut Formatter) -> Result<(), Error> {
        match *self {
            Stage::Verify => write!(f, "Verify"),
            Stage::Acceptance => write!(f, "Acceptance"),
            Stage::All => write!(f, "Every"),
        }
    }
}

impl Stage {
    pub fn phases(&self) -> Vec<Phase> {
        match *self {
            Stage::Verify => vec![
                Phase::Lint,
                Phase::Syntax,
                Phase::Unit,
            ],
            Stage::Acceptance => vec![
                Phase::Provision,
                Phase::Deploy,
                Phase::Smoke,
                Phase::Functional,
                Phase::Cleanup,
            ],
            Stage::All => vec![
                Phase::Lint,
                Phase::Syntax,
                Phase::Unit,
                Phase::Provision,
                Phase::Deploy,
                Phase::Smoke,
                Phase::Functional,
                Phase::Cleanup,
            ],
        }
    }
}

impl Default for ProjectToml {
    fn default() -> Self {
        ProjectToml {
            remote_file: None,
            local_phases: Some(LocalPhases {
                unit: None,
                lint: None,
                syntax: None,
                provision: None,
                deploy: None,
                smoke: None,
                functional: None,
                cleanup: None
            })
        }
    }
}

impl ProjectToml {
    pub fn load_toml(remote_toml: Option<&str>) -> DeliveryResult<ProjectToml> {
        if remote_toml.is_some() {
            let url = remote_toml.unwrap().clone();
            return ProjectToml::load_toml_remote(url)
        }

        let path = ProjectToml::toml_file_path(project::project_path()?);
        let project_toml = ProjectToml::load_toml_file(path)?;

        match project_toml.remote_file {
            Some(url) => ProjectToml::load_toml_remote(&url),
            None => Ok(project_toml)
        }
    }

    fn load_toml_file(toml_path: PathBuf) -> DeliveryResult<ProjectToml> {
        debug!("Loading local project.toml from {:?}", toml_path);
        ProjectToml::validate_file(&toml_path)?;
        let toml = utils::read_file(&toml_path)?;
        ProjectToml::parse_config(&toml)
    }

    fn load_toml_remote(toml_url: &str) -> DeliveryResult<ProjectToml> {
        debug!("Loading remote project.toml from {:?}", toml_url);
        let client = HyperClient::new();
        match client.get(toml_url).send() {
            Ok(mut resp) => {
                let mut toml = String::new();
                resp.read_to_string(&mut toml)?;
                debug!("Content Remote project.toml: {:?}", toml);
                ProjectToml::parse_config(&toml)
            },
            Err(e) => {
                Err(DeliveryError{
                    kind: Kind::HttpError(e),
                    detail: None
                })
            }
        }
    }

    pub fn local_phase(&self, phase: Option<Phase>) -> DeliveryResult<Option<String>> {
        if let Some(p) = phase {
            match self.local_phases {
                Some(ref phases) => {
                    match p {
                        Phase::Unit       => Ok(phases.unit.clone()),
                        Phase::Lint       => Ok(phases.lint.clone()),
                        Phase::Syntax     => Ok(phases.syntax.clone()),
                        Phase::Provision  => Ok(phases.provision.clone()),
                        Phase::Deploy     => Ok(phases.deploy.clone()),
                        Phase::Smoke      => Ok(phases.smoke.clone()),
                        Phase::Functional => Ok(phases.functional.clone()),
                        Phase::Cleanup    => Ok(phases.cleanup.clone()),
                    }
                },
                None => Err(DeliveryError{ kind: Kind::LocalPhasesNotFound, detail: None })
            }
        } else {
            Err(DeliveryError{ kind: Kind::PhaseNotFound, detail: None })
       }
    }

    fn toml_file_path(proj_path: PathBuf) -> PathBuf {
        proj_path.join_many(&[".delivery", "project.toml"])
    }

    fn parse_config(toml: &str) -> DeliveryResult<ProjectToml> {
        debug!("Parsing toml: {}", toml);
        Ok(toml::from_str::<ProjectToml>(toml)?)
    }

    fn validate_file(toml_path: &PathBuf) -> DeliveryResult<()> {
        if toml_path.exists() {
            Ok(())
        } else {
            Err(DeliveryError{
                kind: Kind::MissingConfigFile,
                detail: Some(
                    format!("The .delivery/project.toml file was not found.\n\n\
                            You can generate this file using the command:\n\
                            \tchef generate build-cookbook [NAME]")
                )
            })
        }
    }
}

#[cfg(test)]
mod tests {
    pub use super::{ProjectToml, Phase, Stage};

    #[test]
    fn test_project_toml_with_defaults_plus_overrides() {
        let p_toml= ProjectToml::default();
        let unit = "mvn test".to_string();
        p_toml.local_phases.map(|mut phases| {
            // default is empty phases
            assert_eq!(None, phases.unit);
            // But if we fill them in
            phases.unit = Some(unit.clone());
            assert_eq!(unit, phases.unit.unwrap());
        });
    }

    #[test]
    fn test_parse_config_that_could_be_empty() {
        let  toml = r#"
# Now everything is optional so we can have an empty file
"#;
        let project_toml = ProjectToml::parse_config(toml);
        assert!(project_toml.is_ok());
    }

    #[test]
    fn test_local_phase_accessor() {
        let p_toml= ProjectToml::default();
        // Test failure - When None it must throw an Err()
        assert!(p_toml.local_phase(None).is_err());
        // If one works all of them does :)
        assert_eq!(p_toml.local_phase(Some(Phase::Unit)).unwrap(),
                   p_toml.local_phases.unwrap().unit);
    }

    #[test]
    fn test_stages_phases() {
        let verify = Stage::Verify;
        assert!(verify.phases().contains(&Phase::Syntax));
        assert!(verify.phases().contains(&Phase::Unit));
        assert!(verify.phases().contains(&Phase::Lint));

        let acceptance = Stage::Acceptance;
        assert!(acceptance.phases().contains(&Phase::Smoke));
        assert!(acceptance.phases().contains(&Phase::Deploy));
        assert!(acceptance.phases().contains(&Phase::Provision));
        assert!(acceptance.phases().contains(&Phase::Functional));
        assert!(acceptance.phases().contains(&Phase::Cleanup));

        let all = Stage::All;
        assert!(all.phases().contains(&Phase::Syntax));
        assert!(all.phases().contains(&Phase::Unit));
        assert!(all.phases().contains(&Phase::Lint));
        assert!(all.phases().contains(&Phase::Smoke));
        assert!(all.phases().contains(&Phase::Deploy));
        assert!(all.phases().contains(&Phase::Provision));
        assert!(all.phases().contains(&Phase::Functional));
        assert!(all.phases().contains(&Phase::Cleanup));
    }

    mod when_project_toml {
        pub use super::{ProjectToml, Phase};
        mod is_well_configured {
            fn toml<'a>() -> &'a str {
                r#"
                # This file is coming from chefdk (chef generate build-cookbook)
                [local_phases]
                unit = "rspec spec/"
                lint = "cookstyle"
                syntax = "foodcritic . --exclude spec -f any"
                provision = "chef exec kitchen create"
                deploy = "chef exec kitchen converge"
                smoke = "chef exec kitchen verify"
                cleanup = "chef exec kitchen destroy"
                "#
            }

            #[test]
            fn parse_project_config() {
                let project_toml = super::ProjectToml::parse_config(toml());
                match project_toml {
                    Ok(p_toml) => {
                        p_toml.local_phases.map(|phases| {
                            assert_eq!("rspec spec/".to_string(), phases.unit.unwrap());
                            assert_eq!("cookstyle".to_string(), phases.lint.unwrap());
                            assert_eq!("foodcritic . --exclude spec -f any".to_string(),
                                        phases.syntax.unwrap());
                            assert_eq!("chef exec kitchen create".to_string(),
                                        phases.provision.unwrap());
                            assert_eq!("chef exec kitchen converge".to_string(),
                                        phases.deploy.unwrap());
                            assert_eq!("chef exec kitchen verify".to_string(),
                                        phases.smoke.unwrap());
                            assert_eq!(None, phases.functional);
                            assert_eq!("chef exec kitchen destroy".to_string(),
                                        phases.cleanup.unwrap());
                        });
                    },
                    Err(e) => {
                        panic!("Failed to parse: {:?}", e.detail)
                    }
                }
            }
        }

        mod is_partially_configured {
            fn toml<'a>() -> &'a str {
                 r#"
                # Here we just define three phases
                [local_phases]
                unit = "rspec spec/"
                lint = "cookstyle"
                syntax = "something"
                "#
            }

            #[test]
            fn parse_project_config() {
                let project_toml = super::ProjectToml::parse_config(toml());
                match project_toml {
                    Ok(p_toml) => {
                        p_toml.local_phases.map(|phases| {
                            assert_eq!("rspec spec/".to_string(), phases.unit.unwrap());
                            assert_eq!("cookstyle".to_string(), phases.lint.unwrap());
                            assert_eq!("something".to_string(), phases.syntax.unwrap());
                            // The rest should be defined as None
                            // but we shouldn't fail parsing the file
                            assert!(phases.provision.is_none());
                            assert!(phases.deploy.is_none());
                            assert!(phases.smoke.is_none());
                            assert!(phases.functional.is_none());
                            assert!(phases.cleanup.is_none());
                        });
                    },
                    Err(e) => {
                        panic!("Failed to parse: {:?}", e.detail)
                    }
                }
            }
        }

        mod points_to_a_remote_file {
            fn toml<'a>() -> &'a str {
                r#"
                # No [local_phases]
                # Just point to a remote_file
                remote_file = "url"
                "#
            }

            #[test]
            fn parse_project_config() {
                let project_toml = super::ProjectToml::parse_config(toml());
                match project_toml {
                    Ok(p_toml) => {
                        assert_eq!("url".to_string(), p_toml.remote_file.unwrap());
                        // local_phases should be defined as None
                        assert!(p_toml.local_phases.is_none());
                    },
                    Err(e) => {
                        panic!("Failed to parse: {:?}", e.detail)
                    }
                }
            }
        }

        mod is_misconfigured {
        }
    }

    mod toml_file_path {
        pub use super::{ProjectToml};
        use std::path::{PathBuf};

        #[test]
        fn returns_path_to_toml() {
            let project_path = PathBuf::from("/tmp");
            let expected = PathBuf::from("/tmp/.delivery/project.toml");
            let actual = ProjectToml::toml_file_path(project_path);
            assert_eq!(expected, actual);
        }
    }

    mod load_toml_file {
        pub use super::{ProjectToml};
        use std::path::{PathBuf};
        use std::fs::File;
        use std::io::Write;

        #[test]
        fn returns_toml_from_path() {
            let path = PathBuf::from("/tmp/local.toml");
            let toml = r#"
[local_phases]
unit = "echo local-unit"
lint = "echo local-lint"
syntax = "echo local-syntax"
provision = "echo local-provision"
deploy = "echo local-deploy"
smoke = "echo local-smoke"
functional = "echo local-functional"
cleanup = "echo local-cleanup"
"#;

            let mut file = File::create(&path).expect("Unable to create local toml file");
            file.write_all(toml.as_bytes()).expect("Unable to write local toml file");

            let local_toml = ProjectToml::load_toml_file(path).unwrap();
            assert_eq!("echo local-unit".to_string(), local_toml.local_phases.unwrap().unit.unwrap());
        }
    }

    mod load_toml_remote {
        pub use super::{ProjectToml};
        use mockito::mock;

        #[test]
        fn returns_toml_from_url() {
            let url = "http://0.0.0.0:1234/toml-url";
            let toml = r#"
[local_phases]
unit = "echo remote-unit"
lint = "echo remote-lint"
syntax = "echo remote-syntax"
provision = "echo remote-provision"
deploy = "echo remote-deploy"
smoke = "echo remote-smoke"
functional = "echo remote-functional"
cleanup = "echo remote-cleanup"
"#;

            mock("GET", "/toml-url")
                .with_status(200)
                .with_header("content-type", "text/plain")
                .with_body(toml)
                .create();

            let remote_toml = ProjectToml::load_toml_remote(url).unwrap();
            assert_eq!("echo remote-unit".to_string(),
                       remote_toml.local_phases.unwrap().unit.unwrap());
        }
    }
}
<|endoftext|>


<commit_before>use std::collections::{HashMap, HashSet};

type FeatureName = String;

enum Data {
    String(String),
    RangeData((i64, i64)),
}

struct Entry {
    features: HashSet<FeatureName>,
    data: HashMap<FeatureName, Data>,
}

struct EntrySet(Vec<Entry>);

struct DTree {
    thisFork: FeatureName,
    children: Vec<Box<DTree>>,
}

impl DTree {
    fn new() -> Self {
        DTree {
            thisFork: String::new(),
            children: vec![],
        }
    }
}

type DTreeAlgo = fn(EntrySet, HashSet<FeatureName>, FeatureName) -> DTree;

//only support string now
fn gini(datas: EntrySet, feature: FeatureName, target: FeatureName) -> DTree {
    let mut record = HashMap::new();

    for entry in datas {
        if let split_ = record
            .get_mut(entry.data.get(feature).unwrap())
            .unwrap_or(0)
        {}
    }

    //first, get all value set
    //spilt values
}

fn c4_5(datas: EntrySet, rest_features: HashSet<FeatureName>, target: FeatureName) -> DTree {
    DTree::new()
}

fn new_tree(datas: EntrySet, target: FeatureName, algo: DTreeAlgo) -> DTree {
    DTree::new()
}
<commit_msg>a bit<commit_after>use std::collections::{HashMap, HashSet};

type FeatureName = String;

#[derive(PartialEq, Eq, Hash)]
enum Data {
    String(String),
    RangeData((i64, i64)),
}

#[derive(PartialEq, Eq)]
struct Entry {
    features: HashSet<FeatureName>,
    data: HashMap<FeatureName, Data>,
}

struct EntrySet(Vec<Entry>);

struct DTree {
    thisFork: FeatureName,
    children: Vec<Box<DTree>>,
}

impl DTree {
    fn new() -> Self {
        DTree {
            thisFork: String::new(),
            children: vec![],
        }
    }
}

type DTreeAlgo = fn(EntrySet, HashSet<FeatureName>, FeatureName) -> DTree;

//only support string now
fn gini(datas: EntrySet, feature: FeatureName, target: FeatureName) -> DTree {
    let mut record = HashMap::new();

    for entry in datas {
        if let split_ = record
            .get_mut(entry.data.get(feature).unwrap())
            .unwrap_or(0)
        {}
    }

    //first, get all value set
    //spilt values
}

fn c4_5(datas: EntrySet, rest_features: HashSet<FeatureName>, target: FeatureName) -> DTree {
    DTree::new()
}

fn new_tree(datas: EntrySet, target: FeatureName, algo: DTreeAlgo) -> DTree {
    DTree::new()
}
<|endoftext|>


<commit_before>//! ARMv6K Nintendo 3DS C Newlib definitions

pub type c_char = u8;
pub type c_long = i32;
pub type c_ulong = u32;

pub type wchar_t = ::c_uint;

pub type in_port_t = ::c_ushort;
pub type u_register_t = ::c_uint;
pub type u_char = ::c_uchar;
pub type u_short = ::c_ushort;
pub type u_int = ::c_uint;
pub type u_long = c_ulong;
pub type ushort = ::c_ushort;
pub type uint = ::c_uint;
pub type ulong = c_ulong;
pub type clock_t = c_ulong;
pub type daddr_t = c_long;
pub type caddr_t = *mut c_char;
pub type sbintime_t = ::c_longlong;

// External implementations are needed to use networking and threading.
s! {
    pub struct sockaddr {
        pub sa_family: ::sa_family_t,
        pub sa_data: [::c_char; 26usize],
    }

    pub struct sockaddr_storage {
        pub ss_family: ::sa_family_t,
        pub __ss_padding: [::c_char; 26usize],
    }

    pub struct sockaddr_in {
        pub sin_family: ::sa_family_t,
        pub sin_port: in_port_t,
        pub sin_addr: ::in_addr,
    }

    pub struct sockaddr_in6 {
        pub sin6_family: ::sa_family_t,
        pub sin6_port: ::in_port_t,
        pub sin6_flowinfo: u32,
        pub sin6_addr: ::in6_addr,
        pub sin6_scope_id: u32,
    }

    pub struct sockaddr_un {
        pub sun_len: ::c_uchar,
        pub sun_family: ::sa_family_t,
        pub sun_path: [::c_char; 104usize],
    }
}

pub const SIGEV_NONE: ::c_int = 1;
pub const SIGEV_SIGNAL: ::c_int = 2;
pub const SIGEV_THREAD: ::c_int = 3;
pub const SA_NOCLDSTOP: ::c_int = 1;
pub const MINSIGSTKSZ: ::c_int = 2048;
pub const SIGSTKSZ: ::c_int = 8192;
pub const SS_ONSTACK: ::c_int = 1;
pub const SS_DISABLE: ::c_int = 2;
pub const SIG_SETMASK: ::c_int = 0;
pub const SIG_BLOCK: ::c_int = 1;
pub const SIG_UNBLOCK: ::c_int = 2;
pub const SIGHUP: ::c_int = 1;
pub const SIGINT: ::c_int = 2;
pub const SIGQUIT: ::c_int = 3;
pub const SIGILL: ::c_int = 4;
pub const SIGTRAP: ::c_int = 5;
pub const SIGABRT: ::c_int = 6;
pub const SIGEMT: ::c_int = 7;
pub const SIGFPE: ::c_int = 8;
pub const SIGKILL: ::c_int = 9;
pub const SIGBUS: ::c_int = 10;
pub const SIGSEGV: ::c_int = 11;
pub const SIGSYS: ::c_int = 12;
pub const SIGPIPE: ::c_int = 13;
pub const SIGALRM: ::c_int = 14;
pub const SIGTERM: ::c_int = 15;
pub const SIGURG: ::c_int = 16;
pub const SIGSTOP: ::c_int = 17;
pub const SIGTSTP: ::c_int = 18;
pub const SIGCONT: ::c_int = 19;
pub const SIGCHLD: ::c_int = 20;
pub const SIGCLD: ::c_int = 20;
pub const SIGTTIN: ::c_int = 21;
pub const SIGTTOU: ::c_int = 22;
pub const SIGIO: ::c_int = 23;
pub const SIGPOLL: ::c_int = 23;
pub const SIGXCPU: ::c_int = 24;
pub const SIGXFSZ: ::c_int = 25;
pub const SIGVTALRM: ::c_int = 26;
pub const SIGPROF: ::c_int = 27;
pub const SIGWINCH: ::c_int = 28;
pub const SIGLOST: ::c_int = 29;
pub const SIGUSR1: ::c_int = 30;
pub const SIGUSR2: ::c_int = 31;
pub const NSIG: ::c_int = 32;
pub const CLOCK_ENABLED: ::c_uint = 1;
pub const CLOCK_DISABLED: ::c_uint = 0;
pub const CLOCK_ALLOWED: ::c_uint = 1;
pub const CLOCK_DISALLOWED: ::c_uint = 0;
pub const TIMER_ABSTIME: ::c_uint = 4;
pub const SOL_SOCKET: ::c_int = 65535;
pub const MSG_OOB: ::c_int = 1;
pub const MSG_PEEK: ::c_int = 2;
pub const MSG_DONTWAIT: ::c_int = 4;
pub const MSG_DONTROUTE: ::c_int = 0;
pub const MSG_WAITALL: ::c_int = 0;
pub const MSG_MORE: ::c_int = 0;
pub const MSG_NOSIGNAL: ::c_int = 0;
pub const SOL_CONFIG: ::c_uint = 65534;

pub const _SC_PAGESIZE: ::c_int = 8;
pub const _SC_GETPW_R_SIZE_MAX: ::c_int = 51;

pub const PTHREAD_STACK_MIN: ::size_t = 4096;
pub const WNOHANG: ::c_int = 1;

pub const POLLIN: ::c_short = 0x0001;
pub const POLLPRI: ::c_short = 0x0002;
pub const POLLOUT: ::c_short = 0x0004;
pub const POLLRDNORM: ::c_short = 0x0040;
pub const POLLWRNORM: ::c_short = POLLOUT;
pub const POLLRDBAND: ::c_short = 0x0080;
pub const POLLWRBAND: ::c_short = 0x0100;
pub const POLLERR: ::c_short = 0x0008;
pub const POLLHUP: ::c_short = 0x0010;
pub const POLLNVAL: ::c_short = 0x0020;

pub const EAI_AGAIN: ::c_int = 2;
pub const EAI_BADFLAGS: ::c_int = 3;
pub const EAI_FAIL: ::c_int = 4;
pub const EAI_SERVICE: ::c_int = 9;
pub const EAI_SYSTEM: ::c_int = 11;
pub const EAI_BADHINTS: ::c_int = 12;
pub const EAI_PROTOCOL: ::c_int = 13;
pub const EAI_OVERFLOW: ::c_int = 14;
pub const EAI_MAX: ::c_int = 15;

pub const AF_UNIX: ::c_int = 1;
pub const AF_INET6: ::c_int = 23;

pub const FIONBIO: ::c_ulong = 1;

pub const RTLD_DEFAULT: *mut ::c_void = 0 as *mut ::c_void;

// Horizon OS works doesn't or can't hold any of this information
safe_f! {
    pub {const} fn WIFSTOPPED(_status: ::c_int) -> bool {
        false
    }

    pub {const} fn WSTOPSIG(_status: ::c_int) -> ::c_int {
        0
    }

    pub {const} fn WIFCONTINUED(_status: ::c_int) -> bool {
        true
    }

    pub {const} fn WIFSIGNALED(_status: ::c_int) -> bool {
        false
    }

    pub {const} fn WTERMSIG(_status: ::c_int) -> ::c_int {
        0
    }

    pub {const} fn WIFEXITED(_status: ::c_int) -> bool {
        true
    }

    pub {const} fn WEXITSTATUS(_status: ::c_int) -> ::c_int {
        0
    }

    pub {const} fn WCOREDUMP(_status: ::c_int) -> bool {
        false
    }
}

extern "C" {
    pub fn pthread_create(
        native: *mut ::pthread_t,
        attr: *const ::pthread_attr_t,
        f: extern "C" fn(_: *mut ::c_void) -> *mut ::c_void,
        value: *mut ::c_void,
    ) -> ::c_int;

    pub fn gethostid() -> ::c_long;
}
<commit_msg>Add some extensions to pthread for armv6k-nintendo-3ds<commit_after>//! ARMv6K Nintendo 3DS C Newlib definitions

pub type c_char = u8;
pub type c_long = i32;
pub type c_ulong = u32;

pub type wchar_t = ::c_uint;

pub type in_port_t = ::c_ushort;
pub type u_register_t = ::c_uint;
pub type u_char = ::c_uchar;
pub type u_short = ::c_ushort;
pub type u_int = ::c_uint;
pub type u_long = c_ulong;
pub type ushort = ::c_ushort;
pub type uint = ::c_uint;
pub type ulong = c_ulong;
pub type clock_t = c_ulong;
pub type daddr_t = c_long;
pub type caddr_t = *mut c_char;
pub type sbintime_t = ::c_longlong;

// External implementations are needed to use networking and threading.
s! {
    pub struct sockaddr {
        pub sa_family: ::sa_family_t,
        pub sa_data: [::c_char; 26usize],
    }

    pub struct sockaddr_storage {
        pub ss_family: ::sa_family_t,
        pub __ss_padding: [::c_char; 26usize],
    }

    pub struct sockaddr_in {
        pub sin_family: ::sa_family_t,
        pub sin_port: in_port_t,
        pub sin_addr: ::in_addr,
    }

    pub struct sockaddr_in6 {
        pub sin6_family: ::sa_family_t,
        pub sin6_port: ::in_port_t,
        pub sin6_flowinfo: u32,
        pub sin6_addr: ::in6_addr,
        pub sin6_scope_id: u32,
    }

    pub struct sockaddr_un {
        pub sun_len: ::c_uchar,
        pub sun_family: ::sa_family_t,
        pub sun_path: [::c_char; 104usize],
    }
}

pub const SIGEV_NONE: ::c_int = 1;
pub const SIGEV_SIGNAL: ::c_int = 2;
pub const SIGEV_THREAD: ::c_int = 3;
pub const SA_NOCLDSTOP: ::c_int = 1;
pub const MINSIGSTKSZ: ::c_int = 2048;
pub const SIGSTKSZ: ::c_int = 8192;
pub const SS_ONSTACK: ::c_int = 1;
pub const SS_DISABLE: ::c_int = 2;
pub const SIG_SETMASK: ::c_int = 0;
pub const SIG_BLOCK: ::c_int = 1;
pub const SIG_UNBLOCK: ::c_int = 2;
pub const SIGHUP: ::c_int = 1;
pub const SIGINT: ::c_int = 2;
pub const SIGQUIT: ::c_int = 3;
pub const SIGILL: ::c_int = 4;
pub const SIGTRAP: ::c_int = 5;
pub const SIGABRT: ::c_int = 6;
pub const SIGEMT: ::c_int = 7;
pub const SIGFPE: ::c_int = 8;
pub const SIGKILL: ::c_int = 9;
pub const SIGBUS: ::c_int = 10;
pub const SIGSEGV: ::c_int = 11;
pub const SIGSYS: ::c_int = 12;
pub const SIGPIPE: ::c_int = 13;
pub const SIGALRM: ::c_int = 14;
pub const SIGTERM: ::c_int = 15;
pub const SIGURG: ::c_int = 16;
pub const SIGSTOP: ::c_int = 17;
pub const SIGTSTP: ::c_int = 18;
pub const SIGCONT: ::c_int = 19;
pub const SIGCHLD: ::c_int = 20;
pub const SIGCLD: ::c_int = 20;
pub const SIGTTIN: ::c_int = 21;
pub const SIGTTOU: ::c_int = 22;
pub const SIGIO: ::c_int = 23;
pub const SIGPOLL: ::c_int = 23;
pub const SIGXCPU: ::c_int = 24;
pub const SIGXFSZ: ::c_int = 25;
pub const SIGVTALRM: ::c_int = 26;
pub const SIGPROF: ::c_int = 27;
pub const SIGWINCH: ::c_int = 28;
pub const SIGLOST: ::c_int = 29;
pub const SIGUSR1: ::c_int = 30;
pub const SIGUSR2: ::c_int = 31;
pub const NSIG: ::c_int = 32;
pub const CLOCK_ENABLED: ::c_uint = 1;
pub const CLOCK_DISABLED: ::c_uint = 0;
pub const CLOCK_ALLOWED: ::c_uint = 1;
pub const CLOCK_DISALLOWED: ::c_uint = 0;
pub const TIMER_ABSTIME: ::c_uint = 4;
pub const SOL_SOCKET: ::c_int = 65535;
pub const MSG_OOB: ::c_int = 1;
pub const MSG_PEEK: ::c_int = 2;
pub const MSG_DONTWAIT: ::c_int = 4;
pub const MSG_DONTROUTE: ::c_int = 0;
pub const MSG_WAITALL: ::c_int = 0;
pub const MSG_MORE: ::c_int = 0;
pub const MSG_NOSIGNAL: ::c_int = 0;
pub const SOL_CONFIG: ::c_uint = 65534;

pub const _SC_PAGESIZE: ::c_int = 8;
pub const _SC_GETPW_R_SIZE_MAX: ::c_int = 51;

pub const PTHREAD_STACK_MIN: ::size_t = 4096;
pub const WNOHANG: ::c_int = 1;

pub const POLLIN: ::c_short = 0x0001;
pub const POLLPRI: ::c_short = 0x0002;
pub const POLLOUT: ::c_short = 0x0004;
pub const POLLRDNORM: ::c_short = 0x0040;
pub const POLLWRNORM: ::c_short = POLLOUT;
pub const POLLRDBAND: ::c_short = 0x0080;
pub const POLLWRBAND: ::c_short = 0x0100;
pub const POLLERR: ::c_short = 0x0008;
pub const POLLHUP: ::c_short = 0x0010;
pub const POLLNVAL: ::c_short = 0x0020;

pub const EAI_AGAIN: ::c_int = 2;
pub const EAI_BADFLAGS: ::c_int = 3;
pub const EAI_FAIL: ::c_int = 4;
pub const EAI_SERVICE: ::c_int = 9;
pub const EAI_SYSTEM: ::c_int = 11;
pub const EAI_BADHINTS: ::c_int = 12;
pub const EAI_PROTOCOL: ::c_int = 13;
pub const EAI_OVERFLOW: ::c_int = 14;
pub const EAI_MAX: ::c_int = 15;

pub const AF_UNIX: ::c_int = 1;
pub const AF_INET6: ::c_int = 23;

pub const FIONBIO: ::c_ulong = 1;

pub const RTLD_DEFAULT: *mut ::c_void = 0 as *mut ::c_void;

// Horizon OS works doesn't or can't hold any of this information
safe_f! {
    pub {const} fn WIFSTOPPED(_status: ::c_int) -> bool {
        false
    }

    pub {const} fn WSTOPSIG(_status: ::c_int) -> ::c_int {
        0
    }

    pub {const} fn WIFCONTINUED(_status: ::c_int) -> bool {
        true
    }

    pub {const} fn WIFSIGNALED(_status: ::c_int) -> bool {
        false
    }

    pub {const} fn WTERMSIG(_status: ::c_int) -> ::c_int {
        0
    }

    pub {const} fn WIFEXITED(_status: ::c_int) -> bool {
        true
    }

    pub {const} fn WEXITSTATUS(_status: ::c_int) -> ::c_int {
        0
    }

    pub {const} fn WCOREDUMP(_status: ::c_int) -> bool {
        false
    }
}

extern "C" {
    pub fn pthread_create(
        native: *mut ::pthread_t,
        attr: *const ::pthread_attr_t,
        f: extern "C" fn(_: *mut ::c_void) -> *mut ::c_void,
        value: *mut ::c_void,
    ) -> ::c_int;

    pub fn pthread_attr_setpriority(attr: *mut ::pthread_attr_t, priority: ::c_int) -> ::c_int;

    pub fn pthread_attr_setaffinity(attr: *mut ::pthread_attr_t, affinity: ::c_int) -> ::c_int;

    pub fn pthread_getpriority() -> ::c_int;

    pub fn gethostid() -> ::c_long;
}
<|endoftext|>


<commit_before>extern crate lrs;

#[test]
fn parse_Num() {

}

#[test]
fn parse_Sym() {

}

#[test]
fn parse_Atom() {

}

#[test]
fn parse_List() {

}

#[test]
fn parse_Expr() {

}

#[test]
fn parse_Node() {

}

#[test]
fn parse_Lang() {

}
<commit_msg>Change parsing tests to snake case<commit_after>extern crate lrs;

#[test]
fn parse_num() {

}

#[test]
fn parse_sym() {

}

#[test]
fn parse_atom() {

}

#[test]
fn parse_list() {

}

#[test]
fn parse_expr() {

}

#[test]
fn parse_node() {

}

#[test]
fn parse_lang() {

}
<|endoftext|>


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

use crate::function_wrapper::{FunctionWrapper, FunctionWrapperPayload};
use itertools::Itertools;
use std::collections::HashSet;

/// Instructions for new C++ which we need to generate.
pub(crate) enum AdditionalNeed {
    MakeStringConstructor,
    FunctionWrapper(Box<FunctionWrapper>),
}

#[derive(Ord, PartialOrd, Eq, PartialEq, Clone, Hash)]
struct Header {
    name: &'static str,
    system: bool,
}

impl Header {
    fn system(name: &'static str) -> Self {
        Header { name, system: true }
    }

    fn user(name: &'static str) -> Self {
        Header {
            name,
            system: false,
        }
    }

    fn include_stmt(&self) -> String {
        if self.system {
            format!("#include <{}>", self.name)
        } else {
            format!("#include \"{}\"", self.name)
        }
    }
}

struct AdditionalFunction {
    declaration: String,
    definition: String,
    headers: Vec<Header>,
}

/// Details of additional generated C++.
pub(crate) struct AdditionalCpp {
    pub(crate) declarations: String,
    pub(crate) definitions: String,
}

/// Generates additional C++ glue functions needed by autocxx.
/// In some ways it would be preferable to be able to pass snippets
/// of C++ through to `cxx` for inclusion in the C++ file which it
/// generates, and perhaps we'll explore that in future. But for now,
/// autocxx generates its own _additional_ C++ files which therefore
/// need to be built and included in linking procedures.
pub(crate) struct AdditionalCppGenerator {
    additional_functions: Vec<AdditionalFunction>,
    inclusions: String,
}

impl AdditionalCppGenerator {
    pub(crate) fn new(inclusions: String) -> Self {
        AdditionalCppGenerator {
            additional_functions: Vec::new(),
            inclusions,
        }
    }

    pub(crate) fn add_needs(&mut self, additions: Vec<AdditionalNeed>) {
        for need in additions {
            match need {
                AdditionalNeed::MakeStringConstructor => self.generate_string_constructor(),
                AdditionalNeed::FunctionWrapper(by_value_wrapper) => {
                    self.generate_by_value_wrapper(*by_value_wrapper)
                }
            }
        }
    }

    pub(crate) fn generate(&self) -> Option<AdditionalCpp> {
        if self.additional_functions.is_empty() {
            None
        } else {
            let headers: HashSet<Header> = self
                .additional_functions
                .iter()
                .map(|x| x.headers.iter().cloned())
                .flatten()
                .collect();
            let headers = headers.iter().map(|x| x.include_stmt()).join("\n");
            let declarations = self.concat_additional_items(|x| &x.declaration);
            let declarations = format!("{}\n{}\n{}", headers, self.inclusions, declarations);
            let definitions = self.concat_additional_items(|x| &x.definition);
            let definitions = format!("#include \"autocxxgen.h\"\n{}", definitions);
            Some(AdditionalCpp {
                declarations,
                definitions,
            })
        }
    }

    fn concat_additional_items<F>(&self, field_access: F) -> String
    where
        F: FnMut(&AdditionalFunction) -> &str,
    {
        let mut s = self
            .additional_functions
            .iter()
            .map(field_access)
            .collect::<Vec<&str>>()
            .join("\n");
        s.push('\n');
        s
    }

    fn generate_string_constructor(&mut self) {
        let declaration = "std::unique_ptr<std::string> make_string(::rust::Str str)";
        let definition = format!(
            "{} {{ return std::make_unique<std::string>(std::string(str)); }}",
            declaration
        );
        let declaration = format!("{};", declaration);
        self.additional_functions.push(AdditionalFunction {
            declaration,
            definition,
            headers: vec![
                Header::system("memory"),
                Header::system("string"),
                Header::user("cxx.h"),
            ],
        })
    }

    fn generate_by_value_wrapper(&mut self, details: FunctionWrapper) {
        // Even if the original function call is in a namespace,
        // we generate this wrapper in the global namespace.
        // We could easily do this the other way round, and when
        // cxx::bridge comes to support nested namespace mods then
        // we wil wish to do that to avoid name conflicts. However,
        // at the moment this is simpler because it avoids us having
        // to generate namespace blocks in the generated C++.
        let is_a_method = details.is_a_method;
        let name = details.wrapper_function_name;
        let get_arg_name = |counter: usize| -> String {
            if is_a_method && counter == 0 {
                // For method calls that we generate, the first
                // argument name needs to be such that we recognize
                // it as a method in the second invocation of
                // bridge_converter after it's flowed again through
                // bindgen.
                "autocxx_gen_this".to_string()
            } else {
                format!("arg{}", counter)
            }
        };
        let args = details
            .argument_conversion
            .iter()
            .enumerate()
            .map(|(counter, ty)| format!("{} {}", ty.unconverted_type(), get_arg_name(counter)))
            .join(", ");
        let ret_type = details
            .return_conversion
            .as_ref()
            .map_or("void".to_string(), |x| x.converted_type());
        let declaration = format!("{} {}({})", ret_type, name, args);
        let mut arg_list = details
            .argument_conversion
            .iter()
            .enumerate()
            .map(|(counter, conv)| conv.conversion(&get_arg_name(counter)));
        let receiver = if is_a_method { arg_list.next() } else { None };
        let arg_list = arg_list.join(", ");
        let mut underlying_function_call = match details.payload {
            FunctionWrapperPayload::Constructor => arg_list,
            FunctionWrapperPayload::FunctionCall(ns, id) => match receiver {
                Some(receiver) => format!("{}.{}({})", receiver, id.to_string(), arg_list),
                None => {
                    let underlying_function_call = ns
                        .into_iter()
                        .cloned()
                        .chain(std::iter::once(id.to_string()))
                        .join("::");
                    format!("{}({})", underlying_function_call, arg_list)
                }
            },
        };
        if let Some(ret) = details.return_conversion {
            underlying_function_call =
                format!("return {}", ret.conversion(&underlying_function_call));
        };
        let definition = format!("{} {{ {}; }}", declaration, underlying_function_call,);
        let declaration = format!("{};", declaration);
        self.additional_functions.push(AdditionalFunction {
            declaration,
            definition,
            headers: vec![Header::system("memory")],
        })
    }
}
<commit_msg>Comment.<commit_after>// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use crate::function_wrapper::{FunctionWrapper, FunctionWrapperPayload};
use itertools::Itertools;
use std::collections::HashSet;

/// Instructions for new C++ which we need to generate.
pub(crate) enum AdditionalNeed {
    MakeStringConstructor,
    FunctionWrapper(Box<FunctionWrapper>),
}

#[derive(Ord, PartialOrd, Eq, PartialEq, Clone, Hash)]
struct Header {
    name: &'static str,
    system: bool,
}

impl Header {
    fn system(name: &'static str) -> Self {
        Header { name, system: true }
    }

    fn user(name: &'static str) -> Self {
        Header {
            name,
            system: false,
        }
    }

    fn include_stmt(&self) -> String {
        if self.system {
            format!("#include <{}>", self.name)
        } else {
            format!("#include \"{}\"", self.name)
        }
    }
}

struct AdditionalFunction {
    declaration: String,
    definition: String,
    headers: Vec<Header>,
}

/// Details of additional generated C++.
pub(crate) struct AdditionalCpp {
    pub(crate) declarations: String,
    pub(crate) definitions: String,
}

/// Generates additional C++ glue functions needed by autocxx.
/// In some ways it would be preferable to be able to pass snippets
/// of C++ through to `cxx` for inclusion in the C++ file which it
/// generates, and perhaps we'll explore that in future. But for now,
/// autocxx generates its own _additional_ C++ files which therefore
/// need to be built and included in linking procedures.
pub(crate) struct AdditionalCppGenerator {
    additional_functions: Vec<AdditionalFunction>,
    inclusions: String,
}

impl AdditionalCppGenerator {
    pub(crate) fn new(inclusions: String) -> Self {
        AdditionalCppGenerator {
            additional_functions: Vec::new(),
            inclusions,
        }
    }

    pub(crate) fn add_needs(&mut self, additions: Vec<AdditionalNeed>) {
        for need in additions {
            match need {
                AdditionalNeed::MakeStringConstructor => self.generate_string_constructor(),
                AdditionalNeed::FunctionWrapper(by_value_wrapper) => {
                    self.generate_by_value_wrapper(*by_value_wrapper)
                }
            }
        }
    }

    pub(crate) fn generate(&self) -> Option<AdditionalCpp> {
        if self.additional_functions.is_empty() {
            None
        } else {
            let headers: HashSet<Header> = self
                .additional_functions
                .iter()
                .map(|x| x.headers.iter().cloned())
                .flatten()
                .collect();
            let headers = headers.iter().map(|x| x.include_stmt()).join("\n");
            let declarations = self.concat_additional_items(|x| &x.declaration);
            let declarations = format!("{}\n{}\n{}", headers, self.inclusions, declarations);
            let definitions = self.concat_additional_items(|x| &x.definition);
            let definitions = format!("#include \"autocxxgen.h\"\n{}", definitions);
            Some(AdditionalCpp {
                declarations,
                definitions,
            })
        }
    }

    fn concat_additional_items<F>(&self, field_access: F) -> String
    where
        F: FnMut(&AdditionalFunction) -> &str,
    {
        let mut s = self
            .additional_functions
            .iter()
            .map(field_access)
            .collect::<Vec<&str>>()
            .join("\n");
        s.push('\n');
        s
    }

    fn generate_string_constructor(&mut self) {
        let declaration = "std::unique_ptr<std::string> make_string(::rust::Str str)";
        let definition = format!(
            "{} {{ return std::make_unique<std::string>(std::string(str)); }}",
            declaration
        );
        let declaration = format!("{};", declaration);
        self.additional_functions.push(AdditionalFunction {
            declaration,
            definition,
            headers: vec![
                Header::system("memory"),
                Header::system("string"),
                Header::user("cxx.h"),
            ],
        })
    }

    fn generate_by_value_wrapper(&mut self, details: FunctionWrapper) {
        // Even if the original function call is in a namespace,
        // we generate this wrapper in the global namespace.
        // We could easily do this the other way round, and when
        // cxx::bridge comes to support nested namespace mods then
        // we wil wish to do that to avoid name conflicts. However,
        // at the moment this is simpler because it avoids us having
        // to generate namespace blocks in the generated C++.
        let is_a_method = details.is_a_method;
        let name = details.wrapper_function_name;
        let get_arg_name = |counter: usize| -> String {
            if is_a_method && counter == 0 {
                // For method calls that we generate, the first
                // argument name needs to be such that we recognize
                // it as a method in the second invocation of
                // bridge_converter after it's flowed again through
                // bindgen.
                // TODO this may not be the case any longer. We
                // may be able to remove this.
                "autocxx_gen_this".to_string()
            } else {
                format!("arg{}", counter)
            }
        };
        let args = details
            .argument_conversion
            .iter()
            .enumerate()
            .map(|(counter, ty)| format!("{} {}", ty.unconverted_type(), get_arg_name(counter)))
            .join(", ");
        let ret_type = details
            .return_conversion
            .as_ref()
            .map_or("void".to_string(), |x| x.converted_type());
        let declaration = format!("{} {}({})", ret_type, name, args);
        let mut arg_list = details
            .argument_conversion
            .iter()
            .enumerate()
            .map(|(counter, conv)| conv.conversion(&get_arg_name(counter)));
        let receiver = if is_a_method { arg_list.next() } else { None };
        let arg_list = arg_list.join(", ");
        let mut underlying_function_call = match details.payload {
            FunctionWrapperPayload::Constructor => arg_list,
            FunctionWrapperPayload::FunctionCall(ns, id) => match receiver {
                Some(receiver) => format!("{}.{}({})", receiver, id.to_string(), arg_list),
                None => {
                    let underlying_function_call = ns
                        .into_iter()
                        .cloned()
                        .chain(std::iter::once(id.to_string()))
                        .join("::");
                    format!("{}({})", underlying_function_call, arg_list)
                }
            },
        };
        if let Some(ret) = details.return_conversion {
            underlying_function_call =
                format!("return {}", ret.conversion(&underlying_function_call));
        };
        let definition = format!("{} {{ {}; }}", declaration, underlying_function_call,);
        let declaration = format!("{};", declaration);
        self.additional_functions.push(AdditionalFunction {
            declaration,
            definition,
            headers: vec![Header::system("memory")],
        })
    }
}
<|endoftext|>


<commit_before>#![warn(clippy::while_let_loop, clippy::empty_loop, clippy::while_let_on_iterator)]
#![allow(dead_code, clippy::never_loop, unused, clippy::cognitive_complexity)]

fn main() {
    let y = Some(true);
    loop {
        if let Some(_x) = y {
            let _v = 1;
        } else {
            break;
        }
    }
    loop {
        // no error, break is not in else clause
        if let Some(_x) = y {
            let _v = 1;
        }
        break;
    }
    loop {
        match y {
            Some(_x) => true,
            None => break,
        };
    }
    loop {
        let x = match y {
            Some(x) => x,
            None => break,
        };
        let _x = x;
        let _str = "foo";
    }
    loop {
        let x = match y {
            Some(x) => x,
            None => break,
        };
        {
            let _a = "bar";
        };
        {
            let _b = "foobar";
        }
    }
    loop {
        // no error, else branch does something other than break
        match y {
            Some(_x) => true,
            _ => {
                let _z = 1;
                break;
            },
        };
    }
    while let Some(x) = y {
        // no error, obviously
        println!("{}", x);
    }

    // #675, this used to have a wrong suggestion
    loop {
        let (e, l) = match "".split_whitespace().next() {
            Some(word) => (word.is_empty(), word.len()),
            None => break,
        };

        let _ = (e, l);
    }

    let mut iter = 1..20;
    while let Option::Some(x) = iter.next() {
        println!("{}", x);
    }

    let mut iter = 1..20;
    while let Some(x) = iter.next() {
        println!("{}", x);
    }

    let mut iter = 1..20;
    while let Some(_) = iter.next() {}

    let mut iter = 1..20;
    while let None = iter.next() {} // this is fine (if nonsensical)

    let mut iter = 1..20;
    if let Some(x) = iter.next() {
        // also fine
        println!("{}", x)
    }

    // the following shouldn't warn because it can't be written with a for loop
    let mut iter = 1u32..20;
    while let Some(x) = iter.next() {
        println!("next: {:?}", iter.next())
    }

    // neither can this
    let mut iter = 1u32..20;
    while let Some(x) = iter.next() {
        println!("next: {:?}", iter.next());
    }

    // or this
    let mut iter = 1u32..20;
    while let Some(x) = iter.next() {
        break;
    }
    println!("Remaining iter {:?}", iter);

    // or this
    let mut iter = 1u32..20;
    while let Some(x) = iter.next() {
        iter = 1..20;
    }
}

// regression test (#360)
// this should not panic
// it's ok if further iterations of the lint
// cause this function to trigger it
fn no_panic<T>(slice: &[T]) {
    let mut iter = slice.iter();
    loop {
        let _ = match iter.next() {
            Some(ele) => ele,
            None => break,
        };
        loop {}
    }
}

fn issue1017() {
    let r: Result<u32, u32> = Ok(42);
    let mut len = 1337;

    loop {
        match r {
            Err(_) => len = 0,
            Ok(length) => {
                len = length;
                break;
            },
        }
    }
}

// Issue #1188
fn refutable() {
    let a = [42, 1337];
    let mut b = a.iter();

    // consume all the 42s
    while let Some(&42) = b.next() {}

    let a = [(1, 2, 3)];
    let mut b = a.iter();

    while let Some(&(1, 2, 3)) = b.next() {}

    let a = [Some(42)];
    let mut b = a.iter();

    while let Some(&None) = b.next() {}

    /* This gives “refutable pattern in `for` loop binding: `&_` not covered”
    for &42 in b {}
    for &(1, 2, 3) in b {}
    for &Option::None in b.next() {}
    // */
}

fn nested_loops() {
    let a = [42, 1337];
    let mut y = a.iter();
    loop {
        // x is reused, so don't lint here
        while let Some(v) = y.next() {}
    }

    let mut y = a.iter();
    for _ in 0..2 {
        while let Some(v) = y.next() {
            // y is reused, don't lint
        }
    }

    loop {
        let mut y = a.iter();
        while let Some(v) = y.next() {
            // use a for loop here
        }
    }
}

fn issue1948() {
    // should not trigger clippy::while_let_loop lint because break passes an expression
    let a = Some(10);
    let b = loop {
        if let Some(c) = a {
            break Some(c);
        } else {
            break None;
        }
    };
}

fn issue1121() {
    use std::collections::HashSet;
    let mut values = HashSet::new();
    values.insert(1);

    while let Some(&value) = values.iter().next() {
        values.remove(&value);
    }
}

fn issue2965() {
    // This should not cause an ICE and suggest:
    //
    // for _ in values.iter() {}
    //
    use std::collections::HashSet;
    let mut values = HashSet::new();
    values.insert(1);

    while let Some(..) = values.iter().next() {
        values.remove(&1);
    }
}

fn issue3670() {
    let array = [Some(0), None, Some(1)];
    let mut iter = array.iter();

    while let Some(elem) = iter.next() {
        let _ = elem.or_else(|| *iter.next()?);
    }
}
<commit_msg>Add test for #771.<commit_after>#![warn(clippy::while_let_loop, clippy::empty_loop, clippy::while_let_on_iterator)]
#![allow(dead_code, clippy::never_loop, unused, clippy::cognitive_complexity)]

fn main() {
    let y = Some(true);
    loop {
        if let Some(_x) = y {
            let _v = 1;
        } else {
            break;
        }
    }
    loop {
        // no error, break is not in else clause
        if let Some(_x) = y {
            let _v = 1;
        }
        break;
    }
    loop {
        match y {
            Some(_x) => true,
            None => break,
        };
    }
    loop {
        let x = match y {
            Some(x) => x,
            None => break,
        };
        let _x = x;
        let _str = "foo";
    }
    loop {
        let x = match y {
            Some(x) => x,
            None => break,
        };
        {
            let _a = "bar";
        };
        {
            let _b = "foobar";
        }
    }
    loop {
        // no error, else branch does something other than break
        match y {
            Some(_x) => true,
            _ => {
                let _z = 1;
                break;
            },
        };
    }
    while let Some(x) = y {
        // no error, obviously
        println!("{}", x);
    }

    // #675, this used to have a wrong suggestion
    loop {
        let (e, l) = match "".split_whitespace().next() {
            Some(word) => (word.is_empty(), word.len()),
            None => break,
        };

        let _ = (e, l);
    }

    let mut iter = 1..20;
    while let Option::Some(x) = iter.next() {
        println!("{}", x);
    }

    let mut iter = 1..20;
    while let Some(x) = iter.next() {
        println!("{}", x);
    }

    let mut iter = 1..20;
    while let Some(_) = iter.next() {}

    let mut iter = 1..20;
    while let None = iter.next() {} // this is fine (if nonsensical)

    let mut iter = 1..20;
    if let Some(x) = iter.next() {
        // also fine
        println!("{}", x)
    }

    // the following shouldn't warn because it can't be written with a for loop
    let mut iter = 1u32..20;
    while let Some(x) = iter.next() {
        println!("next: {:?}", iter.next())
    }

    // neither can this
    let mut iter = 1u32..20;
    while let Some(x) = iter.next() {
        println!("next: {:?}", iter.next());
    }

    // or this
    let mut iter = 1u32..20;
    while let Some(x) = iter.next() {
        break;
    }
    println!("Remaining iter {:?}", iter);

    // or this
    let mut iter = 1u32..20;
    while let Some(x) = iter.next() {
        iter = 1..20;
    }
}

// regression test (#360)
// this should not panic
// it's ok if further iterations of the lint
// cause this function to trigger it
fn no_panic<T>(slice: &[T]) {
    let mut iter = slice.iter();
    loop {
        let _ = match iter.next() {
            Some(ele) => ele,
            None => break,
        };
        loop {}
    }
}

fn issue1017() {
    let r: Result<u32, u32> = Ok(42);
    let mut len = 1337;

    loop {
        match r {
            Err(_) => len = 0,
            Ok(length) => {
                len = length;
                break;
            },
        }
    }
}

// Issue #1188
fn refutable() {
    let a = [42, 1337];
    let mut b = a.iter();

    // consume all the 42s
    while let Some(&42) = b.next() {}

    let a = [(1, 2, 3)];
    let mut b = a.iter();

    while let Some(&(1, 2, 3)) = b.next() {}

    let a = [Some(42)];
    let mut b = a.iter();

    while let Some(&None) = b.next() {}

    /* This gives “refutable pattern in `for` loop binding: `&_` not covered”
    for &42 in b {}
    for &(1, 2, 3) in b {}
    for &Option::None in b.next() {}
    // */
}

fn nested_loops() {
    let a = [42, 1337];
    let mut y = a.iter();
    loop {
        // x is reused, so don't lint here
        while let Some(v) = y.next() {}
    }

    let mut y = a.iter();
    for _ in 0..2 {
        while let Some(v) = y.next() {
            // y is reused, don't lint
        }
    }

    loop {
        let mut y = a.iter();
        while let Some(v) = y.next() {
            // use a for loop here
        }
    }
}

fn issue1948() {
    // should not trigger clippy::while_let_loop lint because break passes an expression
    let a = Some(10);
    let b = loop {
        if let Some(c) = a {
            break Some(c);
        } else {
            break None;
        }
    };
}

fn issue1121() {
    use std::collections::HashSet;
    let mut values = HashSet::new();
    values.insert(1);

    while let Some(&value) = values.iter().next() {
        values.remove(&value);
    }
}

fn issue2965() {
    // This should not cause an ICE and suggest:
    //
    // for _ in values.iter() {}
    //
    use std::collections::HashSet;
    let mut values = HashSet::new();
    values.insert(1);

    while let Some(..) = values.iter().next() {
        values.remove(&1);
    }
}

fn issue3670() {
    let array = [Some(0), None, Some(1)];
    let mut iter = array.iter();

    while let Some(elem) = iter.next() {
        let _ = elem.or_else(|| *iter.next()?);
    }
}

fn issue771() {
    let mut a = 100;
    let b = Some(true);
    loop {
        if a > 10 {
            break;
        }

        match b {
            Some(_) => a = 0,
            None => break,
        }
    }
}
<|endoftext|>


<commit_before>#![allow(dead_code)]
use std::io::Result as IoResult;
use std::io::Error  as IoError;
use std::convert::AsRef;
use super::imagemagick_commands;
use image_tools::image_ops::{ElementaryPageOperations, Pixels, Direction};
use image_tools::image_ops::ImageResolution;
use image_tools::image_ops::RunOperation;
use image_tools::image_ops::OperationResults;

type ImageMagickArg = String;
type FileName = String;
type FilePath = String;

#[derive(Clone)]
enum ImageMagickOpType {
    Identify,
    IdentifyVerbose,
    Mogrify,
    Convert,
    NoOperation,
}

trait AsShellCommand {
    fn as_shell_command(&self) -> String;
}

impl AsShellCommand for ImageMagickOpType {
    fn as_shell_command(&self) -> String {
        match *self {
            ImageMagickOpType::Identify        => String::from("identify"),
            ImageMagickOpType::IdentifyVerbose => String::from("identify"),
            ImageMagickOpType::Mogrify         => String::from("mogrify"),
            ImageMagickOpType::Convert         => String::from("convert"),
            ImageMagickOpType::NoOperation     => String::from("echo"),
        }
    }
}

#[derive(Clone)]
struct ImageMagickArgs {
    file_path: FilePath,
    file_name: FileName,
    img_args:  Vec<ImageMagickArg>,
}

impl ImageMagickArgs {
    fn new(file_path: FilePath, file_name: FileName, img_args: &Vec<ImageMagickArg>) -> ImageMagickArgs {
        ImageMagickArgs {
            file_path: file_path,
            file_name: file_name,
            img_args:  img_args.clone(),
        }
    }
}

#[derive(Clone)]
pub struct ElementaryImageMagickOperation {
    op:   ImageMagickOpType,
    args: ImageMagickArgs,
}

impl ElementaryImageMagickOperation {
    fn new(op: ImageMagickOpType, args: ImageMagickArgs) -> ElementaryImageMagickOperation {
        ElementaryImageMagickOperation {
            op:   op,
            args: args,
        }
    }

    fn arg(&mut self, arg: ImageMagickArg) {
        self.args.img_args.push(arg);
    }

    fn args(&mut self, args: &[ImageMagickArg]) {
        for arg in args {
            self.args.img_args.push(arg.clone());
        }
    }

    fn run_operation(&self) -> IoResult<String> {
        match self.op {
            ImageMagickOpType::Identify        => {
                imagemagick_commands::imagemagick_identify_default(&self.args.file_path)
            }
            ImageMagickOpType::IdentifyVerbose => {
                imagemagick_commands::imagemagick_identify_verbose(&self.args.file_path)
            }
            ImageMagickOpType::Convert         => {
                imagemagick_commands::imagemagick_convert(&self.args.file_path, &self.args.img_args)
            }
            ImageMagickOpType::Mogrify         => {
                imagemagick_commands::imagemagick_mogrify(&self.args.file_path, &self.args.img_args)
            }
            ImageMagickOpType::NoOperation     => {
                imagemagick_commands::imagemagick_no_operation()
            }
        }
    }
}

#[derive(Clone)]
pub struct ImageMagickOperation {
    ops: Vec<ElementaryImageMagickOperation>,
}

impl ImageMagickOperation {
    fn new() -> ImageMagickOperation {
        ImageMagickOperation {
            ops: Vec::new(),
        }
    }

    fn add_op(&mut self, op: ElementaryImageMagickOperation)  {
        self.ops.push(op);
    }

    fn add_ops(&mut self, ops: &[ElementaryImageMagickOperation]) {
        for op in ops.iter() {
            self.ops.push(op.clone());
        }
    }
}

// This implementation will be the generator for the sequence of
// ImageMagick commands to implement each operation.
impl ElementaryPageOperations for ImageMagickOperation {

    fn identify(file_name: FileName, file_path: FilePath) -> ImageMagickOperation {
        // Identify presesntly generates an identify command without the -verbose flag.
        // This may change in the future where we use verbose as the default.
        let args = ImageMagickArgs::new(file_name, file_path, &Vec::new());
        let elem_op = ElementaryImageMagickOperation::new(ImageMagickOpType::Identify, args);
        let mut op = ImageMagickOperation::new();
        op.add_op(elem_op);

        op

    }

    fn rescale(amount: Pixels, dir: Direction) -> ImageMagickOperation {
        unimplemented!();
    }

    fn expand_left_edge(amount: Pixels)        -> ImageMagickOperation {
        unimplemented!();
    }

    fn expand_right_edge(amount: Pixels)       -> ImageMagickOperation {
        unimplemented!();
    }

    fn expand_top_edge(amount: Pixels)         -> ImageMagickOperation {
        unimplemented!();
    }

    fn expand_bottom_edge(amount: Pixels)      -> ImageMagickOperation {
        unimplemented!();
    }

    fn trim_left_edge(amount: Pixels)          -> ImageMagickOperation {
        unimplemented!();
    }

    fn trim_right_edge(amount: Pixels)         -> ImageMagickOperation {
        unimplemented!();
    }

    fn trim_top_edge(amount: Pixels)           -> ImageMagickOperation {
        unimplemented!();
    }

    fn trim_bottom_edge(amount: Pixels)        -> ImageMagickOperation {
        unimplemented!();
    }

    fn set_resolution(res: ImageResolution)    -> ImageMagickOperation {
        unimplemented!();
    }
    fn no_operation()                          -> ImageMagickOperation {
        unimplemented!();
    }
}

impl AsRef<[ElementaryImageMagickOperation]> for ImageMagickOperation {
    fn as_ref(&self) -> &[ElementaryImageMagickOperation] {
        self.ops.as_ref()
    }
}

impl RunOperation for ImageMagickOperation {
    fn run_operation(op: ImageMagickOperation) -> OperationResults {
        let mut results = OperationResults::new();

        for action in op.ops {
            let mut result = Vec::new();
            result.push(action.run_operation());
            results.append(&mut OperationResults::from(&mut result));
        }

        results
    }
}
<commit_msg>Add debug instances.<commit_after>#![allow(dead_code)]
use std::io::Result as IoResult;
use std::io::Error  as IoError;
use std::convert::AsRef;
use super::imagemagick_commands;
use image_tools::image_ops::{ElementaryPageOperations, Pixels, Direction};
use image_tools::image_ops::ImageResolution;
use image_tools::image_ops::RunOperation;
use image_tools::image_ops::OperationResults;

type ImageMagickArg = String;
type FileName = String;
type FilePath = String;

#[derive(Clone, Debug)]
enum ImageMagickOpType {
    Identify,
    IdentifyVerbose,
    Mogrify,
    Convert,
    NoOperation,
}

trait AsShellCommand {
    fn as_shell_command(&self) -> String;
}

impl AsShellCommand for ImageMagickOpType {
    fn as_shell_command(&self) -> String {
        match *self {
            ImageMagickOpType::Identify        => String::from("identify"),
            ImageMagickOpType::IdentifyVerbose => String::from("identify"),
            ImageMagickOpType::Mogrify         => String::from("mogrify"),
            ImageMagickOpType::Convert         => String::from("convert"),
            ImageMagickOpType::NoOperation     => String::from("echo"),
        }
    }
}

#[derive(Clone, Debug)]
struct ImageMagickArgs {
    file_path: FilePath,
    file_name: FileName,
    img_args:  Vec<ImageMagickArg>,
}

impl ImageMagickArgs {
    fn new(file_path: FilePath, file_name: FileName, img_args: &Vec<ImageMagickArg>) -> ImageMagickArgs {
        ImageMagickArgs {
            file_path: file_path,
            file_name: file_name,
            img_args:  img_args.clone(),
        }
    }
}

#[derive(Clone, Debug)]
pub struct ElementaryImageMagickOperation {
    op:   ImageMagickOpType,
    args: ImageMagickArgs,
}

impl ElementaryImageMagickOperation {
    fn new(op: ImageMagickOpType, args: ImageMagickArgs) -> ElementaryImageMagickOperation {
        ElementaryImageMagickOperation {
            op:   op,
            args: args,
        }
    }

    fn arg(&mut self, arg: ImageMagickArg) {
        self.args.img_args.push(arg);
    }

    fn args(&mut self, args: &[ImageMagickArg]) {
        for arg in args {
            self.args.img_args.push(arg.clone());
        }
    }

    fn run_operation(&self) -> IoResult<String> {
        match self.op {
            ImageMagickOpType::Identify        => {
                imagemagick_commands::imagemagick_identify_default(&self.args.file_path)
            }
            ImageMagickOpType::IdentifyVerbose => {
                imagemagick_commands::imagemagick_identify_verbose(&self.args.file_path)
            }
            ImageMagickOpType::Convert         => {
                imagemagick_commands::imagemagick_convert(&self.args.file_path, &self.args.img_args)
            }
            ImageMagickOpType::Mogrify         => {
                imagemagick_commands::imagemagick_mogrify(&self.args.file_path, &self.args.img_args)
            }
            ImageMagickOpType::NoOperation     => {
                imagemagick_commands::imagemagick_no_operation()
            }
        }
    }
}

#[derive(Clone, Debug)]
pub struct ImageMagickOperation {
    ops: Vec<ElementaryImageMagickOperation>,
}

impl ImageMagickOperation {
    fn new() -> ImageMagickOperation {
        ImageMagickOperation {
            ops: Vec::new(),
        }
    }

    fn add_op(&mut self, op: ElementaryImageMagickOperation)  {
        self.ops.push(op);
    }

    fn add_ops(&mut self, ops: &[ElementaryImageMagickOperation]) {
        for op in ops.iter() {
            self.ops.push(op.clone());
        }
    }
}

// This implementation will be the generator for the sequence of
// ImageMagick commands to implement each operation.
impl ElementaryPageOperations for ImageMagickOperation {

    fn identify(file_name: FileName, file_path: FilePath) -> ImageMagickOperation {
        // Identify presesntly generates an identify command without the -verbose flag.
        // This may change in the future where we use verbose as the default.
        let args = ImageMagickArgs::new(file_name, file_path, &Vec::new());
        let elem_op = ElementaryImageMagickOperation::new(ImageMagickOpType::Identify, args);
        let mut op = ImageMagickOperation::new();
        op.add_op(elem_op);

        op

    }

    fn rescale(amount: Pixels, dir: Direction) -> ImageMagickOperation {
        unimplemented!();
    }

    fn expand_left_edge(amount: Pixels)        -> ImageMagickOperation {
        unimplemented!();
    }

    fn expand_right_edge(amount: Pixels)       -> ImageMagickOperation {
        unimplemented!();
    }

    fn expand_top_edge(amount: Pixels)         -> ImageMagickOperation {
        unimplemented!();
    }

    fn expand_bottom_edge(amount: Pixels)      -> ImageMagickOperation {
        unimplemented!();
    }

    fn trim_left_edge(amount: Pixels)          -> ImageMagickOperation {
        unimplemented!();
    }

    fn trim_right_edge(amount: Pixels)         -> ImageMagickOperation {
        unimplemented!();
    }

    fn trim_top_edge(amount: Pixels)           -> ImageMagickOperation {
        unimplemented!();
    }

    fn trim_bottom_edge(amount: Pixels)        -> ImageMagickOperation {
        unimplemented!();
    }

    fn set_resolution(res: ImageResolution)    -> ImageMagickOperation {
        unimplemented!();
    }
    fn no_operation()                          -> ImageMagickOperation {
        unimplemented!();
    }
}

impl AsRef<[ElementaryImageMagickOperation]> for ImageMagickOperation {
    fn as_ref(&self) -> &[ElementaryImageMagickOperation] {
        self.ops.as_ref()
    }
}

impl RunOperation for ImageMagickOperation {
    fn run_operation(op: ImageMagickOperation) -> OperationResults {
        let mut results = OperationResults::new();

        for action in op.ops {
            let mut result = Vec::new();
            result.push(action.run_operation());
            results.append(&mut OperationResults::from(&mut result));
        }

        results
    }
}
<|endoftext|>


<commit_before>use std::mem;
use std::usize;

use decoders::{Orientation, RawImage};
use imageops::*;

#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
pub struct OpTransform {
  orientation: Orientation,
}

impl OpTransform {
  pub fn new(img: &RawImage) -> OpTransform {
    OpTransform{
      orientation: img.orientation,
    }
  }
}

impl<'a> ImageOp<'a> for OpTransform {
  fn name(&self) -> &str {"transform"}
  fn run(&self, pipeline: &mut PipelineGlobals, inid: BufHash, outid: BufHash) {
    if self.orientation == Orientation::Normal || self.orientation == Orientation::Unknown {
      pipeline.cache.alias(inid, outid);
    } else {
      let buf = pipeline.cache.get(&inid).unwrap();
      let buf = rotate_buffer(&buf, &self.orientation);
      pipeline.cache.put(outid, buf, 1);
    }
  }
}

fn rotate_buffer(buf: &OpBuffer, orientation: &Orientation) -> OpBuffer {
  // Don't rotate things we don't know how to rotate or don't need to
  if *orientation == Orientation::Normal || *orientation == Orientation::Unknown {
    return buf.clone();
  }

  // Since we are using isize when calculating values for the rotation its
  // indices must be addressable by an isize as well
  if buf.data.len() >= usize::MAX / 2 {
    panic!("Buffer is too wide or high to rotate");
  }

  // We extract buffers parameters early since all math is done with isize.
  // This avoids verbose casts later on
  let mut width = buf.width as isize;
  let mut height = buf.height as isize;
  let colors = buf.colors as isize;

  let (transpose, flip_x, flip_y) = orientation.to_flips();

  let mut base_offset: isize = 0;
  let mut x_step: isize = colors;
  let mut y_step: isize = width * colors;

  if flip_x {
    x_step = -x_step;
    base_offset += (width - 1) * colors;
  }

  if flip_y {
    y_step = -y_step;
    base_offset += width * (height - 1) * colors;
  }

  let mut out = if transpose {
    mem::swap(&mut width, &mut height);
    mem::swap(&mut x_step, &mut y_step);
    OpBuffer::new(buf.height, buf.width, colors as usize)
  } else {
    OpBuffer::new(buf.width, buf.height, colors as usize)
  };

  out.mutate_lines(&(|line: &mut [f32], row| {
    // Calculate the current line's offset in original buffer. When transposing
    // this is the current column's offset in the original buffer
    let line_offset = base_offset + y_step * row as isize;
    for col in 0..width {
      // The current pixel's offset in original buffer
      let offset = line_offset + x_step * col;
      for c in 0..colors {
        line[(col * colors + c) as usize] = buf.data[(offset + c) as usize];
      }
    }
  }));

  out
}

#[cfg(test)]
mod tests {
  use decoders::Orientation;
  use imageops::OpBuffer;
  use super::rotate_buffer;

  // Store a colorful capital F as a constant, since it is used in all tests
  lazy_static! {
      static ref F: OpBuffer = {
        OpBuffer::from_rgb_str_vec(vec![
          "        ",
          " RRRRRR ",
          " GG     ",
          " BBBB   ",
          " GG     ",
          " GG     ",
          "        ",
        ])
      };
  }

  #[test]
  fn rotate_unknown() {
    assert_eq!(rotate_buffer(&F.clone(), &Orientation::Unknown), F.clone());
  }

  #[test]
  fn rotate_normal() {
    assert_eq!(rotate_buffer(&F.clone(), &Orientation::Normal), F.clone());
  }

  #[test]
  fn rotate_flip_x() {
    let output = OpBuffer::from_rgb_str_vec(vec![
      "        ",
      " RRRRRR ",
      "     GG ",
      "   BBBB ",
      "     GG ",
      "     GG ",
      "        ",
    ]);

    assert_eq!(rotate_buffer(&F.clone(), &Orientation::HorizontalFlip), output);
  }

  #[test]
  fn rotate_flip_y() {
    let output = OpBuffer::from_rgb_str_vec(vec![
      "        ",
      " GG     ",
      " GG     ",
      " BBBB   ",
      " GG     ",
      " RRRRRR ",
      "        ",
    ]);
    assert_eq!(rotate_buffer(&F.clone(), &Orientation::VerticalFlip), output);
  }

  #[test]
  fn rotate_rotate90_cw() {
    let output = OpBuffer::from_rgb_str_vec(vec![
      "       ",
      " GGBGR ",
      " GGBGR ",
      "   B R ",
      "   B R ",
      "     R ",
      "     R ",
      "       ",
    ]);
    assert_eq!(rotate_buffer(&F.clone(), &Orientation::Rotate90), output);
  }

  #[test]
  fn rotate_rotate270_cw() {
    let output = OpBuffer::from_rgb_str_vec(vec![
      "       ",
      " R     ",
      " R     ",
      " R B   ",
      " R B   ",
      " RGBGG ",
      " RGBGG ",
      "       ",
    ]);
    assert_eq!(rotate_buffer(&F.clone(), &Orientation::Rotate270), output);
  }

  #[test]
  fn rotate_rotate180() {
    let output = OpBuffer::from_rgb_str_vec(vec![
      "        ",
      "     GG ",
      "     GG ",
      "   BBBB ",
      "     GG ",
      " RRRRRR ",
      "        ",
    ]);
    assert_eq!(rotate_buffer(&F.clone(), &Orientation::Rotate180), output);
  }

  #[test]
  fn rotate_transpose() {
    let output = OpBuffer::from_rgb_str_vec(vec![
      "       ",
      " RGBGG ",
      " RGBGG ",
      " R B   ",
      " R B   ",
      " R     ",
      " R     ",
      "       ",
    ]);
    assert_eq!(rotate_buffer(&F.clone(), &Orientation::Transpose), output);
  }

  #[test]
  fn rotate_transverse() {
    let output = OpBuffer::from_rgb_str_vec(vec![
      "       ",
      "     R ",
      "     R ",
      "   B R ",
      "   B R ",
      " GGBGR ",
      " GGBGR ",
      "       ",
    ]);
    assert_eq!(rotate_buffer(&F.clone(), &Orientation::Transverse), output);
  }
}
<commit_msg>transform: fix cpp=3 to improve optimization<commit_after>use std::mem;
use std::usize;

use decoders::{Orientation, RawImage};
use imageops::*;

#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
pub struct OpTransform {
  orientation: Orientation,
}

impl OpTransform {
  pub fn new(img: &RawImage) -> OpTransform {
    OpTransform{
      orientation: img.orientation,
    }
  }
}

impl<'a> ImageOp<'a> for OpTransform {
  fn name(&self) -> &str {"transform"}
  fn run(&self, pipeline: &mut PipelineGlobals, inid: BufHash, outid: BufHash) {
    if self.orientation == Orientation::Normal || self.orientation == Orientation::Unknown {
      pipeline.cache.alias(inid, outid);
    } else {
      let buf = pipeline.cache.get(&inid).unwrap();
      let buf = rotate_buffer(&buf, &self.orientation);
      pipeline.cache.put(outid, buf, 1);
    }
  }
}

fn rotate_buffer(buf: &OpBuffer, orientation: &Orientation) -> OpBuffer {
  assert_eq!(buf.colors, 3); // When we're rotating we're always at 3 cpp

  // Don't rotate things we don't know how to rotate or don't need to
  if *orientation == Orientation::Normal || *orientation == Orientation::Unknown {
    return buf.clone();
  }

  // Since we are using isize when calculating values for the rotation its
  // indices must be addressable by an isize as well
  if buf.data.len() >= usize::MAX / 2 {
    panic!("Buffer is too wide or high to rotate");
  }

  // We extract buffers parameters early since all math is done with isize.
  // This avoids verbose casts later on
  let mut width = buf.width as isize;
  let mut height = buf.height as isize;

  let (transpose, flip_x, flip_y) = orientation.to_flips();

  let mut base_offset: isize = 0;
  let mut x_step: isize = 3;
  let mut y_step: isize = width * 3;

  if flip_x {
    x_step = -x_step;
    base_offset += (width - 1) * 3;
  }

  if flip_y {
    y_step = -y_step;
    base_offset += width * (height - 1) * 3;
  }

  let mut out = if transpose {
    mem::swap(&mut width, &mut height);
    mem::swap(&mut x_step, &mut y_step);
    OpBuffer::new(buf.height, buf.width, 3 as usize)
  } else {
    OpBuffer::new(buf.width, buf.height, 3 as usize)
  };

  out.mutate_lines(&(|line: &mut [f32], row| {
    // Calculate the current line's offset in original buffer. When transposing
    // this is the current column's offset in the original buffer
    let line_offset = base_offset + y_step * row as isize;
    for col in 0..width {
      // The current pixel's offset in original buffer
      let offset = line_offset + x_step * col;
      for c in 0..3 {
        line[(col * 3 + c) as usize] = buf.data[(offset + c) as usize];
      }
    }
  }));

  out
}

#[cfg(test)]
mod tests {
  use decoders::Orientation;
  use imageops::OpBuffer;
  use super::rotate_buffer;

  // Store a colorful capital F as a constant, since it is used in all tests
  lazy_static! {
      static ref F: OpBuffer = {
        OpBuffer::from_rgb_str_vec(vec![
          "        ",
          " RRRRRR ",
          " GG     ",
          " BBBB   ",
          " GG     ",
          " GG     ",
          "        ",
        ])
      };
  }

  #[test]
  fn rotate_unknown() {
    assert_eq!(rotate_buffer(&F.clone(), &Orientation::Unknown), F.clone());
  }

  #[test]
  fn rotate_normal() {
    assert_eq!(rotate_buffer(&F.clone(), &Orientation::Normal), F.clone());
  }

  #[test]
  fn rotate_flip_x() {
    let output = OpBuffer::from_rgb_str_vec(vec![
      "        ",
      " RRRRRR ",
      "     GG ",
      "   BBBB ",
      "     GG ",
      "     GG ",
      "        ",
    ]);

    assert_eq!(rotate_buffer(&F.clone(), &Orientation::HorizontalFlip), output);
  }

  #[test]
  fn rotate_flip_y() {
    let output = OpBuffer::from_rgb_str_vec(vec![
      "        ",
      " GG     ",
      " GG     ",
      " BBBB   ",
      " GG     ",
      " RRRRRR ",
      "        ",
    ]);
    assert_eq!(rotate_buffer(&F.clone(), &Orientation::VerticalFlip), output);
  }

  #[test]
  fn rotate_rotate90_cw() {
    let output = OpBuffer::from_rgb_str_vec(vec![
      "       ",
      " GGBGR ",
      " GGBGR ",
      "   B R ",
      "   B R ",
      "     R ",
      "     R ",
      "       ",
    ]);
    assert_eq!(rotate_buffer(&F.clone(), &Orientation::Rotate90), output);
  }

  #[test]
  fn rotate_rotate270_cw() {
    let output = OpBuffer::from_rgb_str_vec(vec![
      "       ",
      " R     ",
      " R     ",
      " R B   ",
      " R B   ",
      " RGBGG ",
      " RGBGG ",
      "       ",
    ]);
    assert_eq!(rotate_buffer(&F.clone(), &Orientation::Rotate270), output);
  }

  #[test]
  fn rotate_rotate180() {
    let output = OpBuffer::from_rgb_str_vec(vec![
      "        ",
      "     GG ",
      "     GG ",
      "   BBBB ",
      "     GG ",
      " RRRRRR ",
      "        ",
    ]);
    assert_eq!(rotate_buffer(&F.clone(), &Orientation::Rotate180), output);
  }

  #[test]
  fn rotate_transpose() {
    let output = OpBuffer::from_rgb_str_vec(vec![
      "       ",
      " RGBGG ",
      " RGBGG ",
      " R B   ",
      " R B   ",
      " R     ",
      " R     ",
      "       ",
    ]);
    assert_eq!(rotate_buffer(&F.clone(), &Orientation::Transpose), output);
  }

  #[test]
  fn rotate_transverse() {
    let output = OpBuffer::from_rgb_str_vec(vec![
      "       ",
      "     R ",
      "     R ",
      "   B R ",
      "   B R ",
      " GGBGR ",
      " GGBGR ",
      "       ",
    ]);
    assert_eq!(rotate_buffer(&F.clone(), &Orientation::Transverse), output);
  }
}
<|endoftext|>