codelens_core/output/
csv.rs1use std::io::Write;
4
5use crate::analyzer::stats::AnalysisResult;
6use crate::error::Result;
7
8use super::format::{OutputFormat, OutputOptions};
9
10pub struct CsvOutput;
12
13impl CsvOutput {
14 pub fn new() -> Self {
16 Self
17 }
18}
19
20impl Default for CsvOutput {
21 fn default() -> Self {
22 Self::new()
23 }
24}
25
26impl OutputFormat for CsvOutput {
27 fn name(&self) -> &'static str {
28 "csv"
29 }
30
31 fn extension(&self) -> &'static str {
32 "csv"
33 }
34
35 fn write(
36 &self,
37 result: &AnalysisResult,
38 _options: &OutputOptions,
39 writer: &mut dyn Write,
40 ) -> Result<()> {
41 writeln!(writer, "Language,Files,Code,Comment,Blank,Total,Size")?;
43
44 for (name, stats) in &result.summary.by_language {
46 writeln!(
47 writer,
48 "{},{},{},{},{},{},{}",
49 name,
50 stats.files,
51 stats.lines.code,
52 stats.lines.comment,
53 stats.lines.blank,
54 stats.lines.total,
55 stats.size
56 )?;
57 }
58
59 Ok(())
60 }
61}
62
63#[cfg(test)]
64mod tests {
65 use super::*;
66 use crate::analyzer::stats::{FileStats, LineStats, Summary};
67 use std::path::PathBuf;
68 use std::time::Duration;
69
70 fn make_test_result() -> AnalysisResult {
71 let files = vec![
72 FileStats {
73 path: PathBuf::from("main.rs"),
74 language: "Rust".to_string(),
75 lines: LineStats {
76 total: 100,
77 code: 80,
78 comment: 10,
79 blank: 10,
80 },
81 size: 2000,
82 complexity: Default::default(),
83 },
84 FileStats {
85 path: PathBuf::from("test.py"),
86 language: "Python".to_string(),
87 lines: LineStats {
88 total: 50,
89 code: 40,
90 comment: 5,
91 blank: 5,
92 },
93 size: 1000,
94 complexity: Default::default(),
95 },
96 ];
97 AnalysisResult {
98 summary: Summary::from_file_stats(&files),
99 files,
100 elapsed: Duration::from_millis(100),
101 scanned_files: 2,
102 skipped_files: 0,
103 }
104 }
105
106 #[test]
107 fn test_csv_output_name() {
108 let output = CsvOutput::new();
109 assert_eq!(output.name(), "csv");
110 assert_eq!(output.extension(), "csv");
111 }
112
113 #[test]
114 fn test_csv_output_header() {
115 let output = CsvOutput;
116 let result = make_test_result();
117 let options = OutputOptions::default();
118
119 let mut buffer = Vec::new();
120 output.write(&result, &options, &mut buffer).unwrap();
121
122 let csv_str = String::from_utf8(buffer).unwrap();
123 let lines: Vec<&str> = csv_str.lines().collect();
124
125 assert_eq!(lines[0], "Language,Files,Code,Comment,Blank,Total,Size");
126 }
127
128 #[test]
129 fn test_csv_output_data() {
130 let output = CsvOutput;
131 let result = make_test_result();
132 let options = OutputOptions::default();
133
134 let mut buffer = Vec::new();
135 output.write(&result, &options, &mut buffer).unwrap();
136
137 let csv_str = String::from_utf8(buffer).unwrap();
138
139 assert!(csv_str.contains("Rust"));
141 assert!(csv_str.contains("Python"));
142 assert!(csv_str.contains(",80,")); assert!(csv_str.contains(",40,")); }
145
146 #[test]
147 fn test_csv_output_line_count() {
148 let output = CsvOutput;
149 let result = make_test_result();
150 let options = OutputOptions::default();
151
152 let mut buffer = Vec::new();
153 output.write(&result, &options, &mut buffer).unwrap();
154
155 let csv_str = String::from_utf8(buffer).unwrap();
156 let lines: Vec<&str> = csv_str.lines().collect();
157
158 assert_eq!(lines.len(), 3);
160 }
161}