1use super::Formatter;
2use crate::scanner::{FileNode, TreeStats};
3use anyhow::Result;
4use chrono::{DateTime, Local};
5use csv::Writer;
6use std::io::Write;
7use std::path::Path;
8
9pub struct CsvFormatter;
10
11impl Default for CsvFormatter {
12 fn default() -> Self {
13 Self::new()
14 }
15}
16
17impl CsvFormatter {
18 pub fn new() -> Self {
19 Self
20 }
21}
22
23impl Formatter for CsvFormatter {
24 fn format(
25 &self,
26 writer: &mut dyn Write,
27 nodes: &[FileNode],
28 _stats: &TreeStats,
29 root_path: &Path,
30 ) -> Result<()> {
31 let mut csv_writer = Writer::from_writer(writer);
32
33 csv_writer.write_record([
35 "path",
36 "type",
37 "size",
38 "permissions",
39 "uid",
40 "gid",
41 "modified",
42 "depth",
43 ])?;
44
45 let mut sorted_nodes = nodes.to_vec();
47 sorted_nodes.sort_by(|a, b| a.path.cmp(&b.path));
48
49 for node in &sorted_nodes {
50 let rel_path = if node.path == root_path {
51 ".".to_string()
52 } else {
53 node.path
54 .strip_prefix(root_path)
55 .unwrap_or(&node.path)
56 .to_string_lossy()
57 .to_string()
58 };
59
60 let file_type = if node.is_dir { "d" } else { "f" };
61 let datetime = DateTime::<Local>::from(node.modified);
62
63 csv_writer.write_record(&[
64 rel_path,
65 file_type.to_string(),
66 node.size.to_string(),
67 format!("{:o}", node.permissions),
68 node.uid.to_string(),
69 node.gid.to_string(),
70 datetime.to_rfc3339(),
71 node.depth.to_string(),
72 ])?;
73 }
74
75 csv_writer.flush()?;
76 Ok(())
77 }
78}