1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
/// Output format for fallow results.
///
/// This is command-line and integration metadata, not stored in config files.
/// Keeping it in `fallow-types` lets config, output, CLI, MCP, and API layers
/// agree on the same contract without creating a config-to-output dependency.
#[derive(Debug, Default, Clone, Copy)]
pub enum OutputFormat {
/// Human-readable terminal output with source context.
#[default]
Human,
/// Machine-readable JSON.
Json,
/// SARIF format for GitHub Code Scanning.
Sarif,
/// One issue per line (grep-friendly).
Compact,
/// Markdown for PR comments.
Markdown,
/// `CodeClimate` JSON for GitLab Code Quality.
///
/// CLI aliases: `codeclimate`, `gitlab-codequality`, `gitlab-code-quality`.
CodeClimate,
/// GitHub-flavored sticky PR comment markdown.
PrCommentGithub,
/// GitLab-flavored sticky MR comment markdown.
PrCommentGitlab,
/// GitHub PR review JSON envelope.
ReviewGithub,
/// GitLab MR review JSON envelope.
ReviewGitlab,
/// Shields.io-compatible SVG badge (health command only).
Badge,
/// GitHub Actions workflow-command annotations (`::error` / `::warning` /
/// `::notice` lines). Provider-prefixed name because workflow-command
/// annotations are a GitHub-only concept with no GitLab twin.
GithubAnnotations,
/// GitHub Actions job-summary markdown (for `>> $GITHUB_STEP_SUMMARY`).
/// Provider-prefixed for the same reason as `GithubAnnotations`.
GithubSummary,
}
impl OutputFormat {
/// The `--format` spelling of this variant.
///
/// Notes that name the format a run degraded on have to name it the way the
/// user would pass it, so the sentence doubles as the fix. `codeclimate` is
/// the canonical spelling of the aliased GitLab Code Quality format.
#[must_use]
pub const fn flag_label(self) -> &'static str {
match self {
Self::Human => "human",
Self::Json => "json",
Self::Sarif => "sarif",
Self::Compact => "compact",
Self::Markdown => "markdown",
Self::CodeClimate => "codeclimate",
Self::PrCommentGithub => "pr-comment-github",
Self::PrCommentGitlab => "pr-comment-gitlab",
Self::ReviewGithub => "review-github",
Self::ReviewGitlab => "review-gitlab",
Self::Badge => "badge",
Self::GithubAnnotations => "github-annotations",
Self::GithubSummary => "github-summary",
}
}
}
#[cfg(test)]
mod tests {
use super::*;
const VARIANTS: [OutputFormat; 13] = [
OutputFormat::Human,
OutputFormat::Json,
OutputFormat::Sarif,
OutputFormat::Compact,
OutputFormat::Markdown,
OutputFormat::CodeClimate,
OutputFormat::PrCommentGithub,
OutputFormat::PrCommentGitlab,
OutputFormat::ReviewGithub,
OutputFormat::ReviewGitlab,
OutputFormat::Badge,
OutputFormat::GithubAnnotations,
OutputFormat::GithubSummary,
];
/// The labels are what a note tells a user to pass, so they must be the
/// `--format` values clap accepts rather than prettier prose.
#[test]
fn flag_labels_are_the_format_values() {
let labels: Vec<&str> = VARIANTS
.iter()
.map(|variant| variant.flag_label())
.collect();
assert_eq!(
labels,
vec![
"human",
"json",
"sarif",
"compact",
"markdown",
"codeclimate",
"pr-comment-github",
"pr-comment-gitlab",
"review-github",
"review-gitlab",
"badge",
"github-annotations",
"github-summary",
]
);
}
#[test]
fn default_is_human() {
assert!(matches!(OutputFormat::default(), OutputFormat::Human));
}
#[test]
fn debug_names_remain_stable() {
let names: Vec<String> = VARIANTS
.iter()
.map(|variant| format!("{variant:?}"))
.collect();
assert_eq!(
names,
vec![
"Human",
"Json",
"Sarif",
"Compact",
"Markdown",
"CodeClimate",
"PrCommentGithub",
"PrCommentGitlab",
"ReviewGithub",
"ReviewGitlab",
"Badge",
"GithubAnnotations",
"GithubSummary",
]
);
}
}