datalab_cli/commands/
track_changes.rs1use crate::cache::Cache;
2use crate::client::{add_form_field, build_form_with_file, DatalabClient};
3use crate::error::{DatalabError, Result};
4use crate::output::Progress;
5use clap::Args;
6use reqwest::multipart::Form;
7use serde_json::json;
8use std::fs;
9use std::path::PathBuf;
10
11#[derive(Args, Debug)]
12pub struct TrackChangesArgs {
13 #[arg(value_name = "FILE|URL")]
15 pub input: String,
16
17 #[arg(
19 long,
20 default_value = "html,markdown",
21 value_name = "FORMATS",
22 help_heading = "Output Options"
23 )]
24 pub output_format: String,
25
26 #[arg(long, help_heading = "Output Options")]
28 pub paginate: bool,
29
30 #[arg(long, help_heading = "Cache Options")]
32 pub skip_cache: bool,
33
34 #[arg(long, short, value_name = "FILE", help_heading = "Output Options")]
36 pub output: Option<PathBuf>,
37
38 #[arg(
40 long,
41 default_value = "300",
42 value_name = "SECS",
43 help_heading = "Advanced Options"
44 )]
45 pub timeout: u64,
46}
47
48impl TrackChangesArgs {
49 fn to_cache_params(&self) -> serde_json::Value {
50 json!({
51 "output_format": self.output_format,
52 "paginate": self.paginate,
53 })
54 }
55
56 fn add_to_form(&self, mut form: Form) -> Form {
57 form = add_form_field(form, "output_format", &self.output_format);
58
59 if self.paginate {
60 form = add_form_field(form, "paginate", "true");
61 }
62
63 form
64 }
65}
66
67pub async fn execute(args: TrackChangesArgs, progress: &Progress) -> Result<()> {
68 let client = DatalabClient::new(Some(args.timeout))?;
69 let cache = Cache::new()?;
70
71 let is_url = args.input.starts_with("http://") || args.input.starts_with("https://");
72 let file_path = if is_url {
73 None
74 } else {
75 Some(PathBuf::from(&args.input))
76 };
77
78 let file_str = file_path.as_ref().map(|p| p.to_string_lossy().to_string());
79 progress.start("track-changes", file_str.as_deref());
80
81 let file_hash = if let Some(ref path) = file_path {
82 if !path.exists() {
83 return Err(DatalabError::FileNotFound(path.clone()));
84 }
85 Some(Cache::hash_file(path)?)
86 } else {
87 None
88 };
89
90 let cache_params = args.to_cache_params();
91 let cache_key = Cache::generate_key(
92 file_hash.as_deref(),
93 if is_url { Some(&args.input) } else { None },
94 "track-changes",
95 &cache_params,
96 );
97
98 if !args.skip_cache {
99 if let Some(cached) = cache.get(&cache_key) {
100 progress.cache_hit(&cache_key);
101 output_result(&cached, args.output.as_ref())?;
102 return Ok(());
103 }
104 }
105
106 let form = if let Some(ref path) = file_path {
107 let (form, _) = build_form_with_file(path)?;
108 args.add_to_form(form)
109 } else {
110 let form = Form::new().text("file_url", args.input.clone());
111 args.add_to_form(form)
112 };
113
114 let result = client
115 .submit_and_poll("track-changes", form, progress)
116 .await?;
117
118 let file_path_str = file_path.as_ref().map(|p| p.to_string_lossy().to_string());
119 cache.set(
120 &cache_key,
121 &result,
122 "track-changes",
123 file_hash.as_deref(),
124 file_path_str.as_deref(),
125 )?;
126
127 output_result(&result, args.output.as_ref())?;
128
129 Ok(())
130}
131
132fn output_result(result: &serde_json::Value, output_file: Option<&PathBuf>) -> Result<()> {
133 let json_output = serde_json::to_string_pretty(result)?;
134
135 if let Some(path) = output_file {
136 fs::write(path, &json_output)?;
137 } else {
138 println!("{}", json_output);
139 }
140
141 Ok(())
142}