Skip to content

Commit a7e9baa

Browse files
committed
feat: add output format option
1 parent 32d877a commit a7e9baa

File tree

2 files changed

+74
-33
lines changed

2 files changed

+74
-33
lines changed

readme.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ The list includes the comment text, the file path, and additional metadata, such
128128

129129
Todoctor supports the following command-line options:
130130

131-
### --month \<N>
131+
### --month
132132

133133
Specifies the number of months to include when tracking TODOs in the repository. If not provided, defaults to 3 months.
134134

@@ -191,6 +191,16 @@ Example:
191191
todoctor --exclude-keywords WARNING --exclude-keywords DEPRECATED
192192
```
193193

194+
### --output-format
195+
196+
You can specify the format of the report. Possible options are `html` and `json`. The default value is `html`.
197+
198+
Example:
199+
200+
```sh
201+
todoctor --output-format json
202+
```
203+
194204
### --output
195205

196206
You can define the folder where the report file will be saved. By default it is `todoctor` folder in the project root.

src/main.rs

Lines changed: 63 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
use clap::{ArgAction, CommandFactory, Parser};
1+
use clap::{ArgAction, CommandFactory, Parser, ValueEnum};
22
use futures::future::join_all;
33
use indicatif::{ProgressBar, ProgressStyle};
44
use open;
5-
use serde_json::json;
5+
use serde_json::{json, Value};
66
use std::fs::File;
77
use std::io::{self, BufRead, BufReader, BufWriter, Write};
88
use std::path::{Path, PathBuf};
@@ -33,6 +33,12 @@ use tokio::sync::Semaphore;
3333

3434
const HISTORY_TEMP_FILE: &str = "todo_history_temp.json";
3535

36+
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Debug)]
37+
enum OutputFormat {
38+
Html,
39+
Json,
40+
}
41+
3642
#[derive(Parser, Debug)]
3743
#[command(
3844
name = "todoctor",
@@ -58,6 +64,10 @@ struct Cli {
5864
#[arg(short = 'E', long, action = ArgAction::Append)]
5965
exclude_keywords: Vec<String>,
6066

67+
/// Output format
68+
#[arg(short, long, default_value = "html")]
69+
output_format: OutputFormat,
70+
6171
/// Output directory
6272
#[arg(short, long, default_value = "todoctor")]
6373
output: String,
@@ -91,6 +101,8 @@ async fn main() {
91101
.map(|values| values.map(String::from).collect())
92102
.unwrap_or_else(Vec::new);
93103

104+
let output_format = args.get_one::<OutputFormat>("output_format").unwrap();
105+
94106
let output_directory = args.get_one::<String>("output").unwrap();
95107

96108
if !check_git_repository(".").await {
@@ -358,41 +370,60 @@ async fn main() {
358370
"version": version,
359371
});
360372

361-
let mut escaped_json_data = json_data.clone();
362-
escape_json_values(&mut escaped_json_data);
363-
364-
let escaped_json_string = serde_json::to_string(&escaped_json_data)
365-
.expect("Error: Could not serializing JSON");
373+
generate_output(*output_format, output_directory, json_data).await;
374+
}
366375

367-
let dist_path: PathBuf =
368-
get_dist_path().expect("Error: Could not get current dist path.");
376+
async fn generate_output(
377+
output_format: OutputFormat,
378+
output_directory: &str,
379+
json_data: Value,
380+
) {
381+
match output_format {
382+
OutputFormat::Html => {
383+
let dist_path: PathBuf = get_dist_path()
384+
.expect("Error: Could not get current dist path.");
385+
386+
copy_dir_recursive(&dist_path, Path::new(output_directory))
387+
.await
388+
.expect("Error copying directory");
369389

370-
copy_dir_recursive(&dist_path, Path::new(output_directory))
371-
.await
372-
.expect("Error copying directory");
390+
let mut escaped_json_data = json_data.clone();
391+
escape_json_values(&mut escaped_json_data);
373392

374-
let index_path = Path::new(output_directory).join("index.html");
375-
if fs::metadata(&index_path).await.is_ok() {
376-
let mut index_content = fs::read_to_string(&index_path)
377-
.await
378-
.expect("Error reading index.html");
393+
let escaped_json_string = serde_json::to_string(&escaped_json_data)
394+
.expect("Error: Could not serializing JSON");
379395

380-
if let Some(pos) = index_content.find("</head>") {
381-
let script_tag: String = format!(
382-
"<script>window.data = {};</script>",
383-
escaped_json_string
384-
);
385-
index_content.insert_str(pos, &script_tag);
396+
let index_path = Path::new(output_directory).join("index.html");
397+
if fs::metadata(&index_path).await.is_ok() {
398+
let mut index_content = fs::read_to_string(&index_path)
399+
.await
400+
.expect("Error reading index.html");
401+
402+
if let Some(pos) = index_content.find("</head>") {
403+
let script_tag = format!(
404+
"<script>window.data = {};</script>",
405+
escaped_json_string
406+
);
407+
index_content.insert_str(pos, &script_tag);
408+
409+
fs::write(&index_path, index_content)
410+
.await
411+
.expect("Error writing modified index.html");
412+
} else {
413+
eprintln!("Error: No </head> tag found in index.html");
414+
}
386415

387-
fs::write(&index_path, index_content)
388-
.await
389-
.expect("Error writing modified index.html");
390-
} else {
391-
eprintln!("Error: No </head> tag found in index.html");
416+
if let Err(e) = open::that(&index_path) {
417+
eprintln!("Error: Cannot open index.html: {:?}", e);
418+
}
419+
}
420+
}
421+
OutputFormat::Json => {
422+
let json_path = Path::new(output_directory).join("report.json");
423+
let mut file = File::create(&json_path)
424+
.expect("Failed to create JSON report file");
425+
file.write_all(json_data.to_string().as_bytes())
426+
.expect("Failed to write JSON data");
392427
}
393-
}
394-
395-
if let Err(e) = open::that(&index_path) {
396-
eprintln!("Error: Cannot open index.html: {:?}", e);
397428
}
398429
}

0 commit comments

Comments
 (0)