Rust进度条与状态管理库Nutmeg的使用,Nutmeg提供简洁高效的终端进度显示和任务状态跟踪功能
Rust进度条与状态管理库Nutmeg的使用
Nutmeg是一个Rust库,用于在终端绘制进度指示器,同时让应用程序完全控制其外观和内容。
示例代码
以下是来自examples/basic.rs
的基本使用示例:
use std::io::Write; // 支持write!()宏
// 1. 定义一个结构体来保存渲染进度条所需的所有应用状态
#[derive(Default)]
struct Model {
i: usize,
total: usize,
last_file_name: String,
}
// 2. 定义如何将进度条渲染为字符串
impl nutmeg::Model for Model {
fn render(&mut self, _width: usize) -> String {
format!("{}/{}: {}", self.i, self.total, self.last_file_name)
}
}
fn main() -> std::io::Result<()> {
// 3. 当需要绘制进度条时创建一个View
let mut view = nutmeg::View::new(Model::default(),
nutmeg::Options::default());
// 4. 随着应用程序运行,通过view更新模型
let total_work = 100;
view.update(|model| model.total = total_work);
for i in 0..total_work {
view.update(|model| {
model.i += 1;
model.last_file_name = format!("file{}.txt", i);
});
// 5. 通过写入view来交错输出文本行
if i % 10 == 3 {
writeln!(view, "reached {}", i)?;
}
std::thread::sleep(std::time::Duration::from_millis(100));
}
// 6. 进度条在被丢弃时会自动擦除
Ok(())
}
完整示例
以下是一个更完整的示例,展示了如何使用Nutmeg创建带有自定义格式的进度条:
use std::io::{self, Write};
use std::thread;
use std::time::Duration;
// 自定义模型结构
#[derive(Default)]
struct DownloadProgress {
current_bytes: usize,
total_bytes: usize,
speed: f64, // KB/s
filename: String,
}
// 实现Model trait
impl nutmeg::Model for DownloadProgress {
fn render(&mut self, width: usize) -> String {
let progress = self.current_bytes as f64 / self.total_bytes as f64;
let progress_width = (progress * width as f64) as usize;
let progress_bar = "=".repeat(progress_width) + &">".repeat(1);
let remaining = " ".repeat(width - progress_width);
format!(
"Downloading {}: [{}{}] {:.1}% ({:.1} KB/s)",
self.filename,
progress_bar,
remaining,
progress * 100.0,
self.speed
)
}
}
fn main() -> io::Result<()> {
let mut model = DownloadProgress {
total_bytes: 1024 * 1024, // 1MB
filename: "large_file.zip".to_string(),
..Default::default()
};
let options = nutmeg::Options::default();
let mut view = nutmeg::View::new(model, options);
// 模拟下载过程
for chunk in 0..100 {
let chunk_size = 10 * 1024; // 10KB每块
thread::sleep(Duration::from_millis(100));
view.update(|m| {
m.current_bytes += chunk_size;
m.speed = 100.0; // 模拟固定速度
});
// 每下载10个块输出一条消息
if chunk % 10 == 0 {
writeln!(view, "Downloaded chunk {} of 100", chunk)?;
}
}
writeln!(view, "Download completed successfully!")?;
Ok(())
}
这个示例展示了如何:
- 创建自定义模型结构来跟踪下载进度
- 实现
Model
trait来定义如何渲染进度条 - 使用
View
更新进度状态 - 在进度条中插入文本输出
- 自定义进度条格式和显示内容
Nutmeg非常适合需要显示长时间运行任务进度的CLI应用程序,它提供了简洁的API和灵活的定制选项。
1 回复
Rust进度条与状态管理库Nutmeg使用指南
基本使用方法示例
简单进度条
use nutmeg::View;
fn main() {
// 使用默认选项创建视图
let options = nutmeg::Options::default();
// 初始化进度条,初始值为0,前缀文本为"Processing: "
let mut view = nutmeg::View::new(0u32, "Processing: ", options);
// 模拟处理过程
for i in 0..100 {
// 更新进度值
view.update(|model| *model += 1);
// 模拟处理延迟
std::thread::sleep(std::time::Duration::from_millis(50));
}
// 处理完成后显示完成消息
view.message("Done!");
}
带剩余时间估计的进度条
use nutmeg::View;
fn main() {
// 使用默认选项
let options = nutmeg::Options::default();
// 创建视图,会自动计算并显示剩余时间
let mut view = nutmeg::View::new(0u32, "Processing items: ", options);
for i in 0..100 {
view.update(|model| *model += 1);
std::thread::sleep(std::time::Duration::from_millis(50));
}
}
多任务状态跟踪
use nutmeg::View;
// 自定义状态结构体
struct TaskState {
downloaded: usize,
processed: usize,
total: usize,
}
fn main() {
let options = nutmeg::Options::default();
// 初始化状态
let state = TaskState {
downloaded: 0,
processed: 0,
total: 100,
};
// 创建视图,显示下载和处理的进度
let mut view = View::new(
state,
"Downloading: {downloaded}/{total} | Processing: {processed}/{total}",
options,
);
for i in 0..100 {
view.update(|model| {
model.downloaded += 1;
if i % 2 == 0 {
model.processed += 1;
}
});
std::thread::sleep(std::time::Duration::from_millis(50));
}
}
高级功能示例
自定义显示格式
use nutmeg::View;
fn main() {
// 自定义选项:设置进度条格式和ETA显示格式
let options = nutmeg::Options::default()
.with_progress_format("{bar:40.green/blue} {percent}%")
.with_eta_format("ETA: {eta}");
let mut view = nutmeg::View::new(0u32, "Custom progress: ", options);
for i in 0..100 {
view.update(|model| *model += 1);
std::thread::sleep(std::time::Duration::from_millis(50));
}
}
多线程使用
use nutmeg::View;
use std::sync::Arc;
use std::thread;
fn main() {
let options = nutmeg::Options::default();
// 使用Arc共享视图,便于多线程访问
let view = Arc::new(View::new(0u32, "Threaded progress: ", options));
// 创建4个线程并行处理
let handles: Vec<_> = (0..4).map(|_| {
let view = view.clone();
thread::spawn(move || {
for _ in 0..25 {
view.update(|model| *model += 1);
thread::sleep(std::time::Duration::from_millis(50));
}
})
}).collect();
// 等待所有线程完成
for handle in handles {
handle.join().unwrap();
}
// 显示完成消息
view.message("All threads completed!");
}
完整示例代码
下面是一个结合了多种功能的完整示例:
use nutmeg::View;
use std::sync::Arc;
use std::thread;
use std::time::Duration;
// 自定义任务状态
struct ComplexTaskState {
current: usize,
total: usize,
success: usize,
failed: usize,
}
fn main() {
// 自定义显示选项
let options = nutmeg::Options::default()
.with_progress_format("{bar:30.cyan/yellow} {percent}%")
.with_eta_format("剩余时间: {eta}")
.with_update_rate(Duration::from_millis(100));
// 初始化状态
let state = ComplexTaskState {
current: 0,
total: 200,
success: 0,
failed: 0,
};
// 创建视图
let view = Arc::new(View::new(
state,
"处理进度: {current}/{total} | 成功: {success} | 失败: {failed}",
options,
));
// 创建4个工作线程
let handles: Vec<_> = (0..4).map(|i| {
let view = view.clone();
thread::spawn(move || {
for _ in 0..50 {
view.update(|model| {
model.current += 1;
// 模拟随机成功/失败
if rand::random::<f32>() > 0.1 {
model.success += 1;
} else {
model.failed += 1;
}
});
thread::sleep(Duration::from_millis(rand::random::<u64>() % 100));
}
})
}).collect();
// 等待所有线程完成
for handle in handles {
handle.join().unwrap();
}
// 显示最终结果
view.message(format!(
"处理完成! 总计: {}, 成功: {}, 失败: {}",
view.get_model().total,
view.get_model().success,
view.get_model().failed
));
}
注意事项
- Nutmeg会自动处理终端输出,避免在非终端环境中出现问题
- 进度更新应该适度,过于频繁的更新会影响性能
- 在多线程环境中使用Arc来共享View实例
- 可以通过Options定制各种显示参数
- 对于复杂任务,建议自定义状态结构体以跟踪多个指标
Nutmeg是一个轻量级但功能强大的库,特别适合需要长时间运行任务的CLI应用程序,为用户提供清晰的进度反馈。