structural_typing/
access.rs

1//! Runtime field access for structural types.
2use core::marker::PhantomData;
3
4use crate::presence::InferPresence;
5
6/// Uniform field access for Present, Optional, or Absent.
7pub trait Access<T>: InferPresence<T> {
8    /// Compile-time constant indicating if this field is absent.
9    const IS_ABSENT: bool;
10
11    /// Remainder type when extracting Self from Source.
12    type RemainderFrom<Source: Access<T>>: Access<T>;
13
14    /// Get field reference if present.
15    fn get(&self) -> Option<&T>;
16    /// Get mutable field reference if present.
17    fn get_mut(&mut self) -> Option<&mut T>;
18    /// Consume and convert to Option.
19    fn into_option(self) -> Option<T>;
20
21    /// Construct from a value.
22    fn from_value(value: T) -> (Self, Self::RemainderFrom<T>)
23    where
24        Self: Sized;
25
26    /// Try to construct from an option.
27    ///
28    /// # Errors
29    ///
30    /// Returns `Err` with the original `Option<T>` if the conversion cannot be performed.
31    #[allow(clippy::type_complexity)]
32    fn try_from_option(value: Option<T>) -> Result<(Self, Self::RemainderFrom<Option<T>>), Option<T>>
33    where
34        Self: Sized;
35}
36
37/// Helper function to check if a field is absent (used by serde).
38pub fn is_absent<A: Access<T>, T>(_value: &A) -> bool {
39    A::IS_ABSENT
40}
41
42impl<T> Access<T> for PhantomData<T> {
43    const IS_ABSENT: bool = true;
44
45    type RemainderFrom<Source: Access<T>> = Source;
46
47    #[inline]
48    fn get(&self) -> Option<&T> {
49        None
50    }
51    #[inline]
52    fn get_mut(&mut self) -> Option<&mut T> {
53        None
54    }
55    #[inline]
56    fn into_option(self) -> Option<T> {
57        None
58    }
59
60    #[inline]
61    fn from_value(value: T) -> (Self, Self::RemainderFrom<T>) {
62        (PhantomData, value)
63    }
64
65    #[inline]
66    fn try_from_option(value: Option<T>) -> Result<(Self, Self::RemainderFrom<Option<T>>), Option<T>> {
67        Ok((PhantomData, value))
68    }
69}
70
71impl<T: Sized> Access<T> for T {
72    const IS_ABSENT: bool = false;
73
74    type RemainderFrom<Source: Access<T>> = PhantomData<T>;
75
76    #[inline]
77    fn get(&self) -> Option<&T> {
78        Some(self)
79    }
80    #[inline]
81    fn get_mut(&mut self) -> Option<&mut T> {
82        Some(self)
83    }
84    #[inline]
85    fn into_option(self) -> Option<T> {
86        Some(self)
87    }
88
89    #[inline]
90    fn from_value(value: T) -> (Self, Self::RemainderFrom<T>) {
91        (value, PhantomData)
92    }
93
94    #[inline]
95    fn try_from_option(value: Option<T>) -> Result<(Self, Self::RemainderFrom<Option<T>>), Option<T>> {
96        match value {
97            Some(v) => Ok((v, PhantomData)),
98            None => Err(None),
99        }
100    }
101}
102
103impl<T> Access<T> for Option<T> {
104    const IS_ABSENT: bool = false;
105
106    type RemainderFrom<Source: Access<T>> = PhantomData<T>;
107
108    #[inline]
109    fn get(&self) -> Option<&T> {
110        self.as_ref()
111    }
112    #[inline]
113    fn get_mut(&mut self) -> Option<&mut T> {
114        self.as_mut()
115    }
116    #[inline]
117    fn into_option(self) -> Option<T> {
118        self
119    }
120
121    #[inline]
122    fn from_value(value: T) -> (Self, Self::RemainderFrom<T>) {
123        (Some(value), PhantomData)
124    }
125
126    #[inline]
127    fn try_from_option(value: Option<T>) -> Result<(Self, Self::RemainderFrom<Option<T>>), Option<T>> {
128        Ok((value, PhantomData))
129    }
130}