Skip to content

Commit c4f3e84

Browse files
Added more helper methods for options
1 parent b908278 commit c4f3e84

2 files changed

Lines changed: 187 additions & 1 deletion

File tree

tuplities-flatten-nest/src/nested_option.rs

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,35 @@ pub trait NestedTupleOption {
2525
/// The transposed type: an option of the nested tuple of the inner types.
2626
type Transposed: IntoNestedTupleOption<IntoOptions = Self>;
2727

28+
/// A nested homogeneous tuple type with the same shape as `Self` where each element is `H`.
29+
///
30+
/// Example: for `Self = (Option<T>, (Option<U>,))` then
31+
/// `Self::SameDepth<H>` == `(H, (H,))`.
32+
type SameDepth<H>;
33+
2834
/// Transposes the nested tuple of options into an option of the nested tuple.
2935
///
3036
/// Returns `Some(nested_tuple)` if all elements are `Some`, otherwise `None`.
3137
fn transpose(self) -> Option<Self::Transposed>;
38+
39+
/// Given a parallel homogeneous nested tuple `xs` (same shape as `self`),
40+
/// returns the first `H` that corresponds to the first `None` in `self`,
41+
/// or `None` if no elements are `None`.
42+
fn first_none_with<H>(self, xs: Self::SameDepth<H>) -> Option<H>;
43+
44+
/// Given a parallel homogeneous nested tuple `xs` (same shape as `self`),
45+
/// returns the first `H` that corresponds to the first `Some` in `self`,
46+
/// or `None` if no elements are `Some`.
47+
fn first_some_with<H>(self, xs: Self::SameDepth<H>) -> Option<H>;
48+
49+
/// Like `transpose`, but returns a `Result` with `Ok(Transposed)` when all elements
50+
/// are `Some`, or `Err(H)` with the first `H` corresponding to the first `None`.
51+
///
52+
/// # Errors
53+
///
54+
/// Returns `Err(H)` if any element is `None`, where `H` is from the parallel
55+
/// homogeneous nested tuple `xs`.
56+
fn transpose_or<H>(self, xs: Self::SameDepth<H>) -> Result<Self::Transposed, H>;
3257
}
3358

3459
/// A trait for converting nested tuples into nested tuples of options.
@@ -56,28 +81,73 @@ pub trait IntoNestedTupleOption {
5681

5782
impl NestedTupleOption for () {
5883
type Transposed = ();
84+
type SameDepth<H> = ();
5985

6086
#[inline]
6187
fn transpose(self) -> Option<Self::Transposed> {
6288
Some(())
6389
}
90+
91+
#[inline]
92+
fn first_none_with<H>(self, _xs: Self::SameDepth<H>) -> Option<H> {
93+
None
94+
}
95+
96+
#[inline]
97+
fn first_some_with<H>(self, _xs: Self::SameDepth<H>) -> Option<H> {
98+
None
99+
}
100+
101+
#[inline]
102+
fn transpose_or<H>(self, _xs: Self::SameDepth<H>) -> Result<Self::Transposed, H> {
103+
Ok(())
104+
}
64105
}
65106

66107
impl<T> NestedTupleOption for (Option<T>,) {
67108
type Transposed = (T,);
109+
type SameDepth<H> = (H,);
68110

69111
#[inline]
70112
fn transpose(self) -> Option<Self::Transposed> {
71113
let (opt,) = self;
72114
opt.map(|t| (t,))
73115
}
116+
117+
#[inline]
118+
fn first_none_with<H>(self, xs: Self::SameDepth<H>) -> Option<H> {
119+
let (opt,) = self;
120+
let (h,) = xs;
121+
match opt {
122+
None => Some(h),
123+
Some(_) => None,
124+
}
125+
}
126+
127+
#[inline]
128+
fn first_some_with<H>(self, xs: Self::SameDepth<H>) -> Option<H> {
129+
let (opt,) = self;
130+
let (h,) = xs;
131+
opt.map(|_| h)
132+
}
133+
134+
#[inline]
135+
fn transpose_or<H>(self, xs: Self::SameDepth<H>) -> Result<Self::Transposed, H> {
136+
let (opt,) = self;
137+
let (h,) = xs;
138+
match opt {
139+
Some(t) => Ok((t,)),
140+
None => Err(h),
141+
}
142+
}
74143
}
75144

76145
impl<Head, Tail> NestedTupleOption for (Option<Head>, Tail)
77146
where
78147
Tail: NestedTupleOption,
79148
{
80149
type Transposed = (Head, Tail::Transposed);
150+
type SameDepth<H> = (H, Tail::SameDepth<H>);
81151

82152
#[inline]
83153
fn transpose(self) -> Option<Self::Transposed> {
@@ -87,6 +157,39 @@ where
87157
_ => None,
88158
}
89159
}
160+
161+
#[inline]
162+
fn first_none_with<H>(self, xs: Self::SameDepth<H>) -> Option<H> {
163+
let (head_opt, tail) = self;
164+
let (h_head, h_tail) = xs;
165+
match head_opt {
166+
None => Some(h_head),
167+
Some(_) => tail.first_none_with(h_tail),
168+
}
169+
}
170+
171+
#[inline]
172+
fn first_some_with<H>(self, xs: Self::SameDepth<H>) -> Option<H> {
173+
let (head_opt, tail) = self;
174+
let (h_head, h_tail) = xs;
175+
match head_opt {
176+
Some(_) => Some(h_head),
177+
None => tail.first_some_with(h_tail),
178+
}
179+
}
180+
181+
#[inline]
182+
fn transpose_or<H>(self, xs: Self::SameDepth<H>) -> Result<Self::Transposed, H> {
183+
let (head_opt, tail) = self;
184+
let (h_head, h_tail) = xs;
185+
match head_opt {
186+
None => Err(h_head),
187+
Some(head) => match tail.transpose_or(h_tail) {
188+
Ok(tail_transposed) => Ok((head, tail_transposed)),
189+
Err(h) => Err(h),
190+
},
191+
}
192+
}
90193
}
91194

