todo_tree/printer/
json.rs1use super::options::PrintOptions;
2use serde::Serialize;
3use std::collections::HashMap;
4use std::io::{self, Write};
5use todo_tree_core::ScanResult;
6
7#[derive(Debug, Serialize)]
8pub struct JsonOutput {
9 pub files: Vec<JsonFileEntry>,
10 pub summary: JsonSummary,
11}
12
13#[derive(Debug, Serialize)]
14pub struct JsonFileEntry {
15 pub path: String,
16 pub items: Vec<JsonTodoItem>,
17}
18
19#[derive(Debug, Serialize)]
20pub struct JsonTodoItem {
21 pub tag: String,
22 pub message: String,
23 pub line: usize,
24 pub column: usize,
25 #[serde(skip_serializing_if = "Option::is_none")]
26 pub author: Option<String>,
27 pub priority: String,
28}
29
30#[derive(Debug, Serialize)]
31pub struct JsonSummary {
32 pub total_count: usize,
33 pub files_with_todos: usize,
34 pub files_scanned: usize,
35 pub tag_counts: HashMap<String, usize>,
36}
37
38pub fn print_json<W: Write>(
39 writer: &mut W,
40 result: &ScanResult,
41 options: &PrintOptions,
42) -> io::Result<()> {
43 let json_result = JsonOutput::from_scan_result(result, options);
44 let json_str = serde_json::to_string_pretty(&json_result).map_err(io::Error::other)?;
45 writeln!(writer, "{}", json_str)?;
46 Ok(())
47}
48
49impl JsonOutput {
50 pub fn from_scan_result(result: &ScanResult, options: &PrintOptions) -> Self {
51 let mut files: Vec<JsonFileEntry> = result
52 .sorted_files()
53 .iter()
54 .map(|(path, items)| {
55 let display_path = if options.full_paths {
56 path.display().to_string()
57 } else if let Some(base) = &options.base_path {
58 path.strip_prefix(base)
59 .map(|p| p.display().to_string())
60 .unwrap_or_else(|_| path.display().to_string())
61 } else {
62 path.display().to_string()
63 };
64
65 JsonFileEntry {
66 path: display_path,
67 items: items
68 .iter()
69 .map(|item| JsonTodoItem {
70 tag: item.tag.clone(),
71 message: item.message.clone(),
72 line: item.line,
73 column: item.column,
74 author: item.author.clone(),
75 priority: format!("{:?}", item.priority),
76 })
77 .collect(),
78 }
79 })
80 .collect();
81
82 files.sort_by(|a, b| a.path.cmp(&b.path));
83
84 let summary = JsonSummary {
85 total_count: result.summary.total_count,
86 files_with_todos: result.summary.files_with_todos,
87 files_scanned: result.summary.files_scanned,
88 tag_counts: result.summary.tag_counts.clone(),
89 };
90
91 Self { files, summary }
92 }
93}