novel-cli 0.17.0

A set of tools for downloading novels from the web, manipulating text, and generating EPUB
Documentation
use std::fmt::Write;
use std::io::{self, IsTerminal, Stderr};
use std::sync::{Arc, Mutex};

use color_eyre::eyre::Result;
use indicatif::{ProgressState, ProgressStyle};
use osc94::Progress;

#[must_use]
#[derive(Clone)]
pub struct ProgressBar {
    pb: Option<indicatif::ProgressBar>,
    pb_osc94: Option<Arc<Mutex<Progress<Stderr>>>>,
    message_width: usize,
}

impl ProgressBar {
    pub fn new(total_size: u64) -> Result<Self> {
        let pb = if io::stdout().is_terminal() {
            let pb = indicatif::ProgressBar::new(total_size);
            pb.set_style(ProgressStyle::with_template("{spinner:.green} [{elapsed_precise}] [{wide_bar:.cyan/blue}] {pos:>7}/{len:7} {msg:40} ({eta})")
              .unwrap()
              .with_key("eta", |state: &ProgressState, w: &mut dyn Write| {write!(w, "{:.1}s", state.eta().as_secs_f64()).unwrap()}).progress_chars("#>-"));

            Some(pb)
        } else {
            None
        };

        let pb_osc94 = if novel_api::support_osc94() {
            let mut progress = Progress::default();
            progress.start();

            Some(Arc::new(Mutex::new(progress)))
        } else {
            None
        };

        let message_width = (super::terminal_size().0 / 3) as usize;

        Ok(Self {
            pb,
            pb_osc94,
            message_width,
        })
    }

    pub fn inc<T>(&mut self, msg: T, chunk_size: usize) -> Result<()>
    where
        T: AsRef<str>,
    {
        if let Some(pb) = &self.pb {
            pb.set_message(
                console::truncate_str(msg.as_ref(), self.message_width, "...").to_string(),
            );
            pb.inc(chunk_size as u64);
        }

        if let Some(pb) = &self.pb
            && let Some(pb_osc94) = &self.pb_osc94
        {
            let progress = (pb.position() as f64 / pb.length().unwrap() as f64 * 100.0) as u8;
            pb_osc94.lock().unwrap().progress(progress).flush()?;
        }

        Ok(())
    }

    pub fn finish(&self) -> Result<()> {
        if let Some(pb) = &self.pb {
            pb.finish_and_clear();
        }

        if let Some(pb_osc94) = &self.pb_osc94 {
            pb_osc94.lock().unwrap().hidden().flush()?;
        }

        Ok(())
    }
}