syncable_cli/analyzer/dclint/formatter/
github.rs1use crate::analyzer::dclint::lint::LintResult;
7use crate::analyzer::dclint::types::Severity;
8
9pub fn format(results: &[LintResult]) -> String {
11 let mut output = String::new();
12
13 for result in results {
14 for err in &result.parse_errors {
16 output.push_str(&format!(
17 "::error file={}::Parse error: {}\n",
18 result.file_path,
19 escape_github(err)
20 ));
21 }
22
23 for failure in &result.failures {
25 let level = match failure.severity {
26 Severity::Error => "error",
27 Severity::Warning => "warning",
28 Severity::Info | Severity::Style => "notice",
29 };
30
31 output.push_str(&format!(
32 "::{} file={},line={},col={},title={}::{}\n",
33 level,
34 result.file_path,
35 failure.line,
36 failure.column,
37 failure.code,
38 escape_github(&failure.message)
39 ));
40 }
41 }
42
43 output
44}
45
46fn escape_github(s: &str) -> String {
48 s.replace('%', "%25")
49 .replace('\r', "%0D")
50 .replace('\n', "%0A")
51}
52
53#[cfg(test)]
54mod tests {
55 use super::*;
56 use crate::analyzer::dclint::types::{CheckFailure, RuleCategory};
57
58 #[test]
59 fn test_github_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 "Service has both build and image",
67 5,
68 1,
69 ));
70
71 let output = format(&[result]);
72 assert!(output.contains("::error"));
73 assert!(output.contains("file=docker-compose.yml"));
74 assert!(output.contains("line=5"));
75 assert!(output.contains("col=1"));
76 assert!(output.contains("title=DCL001"));
77 }
78
79 #[test]
80 fn test_github_format_warning() {
81 let mut result = LintResult::new("docker-compose.yml");
82 result.failures.push(CheckFailure::new(
83 "DCL006",
84 "no-version-field",
85 Severity::Warning,
86 RuleCategory::Style,
87 "Version field is deprecated",
88 1,
89 1,
90 ));
91
92 let output = format(&[result]);
93 assert!(output.contains("::warning"));
94 }
95
96 #[test]
97 fn test_github_format_info() {
98 let mut result = LintResult::new("docker-compose.yml");
99 result.failures.push(CheckFailure::new(
100 "DCL007",
101 "require-project-name",
102 Severity::Info,
103 RuleCategory::BestPractice,
104 "Consider adding name field",
105 1,
106 1,
107 ));
108
109 let output = format(&[result]);
110 assert!(output.contains("::notice"));
111 }
112
113 #[test]
114 fn test_escape_github() {
115 assert_eq!(escape_github("hello\nworld"), "hello%0Aworld");
116 assert_eq!(escape_github("100%"), "100%25");
117 }
118}