waterui_core/foundation/
extract.rs1use core::any::{TypeId, type_name};
10use core::ops::{Deref, DerefMut};
11
12use crate::Environment;
13use alloc::{collections::BTreeMap, format};
14use anyhow::Error;
15
16pub trait Extractor: 'static + Sized {
21 fn extract(env: &Environment) -> Result<Self, Error>;
26
27 fn extract_from_action(env: &Environment, state: &mut ExtractionState) -> Result<Self, Error> {
38 let _ = state;
39 Self::extract(env)
40 }
41}
42
43#[derive(Debug, Clone, PartialEq, Eq)]
47pub struct Use<T: 'static>(pub T);
48
49#[derive(Debug, Clone, PartialEq, Eq)]
51pub struct State<T: 'static>(pub T);
52
53#[derive(Debug, Clone, Default)]
55pub struct ExtractionState {
56 positions: BTreeMap<TypeId, usize>,
57}
58
59impl ExtractionState {
60 #[must_use]
62 pub fn take_next<T: 'static>(&mut self) -> usize {
63 let position = self.positions.entry(TypeId::of::<T>()).or_insert(0);
64 let current = *position;
65 *position += 1;
66 current
67 }
68}
69
70impl Extractor for Environment {
71 fn extract(env: &Environment) -> Result<Self, Error> {
73 Ok(env.clone())
74 }
75}
76
77impl<T> Deref for Use<T> {
78 type Target = T;
79
80 fn deref(&self) -> &Self::Target {
81 &self.0
82 }
83}
84
85impl<T> DerefMut for Use<T> {
86 fn deref_mut(&mut self) -> &mut Self::Target {
87 &mut self.0
88 }
89}
90
91impl<T> Deref for State<T> {
92 type Target = T;
93
94 fn deref(&self) -> &Self::Target {
95 &self.0
96 }
97}
98
99impl<T> DerefMut for State<T> {
100 fn deref_mut(&mut self) -> &mut Self::Target {
101 &mut self.0
102 }
103}
104
105impl<T: Extractor> Extractor for Option<T> {
106 fn extract(env: &Environment) -> Result<Self, Error> {
111 Ok(<T as Extractor>::extract(env).ok())
112 }
113
114 fn extract_from_action(env: &Environment, state: &mut ExtractionState) -> Result<Self, Error> {
115 let snapshot = state.clone();
116 T::extract_from_action(env, state).map_or_else(
117 |_| {
118 *state = snapshot;
119 Ok(None)
120 },
121 |value| Ok(Some(value)),
122 )
123 }
124}
125
126impl<T: 'static + Clone> Extractor for Use<T> {
127 fn extract(env: &Environment) -> Result<Self, Error> {
132 env.get::<T>().map_or_else(
133 || {
134 Err(Error::msg(format!(
135 "Environment value `{}` not found",
136 type_name::<T>()
137 )))
138 },
139 |value| Ok(Self(value.clone())),
140 )
141 }
142}
143
144impl<T: 'static + Clone> Extractor for State<T> {
145 fn extract(env: &Environment) -> Result<Self, Error> {
146 env.get::<Self>().map_or_else(
147 || {
148 Err(Error::msg(format!(
149 "Environment state `{}` not found",
150 type_name::<T>()
151 )))
152 },
153 |value| Ok(value.clone()),
154 )
155 }
156
157 fn extract_from_action(env: &Environment, state: &mut ExtractionState) -> Result<Self, Error> {
158 let position = state.take_next::<Self>();
159 env.get_nth::<Self>(position).map_or_else(
160 || {
161 Err(Error::msg(format!(
162 "Environment state `{}` not found at position {}",
163 type_name::<T>(),
164 position
165 )))
166 },
167 |value| Ok(value.clone()),
168 )
169 }
170}
171
172macro_rules! impl_tuple_extractor {
174 ($($T:ident),+) => {
175 impl<$($T: Extractor),+> Extractor for ($($T,)+) {
176 fn extract(env: &Environment) -> Result<Self, Error> {
177 Ok(($($T::extract(env)?,)+))
178 }
179
180 fn extract_from_action(
181 env: &Environment,
182 state: &mut ExtractionState,
183 ) -> Result<Self, Error> {
184 Ok(($($T::extract_from_action(env, state)?,)+))
185 }
186 }
187 };
188}
189
190impl_tuple_extractor!(A, B);
191impl_tuple_extractor!(A, B, C);
192impl_tuple_extractor!(A, B, C, D);
193impl_tuple_extractor!(A, B, C, D, E);
194impl_tuple_extractor!(A, B, C, D, E, F);
195impl_tuple_extractor!(A, B, C, D, E, F, G);
196impl_tuple_extractor!(A, B, C, D, E, F, G, H);