Skip to main content

fallow_config/config/
format.rs

1/// Output format for results.
2///
3/// This is CLI-only (via `--format` flag), not stored in config files.
4#[derive(Debug, Default, Clone, Copy)]
5pub enum OutputFormat {
6    /// Human-readable terminal output with source context.
7    #[default]
8    Human,
9    /// Machine-readable JSON.
10    Json,
11    /// SARIF format for GitHub Code Scanning.
12    Sarif,
13    /// One issue per line (grep-friendly).
14    Compact,
15    /// Markdown for PR comments.
16    Markdown,
17    /// `CodeClimate` JSON for GitLab Code Quality.
18    ///
19    /// CLI aliases: `codeclimate`, `gitlab-codequality`, `gitlab-code-quality`.
20    CodeClimate,
21    /// Shields.io-compatible SVG badge (health command only).
22    Badge,
23}
24
25#[cfg(test)]
26mod tests {
27    use super::*;
28
29    #[test]
30    fn output_format_default_is_human() {
31        let format = OutputFormat::default();
32        assert!(matches!(format, OutputFormat::Human));
33    }
34
35    #[test]
36    fn output_format_all_variants_constructible() {
37        // Verify all variants can be constructed and pattern-matched
38        assert!(matches!(OutputFormat::Human, OutputFormat::Human));
39        assert!(matches!(OutputFormat::Json, OutputFormat::Json));
40        assert!(matches!(OutputFormat::Sarif, OutputFormat::Sarif));
41        assert!(matches!(OutputFormat::Compact, OutputFormat::Compact));
42        assert!(matches!(OutputFormat::Markdown, OutputFormat::Markdown));
43        assert!(matches!(
44            OutputFormat::CodeClimate,
45            OutputFormat::CodeClimate
46        ));
47        assert!(matches!(OutputFormat::Badge, OutputFormat::Badge));
48    }
49
50    #[test]
51    fn output_format_debug_impl() {
52        // Verify Debug is derived and produces reasonable output
53        let human = format!("{:?}", OutputFormat::Human);
54        assert_eq!(human, "Human");
55        let json = format!("{:?}", OutputFormat::Json);
56        assert_eq!(json, "Json");
57        let sarif = format!("{:?}", OutputFormat::Sarif);
58        assert_eq!(sarif, "Sarif");
59        let compact = format!("{:?}", OutputFormat::Compact);
60        assert_eq!(compact, "Compact");
61        let markdown = format!("{:?}", OutputFormat::Markdown);
62        assert_eq!(markdown, "Markdown");
63        let codeclimate = format!("{:?}", OutputFormat::CodeClimate);
64        assert_eq!(codeclimate, "CodeClimate");
65        let badge = format!("{:?}", OutputFormat::Badge);
66        assert_eq!(badge, "Badge");
67    }
68
69    #[test]
70    fn output_format_copy() {
71        let original = OutputFormat::Json;
72        let copied = original;
73        assert!(matches!(copied, OutputFormat::Json));
74        // Original still usable (Copy)
75        assert!(matches!(original, OutputFormat::Json));
76    }
77
78    #[test]
79    #[expect(
80        clippy::clone_on_copy,
81        reason = "explicitly testing the Clone impl for coverage"
82    )]
83    fn output_format_clone_all_variants() {
84        let variants = [
85            OutputFormat::Human,
86            OutputFormat::Json,
87            OutputFormat::Sarif,
88            OutputFormat::Compact,
89            OutputFormat::Markdown,
90            OutputFormat::CodeClimate,
91            OutputFormat::Badge,
92        ];
93        for variant in variants {
94            let cloned = variant.clone();
95            // Debug output must match between original and clone
96            assert_eq!(format!("{cloned:?}"), format!("{variant:?}"));
97        }
98    }
99
100    #[test]
101    #[expect(
102        clippy::clone_on_copy,
103        reason = "explicitly testing the Clone impl for coverage"
104    )]
105    fn output_format_clone_preserves_variant() {
106        let badge = OutputFormat::Badge;
107        let cloned = badge.clone();
108        assert!(matches!(cloned, OutputFormat::Badge));
109
110        let codeclimate = OutputFormat::CodeClimate;
111        let cloned = codeclimate.clone();
112        assert!(matches!(cloned, OutputFormat::CodeClimate));
113    }
114
115    #[test]
116    fn output_format_default_matches_human_debug() {
117        // Default variant should produce "Human" debug string
118        assert_eq!(format!("{:?}", OutputFormat::default()), "Human");
119    }
120
121    #[test]
122    fn output_format_variants_are_distinct() {
123        // Verify each variant has a unique debug representation
124        let debug_strings: Vec<String> = [
125            OutputFormat::Human,
126            OutputFormat::Json,
127            OutputFormat::Sarif,
128            OutputFormat::Compact,
129            OutputFormat::Markdown,
130            OutputFormat::CodeClimate,
131            OutputFormat::Badge,
132        ]
133        .iter()
134        .map(|v| format!("{v:?}"))
135        .collect();
136
137        for (i, a) in debug_strings.iter().enumerate() {
138            for (j, b) in debug_strings.iter().enumerate() {
139                if i != j {
140                    assert_ne!(
141                        a, b,
142                        "variants at index {i} and {j} have the same debug output"
143                    );
144                }
145            }
146        }
147    }
148}