commitlint_rs/rule/
description_empty.rs

1use crate::{message::Message, result::Violation, rule::Rule};
2use serde::{Deserialize, Serialize};
3
4use super::Level;
5
6/// DescriptionEmpty represents the subject-empty rule.
7#[derive(Clone, Debug, Deserialize, Serialize)]
8#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
9pub struct DescriptionEmpty {
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/// DescriptionEmpty represents the description-empty rule.
18impl Rule for DescriptionEmpty {
19    const NAME: &'static str = "description-empty";
20    const LEVEL: Level = Level::Error;
21
22    fn message(&self, _message: &Message) -> String {
23        "description is empty or missing space in the beginning".to_string()
24    }
25
26    fn validate(&self, message: &Message) -> Option<Violation> {
27        match message.description {
28            None => Some(Violation {
29                level: self.level.unwrap_or(Self::LEVEL),
30                message: self.message(message),
31            }),
32            Some(ref desc) if desc.is_empty() => Some(Violation {
33                level: self.level.unwrap_or(Self::LEVEL),
34                message: self.message(message),
35            }),
36            _ => None,
37        }
38    }
39}
40
41/// Default implementation of DescriptionEmpty.
42impl Default for DescriptionEmpty {
43    fn default() -> Self {
44        Self {
45            level: Some(Self::LEVEL),
46        }
47    }
48}
49
50#[cfg(test)]
51mod tests {
52    use super::*;
53
54    #[test]
55    fn test_non_empty_description() {
56        let rule = DescriptionEmpty::default();
57        let message = Message {
58            body: None,
59            description: Some("broadcast $destroy event on scope destruction".to_string()),
60            footers: None,
61            r#type: Some("feat".to_string()),
62            raw: "feat(scope): broadcast $destroy event on scope destruction".to_string(),
63            scope: Some("scope".to_string()),
64            subject: None,
65        };
66
67        assert!(rule.validate(&message).is_none());
68    }
69
70    #[test]
71    fn test_empty_description() {
72        let rule = DescriptionEmpty::default();
73        let message = Message {
74            body: None,
75            description: None,
76            footers: None,
77            r#type: Some("feat".to_string()),
78            raw: "(scope):".to_string(),
79            scope: Some("scope".to_string()),
80            subject: None,
81        };
82
83        let violation = rule.validate(&message);
84        assert!(violation.is_some());
85        assert_eq!(violation.clone().unwrap().level, Level::Error);
86        assert_eq!(
87            violation.unwrap().message,
88            "description is empty or missing space in the beginning".to_string()
89        );
90    }
91
92    #[test]
93    fn test_blank_description() {
94        let rule = DescriptionEmpty::default();
95        let message = Message {
96            body: None,
97            description: Some("".to_string()),
98            footers: None,
99            r#type: Some("feat".to_string()),
100            raw: "(scope):".to_string(),
101            scope: Some("scope".to_string()),
102            subject: None,
103        };
104
105        let violation = rule.validate(&message);
106        assert!(violation.is_some());
107        assert_eq!(violation.clone().unwrap().level, Level::Error);
108        assert_eq!(
109            violation.unwrap().message,
110            "description is empty or missing space in the beginning".to_string()
111        );
112    }
113}