git_checks/
restricted_path.rs1use derive_builder::Builder;
10use git_checks_core::impl_prelude::*;
11
12#[derive(Builder, Debug, Clone)]
14#[builder(field(private))]
15pub struct RestrictedPath {
16 #[builder(setter(into))]
20 path: String,
21 #[builder(default = "true")]
26 required: bool,
27}
28
29impl RestrictedPath {
30 pub fn builder() -> RestrictedPathBuilder {
32 Default::default()
33 }
34}
35
36impl ContentCheck for RestrictedPath {
37 fn name(&self) -> &str {
38 "restricted-path"
39 }
40
41 fn check(
42 &self,
43 _: &CheckGitContext,
44 content: &dyn Content,
45 ) -> Result<CheckResult, Box<dyn Error>> {
46 let mut result = CheckResult::new();
47
48 let is_restricted = content
49 .diffs()
50 .iter()
51 .map(|diff| diff.name.as_path())
52 .any(|path| path.starts_with(&self.path));
53
54 if is_restricted {
55 if self.required {
56 result.add_error(format!(
57 "{}the `{}` path is restricted.",
58 commit_prefix_str(content, "not allowed;"),
59 self.path,
60 ));
61 } else {
62 result.add_warning(format!(
63 "{}the `{}` path is restricted.",
64 commit_prefix_str(content, "should be inspected;"),
65 self.path,
66 ));
67 };
68 }
69
70 Ok(result)
71 }
72}
73
74#[cfg(feature = "config")]
75pub(crate) mod config {
76 use git_checks_config::{register_checks, CommitCheckConfig, IntoCheck, TopicCheckConfig};
77 use serde::Deserialize;
78 #[cfg(test)]
79 use serde_json::json;
80
81 #[cfg(test)]
82 use crate::test;
83 use crate::RestrictedPath;
84
85 #[derive(Deserialize, Debug)]
103 pub struct RestrictedPathConfig {
104 path: String,
105 #[serde(default)]
106 required: Option<bool>,
107 }
108
109 impl IntoCheck for RestrictedPathConfig {
110 type Check = RestrictedPath;
111
112 fn into_check(self) -> Self::Check {
113 let mut builder = RestrictedPath::builder();
114
115 builder.path(self.path);
116
117 if let Some(required) = self.required {
118 builder.required(required);
119 }
120
121 builder
122 .build()
123 .expect("configuration mismatch for `RestrictedPath`")
124 }
125 }
126
127 register_checks! {
128 RestrictedPathConfig {
129 "restricted_path" => CommitCheckConfig,
130 "restricted_path/topic" => TopicCheckConfig,
131 },
132 }
133
134 #[test]
135 fn test_restricted_path_config_empty() {
136 let json = json!({});
137 let err = serde_json::from_value::<RestrictedPathConfig>(json).unwrap_err();
138 test::check_missing_json_field(err, "path");
139 }
140
141 #[test]
142 fn test_restricted_path_config_minimum_fields() {
143 let exp_restricted_path = "path/to/restricted/content";
144 let json = json!({
145 "path": exp_restricted_path,
146 });
147 let check: RestrictedPathConfig = serde_json::from_value(json).unwrap();
148
149 assert_eq!(check.path, exp_restricted_path);
150 assert_eq!(check.required, None);
151
152 let check = check.into_check();
153
154 assert_eq!(check.path, exp_restricted_path);
155 assert!(check.required);
156 }
157
158 #[test]
159 fn test_restricted_path_config_all_fields() {
160 let exp_restricted_path = "path/to/restricted/content";
161 let json = json!({
162 "path": exp_restricted_path,
163 "required": false,
164 });
165 let check: RestrictedPathConfig = serde_json::from_value(json).unwrap();
166
167 assert_eq!(check.path, exp_restricted_path);
168 assert_eq!(check.required, Some(false));
169
170 let check = check.into_check();
171
172 assert_eq!(check.path, exp_restricted_path);
173 assert!(!check.required);
174 }
175}
176
177#[cfg(test)]
178mod tests {
179 use git_checks_core::{Check, TopicCheck};
180
181 use crate::test::*;
182 use crate::RestrictedPath;
183
184 const BAD_TOPIC: &str = "e845fa2521c17bdd31d5891c1c644fb17f0629db";
185 const FIX_TOPIC: &str = "d8a2f22943cdcca373f00892a23b85f3a6ba1196";
186
187 #[test]
188 fn test_restricted_path_builder_default() {
189 assert!(RestrictedPath::builder().build().is_err());
190 }
191
192 #[test]
193 fn test_restricted_path_builder_minimum_fields() {
194 assert!(RestrictedPath::builder().path("restricted").build().is_ok());
195 }
196
197 #[test]
198 fn test_restricted_path_name_commit() {
199 let check = RestrictedPath::builder()
200 .path("restricted")
201 .build()
202 .unwrap();
203 assert_eq!(Check::name(&check), "restricted-path");
204 }
205
206 #[test]
207 fn test_restricted_path_name_topic() {
208 let check = RestrictedPath::builder()
209 .path("restricted")
210 .build()
211 .unwrap();
212 assert_eq!(TopicCheck::name(&check), "restricted-path");
213 }
214
215 #[test]
216 fn test_restricted_path() {
217 let check = RestrictedPath::builder()
218 .path("restricted")
219 .build()
220 .unwrap();
221 let result = run_check("test_restricted_path", BAD_TOPIC, check);
222 test_result_errors(result, &[
223 "commit e845fa2521c17bdd31d5891c1c644fb17f0629db not allowed; the `restricted` path \
224 is restricted.",
225 ]);
226 }
227
228 #[test]
229 fn test_restricted_path_topic() {
230 let check = RestrictedPath::builder()
231 .path("restricted")
232 .build()
233 .unwrap();
234 let result = run_topic_check("test_restricted_path_topic", BAD_TOPIC, check);
235 test_result_errors(result, &["the `restricted` path is restricted."]);
236 }
237
238 #[test]
239 fn test_restricted_path_warning() {
240 let check = RestrictedPath::builder()
241 .path("restricted")
242 .required(false)
243 .build()
244 .unwrap();
245 let result = run_check("test_restricted_path_warning", BAD_TOPIC, check);
246 test_result_warnings(
247 result,
248 &[
249 "commit e845fa2521c17bdd31d5891c1c644fb17f0629db should be inspected; the \
250 `restricted` path is restricted.",
251 ],
252 );
253 }
254
255 #[test]
256 fn test_restricted_path_warning_topic() {
257 let check = RestrictedPath::builder()
258 .path("restricted")
259 .required(false)
260 .build()
261 .unwrap();
262 let result = run_topic_check("test_restricted_path_warning_topic", BAD_TOPIC, check);
263 test_result_warnings(result, &["the `restricted` path is restricted."]);
264 }
265
266 #[test]
267 fn test_restricted_path_topic_fixed() {
268 let check = RestrictedPath::builder()
269 .path("restricted")
270 .build()
271 .unwrap();
272 run_topic_check_ok("test_restricted_path_topic_fixed", FIX_TOPIC, check);
273 }
274}