commitlint_rs/rule/
description_max_length.rs

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