1use std::convert::Infallible;
2use std::ops::{ControlFlow, FromResidual, Try};
3
4use derive_more::From;
5
6#[derive(Default, Debug, From)]
7pub enum Skip<E> {
8 Failed(E),
9 #[default]
10 #[from(ignore)]
11 Skipped,
12}
13
14#[derive(Debug, Default, From)]
15pub enum Execution<E, R = ()> {
16 Failed(E),
17 #[from(ignore)]
18 Completed(R),
19 #[default]
20 #[from(ignore)]
21 Skipped,
22}
23
24pub struct ByteSize;
25
26impl<E, R> Execution<E, R> {
27 pub fn map<V>(self, f: impl Fn(R) -> V) -> Execution<E, V> {
28 match self {
29 Execution::Completed(it) => Execution::Completed(f(it)),
30 Execution::Skipped => Execution::Skipped,
31 Execution::Failed(e) => Execution::Failed(e),
32 }
33 }
34
35 pub fn map_err<F>(self, f: impl Fn(E) -> F) -> Execution<F, R> {
36 match self {
37 Execution::Failed(e) => Execution::Failed(f(e)),
38 Execution::Completed(it) => Execution::Completed(it),
39 Execution::Skipped => Execution::Skipped,
40 }
41 }
42
43 pub fn into_result(self) -> Result<R, E>
44 where
45 R: Default,
46 {
47 match self {
48 Execution::Failed(e) => Err(e),
49 Execution::Completed(it) => Ok(it),
50 Execution::Skipped => Ok(R::default()),
51 }
52 }
53}
54
55impl<R> Execution<Infallible, R> {
56 pub fn unwrap(self) -> Option<R> {
57 match self {
58 Execution::Failed(_) => unreachable!(),
59 Execution::Completed(x) => Some(x),
60 Execution::Skipped => None
61 }
62 }
63}
64
65impl<E, R> FromResidual for Execution<E, R> {
66 fn from_residual(residual: <Self as Try>::Residual) -> Self {
67 match residual {
68 Skip::Failed(e) => Self::Failed(e),
69 Skip::Skipped => Self::Skipped,
70 }
71 }
72}
73
74impl<E1: Into<E2>, E2, R> FromResidual<Result<Infallible, E1>> for Execution<E2, R> {
75 fn from_residual(residual: Result<Infallible, E1>) -> Self {
76 match residual {
77 Ok(_) => unreachable!(),
78 Err(e) => Self::Failed(e.into()),
79 }
80 }
81}
82
83impl<E, R> FromResidual<Option<Infallible>> for Execution<E, R> {
84 fn from_residual(residual: Option<Infallible>) -> Self {
85 match residual {
86 None => Self::Skipped,
87 Some(_) => unreachable!()
88 }
89 }
90}
91
92impl<E, R> Try for Execution<E, R> {
93 type Output = R;
94 type Residual = Skip<E>;
95
96 fn from_output(output: Self::Output) -> Self {
97 Self::Completed(output)
98 }
99
100 fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
101 match self {
102 Execution::Failed(e) => ControlFlow::Break(Skip::Failed(e)),
103 Execution::Completed(it) => ControlFlow::Continue(it),
104 Execution::Skipped => ControlFlow::Break(Skip::Skipped),
105 }
106 }
107}
108
109impl ByteSize {
110 const QUANTITIES: [&'static str; 5] = ["B", "KB", "MB", "GB", "TB"];
111
112 const fn compress_step(value: usize, index: usize) -> (usize, &'static str) {
113 if value < 1024 || index + 1 >= Self::QUANTITIES.len() {
114 (value, Self::QUANTITIES[index])
115 } else {
116 Self::compress_step(value / 1024, index + 1)
117 }
118 }
119
120 pub const fn compress(value: usize) -> (usize, &'static str) {
121 Self::compress_step(value, 0)
122 }
123}
124
125#[cfg(test)]
126mod tests {
127 use rstest::rstest;
128
129 use crate::utils::ByteSize;
130
131 #[rstest]
132 #[case(1, (1, "B"))]
133 #[case(1024, (1, "KB"))]
134 #[case(1024 * 1024 * 10, (10, "MB"))]
135 #[case(1024 * 1024 * 1024 * 10, (10, "GB"))]
136 #[case(1024 * 1024 * 1024 * 1024 * 1024, (1024, "TB"))]
137 fn test_human_readable_bytes(#[case] input: usize, #[case] expected: (usize, &'static str)) {
138 assert_eq!(ByteSize::compress(input), expected)
139 }
140}