Skip to main content

fallow_output/
format.rs

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