syncable_cli/analyzer/dclint/formatter/
json.rs

1//! JSON output formatter for dclint.
2
3use serde_json::json;
4
5use crate::analyzer::dclint::lint::LintResult;
6
7/// Format lint results as JSON.
8pub fn format(results: &[LintResult]) -> String {
9    let output: Vec<serde_json::Value> = results
10        .iter()
11        .map(|result| {
12            let messages: Vec<serde_json::Value> = result
13                .failures
14                .iter()
15                .map(|f| {
16                    json!({
17                        "ruleId": f.code.as_str(),
18                        "ruleName": f.rule_name,
19                        "severity": match f.severity {
20                            crate::analyzer::dclint::types::Severity::Error => 2,
21                            crate::analyzer::dclint::types::Severity::Warning => 1,
22                            crate::analyzer::dclint::types::Severity::Info => 0,
23                            crate::analyzer::dclint::types::Severity::Style => 0,
24                        },
25                        "severityName": f.severity.as_str(),
26                        "category": f.category.as_str(),
27                        "message": f.message,
28                        "line": f.line,
29                        "column": f.column,
30                        "endLine": f.end_line,
31                        "endColumn": f.end_column,
32                        "fixable": f.fixable,
33                        "data": f.data
34                    })
35                })
36                .collect();
37
38            json!({
39                "filePath": result.file_path,
40                "messages": messages,
41                "errorCount": result.error_count,
42                "warningCount": result.warning_count,
43                "fixableErrorCount": result.fixable_error_count,
44                "fixableWarningCount": result.fixable_warning_count,
45                "parseErrors": result.parse_errors
46            })
47        })
48        .collect();
49
50    serde_json::to_string_pretty(&output).unwrap_or_else(|_| "[]".to_string())
51}
52
53#[cfg(test)]
54mod tests {
55    use super::*;
56    use crate::analyzer::dclint::types::{CheckFailure, RuleCategory, Severity};
57
58    #[test]
59    fn test_json_format() {
60        let mut result = LintResult::new("docker-compose.yml");
61        result.failures.push(CheckFailure::new(
62            "DCL001",
63            "no-build-and-image",
64            Severity::Error,
65            RuleCategory::BestPractice,
66            "Test message",
67            5,
68            1,
69        ));
70        result.error_count = 1;
71
72        let output = format(&[result]);
73        let parsed: serde_json::Value = serde_json::from_str(&output).unwrap();
74
75        assert!(parsed.is_array());
76        let arr = parsed.as_array().unwrap();
77        assert_eq!(arr.len(), 1);
78
79        let file_result = &arr[0];
80        assert_eq!(file_result["filePath"], "docker-compose.yml");
81        assert_eq!(file_result["errorCount"], 1);
82
83        let messages = file_result["messages"].as_array().unwrap();
84        assert_eq!(messages.len(), 1);
85        assert_eq!(messages[0]["ruleId"], "DCL001");
86        assert_eq!(messages[0]["line"], 5);
87    }
88
89    #[test]
90    fn test_json_format_empty() {
91        let result = LintResult::new("docker-compose.yml");
92        let output = format(&[result]);
93        let parsed: serde_json::Value = serde_json::from_str(&output).unwrap();
94
95        let arr = parsed.as_array().unwrap();
96        let messages = arr[0]["messages"].as_array().unwrap();
97        assert!(messages.is_empty());
98    }
99}