git_checks/
reject_symlinks.rs1use derive_builder::Builder;
10use git_checks_core::impl_prelude::*;
11
12#[derive(Builder, Debug, Default, Clone, Copy)]
14#[non_exhaustive]
15#[builder(field(private))]
16pub struct RejectSymlinks {}
17
18impl RejectSymlinks {
19 pub fn builder() -> RejectSymlinksBuilder {
21 Default::default()
22 }
23}
24
25impl ContentCheck for RejectSymlinks {
26 fn name(&self) -> &str {
27 "reject-symlinks"
28 }
29
30 fn check(
31 &self,
32 _: &CheckGitContext,
33 content: &dyn Content,
34 ) -> Result<CheckResult, Box<dyn Error>> {
35 let mut result = CheckResult::new();
36
37 for diff in content.diffs() {
38 match diff.status {
39 StatusChange::Added | StatusChange::Modified(_) => (),
40 _ => continue,
41 }
42
43 if diff.new_mode == "120000" {
44 result.add_error(format!(
45 "{}adds a symlink at `{}` which is not allowed.",
46 commit_prefix(content),
47 diff.name,
48 ));
49 }
50 }
51
52 Ok(result)
53 }
54}
55
56#[cfg(feature = "config")]
57pub(crate) mod config {
58 use git_checks_config::{register_checks, CommitCheckConfig, IntoCheck, TopicCheckConfig};
59 use serde::Deserialize;
60 #[cfg(test)]
61 use serde_json::json;
62
63 use crate::RejectSymlinks;
64
65 #[derive(Deserialize, Debug)]
72 pub struct RejectSymlinksConfig {}
73
74 impl IntoCheck for RejectSymlinksConfig {
75 type Check = RejectSymlinks;
76
77 fn into_check(self) -> Self::Check {
78 Default::default()
79 }
80 }
81
82 register_checks! {
83 RejectSymlinksConfig {
84 "reject_symlinks" => CommitCheckConfig,
85 "reject_symlinks/topic" => TopicCheckConfig,
86 },
87 }
88
89 #[test]
90 fn test_reject_symlinks_config_empty() {
91 let json = json!({});
92 let check: RejectSymlinksConfig = serde_json::from_value(json).unwrap();
93
94 let _ = check.into_check();
95 }
96}
97
98#[cfg(test)]
99mod tests {
100 use git_checks_core::{Check, TopicCheck};
101
102 use crate::test::*;
103 use crate::RejectSymlinks;
104
105 const BAD_TOPIC: &str = "00ffdf352196c16a453970de022a8b4343610ccf";
106 const DELETE_TOPIC: &str = "59ebc7302ce05ec21e396e508be99f8dc2924c2d";
107 const FIX_TOPIC: &str = "d93ffc2e8b782ba8dce2278dd86fda0df80f454b";
108
109 #[test]
110 fn test_reject_symlinks_builder_default() {
111 assert!(RejectSymlinks::builder().build().is_ok());
112 }
113
114 #[test]
115 fn test_reject_symlinks_name_commit() {
116 let check = RejectSymlinks::default();
117 assert_eq!(Check::name(&check), "reject-symlinks");
118 }
119
120 #[test]
121 fn test_reject_symlinks_name_topic() {
122 let check = RejectSymlinks::default();
123 assert_eq!(TopicCheck::name(&check), "reject-symlinks");
124 }
125
126 #[test]
127 fn test_reject_symlinks() {
128 let check = RejectSymlinks::default();
129 let result = run_check("test_reject_symlinks", BAD_TOPIC, check);
130 test_result_errors(result, &[
131 "commit 00ffdf352196c16a453970de022a8b4343610ccf adds a symlink at `absolute-link` \
132 which is not allowed.",
133 "commit 00ffdf352196c16a453970de022a8b4343610ccf adds a symlink at `broken-link` \
134 which is not allowed.",
135 "commit 00ffdf352196c16a453970de022a8b4343610ccf adds a symlink at `inside-link` \
136 which is not allowed.",
137 "commit 00ffdf352196c16a453970de022a8b4343610ccf adds a symlink at `outside-link` \
138 which is not allowed.",
139 ]);
140 }
141
142 #[test]
143 fn test_reject_symlinks_topic() {
144 let check = RejectSymlinks::default();
145 let result = run_topic_check("test_reject_symlinks_topic", BAD_TOPIC, check);
146 test_result_errors(
147 result,
148 &[
149 "adds a symlink at `absolute-link` which is not allowed.",
150 "adds a symlink at `broken-link` which is not allowed.",
151 "adds a symlink at `inside-link` which is not allowed.",
152 "adds a symlink at `outside-link` which is not allowed.",
153 ],
154 );
155 }
156
157 #[test]
158 fn test_reject_symlinks_topic_fixed() {
159 let check = RejectSymlinks::default();
160 run_topic_check_ok("test_reject_symlinks_topic_fixed", FIX_TOPIC, check);
161 }
162
163 #[test]
164 fn test_reject_symlinks_delete_file() {
165 let check = RejectSymlinks::default();
166 let conf = make_check_conf(&check);
167
168 let result = test_check_base(
169 "test_reject_symlinks_delete_file",
170 DELETE_TOPIC,
171 BAD_TOPIC,
172 &conf,
173 );
174 test_result_ok(result);
175 }
176
177 #[test]
178 fn test_reject_symlinks_delete_file_topic() {
179 let check = RejectSymlinks::default();
180 run_topic_check_ok(
181 "test_reject_symlinks_delete_file_topic",
182 DELETE_TOPIC,
183 check,
184 );
185 }
186}