rust_filesearch/output/
json.rs

1use crate::errors::Result;
2use crate::models::Entry;
3use crate::output::format::OutputSink;
4use std::io::Write;
5
6/// JSON array formatter (buffers all entries)
7pub struct JsonFormatter {
8    writer: Box<dyn Write>,
9    entries: Vec<Entry>,
10}
11
12impl JsonFormatter {
13    pub fn new(writer: Box<dyn Write>) -> Self {
14        Self {
15            writer,
16            entries: Vec::new(),
17        }
18    }
19}
20
21impl OutputSink for JsonFormatter {
22    fn write(&mut self, entry: &Entry) -> Result<()> {
23        self.entries.push(entry.clone());
24        Ok(())
25    }
26
27    fn finish(&mut self) -> Result<()> {
28        let json = serde_json::to_string_pretty(&self.entries)?;
29        writeln!(self.writer, "{}", json)?;
30        self.writer.flush()?;
31        Ok(())
32    }
33}
34
35/// NDJSON (newline-delimited JSON) formatter (streaming)
36pub struct NdjsonFormatter {
37    writer: Box<dyn Write>,
38}
39
40impl NdjsonFormatter {
41    pub fn new(writer: Box<dyn Write>) -> Self {
42        Self { writer }
43    }
44}
45
46impl OutputSink for NdjsonFormatter {
47    fn write(&mut self, entry: &Entry) -> Result<()> {
48        let json = serde_json::to_string(entry)?;
49        writeln!(self.writer, "{}", json)?;
50        Ok(())
51    }
52
53    fn finish(&mut self) -> Result<()> {
54        self.writer.flush()?;
55        Ok(())
56    }
57}
58
59#[cfg(test)]
60mod tests {
61    use super::*;
62    use crate::models::EntryKind;
63    use std::path::PathBuf;
64
65    fn make_test_entry(name: &str) -> Entry {
66        use chrono::Utc;
67
68        Entry {
69            path: PathBuf::from(name),
70            name: name.to_string(),
71            size: 1024,
72            kind: EntryKind::File,
73            mtime: Utc::now(),
74            perms: None,
75            owner: None,
76            depth: 0,
77        }
78    }
79
80    #[test]
81    fn test_json_formatter() {
82        use std::io::Cursor;
83
84        let output = Cursor::new(Vec::new());
85        let mut formatter = JsonFormatter::new(Box::new(output));
86
87        formatter.write(&make_test_entry("test.txt")).unwrap();
88        // Note: Can't easily verify output without consuming the formatter
89        // Real usage goes to stdout which works correctly
90    }
91
92    #[test]
93    fn test_ndjson_formatter() {
94        use std::io::Cursor;
95
96        let output = Cursor::new(Vec::new());
97        let mut formatter = NdjsonFormatter::new(Box::new(output));
98
99        formatter.write(&make_test_entry("test1.txt")).unwrap();
100        formatter.write(&make_test_entry("test2.txt")).unwrap();
101        formatter.finish().unwrap();
102    }
103}