92195
// Implementations for IntoNestedTupleOption
@@ -124,6 +227,8 @@ where
124227
#[cfg(test)]
125228
mod tests {
126229
use super::*;
230+
#[derive(Debug, PartialEq, Eq)]
231+
struct NoCopy(i32);
127232

128233
#[test]
129234
fn test_transpose_empty() {
@@ -189,4 +294,86 @@ mod tests {
189294
let back = options.transpose().unwrap();
190295
assert_eq!(original, back);
191296
}
297+
298+
#[test]
299+
fn test_first_none_and_first_some_with_homogeneous() {
300+
let options = (Some(1), (None::<i32>, (Some(3),)));
301+
let hom = (10, (20, (30,)));
302+
assert_eq!(options.first_none_with(hom), Some(20));
303+
304+
let hom2 = (11, (12, (13,)));
305+
assert_eq!(options.first_some_with(hom2), Some(11));
306+
}
307+
308+
#[test]
309+
fn test_all_some_first_none_returns_none() {
310+
let options = (Some(1), (Some(2), (Some(3),)));
311+
let hom = (10, (20, (30,)));
312+
assert_eq!(options.first_none_with(hom), None);
313+
}
314+
315+
#[test]
316+
fn test_all_none_first_some_returns_none() {
317+
let options = (None::<i32>, (None::<i32>, (None::<i32>,)));
318+
let hom = (10, (20, (30,)));
319+
assert_eq!(options.first_some_with(hom), None);
320+
}
321+
322+
#[test]
323+
fn test_deeply_nested_first_none_prefers_leftmost() {
324+
let options = (Some(1), (None::<i32>, (None::<i32>,)));
325+
let hom = (10, (20, (30,)));
326+
assert_eq!(options.first_none_with(hom), Some(20));
327+
}
328+
329+
#[test]
330+
fn test_deeply_nested_first_some_returns_deeper_value_when_head_none() {
331+
let options = (None::<i32>, (Some(2), (Some(3),)));
332+
let hom = (10, (20, (30,)));
333+
assert_eq!(options.first_some_with(hom), Some(20));
334+
}
335+
336+
#[test]
337+
fn test_first_none_with_noncopy_hom_movable() {
338+
let options = (Some(1), (None::<i32>, (Some(3),)));
339+
let hom = (NoCopy(10), (NoCopy(20), (NoCopy(30),)));
340+
assert_eq!(options.first_none_with(hom), Some(NoCopy(20)));
341+
}
342+
343+
#[test]
344+
fn test_first_some_with_noncopy_hom_movable() {
345+
let options = (None::<i32>, (Some(2), (None::<i32>,)));
346+
let hom = (NoCopy(11), (NoCopy(22), (NoCopy(33),)));
347+
assert_eq!(options.first_some_with(hom), Some(NoCopy(22)));
348+
}
349+
350+
#[test]
351+
fn test_all_none_first_none_returns_head() {
352+
let options = (None::<i32>, (None::<i32>, (None::<i32>,)));
353+
let hom = (10, (20, (30,)));
354+
assert_eq!(options.first_none_with(hom), Some(10));
355+
}
356+
357+
#[test]
358+
fn test_transpose_or_ok_and_err() {
359+
let options = (Some(1), (None::<i32>, (Some(3),)));
360+
let hom = (10, (20, (30,)));
361+
// Since the second element is None, we expect Err(20)
362+
assert_eq!(options.transpose_or(hom), Err(20));
363+
364+
let options2 = (Some(1), (Some(2), (Some(3),)));
365+
let hom2 = (10, (20, (30,)));
366+
assert_eq!(options2.transpose_or(hom2), Ok((1, (2, (3,)))));
367+
}
368+
369+
#[test]
370+
fn test_transpose_or_single_ok_and_err() {
371+
let options = (None::<i32>,);
372+
let hom = (99,);
373+
assert_eq!(options.transpose_or(hom), Err(99));
374+
375+
let some = (Some(5),);
376+
let hom2 = (77,);
377+
assert_eq!(some.transpose_or(hom2), Ok((5,)));
378+
}
192379
}

tuplities-push-front/Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ keywords.workspace = true
1111
categories.workspace = true
1212
homepage.workspace = true
1313

14-
1514
[dependencies]
1615
tuplities-derive.workspace = true
1716

0 commit comments

Comments
 (0)