1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
//! Crate-local `Either` sum type.
//!
//! This module replaces the external `either` crate (pure dependency
//! reduction). The public path `scirs2_stats::Either` is preserved via a
//! re-export in `lib.rs`, but the type is no longer interoperable with
//! `either::Either` from crates.io — it is an independent, crate-owned enum.
/// A value that is one of two possible variants: [`Either::Left`] or
/// [`Either::Right`].
///
/// This is a minimal, crate-local replacement for the `either` crate's
/// `Either` type (the external dependency was removed for dependency
/// reduction). Only the surface actually needed by `scirs2-stats` is
/// provided: construction, pattern matching, and a small set of
/// ergonomic helpers.
///
/// # Examples
///
/// ```
/// use scirs2_stats::Either;
///
/// let l: Either<i32, &str> = Either::Left(7);
/// assert!(l.is_left());
/// assert_eq!(l.left(), Some(7));
///
/// let r: Either<i32, &str> = Either::Right("stats");
/// assert!(r.is_right());
/// assert_eq!(r.right(), Some("stats"));
/// ```
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Either<L, R> {
/// The left variant.
Left(L),
/// The right variant.
Right(R),
}
impl<L, R> Either<L, R> {
/// Returns `true` if this is the [`Either::Left`] variant.
pub fn is_left(&self) -> bool {
matches!(self, Either::Left(_))
}
/// Returns `true` if this is the [`Either::Right`] variant.
pub fn is_right(&self) -> bool {
matches!(self, Either::Right(_))
}
/// Consumes the value, returning the left value if present.
pub fn left(self) -> Option<L> {
match self {
Either::Left(l) => Some(l),
Either::Right(_) => None,
}
}
/// Consumes the value, returning the right value if present.
pub fn right(self) -> Option<R> {
match self {
Either::Left(_) => None,
Either::Right(r) => Some(r),
}
}
/// Maps the left value with `f`, leaving a right value untouched.
pub fn map_left<T, F: FnOnce(L) -> T>(self, f: F) -> Either<T, R> {
match self {
Either::Left(l) => Either::Left(f(l)),
Either::Right(r) => Either::Right(r),
}
}
/// Maps the right value with `f`, leaving a left value untouched.
pub fn map_right<T, F: FnOnce(R) -> T>(self, f: F) -> Either<L, T> {
match self {
Either::Left(l) => Either::Left(l),
Either::Right(r) => Either::Right(f(r)),
}
}
/// Converts `&Either<L, R>` to `Either<&L, &R>`.
pub fn as_ref(&self) -> Either<&L, &R> {
match self {
Either::Left(l) => Either::Left(l),
Either::Right(r) => Either::Right(r),
}
}
}
#[cfg(test)]
mod tests {
use super::Either;
#[test]
fn construction_and_match() {
let l: Either<i32, f64> = Either::Left(3);
let r: Either<i32, f64> = Either::Right(2.5);
match l {
Either::Left(v) => assert_eq!(v, 3),
Either::Right(_) => panic!("expected Left"),
}
match r {
Either::Left(_) => panic!("expected Right"),
Either::Right(v) => assert!((v - 2.5).abs() < f64::EPSILON),
}
}
#[test]
fn predicates_and_extractors() {
let l: Either<i32, &str> = Either::Left(1);
let r: Either<i32, &str> = Either::Right("x");
assert!(l.is_left());
assert!(!l.is_right());
assert!(r.is_right());
assert!(!r.is_left());
assert_eq!(l.left(), Some(1));
assert_eq!(l.right(), None);
assert_eq!(r.left(), None);
assert_eq!(r.right(), Some("x"));
}
#[test]
fn map_left_and_map_right() {
let l: Either<i32, &str> = Either::Left(10);
let r: Either<i32, &str> = Either::Right("y");
assert_eq!(l.map_left(|v| v * 2), Either::Left(20));
assert_eq!(l.map_right(str::len), Either::Left(10));
assert_eq!(r.map_left(|v| v * 2), Either::Right("y"));
assert_eq!(r.map_right(str::len), Either::Right(1));
}
#[test]
fn as_ref_and_derives() {
let l: Either<i32, &str> = Either::Left(5);
let copied = l; // Copy
assert_eq!(l, copied); // PartialEq
assert_eq!(l.as_ref(), Either::Left(&5));
let cloned = l.clone(); // Clone (explicit for coverage)
assert_eq!(format!("{cloned:?}"), "Left(5)"); // Debug
}
}