kodept_ast/
utils.rs

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}