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(())
}

这个示例展示了如何:

  1. 创建自定义模型结构来跟踪下载进度
  2. 实现Model trait来定义如何渲染进度条
  3. 使用View更新进度状态
  4. 在进度条中插入文本输出
  5. 自定义进度条格式和显示内容

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
    ));
}

注意事项

  1. Nutmeg会自动处理终端输出,避免在非终端环境中出现问题
  2. 进度更新应该适度,过于频繁的更新会影响性能
  3. 在多线程环境中使用Arc来共享View实例
  4. 可以通过Options定制各种显示参数
  5. 对于复杂任务,建议自定义状态结构体以跟踪多个指标

Nutmeg是一个轻量级但功能强大的库,特别适合需要长时间运行任务的CLI应用程序,为用户提供清晰的进度反馈。

回到顶部