git_checks/
reject_separate_root.rs1use derive_builder::Builder;
10use git_checks_core::impl_prelude::*;
11
12#[derive(Builder, Debug, Clone, Copy)]
14#[non_exhaustive]
15#[builder(field(private))]
16pub struct RejectSeparateRoot {
17 #[builder(default = "true")]
22 required: bool,
23}
24
25impl Default for RejectSeparateRoot {
26 fn default() -> Self {
27 RejectSeparateRoot {
28 required: true,
29 }
30 }
31}
32
33impl RejectSeparateRoot {
34 pub fn builder() -> RejectSeparateRootBuilder {
36 Default::default()
37 }
38}
39
40impl Check for RejectSeparateRoot {
41 fn name(&self) -> &str {
42 "reject-separate-root"
43 }
44
45 fn check(&self, _: &CheckGitContext, commit: &Commit) -> Result<CheckResult, Box<dyn Error>> {
46 let mut result = CheckResult::new();
47
48 if commit.parents.is_empty() {
49 let message = format!("commit {} not allowed; it is a root commit.", commit.sha1,);
50 if self.required {
51 result.add_error(message);
52 } else {
53 result.add_warning(message);
54 }
55 }
56
57 Ok(result)
58 }
59}
60
61#[cfg(feature = "config")]
62pub(crate) mod config {
63 use git_checks_config::{register_checks, CommitCheckConfig, IntoCheck};
64 use serde::Deserialize;
65 #[cfg(test)]
66 use serde_json::json;
67
68 use crate::RejectSeparateRoot;
69
70 #[derive(Deserialize, Debug)]
85 pub struct RejectSeparateRootConfig {
86 #[serde(default = "default_required")]
87 required: bool,
88 }
89
90 fn default_required() -> bool {
91 true
92 }
93
94 impl IntoCheck for RejectSeparateRootConfig {
95 type Check = RejectSeparateRoot;
96
97 fn into_check(self) -> Self::Check {
98 let mut builder = RejectSeparateRoot::builder();
99
100 builder.required(self.required);
101
102 builder
103 .build()
104 .expect("configuration mismatch for `RejectSeparateRoot`")
105 }
106 }
107
108 register_checks! {
109 RejectSeparateRootConfig {
110 "reject_separate_root" => CommitCheckConfig,
111 },
112 }
113
114 #[test]
115 fn test_reject_separate_root_config_empty() {
116 let json = json!({});
117 let check: RejectSeparateRootConfig = serde_json::from_value(json).unwrap();
118
119 assert!(check.required);
120
121 let check = check.into_check();
122
123 assert!(check.required);
124 }
125
126 #[test]
127 fn test_reject_separate_root_config_all_fields() {
128 let json = json!({
129 "required": false,
130 });
131 let check: RejectSeparateRootConfig = serde_json::from_value(json).unwrap();
132
133 assert!(!check.required);
134
135 let check = check.into_check();
136
137 assert!(!check.required);
138 }
139}
140
141#[cfg(test)]
142mod tests {
143 use git_checks_core::Check;
144
145 use crate::test::*;
146 use crate::RejectSeparateRoot;
147
148 const NO_ROOT_TOPIC: &str = "ba3dc3cb09a558c88282742413a2dccb17d444fc";
149 const WITH_ROOT_TOPIC: &str = "ff560e8798ef7a9d10bf43660695f7155b49b398";
150
151 #[test]
152 fn test_reject_separate_root_builder_default() {
153 assert!(RejectSeparateRoot::builder().build().is_ok());
154 }
155
156 #[test]
157 fn test_reject_separate_root_name_commit() {
158 let check = RejectSeparateRoot::default();
159 assert_eq!(Check::name(&check), "reject-separate-root");
160 }
161
162 #[test]
163 fn test_reject_separate_root_no_root() {
164 let check = RejectSeparateRoot::default();
165 run_check_ok("test_reject_separate_root_no_root", NO_ROOT_TOPIC, check);
166 }
167
168 #[test]
169 fn test_reject_separate_root_with_root() {
170 let check = RejectSeparateRoot::default();
171 let result = run_check(
172 "test_reject_separate_root_with_root",
173 WITH_ROOT_TOPIC,
174 check,
175 );
176 test_result_errors(
177 result,
178 &["commit ff560e8798ef7a9d10bf43660695f7155b49b398 not allowed; it is a root commit."],
179 );
180 }
181
182 #[test]
183 fn test_reject_separate_root_warn() {
184 let check = RejectSeparateRoot::builder()
185 .required(false)
186 .build()
187 .unwrap();
188 let result = run_check("test_reject_separate_root_warn", WITH_ROOT_TOPIC, check);
189 test_result_warnings(
190 result,
191 &["commit ff560e8798ef7a9d10bf43660695f7155b49b398 not allowed; it is a root commit."],
192 );
193 }
194
195 #[test]
196 fn test_reject_separate_root_warn_no_root() {
197 let check = RejectSeparateRoot::builder()
198 .required(false)
199 .build()
200 .unwrap();
201 run_check_ok(
202 "test_reject_separate_root_warn_no_root",
203 NO_ROOT_TOPIC,
204 check,
205 );
206 }
207}