JavaScript is disabled, refresh for a better experience. ambee/giterated

ambee/giterated

Git repository hosting, collaboration, and discovery for the Fediverse.

Re-expose Operation State in generics.

This is the worst code I have ever written. I hate the way this changes everything. ugh.

Amber - ⁨2⁩ years ago

parent: tbd commit: ⁨90db3e2

⁨giterated-stack/src/substack.rs⁩ - ⁨11076⁩ bytes
Raw
1 use std::{collections::HashMap, marker::PhantomData, sync::Arc};
2
3 use futures_util::FutureExt;
4 use giterated_models::{
5 error::OperationError,
6 instance::Instance,
7 object::{GiteratedObject, ObjectRequest, ObjectResponse},
8 operation::GiteratedOperation,
9 settings::Setting,
10 value::GiteratedObjectValue,
11 };
12 use tracing::{info, trace};
13
14 use crate::{
15 handler::HandlerWrapper, provider::MetadataProvider, AnyFailure, AnyObject, AnyOperation,
16 AnySetting, AnySuccess, AnyValue, GiteratedStack, GiteratedStackState, IntoGiteratedHandler,
17 MaybeDynamicObject, MaybeDynamicValue, ObjectOperationPair, ObjectSettingPair, ObjectValuePair,
18 OperationHandler, OperationState, RuntimeMetadata, SettingChange, SettingGetter,
19 StackOperationState, ValueChange, ValueGetter,
20 };
21
22 pub struct SubstackBuilder<S: GiteratedStackState, OS> {
23 pub(crate) operation_handlers: HashMap<ObjectOperationPair<'static>, OperationHandler<OS>>,
24 pub(crate) value_getters: HashMap<ObjectValuePair<'static>, ValueGetter<OS>>,
25 pub(crate) setting_getters: HashMap<&'static str, SettingGetter<OS>>,
26 pub(crate) metadata: RuntimeMetadata,
27 pub(crate) value_change: HashMap<ObjectValuePair<'static>, ValueChange<OS>>,
28 pub(crate) metadata_providers: Vec<Box<dyn MetadataProvider>>,
29 pub(crate) setting_change: HashMap<ObjectSettingPair<'static>, SettingChange<OS>>,
30
31 pub(crate) state: S,
32 _marker: PhantomData<OS>,
33 }
34
35 impl<S: GiteratedStackState + 'static, OS> SubstackBuilder<S, OS> {
36 pub fn new(state: S) -> Self {
37 Self {
38 operation_handlers: Default::default(),
39 value_getters: Default::default(),
40 setting_getters: Default::default(),
41 metadata: Default::default(),
42 value_change: Default::default(),
43 metadata_providers: Default::default(),
44 setting_change: Default::default(),
45 state,
46 _marker: PhantomData::default(),
47 }
48 }
49 }
50
51 impl<S: Send + Sync + Clone + 'static, OS: Clone + 'static> SubstackBuilder<S, OS> {
52 /// Insert an operation handler into the runtime builder.
53 ///
54 /// # Type Registration
55 /// Inserting the handler will automatically, if required, register the operation type of the
56 /// handler. It will **not** register the object type automatically.
57 pub fn operation<O, D, A, H>(&mut self, handler: H) -> &mut Self
58 where
59 O: GiteratedObject + Clone,
60 D: GiteratedOperation<O> + Clone,
61 H: IntoGiteratedHandler<(O, D), A, S, OS, Result<D::Success, OperationError<D::Failure>>>
62 + Send
63 + Sync
64 + 'static,
65 O: 'static,
66 D: 'static,
67 D::Failure: std::fmt::Debug + 'static,
68 D::Success: 'static,
69 OS: Clone,
70 {
71 let wrapped = HandlerWrapper::new(self.state.clone(), handler);
72
73 let wrapped = wrapped.map(
74 |(any_object, any_operation): &(AnyObject, AnyOperation), _state: &OS| {
75 Ok((
76 any_object.downcast_ref::<O>().unwrap().clone(),
77 any_operation.downcast_ref::<D>().unwrap().clone(),
78 ))
79 },
80 );
81
82 let wrapped = wrapped.map_return(|ret_val, _state| match ret_val {
83 Ok(success) => Ok(AnySuccess(Arc::new(success))),
84 Err(err) => Err(match err {
85 OperationError::Operation(failure) => {
86 OperationError::Operation(AnyFailure(Arc::new(failure)))
87 }
88 OperationError::Internal(err) => OperationError::Internal(err),
89 OperationError::Unhandled => OperationError::Unhandled,
90 }),
91 });
92
93 let pair = ObjectOperationPair::from_types::<O, D>();
94
95 self.operation_handlers.insert(pair, wrapped);
96
97 self.metadata.register_operation::<O, D>();
98
99 self
100 }
101
102 /// Register a [`GiteratedObjectValue<O>`] type with the runtime, providing
103 /// its associated handler for [`GetValue`].
104 ///
105 /// # Type Registration
106 /// This will register the provided [`GiteratedObjectValue`] type for its matching / specified
107 /// object type. It will **not** register the object type automatically.
108 pub fn dynamic_value<O, A, F>(&mut self, handler: F) -> &mut Self
109 where
110 O: MaybeDynamicObject + 'static,
111 F: IntoGiteratedHandler<
112 (O, String),
113 A,
114 S,
115 OS,
116 Result<AnyValue, OperationError<anyhow::Error>>,
117 > + Send
118 + Sync,
119 F: 'static,
120 {
121 let wrapped = HandlerWrapper::new(self.state.clone(), handler);
122
123 let wrapped = wrapped.map(|(any_object, name): &(AnyObject, String), _state: &OS| {
124 Ok((O::from_any(any_object), name.clone()))
125 });
126
127 let wrapped = wrapped.map_return(|ret_val, _state| match ret_val {
128 Ok(success) => Ok(success.into_any()),
129 Err(err) => Err(match err {
130 OperationError::Operation(failure) => OperationError::Internal(failure.into()),
131 OperationError::Internal(err) => OperationError::Internal(err),
132 OperationError::Unhandled => OperationError::Unhandled,
133 }),
134 });
135
136 assert!(self
137 .value_getters
138 .insert(
139 ObjectValuePair {
140 object_kind: O::object_name(),
141 value_kind: "any"
142 },
143 wrapped
144 )
145 .is_none());
146
147 self
148 }
149
150 pub fn value_change<O, A, F, V>(&mut self, handler: F) -> &mut Self
151 where
152 F: IntoGiteratedHandler<(O, V), A, S, OS, Result<(), OperationError<anyhow::Error>>>
153 + Send
154 + Sync,
155 V: MaybeDynamicValue + Clone + 'static,
156 O: 'static + MaybeDynamicObject,
157 V: 'static,
158 F: 'static,
159 {
160 let wrapped = HandlerWrapper::new(self.state.clone(), handler);
161
162 let wrapped = wrapped.map(
163 |(any_object, any_value): &(AnyObject, AnyValue), _state: &OS| {
164 Ok((O::from_any(any_object), V::from_any(any_value)))
165 },
166 );
167
168 assert!(self
169 .value_change
170 .insert(
171 ObjectValuePair {
172 object_kind: O::object_name(),
173 value_kind: V::value_name()
174 },
175 wrapped
176 )
177 .is_none());
178
179 self
180 }
181
182 pub fn object_metadata_provider(&mut self, provider: Box<dyn MetadataProvider>) -> &mut Self {
183 self.metadata_providers.push(provider);
184
185 self
186 }
187 }
188
189 impl<S: Send + Sync + Clone + 'static> SubstackBuilder<S, StackOperationState> {
190 /// Register a [`GiteratedObject`] type with the runtime.
191 ///
192 /// # Type Registration
193 /// This will register the provided object type.
194 pub fn object<O: GiteratedObject + 'static>(&mut self) -> &mut Self {
195 self.metadata.register_object::<O>();
196
197 // Insert handler so ObjectRequest is handled properly
198
199 self.operation(
200 move |_object: Instance,
201 operation: ObjectRequest,
202 _state: S,
203 stack: GiteratedStack<StackOperationState>| {
204 let operation = operation.clone();
205 async move {
206 for (_object_name, object_meta) in stack.inner.metadata.objects.iter() {
207 if (object_meta.from_str)(&operation.0).is_ok() {
208 return Ok(ObjectResponse(operation.0.clone()));
209 }
210 }
211
212 Err(OperationError::Unhandled)
213 }
214 .boxed_local()
215 },
216 );
217
218 self
219 }
220
221 /// Register a [`Setting`] type with the runtime.
222 ///
223 /// # Type Registration
224 /// This will register the provided setting type.
225 pub fn setting<O: GiteratedObject + 'static, T: Setting + 'static + Clone>(
226 &mut self,
227 ) -> &mut Self {
228 self.metadata.register_setting::<O, T>();
229
230 self
231 }
232
233 /// Register a [`GiteratedObjectValue`] that is also a [`Setting`], which
234 /// allows for automatic value updates.
235 pub fn value_setting<
236 O: GiteratedObject + 'static + Clone,
237 T: GiteratedObjectValue<Object = O> + Setting + 'static + Clone,
238 >(
239 &mut self,
240 ) -> &mut Self {
241 self.metadata.register_setting::<O, T>();
242 self.metadata.register_value::<O, T>();
243
244 self.setting_change.insert(
245 ObjectSettingPair::from_types::<O, T>(),
246 HandlerWrapper::new(
247 (),
248 move |object: AnyObject,
249 setting: AnySetting,
250 _state: _,
251 OperationState(operation_state): OperationState<StackOperationState>,
252 stack: GiteratedStack<StackOperationState>| {
253 trace!(
254 "value setting updated {}::{}",
255 O::object_name(),
256 T::value_name()
257 );
258 let object = object.clone();
259 async move {
260 let object = object.downcast_ref::<O>().unwrap();
261 let setting = setting.downcast_ref::<T>().unwrap();
262 stack
263 .value_update(
264 object.clone(),
265 AnyValue::new(setting.clone()),
266 &operation_state,
267 )
268 .await;
269 Ok(())
270 }
271 .boxed_local()
272 },
273 ),
274 );
275
276 let wrapped = HandlerWrapper::new(
277 self.state.clone(),
278 |object: AnyObject,
279 _name: String,
280 _state: _,
281 stack: GiteratedStack<StackOperationState>| {
282 info!("a setting handler called");
283 let object = object.clone();
284 async move {
285 match stack
286 .new_get_setting::<O, T>(object.downcast_ref().unwrap())
287 .await
288 {
289 Ok(setting) => Ok(AnyValue::new(setting)),
290 Err(err) => {
291 panic!("Error: {:?}", err);
292 }
293 }
294 }
295 .boxed_local()
296 },
297 );
298
299 self.value_getters
300 .insert(ObjectValuePair::from_types::<O, T>(), wrapped);
301
302 self
303 }
304 }
305
306 // Placeholder
307 impl<S: Send + Sync + Clone + 'static, OS> SubstackBuilder<S, OS> {
308 pub fn dynamic_operation<H>(&mut self, _handler: H) -> &mut Self {
309 tracing::error!("Dynamic unimplemented");
310
311 self
312 }
313 }
314 #[derive(Debug, Clone, thiserror::Error)]
315 #[error("downcast error")]
316 pub struct DowncastError;
317