use std::{collections::HashMap, pin::Pin, sync::Arc}; use futures_util::{future::BoxFuture, Future, FutureExt}; use giterated_models::{ error::{GetValueError, OperationError}, model::{repository::Repository, settings::AnySetting, user::User}, operation::{AnyObject, AnyOperation, GetValue, GiteratedObject, GiteratedOperation}, values::{AnyValue, GetSetting, GetSettingError, SetSetting, SetSettingError}, }; use super::DatabaseBackend; #[async_trait::async_trait] pub trait GiteratedOperationHandler< O: GiteratedObject, D: GiteratedOperation, S: Send + Sync + Clone, > { fn operation_name(&self) -> &str; fn object_name(&self) -> &str; async fn handle( &self, object: &O, operation: D, state: S, ) -> Result>; } #[async_trait::async_trait] impl GiteratedOperationHandler for F where F: FnMut( &O, D, S, ) -> Pin< Box>> + Send>, > + Send + Sync + Clone, O: GiteratedObject + Send + Sync, D: GiteratedOperation + 'static, >::Failure: Send, S: Send + Sync + Clone + 'static, { fn operation_name(&self) -> &str { D::operation_name() } fn object_name(&self) -> &str { O::object_name() } async fn handle( &self, object: &O, operation: D, state: S, ) -> Result> { self.clone()(object, operation, state).await } } pub struct OperationWrapper( Box< dyn Fn( AnyObject, AnyOperation, S, ) -> Pin, OperationError>>> + Send>> + Send + Sync, >, ); impl OperationWrapper { pub fn new< O: GiteratedObject + Send + Sync, D: GiteratedOperation + 'static, F: GiteratedOperationHandler + Send + Sync + 'static + Clone, >( handler: F, ) -> Self { let handler = Arc::new(Box::pin(handler)); Self(Box::new(move |any_object, any_operation, state| { let handler = handler.clone(); async move { let handler = handler.clone(); let object: O = O::from_object_str(&any_object.0).unwrap(); let operation: D = serde_json::from_value(any_operation.0.clone()).unwrap(); let result = handler.handle(&object, operation, state).await; result .map(|success| serde_json::to_vec(&success).unwrap()) .map_err(|err| match err { OperationError::Operation(err) => { OperationError::Operation(serde_json::to_vec(&err).unwrap()) } OperationError::Internal(internal) => OperationError::Internal(internal), OperationError::Unhandled => OperationError::Unhandled, }) } .boxed() })) } async fn handle( &mut self, object: AnyObject, operation: AnyOperation, state: S, ) -> Result, OperationError>> { self.0(object, operation, state).await } } pub fn user_get_value( object: &User, operation: GetValue>, state: DatabaseBackend, ) -> BoxFuture<'static, Result, OperationError>> { let object = object.clone(); async move { let mut user_backend = state.user_backend.lock().await; let value = user_backend .get_value(&object, &operation.value_name) .await .map_err(|e| OperationError::Internal(e.to_string()))?; Ok(value) } .boxed() } pub fn user_get_setting( object: &User, operation: GetSetting, state: DatabaseBackend, ) -> BoxFuture<'static, Result>> { let object = object.clone(); async move { let mut user_backend = state.user_backend.lock().await; let value = user_backend .get_setting(&object, &operation.setting_name) .await .map_err(|e| OperationError::Internal(e.to_string()))?; Ok(value) } .boxed() } pub fn user_set_setting( object: &User, operation: SetSetting, state: DatabaseBackend, ) -> BoxFuture<'static, Result<(), OperationError>> { let object = object.clone(); async move { let mut user_backend = state.user_backend.lock().await; let value = user_backend .write_setting(&object, &operation.setting_name, &operation.value.0) .await .map_err(|e| OperationError::Internal(e.to_string()))?; Ok(value) } .boxed() } pub fn repository_get_value( object: &Repository, operation: GetValue>, state: DatabaseBackend, ) -> BoxFuture<'static, Result, OperationError>> { let object = object.clone(); async move { let mut repository_backend = state.repository_backend.lock().await; let value = repository_backend .get_value(&object, &operation.value_name) .await .map_err(|e| OperationError::Internal(e.to_string()))?; Ok(value) } .boxed() } pub fn repository_get_setting( object: &Repository, operation: GetSetting, state: DatabaseBackend, ) -> BoxFuture<'static, Result>> { let object = object.clone(); async move { let mut repository_backend = state.repository_backend.lock().await; let value = repository_backend .get_setting(&object, &operation.setting_name) .await .map_err(|e| OperationError::Internal(e.to_string()))?; Ok(value) } .boxed() } pub fn repository_set_setting( object: &Repository, operation: SetSetting, state: DatabaseBackend, ) -> BoxFuture<'static, Result<(), OperationError>> { let object = object.clone(); async move { let mut repository_backend = state.repository_backend.lock().await; let value = repository_backend .write_setting(&object, &operation.setting_name, &operation.value.0) .await .map_err(|e| OperationError::Internal(e.to_string()))?; Ok(value) } .boxed() } pub struct OperationHandlers { operations: HashMap>, } impl Default for OperationHandlers { fn default() -> Self { Self { operations: HashMap::new(), } } } impl OperationHandlers { pub fn insert< O: GiteratedObject + Send + Sync, D: GiteratedOperation + 'static, H: GiteratedOperationHandler + Send + Sync + 'static + Clone, >( &mut self, handler: H, ) -> &mut Self { let operation_name = handler.operation_name().to_string(); let wrapped = OperationWrapper::new(handler); self.operations.insert(operation_name, wrapped); self } pub async fn handle( &mut self, object: &O, operation_name: &str, operation: AnyOperation, state: S, ) -> Result, OperationError>> { if let Some(handler) = self.operations.get_mut(operation_name) { handler .handle(AnyObject(object.to_string()), operation, state) .await } else { Err(OperationError::Internal(format!( "unknown operation: {}", operation_name ))) } } }