codelens_core/output/
json.rs1use std::io::Write;
4
5use crate::analyzer::stats::AnalysisResult;
6use crate::error::Result;
7
8use super::format::{OutputFormat, OutputOptions, Report};
9
10pub struct JsonOutput {
12 pretty: bool,
13}
14
15impl JsonOutput {
16 pub fn new(pretty: bool) -> Self {
18 Self { pretty }
19 }
20}
21
22impl OutputFormat for JsonOutput {
23 fn name(&self) -> &'static str {
24 "json"
25 }
26
27 fn extension(&self) -> &'static str {
28 "json"
29 }
30
31 fn write(
32 &self,
33 report: &Report,
34 options: &OutputOptions,
35 writer: &mut dyn Write,
36 ) -> Result<()> {
37 match report {
38 Report::Analysis(result) => self.write_analysis(result, options, writer),
39 Report::Health(report) => self.write_json(report, writer),
40 Report::Hotspot(report) => self.write_json(report, writer),
41 Report::Trend(report) => self.write_json(report, writer),
42 Report::Estimation(report) => self.write_json(report, writer),
43 Report::EstimationComparison(report) => self.write_json(report, writer),
44 }
45 }
46}
47
48impl JsonOutput {
49 fn write_analysis(
50 &self,
51 result: &AnalysisResult,
52 _options: &OutputOptions,
53 writer: &mut dyn Write,
54 ) -> Result<()> {
55 self.write_json(result, writer)
56 }
57
58 fn write_json<T: serde::Serialize>(&self, data: &T, writer: &mut dyn Write) -> Result<()> {
59 if self.pretty {
60 serde_json::to_writer_pretty(&mut *writer, data)?;
61 } else {
62 serde_json::to_writer(&mut *writer, data)?;
63 }
64 writeln!(writer)?;
65 Ok(())
66 }
67}
68
69#[cfg(test)]
70mod tests {
71 use super::Report;
72 use super::*;
73 use crate::analyzer::stats::{FileStats, LineStats, Summary};
74 use std::path::PathBuf;
75 use std::time::Duration;
76
77 fn make_test_result() -> AnalysisResult {
78 AnalysisResult {
79 files: vec![FileStats {
80 path: PathBuf::from("test.rs"),
81 language: "Rust".to_string(),
82 lines: LineStats {
83 total: 100,
84 code: 80,
85 comment: 10,
86 blank: 10,
87 },
88 size: 2000,
89 complexity: Default::default(),
90 }],
91 summary: Summary::from_file_stats(&[FileStats {
92 path: PathBuf::from("test.rs"),
93 language: "Rust".to_string(),
94 lines: LineStats {
95 total: 100,
96 code: 80,
97 comment: 10,
98 blank: 10,
99 },
100 size: 2000,
101 complexity: Default::default(),
102 }]),
103 elapsed: Duration::from_millis(100),
104 scanned_files: 1,
105 skipped_files: 0,
106 }
107 }
108
109 #[test]
110 fn test_json_output_name() {
111 let output = JsonOutput::new(false);
112 assert_eq!(output.name(), "json");
113 assert_eq!(output.extension(), "json");
114 }
115
116 #[test]
117 fn test_json_output_compact() {
118 let output = JsonOutput::new(false);
119 let result = make_test_result();
120 let options = OutputOptions::default();
121
122 let mut buffer = Vec::new();
123 output
124 .write(&Report::Analysis(result), &options, &mut buffer)
125 .unwrap();
126
127 let json_str = String::from_utf8(buffer).unwrap();
128 assert!(json_str.contains("\"scanned_files\":1"));
129 assert!(json_str.contains("\"language\":\"Rust\""));
130 assert!(!json_str.contains(" \""));
132 }
133
134 #[test]
135 fn test_json_output_pretty() {
136 let output = JsonOutput::new(true);
137 let result = make_test_result();
138 let options = OutputOptions::default();
139
140 let mut buffer = Vec::new();
141 output
142 .write(&Report::Analysis(result), &options, &mut buffer)
143 .unwrap();
144
145 let json_str = String::from_utf8(buffer).unwrap();
146 assert!(json_str.contains(" "));
148 }
149
150 #[test]
151 fn test_json_output_valid_json() {
152 let output = JsonOutput::new(false);
153 let result = make_test_result();
154 let options = OutputOptions::default();
155
156 let mut buffer = Vec::new();
157 output
158 .write(&Report::Analysis(result), &options, &mut buffer)
159 .unwrap();
160
161 let json_str = String::from_utf8(buffer).unwrap();
162 let parsed: serde_json::Value = serde_json::from_str(&json_str).unwrap();
164 assert!(parsed.is_object());
165 }
166}