syncable_cli/analyzer/dclint/rules/
dcl007.rs1use crate::analyzer::dclint::rules::{LintContext, Rule, SimpleRule, make_failure};
6use crate::analyzer::dclint::types::{CheckFailure, RuleCategory, Severity};
7
8const CODE: &str = "DCL007";
9const NAME: &str = "require-project-name-field";
10const DESCRIPTION: &str = "The top-level `name` field should be set for explicit project naming.";
11const URL: &str = "https://github.com/zavoloklom/docker-compose-linter/blob/main/docs/rules/require-project-name-field-rule.md";
12
13pub fn rule() -> impl Rule {
14 SimpleRule::new(
15 CODE,
16 NAME,
17 Severity::Info,
18 RuleCategory::BestPractice,
19 DESCRIPTION,
20 URL,
21 check,
22 )
23}
24
25fn check(ctx: &LintContext) -> Vec<CheckFailure> {
26 let mut failures = Vec::new();
27
28 if ctx.compose.name.is_none() {
29 let message = "Consider adding a `name` field to explicitly set the project name instead of relying on the directory name.".to_string();
30
31 failures.push(make_failure(
32 &CODE.into(),
33 NAME,
34 Severity::Info,
35 RuleCategory::BestPractice,
36 message,
37 1,
38 1,
39 false,
40 ));
41 }
42
43 failures
44}
45
46#[cfg(test)]
47mod tests {
48 use super::*;
49 use crate::analyzer::dclint::parser::parse_compose;
50
51 fn check_yaml(yaml: &str) -> Vec<CheckFailure> {
52 let compose = parse_compose(yaml).unwrap();
53 let ctx = LintContext::new(&compose, yaml, "docker-compose.yml");
54 check(&ctx)
55 }
56
57 #[test]
58 fn test_no_violation_has_name() {
59 let yaml = r#"
60name: myproject
61services:
62 web:
63 image: nginx
64"#;
65 assert!(check_yaml(yaml).is_empty());
66 }
67
68 #[test]
69 fn test_violation_no_name() {
70 let yaml = r#"
71services:
72 web:
73 image: nginx
74"#;
75 let failures = check_yaml(yaml);
76 assert_eq!(failures.len(), 1);
77 assert!(failures[0].message.contains("name"));
78 }
79}