structural_typing/
split.rs

1//! Split structs into selected fields and remainder.
2//!
3//! Use `split()` when extraction always succeeds (e.g., `Present` → `Optional`).
4//! Use `try_split()` when extraction may fail (e.g., `Optional` → `Present` can fail if `None`).
5
6use core::marker::PhantomData;
7
8use crate::presence::{Absent, Optional, Presence, Present};
9
10/// Trait for compile-time checked splitting from one presence to another.
11///
12/// Returns both the selected value (in target presence state) and the remainder.
13pub trait Split<To: Presence>: Presence {
14    /// Split a value into selected and remainder parts.
15    fn split<T>(value: Self::Output<T>) -> SplitResult<To, Self, T>
16    where
17        Self: Sized;
18}
19
20/// Return type for [`Split::split`].
21///
22/// Returns `(selected, remainder)` where `selected` has the target presence type `To`,
23/// and `remainder` contains the fields not selected.
24pub type SplitResult<To, From, T> = (
25    <To as Presence>::Output<T>,
26    <<To as Presence>::RemainderFrom<From> as Presence>::Output<T>,
27);
28
29/// Trait for runtime checked splitting that may fail.
30///
31/// Returns Ok with (selected, remainder) on success, or Err with original value on failure.
32/// Automatically succeeds when `Split` is implemented (blanket impl); only fails when `Optional` → `Present` with `None`.
33pub trait TrySplit<To: Presence>: Presence {
34    /// Try to split a value into selected and remainder parts.
35    ///
36    /// # Errors
37    ///
38    /// Returns `Err` with the original value if the split cannot be performed
39    /// (e.g., when trying to convert `Optional` to `Present` but the value is `None`).
40    fn try_split<T>(value: Self::Output<T>) -> Result<SplitResult<To, Self, T>, Self::Output<T>>
41    where
42        Self: Sized;
43}
44
45impl<From: Presence, To: Presence> TrySplit<To> for From
46where
47    From: Split<To>,
48{
49    fn try_split<T>(value: From::Output<T>) -> Result<SplitResult<To, From, T>, Self::Output<T>> {
50        Ok(From::split(value))
51    }
52}
53
54impl Split<Absent> for Present {
55    fn split<T>(value: T) -> (PhantomData<T>, T) {
56        (PhantomData, value)
57    }
58}
59
60impl Split<Present> for Present {
61    fn split<T>(value: T) -> (T, PhantomData<T>) {
62        (value, PhantomData)
63    }
64}
65
66impl Split<Optional> for Present {
67    fn split<T>(value: T) -> (Option<T>, PhantomData<T>) {
68        (Some(value), PhantomData)
69    }
70}
71
72impl Split<Absent> for Optional {
73    fn split<T>(value: Option<T>) -> (PhantomData<T>, Option<T>) {
74        (PhantomData, value)
75    }
76}
77
78impl Split<Optional> for Optional {
79    fn split<T>(value: Option<T>) -> (Option<T>, PhantomData<T>) {
80        (value, PhantomData)
81    }
82}
83
84impl TrySplit<Present> for Optional {
85    fn try_split<T>(value: Option<T>) -> Result<(T, PhantomData<T>), Option<T>> {
86        match value {
87            Some(v) => Ok((v, PhantomData)),
88            None => Err(None),
89        }
90    }
91}
92
93impl Split<Absent> for Absent {
94    fn split<T>(_value: PhantomData<T>) -> (PhantomData<T>, PhantomData<T>) {
95        (PhantomData, PhantomData)
96    }
97}
98
99impl Split<Optional> for Absent {
100    fn split<T>(_value: PhantomData<T>) -> (Option<T>, PhantomData<T>) {
101        (None, PhantomData)
102    }
103}