fallow_types/output_format.rs
1/// Output format for fallow results.
2///
3/// This is command-line and integration metadata, not stored in config files.
4/// Keeping it in `fallow-types` lets config, output, CLI, MCP, and API layers
5/// agree on the same contract without creating a config-to-output dependency.
6#[derive(Debug, Default, Clone, Copy)]
7pub enum OutputFormat {
8 /// Human-readable terminal output with source context.
9 #[default]
10 Human,
11 /// Machine-readable JSON.
12 Json,
13 /// SARIF format for GitHub Code Scanning.
14 Sarif,
15 /// One issue per line (grep-friendly).
16 Compact,
17 /// Markdown for PR comments.
18 Markdown,
19 /// `CodeClimate` JSON for GitLab Code Quality.
20 ///
21 /// CLI aliases: `codeclimate`, `gitlab-codequality`, `gitlab-code-quality`.
22 CodeClimate,
23 /// GitHub-flavored sticky PR comment markdown.
24 PrCommentGithub,
25 /// GitLab-flavored sticky MR comment markdown.
26 PrCommentGitlab,
27 /// GitHub PR review JSON envelope.
28 ReviewGithub,
29 /// GitLab MR review JSON envelope.
30 ReviewGitlab,
31 /// Shields.io-compatible SVG badge (health command only).
32 Badge,
33 /// GitHub Actions workflow-command annotations (`::error` / `::warning` /
34 /// `::notice` lines). Provider-prefixed name because workflow-command
35 /// annotations are a GitHub-only concept with no GitLab twin.
36 GithubAnnotations,
37 /// GitHub Actions job-summary markdown (for `>> $GITHUB_STEP_SUMMARY`).
38 /// Provider-prefixed for the same reason as `GithubAnnotations`.
39 GithubSummary,
40}
41
42#[cfg(test)]
43mod tests {
44 use super::*;
45
46 const VARIANTS: [OutputFormat; 13] = [
47 OutputFormat::Human,
48 OutputFormat::Json,
49 OutputFormat::Sarif,
50 OutputFormat::Compact,
51 OutputFormat::Markdown,
52 OutputFormat::CodeClimate,
53 OutputFormat::PrCommentGithub,
54 OutputFormat::PrCommentGitlab,
55 OutputFormat::ReviewGithub,
56 OutputFormat::ReviewGitlab,
57 OutputFormat::Badge,
58 OutputFormat::GithubAnnotations,
59 OutputFormat::GithubSummary,
60 ];
61
62 #[test]
63 fn default_is_human() {
64 assert!(matches!(OutputFormat::default(), OutputFormat::Human));
65 }
66
67 #[test]
68 fn debug_names_remain_stable() {
69 let names: Vec<String> = VARIANTS
70 .iter()
71 .map(|variant| format!("{variant:?}"))
72 .collect();
73 assert_eq!(
74 names,
75 vec![
76 "Human",
77 "Json",
78 "Sarif",
79 "Compact",
80 "Markdown",
81 "CodeClimate",
82 "PrCommentGithub",
83 "PrCommentGitlab",
84 "ReviewGithub",
85 "ReviewGitlab",
86 "Badge",
87 "GithubAnnotations",
88 "GithubSummary",
89 ]
90 );
91 }
92
93 #[test]
94 fn variants_are_distinct() {
95 let names: Vec<String> = VARIANTS
96 .iter()
97 .map(|variant| format!("{variant:?}"))
98 .collect();
99
100 for (i, a) in names.iter().enumerate() {
101 for (j, b) in names.iter().enumerate() {
102 if i != j {
103 assert_ne!(a, b);
104 }
105 }
106 }
107 }
108}