1use cli_ui::progress::format_bytes;
10use cli_ui::styles::{paint, CYAN, DIM, OK, YELLOW};
11use cli_ui::{header, ok, phase, step, summary, CliCommand, CliOptions, Result};
12use std::path::PathBuf;
13
14#[derive(CliOptions)]
17struct Global {
18 #[arg(short = 'v', long = "verbose", negatable)]
20 verbose: bool,
21
22 #[arg(short = 'c', long = "config")]
24 config: Option<PathBuf>,
25}
26
27#[derive(CliOptions)]
30#[cli(
31 about = "download a file from URL",
32 example = "mytool download https://example.com/file.csv",
33 example = "mytool dl https://example.com/file.csv -o ./data/ -j 8"
34)]
35struct DownloadOpt {
36 #[arg(positional)]
38 url: String,
39
40 #[arg(short = 'o', long = "output", default = ".")]
42 output: PathBuf,
43
44 #[arg(section = "Performance", short = 'j', long = "jobs", default = 4)]
46 jobs: usize,
47}
48
49#[derive(CliOptions)]
50#[cli(about = "upload a file to remote storage")]
51struct UploadOpt {
52 #[arg(positional)]
54 file: PathBuf,
55
56 #[arg(positional)]
58 remote: String,
59
60 #[arg(section = "Transfer", short = 'z', long = "compress", negatable)]
62 compress: bool,
63}
64
65#[derive(CliOptions)]
66#[cli(
67 about = "download from a network endpoint",
68 example = "mytool net download https://example.com/data --timeout 60"
69)]
70struct NetDownloadOpt {
71 #[arg(positional)]
73 url: String,
74
75 #[arg(long = "timeout", default = 30)]
77 timeout: u32,
78}
79
80#[derive(CliCommand)]
83#[cli(about = "network diagnostics and operations")]
84enum NetCmd {
85 #[cli(alias = "dl")]
87 Download(NetDownloadOpt), Ping, }
92
93#[derive(CliCommand)]
96#[cli(
97 name = "mytool",
98 about = "batch file processor",
99 tagline = "fast and parallel",
100 theme = "cyan",
101 global = Global,
102 example = "mytool download https://example.com/file.csv",
103 example = "mytool -v upload ./report.pdf s3://bucket/report.pdf --compress",
104 url = "https://github.com/you/mytool",
105)]
106enum Cmd {
107 #[cli(alias = "dl")]
109 Download(DownloadOpt), #[cli(alias = "up")]
113 Upload(UploadOpt), #[cli(alias = "net")]
117 Network(NetCmd), Status, }
122
123fn download(g: &Global, opt: DownloadOpt) {
126 header!(
127 "mytool",
128 env!("CARGO_PKG_VERSION"),
129 "batch file processor",
130 "fast and parallel"
131 );
132 phase!("download", "{}", opt.url);
133 if g.verbose {
134 phase!("debug", "output={} jobs={}", opt.output.display(), opt.jobs);
135 }
136 step!("fetching");
137 ok!(opt.output.join("file.csv").display());
138 summary! {
139 done: "Download complete",
140 "url" => paint(CYAN, &opt.url),
141 "output" => paint(CYAN, &opt.output.display().to_string()),
142 section,
143 "size" => paint(YELLOW, &format_bytes(43_100)),
144 "time" => paint(DIM, "320ms"),
145 }
146}
147
148fn upload(g: &Global, opt: UploadOpt) {
149 header!(
150 "mytool",
151 env!("CARGO_PKG_VERSION"),
152 "batch file processor",
153 "fast and parallel"
154 );
155 phase!("upload", "{} → {}", opt.file.display(), opt.remote);
156 if g.verbose {
157 phase!("debug", "compress={}", opt.compress);
158 }
159 step!("uploading");
160 ok!(opt.remote.as_str());
161 summary! {
162 done: "Upload complete",
163 "file" => paint(CYAN, &opt.file.display().to_string()),
164 "remote" => paint(CYAN, &opt.remote),
165 }
166}
167
168fn net_download(g: &Global, opt: NetDownloadOpt) {
169 header!(
170 "mytool",
171 env!("CARGO_PKG_VERSION"),
172 "batch file processor",
173 "fast and parallel"
174 );
175 phase!("net-download", "{} (timeout: {}s)", opt.url, opt.timeout);
176 if g.verbose {
177 phase!("debug", "using network stack");
178 }
179 step!("fetching");
180 ok!("./data/file.bin");
181 summary! {
182 done: "Download complete",
183 "url" => paint(CYAN, &opt.url),
184 "time" => paint(DIM, "120ms"),
185 }
186}
187
188fn net_ping(g: &Global) {
189 header!(
190 "mytool",
191 env!("CARGO_PKG_VERSION"),
192 "batch file processor",
193 "fast and parallel"
194 );
195 phase!("ping", "checking connectivity...");
196 if g.verbose {
197 phase!("debug", "sending ICMP");
198 }
199 summary! {
200 done: "Reachable",
201 "latency" => paint(OK, "12ms"),
202 }
203}
204
205fn status(g: &Global) {
206 header!(
207 "mytool",
208 env!("CARGO_PKG_VERSION"),
209 "batch file processor",
210 "fast and parallel"
211 );
212 phase!("status", "checking...");
213 if g.verbose {
214 phase!("debug", "reading config");
215 }
216 summary! {
217 done: "All systems operational",
218 "version" => paint(OK, env!("CARGO_PKG_VERSION")),
219 "config" => paint(DIM, &g.config.as_deref()
220 .map(|p| p.display().to_string())
221 .unwrap_or_else(|| "default".into())),
222 }
223}
224
225fn main() -> Result<()> {
228 match Cmd::parse()? {
229 Cmd::Download(opt) => download(Cmd::global(), opt),
230 Cmd::Upload(opt) => upload(Cmd::global(), opt),
231 Cmd::Status => status(Cmd::global()),
232
233 Cmd::Network(sub) => match sub {
234 NetCmd::Download(opt) => net_download(Cmd::global(), opt),
235 NetCmd::Ping => net_ping(Cmd::global()),
236 },
237 }
238
239 Ok(())
240}