use std::{collections::HashMap, marker::PhantomData, sync::Arc}; use anyhow::Error; use futures_util::FutureExt; use giterated_models::{ error::OperationError, instance::Instance, object::{GiteratedObject, ObjectRequest, ObjectResponse}, operation::GiteratedOperation, settings::Setting, value::GiteratedObjectValue, }; use tracing::{info, trace}; use crate::{ handler::HandlerWrapper, provider::MetadataProvider, AnyFailure, AnyObject, AnyOperation, AnySetting, AnySuccess, AnyValue, GiteratedStack, GiteratedStackState, HandlerResolvable, IntoGiteratedHandler, MaybeDynamicObject, MaybeDynamicValue, ObjectOperationPair, ObjectSettingPair, ObjectValuePair, OperationHandler, OperationState, RuntimeMetadata, SettingChange, SettingGetter, StackOperationState, ValueChange, ValueGetter, }; pub struct SubstackBuilder { pub(crate) operation_handlers: HashMap, OperationHandler>, pub(crate) value_getters: HashMap, ValueGetter>, pub(crate) setting_getters: HashMap<&'static str, SettingGetter>, pub(crate) metadata: RuntimeMetadata, pub(crate) value_change: HashMap, ValueChange>, pub(crate) metadata_providers: Vec>, pub(crate) setting_change: HashMap, SettingChange>, pub(crate) state: S, _marker: PhantomData, } impl SubstackBuilder { pub fn new(state: S) -> Self { Self { operation_handlers: Default::default(), value_getters: Default::default(), setting_getters: Default::default(), metadata: Default::default(), value_change: Default::default(), metadata_providers: Default::default(), setting_change: Default::default(), state, _marker: PhantomData::default(), } } } impl SubstackBuilder { /// Insert an operation handler into the runtime builder. /// /// # Type Registration /// Inserting the handler will automatically, if required, register the operation type of the /// handler. It will **not** register the object type automatically. pub fn operation(&mut self, handler: H) -> &mut Self where O: GiteratedObject + Clone, D: GiteratedOperation + Clone, H: IntoGiteratedHandler<(O, D), A, S, OS, Result>> + Send + Sync + 'static, O: 'static, D: 'static, D::Failure: std::fmt::Debug + 'static, D::Success: 'static, OS: Clone, { let wrapped = HandlerWrapper::new(self.state.clone(), handler); let wrapped = wrapped.map( |(any_object, any_operation): &(AnyObject, AnyOperation), _state: &OS| { Ok(( any_object.downcast_ref::().unwrap().clone(), any_operation.downcast_ref::().unwrap().clone(), )) }, ); let wrapped = wrapped.map_return(|ret_val, _state| match ret_val { Ok(success) => Ok(AnySuccess(Arc::new(success))), Err(err) => Err(match err { OperationError::Operation(failure) => { OperationError::Operation(AnyFailure(Arc::new(failure))) } OperationError::Internal(err) => OperationError::Internal(err), OperationError::Unhandled => OperationError::Unhandled, }), }); let pair = ObjectOperationPair::from_types::(); self.operation_handlers.insert(pair, wrapped); self.metadata.register_operation::(); self } /// Register a [`GiteratedObjectValue`] type with the runtime, providing /// its associated handler for [`GetValue`]. /// /// # Type Registration /// This will register the provided [`GiteratedObjectValue`] type for its matching / specified /// object type. It will **not** register the object type automatically. pub fn dynamic_value(&mut self, handler: F) -> &mut Self where O: MaybeDynamicObject + 'static, F: IntoGiteratedHandler< (O, String), A, S, OS, Result>, > + Send + Sync, F: 'static, { let wrapped = HandlerWrapper::new(self.state.clone(), handler); let wrapped = wrapped.map(|(any_object, name): &(AnyObject, String), _state: &OS| { Ok((O::from_any(any_object), name.clone())) }); let wrapped = wrapped.map_return(|ret_val, _state| match ret_val { Ok(success) => Ok(success.into_any()), Err(err) => Err(match err { OperationError::Operation(failure) => OperationError::Internal(failure.into()), OperationError::Internal(err) => OperationError::Internal(err), OperationError::Unhandled => OperationError::Unhandled, }), }); assert!(self .value_getters .insert( ObjectValuePair { object_kind: O::object_name(), value_kind: "any" }, wrapped ) .is_none()); self } pub fn value_change(&mut self, handler: F) -> &mut Self where F: IntoGiteratedHandler<(O, V), A, S, OS, Result<(), OperationError>> + Send + Sync, V: MaybeDynamicValue + Clone + 'static, O: 'static + MaybeDynamicObject, V: 'static, F: 'static, { let wrapped = HandlerWrapper::new(self.state.clone(), handler); let wrapped = wrapped.map( |(any_object, any_value): &(AnyObject, AnyValue), _state: &OS| { Ok((O::from_any(any_object), V::from_any(any_value))) }, ); assert!(self .value_change .insert( ObjectValuePair { object_kind: O::object_name(), value_kind: V::value_name() }, wrapped ) .is_none()); self } pub fn object_metadata_provider(&mut self, provider: Box) -> &mut Self { self.metadata_providers.push(provider); self } /// Register a [`GiteratedObject`] type with the runtime. /// /// # Type Registration /// This will register the provided object type. pub fn object(&mut self) -> &mut Self where GiteratedStack: HandlerResolvable<(Instance, ObjectRequest), OS>, as HandlerResolvable<(Instance, ObjectRequest), OS>>::Error: Into, { self.metadata.register_object::(); // Insert handler so ObjectRequest is handled properly self.operation( move |_object: Instance, operation: ObjectRequest, _state: S, stack: GiteratedStack| { let operation = operation.clone(); async move { for (_object_name, object_meta) in stack.inner.metadata.objects.iter() { if (object_meta.from_str)(&operation.0).is_ok() { return Ok(ObjectResponse(operation.0.clone())); } } Err(OperationError::Unhandled) } .boxed_local() }, ); self } } impl SubstackBuilder { /// Register a [`Setting`] type with the runtime. /// /// # Type Registration /// This will register the provided setting type. pub fn setting( &mut self, ) -> &mut Self { self.metadata.register_setting::(); self } /// Register a [`GiteratedObjectValue`] that is also a [`Setting`], which /// allows for automatic value updates. pub fn value_setting< O: GiteratedObject + 'static + Clone, T: GiteratedObjectValue + Setting + 'static + Clone, >( &mut self, ) -> &mut Self { self.metadata.register_setting::(); self.metadata.register_value::(); self.setting_change.insert( ObjectSettingPair::from_types::(), HandlerWrapper::new( (), move |object: AnyObject, setting: AnySetting, _state: _, OperationState(operation_state): OperationState, stack: GiteratedStack| { trace!( "value setting updated {}::{}", O::object_name(), T::value_name() ); let object = object.clone(); async move { let object = object.downcast_ref::().unwrap(); let setting = setting.downcast_ref::().unwrap(); stack .value_update( object.clone(), AnyValue::new(setting.clone()), &operation_state, ) .await; Ok(()) } .boxed_local() }, ), ); let wrapped = HandlerWrapper::new( self.state.clone(), |object: AnyObject, _name: String, _state: _, stack: GiteratedStack| { info!("a setting handler called"); let object = object.clone(); async move { match stack .new_get_setting::(object.downcast_ref().unwrap()) .await { Ok(setting) => Ok(AnyValue::new(setting)), Err(err) => { panic!("Error: {:?}", err); } } } .boxed_local() }, ); self.value_getters .insert(ObjectValuePair::from_types::(), wrapped); self } } // Placeholder impl SubstackBuilder { pub fn dynamic_operation(&mut self, handler: H) -> &mut Self where H: IntoGiteratedHandler< (AnyObject, AnyOperation), A, S, OS, Result>, > + Send + Sync + 'static, OS: Clone + Send + Sync + 'static, { let wrapped = HandlerWrapper::new(self.state.clone(), handler); self.operation_handlers.insert( ObjectOperationPair { object_name: "any", operation_name: "any", }, wrapped, ); self } } #[derive(Debug, Clone, thiserror::Error)] #[error("downcast error")] pub struct DowncastError;