Skip to main content

error_combinator/
check.rs

1use std::{
2    marker::PhantomData,
3    borrow::Borrow,
4};
5
6use crate::cmberr::{
7    CombineErrorBuilder,
8    CombineError
9};
10
11pub trait Check<T, PreState> 
12    where Self:Sized
13{
14    type PostState;
15    type Error;
16
17    fn check(self, value: CheckState<T, PreState>)
18        -> CheckOutcome<T, Self::PostState, Self::Error>;
19
20    fn and<B, C>(self, b: B) -> And<Self, B, C>
21        where
22            B: Check<T, Self::PostState>,
23            C: CombineErrorBuilder<Self::Error, B::Error>
24    {
25        And { a: self, b, _combine:PhantomData }
26    }
27
28    fn or<B, C>(self, b: B) -> Or<Self, B, C>
29        where 
30            B: Check<T, Self::PostState>,
31            C: CombineErrorBuilder<Self::Error, B::Error>
32    {
33        Or { a: self, b, _combine: PhantomData }
34    }
35}
36
37pub enum CheckOutcome<T, State, E> {
38    Passed(CheckState<T, State>),
39    Failed{
40        state: CheckState<T, State>,
41        err: E
42    },
43}
44
45impl<T, State, E> CheckOutcome<T, State, E> {
46    pub fn to_result(self) -> Result<T, E> {
47        match self {
48            CheckOutcome::Passed(v) => {
49                Ok(v.value)
50            }
51            CheckOutcome::Failed{state:_, err} => {
52                Err(err)
53            }
54        }
55    }
56
57    pub fn to_result_with_data(self) -> Result<T, (T, E)> {
58        match self {
59            CheckOutcome::Passed(v) => {
60                Ok(v.value)
61            }
62            CheckOutcome::Failed{state, err} => {
63                Err((state.value, err))
64            }
65        }
66    }
67}
68
69pub struct CheckState<T: Sized, S> 
70    where Self: Sized 
71{
72    pub value: T,
73    _state: PhantomData<S>
74}
75
76impl<T, S> CheckState<T, S> {
77    pub fn new(value: T) -> Self {
78        Self { value, _state: PhantomData }
79    }
80}
81
82pub struct And<A, B, C> {
83    a: A,
84    b: B,
85    _combine: PhantomData<C>
86}
87
88impl<T, PreState, A, B, C> Check<T, PreState> for And<A, B, C>
89where
90    A: Check<T, PreState>,
91    B: Check<T, A::PostState>,
92    C: CombineErrorBuilder<A::Error, B::Error>,
93{
94    type PostState = B::PostState;
95    type Error =  <C::Combiner as CombineError<A::Error, B::Error>>::Out;
96
97    fn check(self, value: CheckState<T, PreState>)
98        -> CheckOutcome<T, Self::PostState, Self::Error>
99    {
100        let mut combine = <C as CombineErrorBuilder<A::Error, B::Error>>::build();
101
102        match self.a.check(value) {
103            CheckOutcome::Passed(v) => {
104                match self.b.check(v){
105                    CheckOutcome::Passed(vv) => {
106                        // success A and success B
107                        CheckOutcome::Passed(vv)
108                    }
109                    CheckOutcome::Failed{state, err} => {
110                        // success A and failed B
111                        combine.right(err);
112                        CheckOutcome::Failed{
113                            state: CheckState { value: state.value, _state: PhantomData },
114                            err: combine.finish()
115                        }
116                    }
117                }
118            }
119            CheckOutcome::Failed{state, err} => {
120                // failed A
121                combine.left(err);
122                CheckOutcome::Failed{
123                    state: CheckState { value: state.value, _state: PhantomData },
124                    err: combine.finish()
125                }
126            }
127        }
128    }
129}
130
131pub struct Or<A, B, C> {
132    a: A,
133    b: B,
134    _combine: PhantomData<C>
135}
136
137impl<T, PreState, A, B, C> Check<T, PreState> for Or<A, B, C>
138where
139    A: Check<T, PreState>,
140    B: Check<T, A::PostState>,
141    C: CombineErrorBuilder<A::Error, B::Error>,
142{
143    type PostState = B::PostState;
144    type Error = C::Out;
145
146    fn check(self, value: CheckState<T, PreState>)
147        -> CheckOutcome<T, Self::PostState, Self::Error>
148    {
149        let mut combine = <C as CombineErrorBuilder<A::Error, B::Error>>::build();
150
151        match self.a.check(value) {
152            CheckOutcome::Passed(v) => {
153                // success A
154                match self.b.check(v) {
155                    CheckOutcome::Passed(vv) => {
156                        // success A and success B
157                        CheckOutcome::Passed(vv)
158                    }
159                    CheckOutcome::Failed{state, err} => {
160                        // success A and failed B
161                        combine.right(err);
162                        CheckOutcome::Failed{
163                            state: CheckState { value: state.value, _state: PhantomData },
164                            err: combine.finish()
165                        }
166                    }
167                }
168            }
169            CheckOutcome::Failed{state, err} => {
170                // failed A
171                combine.left(err);
172                match self.b.check(state) {
173                    CheckOutcome::Passed(vv) => {
174                        // failed A and success B
175                        CheckOutcome::Failed{
176                            state: CheckState { value: vv.value, _state: PhantomData },
177                            err: combine.finish()
178                        }
179                    }
180                    CheckOutcome::Failed{state, err} => {
181                        // failed A and failed B
182                        combine.right(err);
183                        CheckOutcome::Failed{
184                            state: CheckState { value: state.value, _state: PhantomData },
185                            err: combine.finish()
186                        }
187                    }
188                }
189            }
190        }
191    }
192}
193
194impl<T, PreState, PostState, F, E> Check<T, PreState> for F
195where
196    F: Fn(CheckState<T, PreState>) -> CheckOutcome<T, PostState, E>,
197{
198    type PostState = PostState;
199    type Error = E;
200
201    fn check(self, value: CheckState<T, PreState>)
202        -> CheckOutcome<T, Self::PostState, Self::Error>
203    {
204        self(value)
205    }
206}
207
208
209pub fn lift<V, T, Pre, Post, E, F>(
210    f: F
211) -> impl Check<V, Pre, PostState = Post, Error = E>
212where
213    T: ?Sized,
214    V: Borrow<T>,
215    F: Fn(&T) -> Result<(), E>,
216{
217    move |state: CheckState<V, Pre>| {
218        match f(state.value.borrow()) {
219            Ok(()) => CheckOutcome::Passed(CheckState::new(state.value)),
220            Err(e) => CheckOutcome::Failed {
221                state: CheckState::new(state.value),
222                err: e,
223            },
224        }
225    }
226}