1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
use console::Term;
use std::io;
use std::io::Write;
use unicode_width::{UnicodeWidthChar, UnicodeWidthStr};

pub struct LineProgress {
    pub length: u64,
    term: Term,
    term_width: u16,
    last_progress: u64,
    last_message: Option<String>,
}

impl LineProgress {
    pub fn new(len: u64) -> Self {
        let term = Term::stdout();
        let term_size = term.size();
        Self {
            length: len,
            term,
            term_width: term_size.1,
            last_progress: 0,
            last_message: None,
        }
    }

    fn progress_prefix(progress: u64, length: u64) -> String {
        format!("[{}/{}]", progress, length)
    }

    pub fn update(&mut self, progress: u64, message: String) -> io::Result<()> {
        self.last_progress = progress;
        self.last_message.replace(message);

        let message = self.last_message.as_ref();
        let message = message.unwrap();

        let progress_prefix = Self::progress_prefix(progress, self.length);
        let progress_prefix_width = progress_prefix.width();
        let message_width = message.width();
        let space_width = ' '.width().unwrap();
        let ellipse_width = "...".width();

        let line_string =
            if progress_prefix_width + space_width + message_width <= self.term_width as usize {
                format!("{} {}", progress_prefix, message)
            } else {
                let message_remain_width =
                    self.term_width as usize - progress_prefix_width - ellipse_width - space_width;
                let chars = message.chars().rev();
                let mut omitted_message_buf = String::new();
                let mut width_sum = 0_usize;
                for c in chars {
                    let width = c.width().unwrap_or(0);
                    width_sum += width;
                    if width_sum > message_remain_width {
                        break;
                    }
                    omitted_message_buf.push(c);
                }
                let omitted_message = omitted_message_buf.chars().rev().collect::<String>();
                format!("{} ...{}", progress_prefix, omitted_message)
            };

        self.term.clear_line()?;
        self.term.write_all(line_string.as_bytes())?;
        Ok(())
    }

    pub fn message(&mut self, msg: &str) -> io::Result<()> {
        self.term.clear_line()?;
        self.term.write_line(msg)?;
        match self.last_message {
            None => {}
            Some(ref msg) => {
                self.term.write_all(
                    format!(
                        "{} {}",
                        Self::progress_prefix(self.last_progress, self.length),
                        msg
                    )
                    .as_bytes(),
                )?;
            }
        }
        Ok(())
    }
}