git-checks 4.0.1

Checks to run against a topic in git to enforce coding standards.
Documentation
// Copyright Kitware, Inc.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use crates::git_checks_core::impl_prelude::*;

/// Rejects the addition of symlinks to a repository.
#[derive(Builder, Debug, Default, Clone, Copy)]
#[builder(field(private))]
pub struct RejectSymlinks {}

impl RejectSymlinks {
    /// Create a new builder.
    pub fn builder() -> RejectSymlinksBuilder {
        RejectSymlinksBuilder::default()
    }
}

impl ContentCheck for RejectSymlinks {
    fn name(&self) -> &str {
        "reject-symlinks"
    }

    fn check(
        &self,
        _: &CheckGitContext,
        content: &dyn Content,
    ) -> Result<CheckResult, Box<dyn Error>> {
        let mut result = CheckResult::new();

        for diff in content.diffs() {
            match diff.status {
                StatusChange::Added | StatusChange::Modified(_) => (),
                _ => continue,
            }

            if diff.new_mode == "120000" {
                result.add_error(format!(
                    "{}adds a symlink at `{}` which is not allowed.",
                    commit_prefix(content),
                    diff.name,
                ));
            }
        }

        Ok(result)
    }
}

#[cfg(feature = "config")]
pub(crate) mod config {
    use crates::git_checks_config::{CommitCheckConfig, IntoCheck, TopicCheckConfig};
    use crates::inventory;
    #[cfg(test)]
    use crates::serde_json;

    use RejectSymlinks;

    /// Configuration for the `RejectSymlinks` check.
    ///
    /// No configuration available.
    ///
    /// This check is registered as a commit check with the name `"reject_symlinks"` and a topic
    /// check with the name `"reject_symlinks/topic"`.
    #[derive(Deserialize, Debug)]
    pub struct RejectSymlinksConfig {}

    impl IntoCheck for RejectSymlinksConfig {
        type Check = RejectSymlinks;

        fn into_check(self) -> Self::Check {
            RejectSymlinks::default()
        }
    }

    register_checks! {
        RejectSymlinksConfig {
            "reject_symlinks" => CommitCheckConfig,
            "reject_symlinks/topic" => TopicCheckConfig,
        },
    }

    #[test]
    fn test_reject_symlinks_config_empty() {
        let json = json!({});
        serde_json::from_value::<RejectSymlinksConfig>(json).unwrap();
    }
}

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

    const BAD_TOPIC: &str = "00ffdf352196c16a453970de022a8b4343610ccf";
    const FIX_TOPIC: &str = "d93ffc2e8b782ba8dce2278dd86fda0df80f454b";

    #[test]
    fn test_reject_symlinks_builder_default() {
        assert!(RejectSymlinks::builder().build().is_ok());
    }

    #[test]
    fn test_reject_symlinks() {
        let check = RejectSymlinks::default();
        let result = run_check("test_reject_symlinks", BAD_TOPIC, check);
        test_result_errors(result, &[
            "commit 00ffdf352196c16a453970de022a8b4343610ccf adds a symlink at `absolute-link` \
             which is not allowed.",
            "commit 00ffdf352196c16a453970de022a8b4343610ccf adds a symlink at `broken-link` \
             which is not allowed.",
            "commit 00ffdf352196c16a453970de022a8b4343610ccf adds a symlink at `inside-link` \
             which is not allowed.",
            "commit 00ffdf352196c16a453970de022a8b4343610ccf adds a symlink at `outside-link` \
             which is not allowed.",
        ]);
    }

    #[test]
    fn test_reject_symlinks_topic() {
        let check = RejectSymlinks::default();
        let result = run_topic_check("test_reject_symlinks_topic", BAD_TOPIC, check);
        test_result_errors(
            result,
            &[
                "adds a symlink at `absolute-link` which is not allowed.",
                "adds a symlink at `broken-link` which is not allowed.",
                "adds a symlink at `inside-link` which is not allowed.",
                "adds a symlink at `outside-link` which is not allowed.",
            ],
        );
    }

    #[test]
    fn test_reject_symlinks_topic_fixed() {
        let check = RejectSymlinks::default();
        run_topic_check_ok("test_reject_symlinks_topic_fixed", FIX_TOPIC, check);
    }
}