Skip to main content

oxilean_std/either/
types.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4use super::functions::*;
5
6/// A monad transformer wrapper representing `M (Either E A)`.
7///
8/// `EitherTMonad` is a thin Rust-level encoding of the EitherT transformer,
9/// holding the inner monadic value as an `OxiEither<A, E>` wrapped in a `Vec`
10/// (as a stand-in for a generic monad `M`).
11#[allow(dead_code)]
12pub struct EitherTMonad<M, E, A> {
13    /// The inner value, represented as `M (Either E A)`.
14    /// We use `Vec` as a concrete stand-in for the monad `M`.
15    pub inner: Vec<OxiEither<A, E>>,
16    /// Phantom to hold the monad type parameter.
17    _phantom: std::marker::PhantomData<M>,
18}
19#[allow(dead_code)]
20impl<M, E: Clone, A: Clone> EitherTMonad<M, E, A> {
21    /// Wrap a single `OxiEither` value into the transformer.
22    pub fn new(value: OxiEither<A, E>) -> Self {
23        Self {
24            inner: vec![value],
25            _phantom: std::marker::PhantomData,
26        }
27    }
28    /// Lift a pure value into the transformer (right-inject).
29    pub fn pure(a: A) -> Self {
30        Self::new(OxiEither::Left(a))
31    }
32    /// Fail with an error (left-inject).
33    pub fn throw(e: E) -> Self {
34        Self::new(OxiEither::Right(e))
35    }
36    /// Run the transformer, extracting the inner value.
37    pub fn run(self) -> Option<OxiEither<A, E>> {
38        self.inner.into_iter().next()
39    }
40    /// Bind over the transformer.
41    pub fn bind<B: Clone, F>(self, f: F) -> EitherTMonad<M, E, B>
42    where
43        F: FnOnce(A) -> EitherTMonad<M, E, B>,
44    {
45        match self.run() {
46            Some(OxiEither::Left(a)) => f(a),
47            Some(OxiEither::Right(e)) => EitherTMonad::<M, E, B>::throw(e),
48            None => EitherTMonad {
49                inner: vec![],
50                _phantom: std::marker::PhantomData,
51            },
52        }
53    }
54    /// Map over the success value.
55    pub fn map<B: Clone, F: FnOnce(A) -> B>(self, f: F) -> EitherTMonad<M, E, B> {
56        match self.run() {
57            Some(OxiEither::Left(a)) => EitherTMonad::<M, E, B>::new(OxiEither::Left(f(a))),
58            Some(OxiEither::Right(e)) => EitherTMonad::<M, E, B>::new(OxiEither::Right(e)),
59            None => EitherTMonad {
60                inner: vec![],
61                _phantom: std::marker::PhantomData,
62            },
63        }
64    }
65}
66/// A Kleisli arrow `A → Either E B` in the Either monad's Kleisli category.
67#[allow(dead_code)]
68pub struct EitherKleisli<E, A, B> {
69    /// The underlying function from `A` to `OxiEither<B, E>`.
70    pub run: Box<dyn Fn(A) -> OxiEither<B, E>>,
71}
72#[allow(dead_code)]
73impl<E: 'static, A: 'static, B: 'static> EitherKleisli<E, A, B> {
74    /// Construct a new Kleisli arrow from a function.
75    pub fn new<F: Fn(A) -> OxiEither<B, E> + 'static>(f: F) -> Self {
76        Self { run: Box::new(f) }
77    }
78    /// Apply the Kleisli arrow to an input.
79    pub fn apply(&self, a: A) -> OxiEither<B, E> {
80        (self.run)(a)
81    }
82    /// Compose two Kleisli arrows: (f >=> g)
83    pub fn compose<C: 'static>(self, g: EitherKleisli<E, B, C>) -> EitherKleisli<E, A, C>
84    where
85        E: Clone + 'static,
86    {
87        EitherKleisli::new(move |a: A| match (self.run)(a) {
88            OxiEither::Left(b) => (g.run)(b),
89            OxiEither::Right(e) => OxiEither::Right(e),
90        })
91    }
92    /// Lift a plain function into the Kleisli category (i.e., return ∘ f).
93    pub fn lift_fn<F: Fn(A) -> B + 'static>(f: F) -> Self {
94        Self::new(move |a| OxiEither::Left(f(a)))
95    }
96}
97/// The select combinator for `Either`, implementing `Selective` behaviour.
98///
99/// `select` runs a computation that may short-circuit or choose between two
100/// branches. Given `OxiEither<A, E>` (lhs) and `OxiEither<Box<dyn Fn(A) -> B>, E>` (rhs),
101/// it applies the function when lhs is `Left(a)`, otherwise short-circuits.
102#[allow(dead_code)]
103pub struct SelectCombinator<E, A, B> {
104    /// The left-or-value argument.
105    pub lhs: OxiEither<A, E>,
106    /// The function-or-error argument.
107    pub rhs: OxiEither<Box<dyn Fn(A) -> B>, E>,
108}
109#[allow(dead_code)]
110impl<E: Clone, A, B> SelectCombinator<E, A, B> {
111    /// Construct a new select combinator.
112    pub fn new(lhs: OxiEither<A, E>, rhs: OxiEither<Box<dyn Fn(A) -> B>, E>) -> Self {
113        Self { lhs, rhs }
114    }
115    /// Run the select combinator, returning `OxiEither<B, E>`.
116    pub fn select(self) -> OxiEither<B, E> {
117        match self.lhs {
118            OxiEither::Left(a) => match self.rhs {
119                OxiEither::Left(f) => OxiEither::Left(f(a)),
120                OxiEither::Right(e) => OxiEither::Right(e),
121            },
122            OxiEither::Right(e) => OxiEither::Right(e),
123        }
124    }
125    /// Witness of: select (Right e) _ = Right e
126    pub fn select_right_law(e: E) -> OxiEither<B, E> {
127        OxiEither::Right(e)
128    }
129}
130/// An iterator that yields the left value of an `OxiEither` once.
131pub struct EitherLeftIter<A: Clone, B> {
132    pub(super) inner: Option<OxiEither<A, B>>,
133}
134impl<A: Clone, B> EitherLeftIter<A, B> {
135    /// Create from an either value.
136    pub fn new(e: OxiEither<A, B>) -> Self {
137        Self { inner: Some(e) }
138    }
139}
140/// A Rust-level sum type representing one of two values.
141/// Used by iterator utilities and functional combinators in this module.
142#[derive(Clone, Debug, PartialEq, Eq)]
143pub enum OxiEither<A, B> {
144    /// The left variant (analogous to `Either.inl`).
145    Left(A),
146    /// The right variant (analogous to `Either.inr`).
147    Right(B),
148}
149impl<A, B> OxiEither<A, B> {
150    /// Check if this is a `Left` value.
151    pub fn is_left(&self) -> bool {
152        matches!(self, OxiEither::Left(_))
153    }
154    /// Check if this is a `Right` value.
155    pub fn is_right(&self) -> bool {
156        matches!(self, OxiEither::Right(_))
157    }
158    /// Map over the `Left` value.
159    pub fn map_left<C, F: FnOnce(A) -> C>(self, f: F) -> OxiEither<C, B> {
160        match self {
161            OxiEither::Left(a) => OxiEither::Left(f(a)),
162            OxiEither::Right(b) => OxiEither::Right(b),
163        }
164    }
165    /// Map over the `Right` value.
166    pub fn map_right<C, F: FnOnce(B) -> C>(self, f: F) -> OxiEither<A, C> {
167        match self {
168            OxiEither::Left(a) => OxiEither::Left(a),
169            OxiEither::Right(b) => OxiEither::Right(f(b)),
170        }
171    }
172    /// Fold into a single value using two functions.
173    pub fn fold<C, FL: FnOnce(A) -> C, FR: FnOnce(B) -> C>(self, fl: FL, fr: FR) -> C {
174        match self {
175            OxiEither::Left(a) => fl(a),
176            OxiEither::Right(b) => fr(b),
177        }
178    }
179    /// Swap left and right.
180    pub fn swap(self) -> OxiEither<B, A> {
181        match self {
182            OxiEither::Left(a) => OxiEither::Right(a),
183            OxiEither::Right(b) => OxiEither::Left(b),
184        }
185    }
186    /// Extract left value or use a default.
187    pub fn left_or(self, default: A) -> A {
188        match self {
189            OxiEither::Left(a) => a,
190            OxiEither::Right(_) => default,
191        }
192    }
193    /// Extract right value or use a default.
194    pub fn right_or(self, default: B) -> B {
195        match self {
196            OxiEither::Left(_) => default,
197            OxiEither::Right(b) => b,
198        }
199    }
200    /// Convert to Option of left.
201    pub fn left(self) -> Option<A> {
202        match self {
203            OxiEither::Left(a) => Some(a),
204            OxiEither::Right(_) => None,
205        }
206    }
207    /// Convert to Option of right.
208    pub fn right(self) -> Option<B> {
209        match self {
210            OxiEither::Left(_) => None,
211            OxiEither::Right(b) => Some(b),
212        }
213    }
214    /// Borrow the left value as an `Option<&A>`.
215    pub fn as_left(&self) -> Option<&A> {
216        match self {
217            OxiEither::Left(a) => Some(a),
218            OxiEither::Right(_) => None,
219        }
220    }
221    /// Borrow the right value as an `Option<&B>`.
222    pub fn as_right(&self) -> Option<&B> {
223        match self {
224            OxiEither::Left(_) => None,
225            OxiEither::Right(b) => Some(b),
226        }
227    }
228}
229impl<T> OxiEither<T, T> {
230    /// Merge both sides using a function.
231    pub fn merge<F: FnOnce(T) -> T>(self, f: F) -> T {
232        match self {
233            OxiEither::Left(a) | OxiEither::Right(a) => f(a),
234        }
235    }
236    /// Extract the value regardless of which side it's on.
237    pub fn into_inner(self) -> T {
238        match self {
239            OxiEither::Left(a) | OxiEither::Right(a) => a,
240        }
241    }
242}
243/// An iterator that yields the right value of an `OxiEither` once.
244pub struct EitherRightIter<A, B> {
245    pub(super) inner: Option<OxiEither<A, B>>,
246}
247impl<A, B: Clone> EitherRightIter<A, B> {
248    /// Create from an either value.
249    pub fn new(e: OxiEither<A, B>) -> Self {
250        Self { inner: Some(e) }
251    }
252}
253/// A partition result holding separated Left and Right values from a collection.
254#[allow(dead_code)]
255pub struct EitherPartition<L, R> {
256    /// All Left values collected from the input.
257    pub lefts: Vec<L>,
258    /// All Right values collected from the input.
259    pub rights: Vec<R>,
260}
261#[allow(dead_code)]
262impl<L, R> EitherPartition<L, R> {
263    /// Partition an iterator of `OxiEither` values into lefts and rights.
264    pub fn from_iter<I: IntoIterator<Item = OxiEither<L, R>>>(iter: I) -> Self {
265        let mut lefts = Vec::new();
266        let mut rights = Vec::new();
267        for item in iter {
268            match item {
269                OxiEither::Left(l) => lefts.push(l),
270                OxiEither::Right(r) => rights.push(r),
271            }
272        }
273        Self { lefts, rights }
274    }
275    /// Returns the total count of collected items.
276    pub fn total(&self) -> usize {
277        self.lefts.len() + self.rights.len()
278    }
279    /// Returns true if there are no Left values.
280    pub fn no_lefts(&self) -> bool {
281        self.lefts.is_empty()
282    }
283    /// Returns true if there are no Right values.
284    pub fn no_rights(&self) -> bool {
285        self.rights.is_empty()
286    }
287    /// Returns the ratio of lefts to total (0.0 if empty).
288    pub fn left_ratio(&self) -> f64 {
289        let total = self.total();
290        if total == 0 {
291            0.0
292        } else {
293            self.lefts.len() as f64 / total as f64
294        }
295    }
296}
297/// Iterator over the `Right` values in a collection.
298pub struct RightIter<A, B, I: Iterator<Item = OxiEither<A, B>>> {
299    pub(super) inner: I,
300}
301/// Iterator over the `Left` values in a collection.
302pub struct LeftIter<A, B, I: Iterator<Item = OxiEither<A, B>>> {
303    pub(super) inner: I,
304}
305/// A triple sum type (three possible values).
306#[derive(Clone, Debug, PartialEq, Eq)]
307pub enum TripleSum<A, B, C> {
308    /// First variant.
309    First(A),
310    /// Second variant.
311    Second(B),
312    /// Third variant.
313    Third(C),
314}
315impl<A, B, C> TripleSum<A, B, C> {
316    /// Convert to `OxiEither<A, OxiEither<B, C>>`.
317    pub fn to_nested(self) -> OxiEither<A, OxiEither<B, C>> {
318        match self {
319            TripleSum::First(a) => OxiEither::Left(a),
320            TripleSum::Second(b) => OxiEither::Right(OxiEither::Left(b)),
321            TripleSum::Third(c) => OxiEither::Right(OxiEither::Right(c)),
322        }
323    }
324    /// Check which variant is present.
325    pub fn is_first(&self) -> bool {
326        matches!(self, TripleSum::First(_))
327    }
328    /// Check which variant is present.
329    pub fn is_second(&self) -> bool {
330        matches!(self, TripleSum::Second(_))
331    }
332    /// Check which variant is present.
333    pub fn is_third(&self) -> bool {
334        matches!(self, TripleSum::Third(_))
335    }
336}
337/// A traversal context for applying an Either-returning function across a collection.
338#[allow(dead_code)]
339pub struct EitherTraversal<A, B> {
340    /// The accumulated successful (Left in OxiEither) results so far.
341    pub successes: Vec<B>,
342    /// The first error encountered, if any.
343    pub error: Option<A>,
344}
345#[allow(dead_code)]
346impl<A, B> EitherTraversal<A, B> {
347    /// Create a new, empty traversal context.
348    pub fn new() -> Self {
349        Self {
350            successes: Vec::new(),
351            error: None,
352        }
353    }
354    /// Step the traversal with a new Either value.
355    /// Once an error is set, subsequent values are ignored.
356    pub fn step(&mut self, value: OxiEither<B, A>) {
357        if self.error.is_some() {
358            return;
359        }
360        match value {
361            OxiEither::Left(b) => self.successes.push(b),
362            OxiEither::Right(a) => self.error = Some(a),
363        }
364    }
365    /// Finalise the traversal, consuming self.
366    /// Returns `Left(successes)` if no error occurred, or `Right(error)`.
367    pub fn finish(self) -> OxiEither<Vec<B>, A> {
368        match self.error {
369            None => OxiEither::Left(self.successes),
370            Some(e) => OxiEither::Right(e),
371        }
372    }
373    /// Returns true if traversal ended with an error.
374    pub fn has_error(&self) -> bool {
375        self.error.is_some()
376    }
377    /// Returns the count of accumulated successes.
378    pub fn success_count(&self) -> usize {
379        self.successes.len()
380    }
381}
382/// An `EitherIter` yields one element: the single value in an `OxiEither`.
383#[derive(Clone, Debug)]
384pub struct EitherIter<A, B> {
385    inner: OxiEither<A, B>,
386    done: bool,
387}
388impl<A: Clone, B: Clone> EitherIter<A, B> {
389    /// Create an iterator from an `OxiEither`.
390    pub fn new(e: OxiEither<A, B>) -> Self {
391        Self {
392            inner: e,
393            done: false,
394        }
395    }
396}