pub struct Progress { /* private fields */ }Expand description
Tracks download progress and prints styled result lines.
Implementations§
Source§impl Progress
impl Progress
Sourcepub fn new(total: usize) -> Self
pub fn new(total: usize) -> Self
Create a new progress tracker for total items.
Examples found in repository?
examples/mytool.rs (line 141)
82fn main() {
83 // parse() handles --help, --version, --completions,
84 // unknown flags, and missing positionals automatically
85 let opt = Opt::parse();
86
87 // resolved_alt_input() generated from conflicts_with = "input"
88 let input = opt.resolved_alt_input();
89 let output = opt.resolved_alt_output();
90 let start = std::time::Instant::now();
91
92 // bail! prints error and exits 1
93 if !input.exists() {
94 bail!("input path does not exist: {}", input.display());
95 }
96
97 header!(
98 "mytool",
99 env!("CARGO_PKG_VERSION"),
100 "batch file processor",
101 "fast and parallel"
102 );
103
104 phase!("init", "scanning {}", input.display());
105 phase!(
106 "scan",
107 "found {} remote · {} local jobs: {}",
108 8,
109 3,
110 opt.jobs
111 );
112
113 if opt.dry_run {
114 phase!("mode", "dry run — nothing will be written");
115 }
116 if !opt.include.is_empty() {
117 phase!("filter", "include: {}", opt.include.join(", "));
118 }
119 if !opt.exclude.is_empty() {
120 phase!("filter", "exclude: {}", opt.exclude.join(", "));
121 }
122
123 // ── remote files ──────────────────────────────────────────────────
124 let remote: &[(&str, &str, usize)] = &[
125 ("https://example.com/data.csv", "./data/data.csv", 43_100),
126 ("https://example.com/ref.json", "./data/ref.json", 8_500),
127 (
128 "https://example.com/schema.json",
129 "./data/schema.json",
130 2_300,
131 ),
132 ("https://cdn.example.com/lib.js", "./js/lib.js", 128_000),
133 (
134 "https://cdn.example.com/style.css",
135 "./css/style.css",
136 12_400,
137 ),
138 ];
139
140 step!("downloading remote files");
141 let pb = Progress::new(remote.len());
142 let mut errors = 0usize;
143 for (url, local, bytes) in remote {
144 // simulate one failure
145 if url.contains("lib.js") {
146 pb.fail("file", url, "connection timeout");
147 errors += 1;
148 } else {
149 pb.ok("file", "remote", url, local, *bytes);
150 }
151 }
152 pb.finish();
153
154 // ── local files ───────────────────────────────────────────────────
155 let local: &[(&str, &str, usize)] = &[
156 ("config.toml", "./cfg/config.toml", 1_200),
157 ("rules.yaml", "./cfg/rules.yaml", 3_400),
158 ("README.md", "./README.md", 8_800),
159 ];
160
161 step!("copying local files");
162 let pb2 = Progress::new(local.len());
163 for (src, dst, bytes) in local {
164 pb2.ok("file", "local", src, dst, *bytes);
165 }
166 pb2.finish();
167
168 // ── nested operations ─────────────────────────────────────────────
169 step!("resolving references in style.css");
170 substep!(
171 "https://fonts.example.com/Inter.woff2",
172 "./fonts/Inter.woff2"
173 );
174 substep!(
175 "https://fonts.example.com/JetBrains.woff2",
176 "./fonts/JetBrains.woff2"
177 );
178
179 // ── write ─────────────────────────────────────────────────────────
180 if !opt.dry_run {
181 step!("writing output");
182 ok!(output.join(format!("result.{}", opt.format)).display());
183 }
184
185 // ── summary ───────────────────────────────────────────────────────
186 let remote_ok = remote.len() - errors;
187 let total_files = remote_ok + local.len();
188 let total_bytes: usize = remote
189 .iter()
190 .filter(|(u, _, _)| !u.contains("lib.js"))
191 .map(|(_, _, b)| b)
192 .chain(local.iter().map(|(_, _, b)| b))
193 .sum();
194
195 let input_val = paint(CYAN, &input.display().to_string());
196 let output_val = paint(
197 BOLD,
198 &output
199 .join(format!("result.{}", opt.format))
200 .display()
201 .to_string(),
202 );
203 let files_val = format!(
204 "{} total · {} errors",
205 paint(OK, &total_files.to_string()),
206 paint(if errors > 0 { ERR } else { DIM }, &errors.to_string())
207 );
208 let size_val = paint(YELLOW, &format_bytes(total_bytes));
209 let time_val = paint(DIM, &format!("{}ms", start.elapsed().as_millis()));
210
211 if errors == 0 {
212 summary! {
213 done: "All files processed",
214 "input" => input_val,
215 "output" => output_val,
216 section,
217 "files" => files_val,
218 "size" => size_val,
219 "time" => time_val,
220 }
221 } else {
222 summary! {
223 warn: "Completed with errors",
224 "input" => input_val,
225 "output" => output_val,
226 section,
227 "files" => files_val,
228 "size" => size_val,
229 "time" => time_val,
230 }
231 }
232}Sourcepub fn ok(&self, kind: &str, source: &str, url: &str, local: &str, bytes: usize)
pub fn ok(&self, kind: &str, source: &str, url: &str, local: &str, bytes: usize)
Print a successful download line.
✓ [style ][remote] https://… → ./css/file.css [1.37 KB]Examples found in repository?
examples/mytool.rs (line 149)
82fn main() {
83 // parse() handles --help, --version, --completions,
84 // unknown flags, and missing positionals automatically
85 let opt = Opt::parse();
86
87 // resolved_alt_input() generated from conflicts_with = "input"
88 let input = opt.resolved_alt_input();
89 let output = opt.resolved_alt_output();
90 let start = std::time::Instant::now();
91
92 // bail! prints error and exits 1
93 if !input.exists() {
94 bail!("input path does not exist: {}", input.display());
95 }
96
97 header!(
98 "mytool",
99 env!("CARGO_PKG_VERSION"),
100 "batch file processor",
101 "fast and parallel"
102 );
103
104 phase!("init", "scanning {}", input.display());
105 phase!(
106 "scan",
107 "found {} remote · {} local jobs: {}",
108 8,
109 3,
110 opt.jobs
111 );
112
113 if opt.dry_run {
114 phase!("mode", "dry run — nothing will be written");
115 }
116 if !opt.include.is_empty() {
117 phase!("filter", "include: {}", opt.include.join(", "));
118 }
119 if !opt.exclude.is_empty() {
120 phase!("filter", "exclude: {}", opt.exclude.join(", "));
121 }
122
123 // ── remote files ──────────────────────────────────────────────────
124 let remote: &[(&str, &str, usize)] = &[
125 ("https://example.com/data.csv", "./data/data.csv", 43_100),
126 ("https://example.com/ref.json", "./data/ref.json", 8_500),
127 (
128 "https://example.com/schema.json",
129 "./data/schema.json",
130 2_300,
131 ),
132 ("https://cdn.example.com/lib.js", "./js/lib.js", 128_000),
133 (
134 "https://cdn.example.com/style.css",
135 "./css/style.css",
136 12_400,
137 ),
138 ];
139
140 step!("downloading remote files");
141 let pb = Progress::new(remote.len());
142 let mut errors = 0usize;
143 for (url, local, bytes) in remote {
144 // simulate one failure
145 if url.contains("lib.js") {
146 pb.fail("file", url, "connection timeout");
147 errors += 1;
148 } else {
149 pb.ok("file", "remote", url, local, *bytes);
150 }
151 }
152 pb.finish();
153
154 // ── local files ───────────────────────────────────────────────────
155 let local: &[(&str, &str, usize)] = &[
156 ("config.toml", "./cfg/config.toml", 1_200),
157 ("rules.yaml", "./cfg/rules.yaml", 3_400),
158 ("README.md", "./README.md", 8_800),
159 ];
160
161 step!("copying local files");
162 let pb2 = Progress::new(local.len());
163 for (src, dst, bytes) in local {
164 pb2.ok("file", "local", src, dst, *bytes);
165 }
166 pb2.finish();
167
168 // ── nested operations ─────────────────────────────────────────────
169 step!("resolving references in style.css");
170 substep!(
171 "https://fonts.example.com/Inter.woff2",
172 "./fonts/Inter.woff2"
173 );
174 substep!(
175 "https://fonts.example.com/JetBrains.woff2",
176 "./fonts/JetBrains.woff2"
177 );
178
179 // ── write ─────────────────────────────────────────────────────────
180 if !opt.dry_run {
181 step!("writing output");
182 ok!(output.join(format!("result.{}", opt.format)).display());
183 }
184
185 // ── summary ───────────────────────────────────────────────────────
186 let remote_ok = remote.len() - errors;
187 let total_files = remote_ok + local.len();
188 let total_bytes: usize = remote
189 .iter()
190 .filter(|(u, _, _)| !u.contains("lib.js"))
191 .map(|(_, _, b)| b)
192 .chain(local.iter().map(|(_, _, b)| b))
193 .sum();
194
195 let input_val = paint(CYAN, &input.display().to_string());
196 let output_val = paint(
197 BOLD,
198 &output
199 .join(format!("result.{}", opt.format))
200 .display()
201 .to_string(),
202 );
203 let files_val = format!(
204 "{} total · {} errors",
205 paint(OK, &total_files.to_string()),
206 paint(if errors > 0 { ERR } else { DIM }, &errors.to_string())
207 );
208 let size_val = paint(YELLOW, &format_bytes(total_bytes));
209 let time_val = paint(DIM, &format!("{}ms", start.elapsed().as_millis()));
210
211 if errors == 0 {
212 summary! {
213 done: "All files processed",
214 "input" => input_val,
215 "output" => output_val,
216 section,
217 "files" => files_val,
218 "size" => size_val,
219 "time" => time_val,
220 }
221 } else {
222 summary! {
223 warn: "Completed with errors",
224 "input" => input_val,
225 "output" => output_val,
226 section,
227 "files" => files_val,
228 "size" => size_val,
229 "time" => time_val,
230 }
231 }
232}Sourcepub fn fail(&self, kind: &str, url: &str, error: &str)
pub fn fail(&self, kind: &str, url: &str, error: &str)
Print a failed download line, aligned with ok() lines.
(4/5) [file ][error ] https://… (connection timeout)Examples found in repository?
examples/mytool.rs (line 146)
82fn main() {
83 // parse() handles --help, --version, --completions,
84 // unknown flags, and missing positionals automatically
85 let opt = Opt::parse();
86
87 // resolved_alt_input() generated from conflicts_with = "input"
88 let input = opt.resolved_alt_input();
89 let output = opt.resolved_alt_output();
90 let start = std::time::Instant::now();
91
92 // bail! prints error and exits 1
93 if !input.exists() {
94 bail!("input path does not exist: {}", input.display());
95 }
96
97 header!(
98 "mytool",
99 env!("CARGO_PKG_VERSION"),
100 "batch file processor",
101 "fast and parallel"
102 );
103
104 phase!("init", "scanning {}", input.display());
105 phase!(
106 "scan",
107 "found {} remote · {} local jobs: {}",
108 8,
109 3,
110 opt.jobs
111 );
112
113 if opt.dry_run {
114 phase!("mode", "dry run — nothing will be written");
115 }
116 if !opt.include.is_empty() {
117 phase!("filter", "include: {}", opt.include.join(", "));
118 }
119 if !opt.exclude.is_empty() {
120 phase!("filter", "exclude: {}", opt.exclude.join(", "));
121 }
122
123 // ── remote files ──────────────────────────────────────────────────
124 let remote: &[(&str, &str, usize)] = &[
125 ("https://example.com/data.csv", "./data/data.csv", 43_100),
126 ("https://example.com/ref.json", "./data/ref.json", 8_500),
127 (
128 "https://example.com/schema.json",
129 "./data/schema.json",
130 2_300,
131 ),
132 ("https://cdn.example.com/lib.js", "./js/lib.js", 128_000),
133 (
134 "https://cdn.example.com/style.css",
135 "./css/style.css",
136 12_400,
137 ),
138 ];
139
140 step!("downloading remote files");
141 let pb = Progress::new(remote.len());
142 let mut errors = 0usize;
143 for (url, local, bytes) in remote {
144 // simulate one failure
145 if url.contains("lib.js") {
146 pb.fail("file", url, "connection timeout");
147 errors += 1;
148 } else {
149 pb.ok("file", "remote", url, local, *bytes);
150 }
151 }
152 pb.finish();
153
154 // ── local files ───────────────────────────────────────────────────
155 let local: &[(&str, &str, usize)] = &[
156 ("config.toml", "./cfg/config.toml", 1_200),
157 ("rules.yaml", "./cfg/rules.yaml", 3_400),
158 ("README.md", "./README.md", 8_800),
159 ];
160
161 step!("copying local files");
162 let pb2 = Progress::new(local.len());
163 for (src, dst, bytes) in local {
164 pb2.ok("file", "local", src, dst, *bytes);
165 }
166 pb2.finish();
167
168 // ── nested operations ─────────────────────────────────────────────
169 step!("resolving references in style.css");
170 substep!(
171 "https://fonts.example.com/Inter.woff2",
172 "./fonts/Inter.woff2"
173 );
174 substep!(
175 "https://fonts.example.com/JetBrains.woff2",
176 "./fonts/JetBrains.woff2"
177 );
178
179 // ── write ─────────────────────────────────────────────────────────
180 if !opt.dry_run {
181 step!("writing output");
182 ok!(output.join(format!("result.{}", opt.format)).display());
183 }
184
185 // ── summary ───────────────────────────────────────────────────────
186 let remote_ok = remote.len() - errors;
187 let total_files = remote_ok + local.len();
188 let total_bytes: usize = remote
189 .iter()
190 .filter(|(u, _, _)| !u.contains("lib.js"))
191 .map(|(_, _, b)| b)
192 .chain(local.iter().map(|(_, _, b)| b))
193 .sum();
194
195 let input_val = paint(CYAN, &input.display().to_string());
196 let output_val = paint(
197 BOLD,
198 &output
199 .join(format!("result.{}", opt.format))
200 .display()
201 .to_string(),
202 );
203 let files_val = format!(
204 "{} total · {} errors",
205 paint(OK, &total_files.to_string()),
206 paint(if errors > 0 { ERR } else { DIM }, &errors.to_string())
207 );
208 let size_val = paint(YELLOW, &format_bytes(total_bytes));
209 let time_val = paint(DIM, &format!("{}ms", start.elapsed().as_millis()));
210
211 if errors == 0 {
212 summary! {
213 done: "All files processed",
214 "input" => input_val,
215 "output" => output_val,
216 section,
217 "files" => files_val,
218 "size" => size_val,
219 "time" => time_val,
220 }
221 } else {
222 summary! {
223 warn: "Completed with errors",
224 "input" => input_val,
225 "output" => output_val,
226 section,
227 "files" => files_val,
228 "size" => size_val,
229 "time" => time_val,
230 }
231 }
232}Sourcepub fn finish(&self)
pub fn finish(&self)
Print a blank line after the download block.
Examples found in repository?
examples/mytool.rs (line 152)
82fn main() {
83 // parse() handles --help, --version, --completions,
84 // unknown flags, and missing positionals automatically
85 let opt = Opt::parse();
86
87 // resolved_alt_input() generated from conflicts_with = "input"
88 let input = opt.resolved_alt_input();
89 let output = opt.resolved_alt_output();
90 let start = std::time::Instant::now();
91
92 // bail! prints error and exits 1
93 if !input.exists() {
94 bail!("input path does not exist: {}", input.display());
95 }
96
97 header!(
98 "mytool",
99 env!("CARGO_PKG_VERSION"),
100 "batch file processor",
101 "fast and parallel"
102 );
103
104 phase!("init", "scanning {}", input.display());
105 phase!(
106 "scan",
107 "found {} remote · {} local jobs: {}",
108 8,
109 3,
110 opt.jobs
111 );
112
113 if opt.dry_run {
114 phase!("mode", "dry run — nothing will be written");
115 }
116 if !opt.include.is_empty() {
117 phase!("filter", "include: {}", opt.include.join(", "));
118 }
119 if !opt.exclude.is_empty() {
120 phase!("filter", "exclude: {}", opt.exclude.join(", "));
121 }
122
123 // ── remote files ──────────────────────────────────────────────────
124 let remote: &[(&str, &str, usize)] = &[
125 ("https://example.com/data.csv", "./data/data.csv", 43_100),
126 ("https://example.com/ref.json", "./data/ref.json", 8_500),
127 (
128 "https://example.com/schema.json",
129 "./data/schema.json",
130 2_300,
131 ),
132 ("https://cdn.example.com/lib.js", "./js/lib.js", 128_000),
133 (
134 "https://cdn.example.com/style.css",
135 "./css/style.css",
136 12_400,
137 ),
138 ];
139
140 step!("downloading remote files");
141 let pb = Progress::new(remote.len());
142 let mut errors = 0usize;
143 for (url, local, bytes) in remote {
144 // simulate one failure
145 if url.contains("lib.js") {
146 pb.fail("file", url, "connection timeout");
147 errors += 1;
148 } else {
149 pb.ok("file", "remote", url, local, *bytes);
150 }
151 }
152 pb.finish();
153
154 // ── local files ───────────────────────────────────────────────────
155 let local: &[(&str, &str, usize)] = &[
156 ("config.toml", "./cfg/config.toml", 1_200),
157 ("rules.yaml", "./cfg/rules.yaml", 3_400),
158 ("README.md", "./README.md", 8_800),
159 ];
160
161 step!("copying local files");
162 let pb2 = Progress::new(local.len());
163 for (src, dst, bytes) in local {
164 pb2.ok("file", "local", src, dst, *bytes);
165 }
166 pb2.finish();
167
168 // ── nested operations ─────────────────────────────────────────────
169 step!("resolving references in style.css");
170 substep!(
171 "https://fonts.example.com/Inter.woff2",
172 "./fonts/Inter.woff2"
173 );
174 substep!(
175 "https://fonts.example.com/JetBrains.woff2",
176 "./fonts/JetBrains.woff2"
177 );
178
179 // ── write ─────────────────────────────────────────────────────────
180 if !opt.dry_run {
181 step!("writing output");
182 ok!(output.join(format!("result.{}", opt.format)).display());
183 }
184
185 // ── summary ───────────────────────────────────────────────────────
186 let remote_ok = remote.len() - errors;
187 let total_files = remote_ok + local.len();
188 let total_bytes: usize = remote
189 .iter()
190 .filter(|(u, _, _)| !u.contains("lib.js"))
191 .map(|(_, _, b)| b)
192 .chain(local.iter().map(|(_, _, b)| b))
193 .sum();
194
195 let input_val = paint(CYAN, &input.display().to_string());
196 let output_val = paint(
197 BOLD,
198 &output
199 .join(format!("result.{}", opt.format))
200 .display()
201 .to_string(),
202 );
203 let files_val = format!(
204 "{} total · {} errors",
205 paint(OK, &total_files.to_string()),
206 paint(if errors > 0 { ERR } else { DIM }, &errors.to_string())
207 );
208 let size_val = paint(YELLOW, &format_bytes(total_bytes));
209 let time_val = paint(DIM, &format!("{}ms", start.elapsed().as_millis()));
210
211 if errors == 0 {
212 summary! {
213 done: "All files processed",
214 "input" => input_val,
215 "output" => output_val,
216 section,
217 "files" => files_val,
218 "size" => size_val,
219 "time" => time_val,
220 }
221 } else {
222 summary! {
223 warn: "Completed with errors",
224 "input" => input_val,
225 "output" => output_val,
226 section,
227 "files" => files_val,
228 "size" => size_val,
229 "time" => time_val,
230 }
231 }
232}Auto Trait Implementations§
impl Freeze for Progress
impl RefUnwindSafe for Progress
impl Send for Progress
impl Sync for Progress
impl Unpin for Progress
impl UnsafeUnpin for Progress
impl UnwindSafe for Progress
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more