commitlint_rs/rule/
scope_empty.rs

1use crate::{message::Message, result::Violation, rule::Rule};
2use serde::{Deserialize, Serialize};
3
4use super::Level;
5
6/// ScopeEmpty represents the subject-empty rule.
7#[derive(Clone, Debug, Deserialize, Serialize)]
8#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
9pub struct ScopeEmpty {
10    /// Level represents the level of the rule.
11    ///
12    // Note that currently the default literal is not supported.
13    // See: https://github.com/serde-rs/serde/issues/368
14    level: Option<Level>,
15}
16
17/// ScopeEmpty represents the scope-empty rule.
18impl Rule for ScopeEmpty {
19    const NAME: &'static str = "scope-empty";
20    const LEVEL: Level = Level::Error;
21
22    fn message(&self, _message: &Message) -> String {
23        "scope is empty".to_string()
24    }
25
26    fn validate(&self, message: &Message) -> Option<Violation> {
27        if message.scope.is_none() {
28            return Some(Violation {
29                level: self.level.unwrap_or(Self::LEVEL),
30                message: self.message(message),
31            });
32        }
33
34        None
35    }
36}
37
38/// Default implementation of ScopeEmpty.
39impl Default for ScopeEmpty {
40    fn default() -> Self {
41        Self {
42            level: Some(Self::LEVEL),
43        }
44    }
45}
46
47#[cfg(test)]
48mod tests {
49    use super::*;
50
51    #[test]
52    fn test_non_empty_subject() {
53        let rule = ScopeEmpty::default();
54        let message = Message {
55            body: None,
56            description: None,
57            footers: None,
58            r#type: Some("feat".to_string()),
59            raw: "feat(scope): broadcast $destroy event on scope destruction".to_string(),
60            scope: Some("scope".to_string()),
61            subject: None,
62        };
63
64        assert!(rule.validate(&message).is_none());
65    }
66
67    #[test]
68    fn test_no_subject() {
69        let rule = ScopeEmpty::default();
70        let message = Message {
71            body: None,
72            description: None,
73            footers: None,
74            r#type: Some("feat".to_string()),
75            raw: "feat: broadcast $destroy event on scope destruction".to_string(),
76            scope: None,
77            subject: None,
78        };
79
80        let violation = rule.validate(&message);
81        assert!(violation.is_some());
82        assert_eq!(violation.clone().unwrap().level, Level::Error);
83        assert_eq!(violation.unwrap().message, "scope is empty".to_string());
84    }
85
86    #[test]
87    fn test_empty_subject() {
88        let rule = ScopeEmpty::default();
89        let message = Message {
90            body: None,
91            description: None,
92            footers: None,
93            r#type: Some("feat".to_string()),
94            raw: "feat(): broadcast $destroy event on scope destruction".to_string(),
95            scope: None,
96            subject: None,
97        };
98
99        let violation = rule.validate(&message);
100        assert!(violation.is_some());
101        assert_eq!(violation.clone().unwrap().level, Level::Error);
102        assert_eq!(violation.unwrap().message, "scope is empty".to_string());
103    }
104}