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}
34
35#[cfg(test)]
36mod tests {
37 use super::*;
38
39 const VARIANTS: [OutputFormat; 11] = [
40 OutputFormat::Human,
41 OutputFormat::Json,
42 OutputFormat::Sarif,
43 OutputFormat::Compact,
44 OutputFormat::Markdown,
45 OutputFormat::CodeClimate,
46 OutputFormat::PrCommentGithub,
47 OutputFormat::PrCommentGitlab,
48 OutputFormat::ReviewGithub,
49 OutputFormat::ReviewGitlab,
50 OutputFormat::Badge,
51 ];
52
53 #[test]
54 fn default_is_human() {
55 assert!(matches!(OutputFormat::default(), OutputFormat::Human));
56 }
57
58 #[test]
59 fn debug_names_remain_stable() {
60 let names: Vec<String> = VARIANTS
61 .iter()
62 .map(|variant| format!("{variant:?}"))
63 .collect();
64 assert_eq!(
65 names,
66 vec![
67 "Human",
68 "Json",
69 "Sarif",
70 "Compact",
71 "Markdown",
72 "CodeClimate",
73 "PrCommentGithub",
74 "PrCommentGitlab",
75 "ReviewGithub",
76 "ReviewGitlab",
77 "Badge",
78 ]
79 );
80 }
81
82 #[test]
83 fn variants_are_distinct() {
84 let names: Vec<String> = VARIANTS
85 .iter()
86 .map(|variant| format!("{variant:?}"))
87 .collect();
88
89 for (i, a) in names.iter().enumerate() {
90 for (j, b) in names.iter().enumerate() {
91 if i != j {
92 assert_ne!(a, b);
93 }
94 }
95 }
96 }
97}