otter_support/
progress.rs

1// Copyright 2020-2021 Ian Jackson and contributors to Otter
2// SPDX-License-Identifier: AGPL-3.0-or-later
3// There is NO WARRANTY.
4
5use crate::prelude::*;
6
7#[derive(Debug,Clone,Serialize,Deserialize,IntoOwned)]
8pub struct ProgressInfo<'pi> {
9  pub phase: Count<'pi>,
10  pub item:  Count<'pi>,
11}
12
13#[derive(Debug,Clone,Serialize,Deserialize,IntoOwned)]
14pub struct Count<'pi> {
15  pub value: Value,
16  pub desc: Cow<'pi, str>,
17}
18
19#[derive(Debug,Clone,Serialize,Deserialize,IntoOwned)]
20#[derive(Educe)]
21#[educe(Default)]
22pub enum Value {
23  #[educe(Default)] Exact {
24    i: usize,
25    n: usize,
26  },
27  Fraction {
28    f: f32, // [0..1]
29  }
30}
31
32impl Value {
33  pub fn fraction(&self) -> f32 {
34    match self {
35      &Value::Exact{ i:_, n } if n == 0 => 0.,
36      &Value::Exact{ i, n } => (i as f32) / (n as f32),
37      &Value::Fraction{ f } => f,
38    }
39  }
40}
41
42pub trait Originator {
43  fn report(&mut self, info: ProgressInfo<'_>);
44  fn phase_begin_(&mut self, phase: Count<'_>, len: usize);
45  fn item_(&mut self, item: usize, desc: Cow<'_, str>);
46}
47
48pub struct ResponseOriginator<'c,'w,W,F> where W: Write {
49  chan: &'c mut ResponseWriter<'w,W>,
50  phase: Count<'static>,
51  len: usize,
52  formatter: F,
53}
54impl<'c,'w,W,F> ResponseOriginator<'c,'w,W,F> where W: Write {
55  pub fn new(chan: &'c mut ResponseWriter<'w,W>,
56             formatter: F) -> Self {
57    Self {
58      chan,
59      phase: Count { value: default(), desc: Cow::Borrowed("") },
60      len: 0,
61      formatter,
62    }
63  }
64}
65
66impl<W,F,M> Originator for ResponseOriginator<'_,'_,W,F>
67where W: Write,
68      F: Fn(ProgressInfo<'_>) -> M,
69      M: Serialize,
70{
71  fn report(&mut self, pi: ProgressInfo<'_>) {
72    let resp = (self.formatter)(pi);
73    self.chan.progress_with(resp).unwrap_or(());
74  }
75  fn phase_begin_(&mut self, phase: Count<'_>, len: usize) {
76    self.phase = phase.into_owned();
77    self.len = len;
78  }
79  fn item_(&mut self, item: usize, desc: Cow<'_, str>) {
80    let value = Value::Exact { i: item, n: self.len };
81    self.report(ProgressInfo {
82      phase: self.phase.clone(),
83      item: Count { value, desc }
84    })
85  }
86}
87
88#[allow(unused_variables)]
89impl Originator for () {
90  fn report(&mut self, pi: ProgressInfo<'_>) { }
91  fn phase_begin_(&mut self, phase: Count<'_>, len: usize) { }
92  fn item_(&mut self, item: usize, desc: Cow<'_, str>) { }
93}
94
95pub trait Enum: EnumCount + ToPrimitive + EnumMessage { }
96impl<T> From<T> for Count<'static> where T: Enum {
97  fn from(t: T) -> Count<'static> {
98    let value = Value::Exact { 
99      i: t.to_usize().unwrap(),
100      n: T::COUNT - 1,
101    };
102    Count {
103      value,
104      // Show be Borrowed  https://github.com/Peternator7/strum/issues/159
105      desc: Cow::Owned(t.get_message().unwrap_or("...").to_owned()),
106    }
107  }
108}
109impl<'t> From<&'t str> for Count<'t> {
110  fn from(s: &'t str) -> Count<'t> {
111    Count { value: default(), desc: Cow::Borrowed(s) }
112  }
113}
114impl From<String> for Count<'static> {
115  fn from(s: String) -> Count<'static> {
116    Count { value: default(), desc: Cow::Owned(s) }
117  }
118}
119impl<'t> From<()> for Count<'t> { fn from(_:()) -> Count<'t> {
120    Count { value: default(), desc: Cow::Borrowed("") }
121} }
122
123#[ext(pub, name=OriginatorExt)]
124impl &'_ mut (dyn Originator + '_) {
125  fn phase_item<'p,'e,P,E>(&mut self, phase: P, item: E)
126  where P: Into<Count<'p>>,
127        E: Into<Count<'e>>,
128  {
129    let phase = phase.into();
130    let item  = item .into();
131    self.report(ProgressInfo { phase, item });
132  }
133
134  fn phase<'p,P>(&mut self, phase: P, len: usize)
135  where P: Into<Count<'p>>,
136  {
137    self.phase_begin_(phase.into(), len)
138  }
139
140  fn item<'s,S>(&mut self, item: usize, desc: S)
141  where S: Into<Cow<'s, str>>,
142  {
143    self.item_(item, desc.into())
144  }
145}
146
147pub struct ReadOriginator<'o,R:Read> {
148  r: R,
149  total_len: usize,
150  orig: &'o mut dyn Originator,
151  // state:
152  counter: usize,
153  last_report: usize,
154}
155
156impl<'oo,'o,R:Read> ReadOriginator<'o,R> {
157  pub fn new<'p,P>(mut orig: &'o mut dyn Originator, phase: P,
158                   total_len: usize, r: R) -> Self
159  where P: Into<Count<'p>>
160  {
161    orig.phase(phase, total_len);
162    let mut ro = ReadOriginator {
163      r, orig, total_len,
164      counter: 0,
165      last_report: 0,
166    };
167    ro.report();
168    ro
169  }
170
171  fn report(&mut self) {
172    let t = self.total_len.to_string();
173    let c = format!("{:>width$}", self.counter, width=t.len());
174    let m = |s: String| {
175      izip!( iter::once("").chain(["",""," "].iter().cloned().cycle()),
176             s.chars().rev() )
177        .map(|(s,c)| [s.chars().next(), Some(c)])
178        .flatten()
179        .flatten()
180        .collect::<String>()
181        .chars().rev()
182        .collect::<String>()
183    };
184    let desc = format!(" {} of {}", m(c), m(t));
185    self.orig.item(self.counter, desc);
186    self.last_report = self.counter;
187  }
188}
189
190impl<R:Read> Read for ReadOriginator<'_,R> {
191  #[throws(io::Error)]
192  fn read(&mut self, buf: &mut [u8]) -> usize {
193    let got = self.r.read(buf)?;
194    self.counter += got;
195    if self.counter - self.last_report > 10000 {
196      self.report();
197    }
198    got
199  }
200}