diff --git a/giterated-daemon/src/backend/mod.rs b/giterated-daemon/src/backend/mod.rs index 092cf30..f73b5c8 100644 --- a/giterated-daemon/src/backend/mod.rs +++ b/giterated-daemon/src/backend/mod.rs @@ -78,7 +78,7 @@ pub trait RepositoryBackend { async fn get_setting(&mut self, user: &Repository, name: &str) -> Result; async fn write_setting( &mut self, - user: &Repository, + repository: &Repository, name: &str, setting: &Value, ) -> Result<(), Error>; diff --git a/giterated-daemon/src/database_backend/handler.rs b/giterated-daemon/src/database_backend/handler.rs index 8d957bb..d5942fd 100644 --- a/giterated-daemon/src/database_backend/handler.rs +++ b/giterated-daemon/src/database_backend/handler.rs @@ -9,17 +9,19 @@ use giterated_models::{ }, object_backend::ObjectBackend, repository::{ - Commit, DefaultBranch, Description, LatestCommit, Repository, + AccessList, Commit, DefaultBranch, Description, LatestCommit, Repository, RepositoryCommitBeforeRequest, RepositoryDiff, RepositoryDiffPatchRequest, RepositoryDiffRequest, RepositoryFile, RepositoryFileFromIdRequest, RepositoryFileFromPathRequest, RepositoryFileInspectRequest, RepositoryInfoRequest, RepositorySummary, RepositoryView, Visibility, }, settings::{AnySetting, GetSetting, GetSettingError, SetSetting, SetSettingError}, - user::{User, UserRepositoriesRequest}, + user::{Bio, DisplayName, User, UserRepositoriesRequest}, value::{AnyValue, GetValue}, }; -use giterated_stack::{AuthenticatedUser, AuthorizedInstance, BackendWrapper, StackOperationState}; +use giterated_stack::{ + AuthenticatedUser, AuthorizedInstance, AuthorizedUser, BackendWrapper, StackOperationState, +}; use super::DatabaseBackend; @@ -44,7 +46,11 @@ pub fn user_get_repositories( let mut repositories = vec![]; for repository in repositories_response { - if repositories_backend.exists(&requester, &repository.repository).await.map_err(|e| OperationError::Internal(e.to_string()))? { + if repositories_backend + .exists(&requester, &repository.repository) + .await + .map_err(|e| OperationError::Internal(e.to_string()))? + { repositories.push(repository); } } @@ -92,24 +98,24 @@ pub fn user_get_setting( .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 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_info( object: &Repository, @@ -356,24 +362,24 @@ pub fn repository_get_setting( .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 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 fn instance_authentication_request( object: &Instance, @@ -430,3 +436,88 @@ pub fn instance_create_repository_request( } .boxed() } + +pub fn user_get_value_display_name( + object: &User, + operation: GetValue, + state: DatabaseBackend, + _requester: AuthorizedUser, +) -> BoxFuture<'static, Result>> { + let object = object.clone(); + + async move { + let mut backend = state.user_backend.lock().await; + + let raw_value = backend + .get_value(&object, &operation.value_name) + .await + .map_err(|e| OperationError::Internal(e.to_string()))?; + + Ok(serde_json::from_value(raw_value.into_inner()) + .map_err(|e| OperationError::Internal(e.to_string()))?) + } + .boxed() +} + +pub fn user_get_value_bio( + object: &User, + operation: GetValue, + state: DatabaseBackend, +) -> BoxFuture<'static, Result>> { + let object = object.clone(); + + async move { + let mut backend = state.user_backend.lock().await; + + let raw_value = backend + .get_value(&object, &operation.value_name) + .await + .map_err(|e| OperationError::Internal(e.to_string()))?; + + Ok(serde_json::from_value(raw_value.into_inner()) + .map_err(|e| OperationError::Internal(e.to_string()))?) + } + .boxed() +} + +pub fn repository_get_value_description( + object: &Repository, + operation: GetValue, + state: DatabaseBackend, +) -> BoxFuture<'static, Result>> { + let object = object.clone(); + + async move { + let mut backend = state.repository_backend.lock().await; + + let raw_value = backend + .get_value(&object, &operation.value_name) + .await + .map_err(|e| OperationError::Internal(e.to_string()))?; + + Ok(serde_json::from_value(raw_value.into_inner()) + .map_err(|e| OperationError::Internal(e.to_string()))?) + } + .boxed() +} + +pub fn repository_get_value_visibility( + object: &Repository, + operation: GetValue, + state: DatabaseBackend, +) -> BoxFuture<'static, Result>> { + let object = object.clone(); + + async move { + let mut backend = state.repository_backend.lock().await; + + let raw_value = backend + .get_value(&object, &operation.value_name) + .await + .map_err(|e| OperationError::Internal(e.to_string()))?; + + Ok(serde_json::from_value(raw_value.into_inner()) + .map_err(|e| OperationError::Internal(e.to_string()))?) + } + .boxed() +} diff --git a/giterated-daemon/src/database_backend/mod.rs b/giterated-daemon/src/database_backend/mod.rs index bd4ca54..0179a90 100644 --- a/giterated-daemon/src/database_backend/mod.rs +++ b/giterated-daemon/src/database_backend/mod.rs @@ -1,4 +1,5 @@ pub mod handler; +pub mod updates; use std::sync::Arc; @@ -7,8 +8,8 @@ use giterated_models::instance::Instance; use giterated_models::object::{GiteratedObject, Object, ObjectRequestError}; use giterated_models::object_backend::ObjectBackend; use giterated_models::operation::GiteratedOperation; -use giterated_models::repository::Repository; -use giterated_models::user::User; +use giterated_models::repository::{DefaultBranch, Description, Repository, Visibility}; +use giterated_models::user::{Bio, DisplayName, User}; use giterated_stack::handler::GiteratedBackend; use giterated_stack::{OperationHandlers, StackOperationState}; use std::fmt::Debug; @@ -20,8 +21,12 @@ use self::handler::{ instance_authentication_request, instance_create_repository_request, instance_registration_request, repository_commit_before, repository_diff, repository_diff_patch, repository_file_from_id, repository_file_from_path, - repository_get_setting, repository_get_value, repository_info, repository_set_setting, - user_get_repositories, user_get_setting, user_get_value, user_set_setting, + repository_get_setting, repository_get_value, repository_get_value_description, + repository_get_value_visibility, repository_info, user_get_repositories, user_get_setting, + user_get_value, user_get_value_bio, user_get_value_display_name, +}; +use self::updates::{ + repository_set_setting, repository_set_value, user_set_setting, user_set_value, }; #[derive(Clone, Debug)] @@ -75,24 +80,29 @@ impl DatabaseBackend { handlers .insert(user_get_repositories) - .insert(user_get_value) - .insert(user_get_setting) - .insert(user_set_setting) .insert(repository_info) .insert(repository_file_from_id) .insert(repository_file_from_path) .insert(repository_diff) .insert(repository_diff_patch) .insert(repository_commit_before) - .insert(repository_get_value) - .insert(repository_get_setting) - .insert(repository_set_setting) .insert(instance_registration_request) .insert(instance_authentication_request) .insert(instance_create_repository_request) - .register_object::() - .register_object::() - .register_object::(); + .setting_getter(user_get_setting) + .setting_getter(repository_get_setting) + .value_getter(user_get_value_display_name) + .value_getter(user_get_value_bio) + .value_getter(repository_get_value_description) + .value_getter(repository_get_value_visibility) + .setting::() + .setting::() + .setting::() + .setting::() + .setting::() + .object::() + .object::() + .object::(); GiteratedBackend::new(self.clone(), handlers) } diff --git a/giterated-daemon/src/database_backend/updates.rs b/giterated-daemon/src/database_backend/updates.rs new file mode 100644 index 0000000..f5994b5 --- /dev/null +++ b/giterated-daemon/src/database_backend/updates.rs @@ -0,0 +1,87 @@ +use futures_util::{future::BoxFuture, FutureExt}; +use giterated_models::{ + repository::{DefaultBranch, Description, Repository}, + settings::{AnySetting, Setting}, + update::ValueUpdate, + user::User, + value::{AnyValue, GiteratedObjectValue}, +}; +use giterated_stack::{AuthorizedUser, StackOperationState}; + +use super::DatabaseBackend; + +pub fn user_set_value( + object: User, + value_name: String, + value: AnyValue, + operation_state: &StackOperationState, +) -> BoxFuture<'static, Result<(), ()>> { + todo!() +} + +pub fn user_set_setting( + object: User, + value_name: String, + value: AnySetting, + operation_state: &StackOperationState, +) -> BoxFuture<'static, Result<(), ()>> { + todo!() +} + +pub fn repository_set_value( + object: Repository, + value_name: String, + value: AnyValue, + operation_state: &StackOperationState, +) -> BoxFuture<'static, Result<(), ()>> { + todo!() +} + +pub fn repository_set_setting( + object: Repository, + value_name: String, + value: AnySetting, + operation_state: &StackOperationState, +) -> BoxFuture<'static, Result<(), ()>> { + todo!() +} + +pub fn repository_set_description( + object: Repository, + description: Description, + user: AuthorizedUser, +) -> BoxFuture<'static, Result<(), ()>> { + async { Ok(()) }.boxed() +} + +pub fn repository_set_default_branch( + object: Repository, + default_branch: DefaultBranch, + // Ensure user is authorized for this request + _user: AuthorizedUser, + backend: DatabaseBackend, +) -> BoxFuture<'static, Result<(), ()>> { + async move { + let mut repository_backend = backend.repository_backend.lock().await; + + repository_backend + .write_setting( + &object, + DefaultBranch::name(), + &serde_json::to_value(default_branch.clone()).unwrap(), + ) + .await + .unwrap(); + + let set_value = ValueUpdate { + object: object.to_string(), + value_name: DefaultBranch::value_name().to_owned(), + value: unsafe { AnyValue::from_raw(serde_json::to_value(default_branch).unwrap()) }, + }; + + // Submit value update back to the daemon + // state.value_update(set_value); + Ok(()) + } + .boxed() +} diff --git a/giterated-models/src/authenticated.rs b/giterated-models/src/authenticated.rs index 94e8a2f..4f3944f 100644 --- a/giterated-models/src/authenticated.rs +++ b/giterated-models/src/authenticated.rs @@ -14,7 +14,7 @@ use crate::{ instance::Instance, message::GiteratedMessage, object::{AnyObject, GiteratedObject}, - operation::{AnyOperation, GiteratedOperation}, + operation::{AnyOperation, AnyOperationV2, GiteratedOperation}, user::User, }; @@ -48,6 +48,14 @@ impl AuthenticatedPayload { payload: AnyOperation(payload), } } + pub fn into_message_v2(self) -> GiteratedMessage { + let payload = serde_json::from_slice::(&self.payload).unwrap(); + GiteratedMessage { + object: AnyObject(self.object), + operation: self.operation, + payload: AnyOperationV2(serde_json::to_vec(&self.payload).unwrap()), + } + } } pub trait AuthenticationSourceProvider: Debug { diff --git a/giterated-models/src/lib.rs b/giterated-models/src/lib.rs index 0c0c39d..0ecbafe 100644 --- a/giterated-models/src/lib.rs +++ b/giterated-models/src/lib.rs @@ -13,5 +13,6 @@ pub mod object_backend; pub mod operation; pub mod repository; pub mod settings; +pub mod update; pub mod user; pub mod value; diff --git a/giterated-models/src/object.rs b/giterated-models/src/object.rs index 20d0be4..8c9a7b3 100644 --- a/giterated-models/src/object.rs +++ b/giterated-models/src/object.rs @@ -10,7 +10,7 @@ use crate::{ error::{GetValueError, OperationError}, object_backend::ObjectBackend, operation::GiteratedOperation, - settings::{GetSetting, GetSettingError, SetSetting, SetSettingError, Setting}, + settings::{AnySetting, GetSetting, GetSettingError, SetSetting, SetSettingError, Setting}, value::{GetValue, GiteratedObjectValue}, }; @@ -56,14 +56,18 @@ impl< } } -pub trait GiteratedObject: Send + Display + FromStr { +pub trait GiteratedObject: Send + Display + FromStr + Sync { fn object_name() -> &'static str; fn from_object_str(object_str: &str) -> Result; } -impl<'b, I: Clone + Send + Sync, O: GiteratedObject + Clone + Debug, B: ObjectBackend> - Object<'b, I, O, B> +impl< + 'b, + I: Clone + Send + Sync, + O: GiteratedObject + Clone + Debug + 'static, + B: ObjectBackend, + > Object<'b, I, O, B> { pub async fn get + Send + Debug>( &mut self, @@ -89,11 +93,11 @@ impl<'b, I: Clone + Send + Sync, O: GiteratedObject + Clone + Debug, B: ObjectBa self.request( GetSetting { setting_name: S::name().to_string(), - _marker: PhantomData, }, operation_state, ) .await + .map(|success| serde_json::from_value(success).unwrap()) } pub async fn set_setting( @@ -104,7 +108,7 @@ impl<'b, I: Clone + Send + Sync, O: GiteratedObject + Clone + Debug, B: ObjectBa self.request( SetSetting { setting_name: S::name().to_string(), - value: setting, + value: AnySetting(serde_json::to_value(setting).unwrap()), }, operation_state, ) diff --git a/giterated-models/src/object_backend.rs b/giterated-models/src/object_backend.rs index 6af7bfb..31df970 100644 --- a/giterated-models/src/object_backend.rs +++ b/giterated-models/src/object_backend.rs @@ -16,7 +16,7 @@ pub trait ObjectBackend: Send + Sync + Sized + Clone { operation_state: &S, ) -> Result> where - O: GiteratedObject + Debug, + O: GiteratedObject + Debug + 'static, D: GiteratedOperation + Debug; async fn get_object( diff --git a/giterated-models/src/operation.rs b/giterated-models/src/operation.rs index 3b3dc23..3cbb488 100644 --- a/giterated-models/src/operation.rs +++ b/giterated-models/src/operation.rs @@ -5,7 +5,9 @@ use serde_json::Value; use crate::object::GiteratedObject; -pub trait GiteratedOperation: Send + Serialize + DeserializeOwned { +pub trait GiteratedOperation: + Send + Sync + Serialize + DeserializeOwned +{ type Success: Serialize + DeserializeOwned + Send; type Failure: Serialize + DeserializeOwned + Send; @@ -25,6 +27,17 @@ impl GiteratedOperation for AnyOperation { type Failure = Value; } +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(transparent)] +#[repr(transparent)] +pub struct AnyOperationV2(pub Vec); + +impl GiteratedOperation for AnyOperationV2 { + type Success = Vec; + + type Failure = Vec; +} + /// The internal state of an operation, used to provide authentication information /// and the ability to make giterated calls within handlers. #[derive(Clone)] diff --git a/giterated-models/src/settings/mod.rs b/giterated-models/src/settings/mod.rs index 38c009e..39e95e5 100644 --- a/giterated-models/src/settings/mod.rs +++ b/giterated-models/src/settings/mod.rs @@ -4,7 +4,7 @@ pub use operations::*; use serde::{de::DeserializeOwned, Deserialize, Serialize}; use serde_json::Value; -pub trait Setting: Serialize + DeserializeOwned { +pub trait Setting: Serialize + DeserializeOwned + Send + Sync { fn name() -> &'static str; } diff --git a/giterated-models/src/settings/operations.rs b/giterated-models/src/settings/operations.rs index 012a90e..3a9c091 100644 --- a/giterated-models/src/settings/operations.rs +++ b/giterated-models/src/settings/operations.rs @@ -1,26 +1,24 @@ use std::{fmt::Debug, marker::PhantomData}; use serde::{de::DeserializeOwned, Deserialize, Serialize}; +use serde_json::Value; use thiserror::Error; use crate::{object::GiteratedObject, operation::GiteratedOperation}; -use super::Setting; +use super::{AnySetting, Setting}; #[derive(Serialize, Deserialize, Debug, Clone)] -pub struct GetSetting { +pub struct GetSetting { pub setting_name: String, - pub _marker: PhantomData, } -impl GiteratedOperation - for GetSetting -{ +impl GiteratedOperation for GetSetting { fn operation_name() -> &'static str { "get_setting" } - type Success = S; + type Success = Value; type Failure = GetSettingError; } @@ -28,13 +26,12 @@ impl G #[derive(Error, Debug, Serialize, Deserialize)] pub enum GetSettingError {} #[derive(Serialize, Deserialize, Debug, Clone)] -#[serde(bound(deserialize = "S: Setting"))] -pub struct SetSetting { +pub struct SetSetting { pub setting_name: String, - pub value: S, + pub value: AnySetting, } -impl GiteratedOperation for SetSetting { +impl GiteratedOperation for SetSetting { fn operation_name() -> &'static str { "set_setting" } diff --git a/giterated-models/src/update/instance.rs b/giterated-models/src/update/instance.rs new file mode 100644 index 0000000..d6ff76d --- /dev/null +++ b/giterated-models/src/update/instance.rs @@ -0,0 +1 @@ +pub struct InstanceUpdate {} diff --git a/giterated-models/src/update/mod.rs b/giterated-models/src/update/mod.rs new file mode 100644 index 0000000..d7fcacb --- /dev/null +++ b/giterated-models/src/update/mod.rs @@ -0,0 +1,23 @@ +use crate::value::AnyValue; + +mod instance; +mod repository; +mod user; + +pub struct ValueUpdate { + pub object: String, + pub value_name: String, + pub value: AnyValue<()>, +} + +pub struct SettingUpdate { + object: String, + value_name: String, + value: AnyValue<()>, +} + +pub enum GiteratedUpdateKind { + Instance, + Repository, + Value, +} diff --git a/giterated-models/src/update/repository.rs b/giterated-models/src/update/repository.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/giterated-models/src/update/repository.rs @@ -0,0 +1 @@ + diff --git a/giterated-models/src/update/user.rs b/giterated-models/src/update/user.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/giterated-models/src/update/user.rs @@ -0,0 +1 @@ + diff --git a/giterated-models/src/value.rs b/giterated-models/src/value.rs index 15b3111..1efe09d 100644 --- a/giterated-models/src/value.rs +++ b/giterated-models/src/value.rs @@ -5,7 +5,7 @@ use serde_json::Value; use crate::{error::GetValueError, object::GiteratedObject, operation::GiteratedOperation}; -pub trait GiteratedObjectValue: Serialize + DeserializeOwned { +pub trait GiteratedObjectValue: Send + Sync + Serialize + DeserializeOwned { type Object: GiteratedObject; fn value_name() -> &'static str; @@ -17,6 +17,11 @@ pub struct GetValue { pub(crate) _marker: PhantomData, } +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct GetValueV2 { + pub value_name: String, +} + impl + Send> GiteratedOperation for GetValue { @@ -35,13 +40,17 @@ pub struct AnyValue { _marker: PhantomData, } -impl AnyValue { +impl AnyValue { pub unsafe fn from_raw(value: Value) -> Self { Self { value, _marker: Default::default(), } } + + pub fn into_inner(self) -> Value { + self.value + } } impl GiteratedObjectValue for AnyValue { diff --git a/giterated-stack/src/handler.rs b/giterated-stack/src/handler.rs index 10cbde7..46c5af2 100644 --- a/giterated-stack/src/handler.rs +++ b/giterated-stack/src/handler.rs @@ -1,15 +1,21 @@ use giterated_models::{ error::OperationError, + instance::Instance, object::{ AnyObject, GiteratedObject, Object, ObjectRequest, ObjectRequestError, ObjectResponse, }, object_backend::ObjectBackend, operation::{AnyOperation, GiteratedOperation}, }; -use std::{fmt::Debug, str::FromStr, sync::Arc}; +use std::{any::Any, collections::HashMap, fmt::Debug, str::FromStr, sync::Arc}; +use tokio::sync::Mutex; use tracing::warn; -use crate::{state::HandlerState, OperationHandlers}; +use crate::{ + state::HandlerState, + update::{HandleSettingUpdatedFunction, HandleValueUpdatedFunction, ValueUpdateKind}, + OperationHandlers, +}; use crate::StackOperationState; @@ -42,19 +48,23 @@ impl ObjectBackend for GiteratedBackend operation_state: &StackOperationState, ) -> Result> where - O: GiteratedObject + Debug, + O: GiteratedObject + Debug + 'static, D: GiteratedOperation + Debug, { let serialized = serde_json::to_value(payload).map_err(|e| OperationError::Internal(e.to_string()))?; - let object = object.to_string(); + let object = (Box::new(object) as Box) + .downcast::() + .unwrap(); if operation == ObjectRequest::operation_name() { // We're doing an object request let raw_result = self .handlers .resolve_object( - AnyObject(object.clone()), + *(Box::new(object) as Box) + .downcast() + .unwrap(), serde_json::from_value(serialized).unwrap(), self.state.clone(), &operation_state, @@ -85,7 +95,7 @@ impl ObjectBackend for GiteratedBackend let raw_result = self .handlers .handle( - &AnyObject(object), + &*object, operation, AnyOperation(serialized), self.state.clone(), @@ -122,7 +132,7 @@ impl ObjectBackend for GiteratedBackend let raw_result = self .handlers .resolve_object( - AnyObject("giterated.dev".to_string()), + Instance::from_str("giterated.dev").unwrap(), ObjectRequest(object_str.to_string()), self.state.clone(), operation_state, diff --git a/giterated-stack/src/lib.rs b/giterated-stack/src/lib.rs index 686f411..569f125 100644 --- a/giterated-stack/src/lib.rs +++ b/giterated-stack/src/lib.rs @@ -1,7 +1,11 @@ pub mod handler; +pub mod runtime; pub mod state; +pub mod update; -use std::{collections::HashMap, future::Future, ops::Deref, pin::Pin, str::FromStr, sync::Arc}; +use std::{ + any::Any, collections::HashMap, future::Future, ops::Deref, pin::Pin, str::FromStr, sync::Arc, +}; use futures_util::FutureExt; use giterated_models::{ @@ -14,15 +18,24 @@ use giterated_models::{ }, object_backend::ObjectBackend, operation::{AnyOperation, GiteratedOperation}, - repository::Repository, + repository::{AccessList, Repository}, + settings::{AnySetting, GetSetting, SetSetting, Setting}, user::User, + value::{AnyValue, GetValue, GiteratedObjectValue}, }; use handler::GiteratedBackend; use serde::{de::DeserializeOwned, Serialize}; use serde_json::Value; use state::HandlerState; -use tokio::{sync::mpsc::channel, task::JoinHandle}; +use tokio::{ + sync::{mpsc::channel, Mutex}, + task::JoinHandle, +}; use tracing::{error, warn}; +use update::{ + HandleSettingUpdate, HandleSettingUpdatedFunction, HandleValueUpdate, + HandleValueUpdatedFunction, SettingUpdateKind, ValueUpdateKind, +}; #[derive(Clone, Debug, Hash, Eq, PartialEq)] struct ObjectOperationPair { @@ -30,9 +43,48 @@ struct ObjectOperationPair { operation_name: String, } +pub struct SettingMeta { + name: String, + deserialize: Box Result, serde_json::Error> + Send + Sync>, +} + +pub struct ValueMeta { + name: String, + deserialize: Box Result, serde_json::Error> + Send + Sync>, +} + +pub struct ObjectMeta { + name: String, + from_str: Box Result, ()> + Send + Sync>, + any_is_same: Box bool + Send + Sync>, +} + +pub struct OperationMeta { + name: String, + object_kind: String, + deserialize: Box Result, ()> + Send + Sync>, + any_is_same: Box bool + Send + Sync>, + serialize_success: + Box) -> Result, serde_json::Error> + Send + Sync>, + serialize_error: Box) -> Result, serde_json::Error> + Send + Sync>, +} + pub struct OperationHandlers { operations: HashMap>, get_object: Vec>, + value_getters: HashMap>, + settings_getter: HashMap>, + settings: HashMap, + objects: HashMap, + operations_meta: HashMap, + value_updated: HashMap, + setting_updated: HashMap, +} + +#[derive(Clone, Debug, Hash, Eq, PartialEq)] +pub struct ObjectValuePair { + pub object_kind: String, + pub value_kind: String, } impl Default for OperationHandlers { @@ -40,15 +92,58 @@ impl Default for OperationHandlers { Self { operations: HashMap::new(), get_object: Vec::new(), + value_updated: HashMap::default(), + setting_updated: HashMap::default(), + value_getters: HashMap::default(), + settings_getter: HashMap::default(), + settings: HashMap::default(), + objects: HashMap::default(), + operations_meta: HashMap::default(), } } } impl OperationHandlers { + fn insert_operation + Send + Sync + 'static>( + &mut self, + ) { + // let object_name = O::object_name().to_string(); + // let operation_name = D::operation_name().to_string(); + + // self.operations_meta.insert( + // ObjectOperationPair { + // object_name: object_name.clone(), + // operation_name: operation_name.clone(), + // }, + // OperationMeta { + // name: operation_name, + // object_kind: object_name, + // deserialize: Box::new(|bytes| { + // Ok(Box::new(serde_json::from_slice::(bytes).unwrap()) + // as Box) + // }), + // any_is_same: Box::new(|any_box| any_box.is::()), + // }, + // ); + } + + pub fn setting(&mut self) -> &mut Self { + let setting_meta = SettingMeta { + name: T::name().to_string(), + deserialize: Box::new(|slice| { + Ok(Box::new(serde_json::from_slice(slice)?) as Box) + }), + }; + + self.settings.insert(T::name().to_string(), setting_meta); + + self + } + pub fn insert< A, - O: GiteratedObject + Send + Sync, - D: GiteratedOperation + 'static, + O: GiteratedObject + Send + Sync + 'static, + D: GiteratedOperation + Send + Sync + 'static, H: GiteratedOperationHandler + Send + Sync + 'static + Clone, >( &mut self, @@ -66,10 +161,21 @@ impl OperationHandlers { assert!(self.operations.insert(pair, wrapped).is_none()); + self.insert_operation::(); + self } - pub fn register_object(&mut self) -> &mut Self { + pub fn object(&mut self) -> &mut Self { + // let object_meta = ObjectMeta { + // name: O::object_name().to_string(), + // from_str: Box::new(|str| Ok(Box::new(O::from_str(&str).map_err(|_| ())?))), + + // }; + + // self.objects + // .insert(O::object_name().to_string(), object_meta); + let closure = |_: &Instance, operation: ObjectRequest, _state| { async move { if O::from_str(&operation.0).is_ok() { @@ -98,33 +204,32 @@ impl OperationHandlers { ) -> Result, OperationError>> { // TODO let object = object.to_string(); + let object_name = O::object_name().to_string(); - let object_name = { - if User::from_str(&object).is_ok() { - User::object_name() - } else if Repository::from_str(&object).is_ok() { - Repository::object_name() - } else if Instance::from_str(&object).is_ok() { - Instance::object_name() - } else { - return Err(OperationError::Unhandled); - } - } - .to_string(); + let object_meta = self + .objects + .get(&object_name) + .ok_or_else(|| OperationError::Unhandled)?; + + let object_box = (object_meta.from_str)(&object).map_err(|_| OperationError::Unhandled)?; let target_handler = ObjectOperationPair { object_name, operation_name: operation_name.to_string(), }; + let operation_meta = self + .operations_meta + .get(&target_handler) + .ok_or_else(|| OperationError::Unhandled)?; + + let operation_box = + (operation_meta.deserialize)(&serde_json::to_vec(&operation.0).unwrap()) + .map_err(|_| OperationError::Unhandled)?; + if let Some(handler) = self.operations.get(&target_handler) { handler - .handle( - AnyObject(object.to_string()), - operation.clone(), - state.clone(), - operation_state, - ) + .handle(object_box, operation_box, state.clone(), operation_state) .await } else { Err(OperationError::Unhandled) @@ -133,7 +238,7 @@ impl OperationHandlers { pub async fn resolve_object( &self, - instance: AnyObject, + instance: Instance, request: ObjectRequest, state: S, operation_state: &StackOperationState, @@ -141,8 +246,8 @@ impl OperationHandlers { for handler in self.get_object.iter() { if let Ok(response) = handler .handle( - instance.clone(), - AnyOperation(serde_json::to_value(request.clone()).unwrap()), + Box::new(instance.clone()) as _, + Box::new(request.clone()) as _, state.clone(), operation_state, ) @@ -154,6 +259,96 @@ impl OperationHandlers { Err(OperationError::Unhandled) } + + pub fn insert_value_update_handler< + H: HandleValueUpdate + Send + Sync + Clone + 'static, + O: GiteratedObject + Send + Sync, + V: GiteratedObjectValue + Send + Sync, + >( + &mut self, + handler: H, + ) -> &mut Self { + let wrapper = HandleValueUpdatedFunction::new(handler, V::value_name()); + + assert!(self + .value_updated + .insert(wrapper.target.clone(), wrapper) + .is_none()); + + self + } + + pub fn insert_setting_update_handler< + H: HandleSettingUpdate + Send + Sync + Clone + 'static, + O: GiteratedObject + Send + Sync, + T: Setting + Send + Sync, + >( + &mut self, + handler: H, + ) -> &mut Self { + let wrapper = HandleSettingUpdatedFunction::new(handler, T::name()); + + assert!(self + .setting_updated + .insert(wrapper.target.clone(), wrapper) + .is_none()); + + self + } + + pub fn value_getter(&mut self, handler: F) -> &mut Self + where + O: GiteratedObject + Send + Sync + 'static, + V: GiteratedObjectValue + Send + Sync + 'static, + F: GiteratedOperationHandler, S> + Send + Sync + Clone + 'static, + { + let object_name = handler.object_name().to_string(); + let value_name = V::value_name().to_string(); + + let wrapped = OperationWrapper::new(handler); + + assert!(self + .value_getters + .insert( + ValueGetter { + object_type: object_name, + value_type: value_name + }, + wrapped + ) + .is_none()); + + self + } + + pub fn setting_getter(&mut self, handler: F) -> &mut Self + where + O: GiteratedObject + Send + Sync + 'static, + F: GiteratedOperationHandler + Send + Sync + Clone + 'static, + { + let object_name = handler.object_name().to_string(); + + let wrapped = OperationWrapper::new(handler); + + assert!(self.settings_getter.insert(object_name, wrapped).is_none()); + + self + } +} + +#[derive(Clone, Debug, Hash, PartialEq, Eq)] +pub struct ValueGetter { + pub object_type: String, + pub value_type: String, +} + +impl Default for ValueGetter { + fn default() -> Self { + Self { + object_type: AnyObject::object_name().to_string(), + value_type: "any".to_string(), + } + } } #[async_trait::async_trait] @@ -162,7 +357,7 @@ pub trait GiteratedOperationHandler< O: GiteratedObject, D: GiteratedOperation, S: Send + Sync + Clone, -> +>: Send + Sync { fn operation_name(&self) -> &str; fn object_name(&self) -> &str; @@ -353,8 +548,8 @@ where pub struct OperationWrapper { func: Box< dyn Fn( - AnyObject, - AnyOperation, + Box, + Box, S, StackOperationState, ) @@ -368,7 +563,7 @@ pub struct OperationWrapper { impl OperationWrapper { pub fn new< A, - O: GiteratedObject + Send + Sync, + O: GiteratedObject + Send + Sync + 'static, D: GiteratedOperation + 'static, F: GiteratedOperationHandler + Send + Sync + 'static + Clone, >( @@ -376,17 +571,15 @@ impl OperationWrapper { ) -> Self { let handler = Arc::new(Box::pin(handler)); Self { - func: Box::new(move |any_object, any_operation, state, operation_state| { + func: Box::new(move |object, operation, state, operation_state| { let handler = handler.clone(); async move { let handler = handler.clone(); - let object: O = - O::from_object_str(&any_object.0).map_err(|_| OperationError::Unhandled)?; - let operation: D = serde_json::from_value(any_operation.0.clone()) - .map_err(|_| OperationError::Unhandled)?; + let object: Box = object.downcast().unwrap(); + let operation: Box = operation.downcast().unwrap(); let result = handler - .handle(&object, operation, state, &operation_state) + .handle(&object, *operation, state, &operation_state) .await; result .map(|success| serde_json::to_vec(&success).unwrap()) @@ -408,8 +601,8 @@ impl OperationWrapper { async fn handle( &self, - object: AnyObject, - operation: AnyOperation, + object: Box, + operation: Box, state: S, operation_state: &StackOperationState, ) -> Result, OperationError>> { @@ -530,6 +723,129 @@ pub trait AuthorizedOperation: GiteratedOperation { } #[async_trait::async_trait] +impl< + O: GiteratedObject + Send + Sync + Debug, + V: GiteratedObjectValue + Send + Sync, + > AuthorizedOperation for GetValue +{ + async fn authorize( + &self, + authorize_for: &O, + operation_state: &StackOperationState, + ) -> Result> { + Ok(operation_state + .giterated_backend + .get_object::(&authorize_for.to_string(), operation_state) + .await + .is_ok()) + } +} + +#[async_trait::async_trait] +impl AuthorizedOperation for SetSetting { + async fn authorize( + &self, + authorize_for: &User, + operation_state: &StackOperationState, + ) -> Result> { + let authenticated_user = operation_state + .user + .as_ref() + .ok_or_else(|| OperationError::Operation(()))?; + + Ok(authorize_for == authenticated_user.deref()) + } +} + +#[async_trait::async_trait] +impl AuthorizedOperation for GetSetting { + async fn authorize( + &self, + authorize_for: &User, + operation_state: &StackOperationState, + ) -> Result> { + let authenticated_user = operation_state + .user + .as_ref() + .ok_or_else(|| OperationError::Operation(()))?; + + Ok(authorize_for == authenticated_user.deref()) + } +} + +#[async_trait::async_trait] +impl AuthorizedOperation for SetSetting { + async fn authorize( + &self, + authorize_for: &Repository, + operation_state: &StackOperationState, + ) -> Result> { + let authenticated_user = operation_state + .user + .as_ref() + .ok_or_else(|| OperationError::Operation(()))?; + + let mut object = operation_state + .giterated_backend + .get_object::(&authorize_for.to_string(), operation_state) + .await + .map_err(|e| OperationError::Internal(e.to_string()))?; + + let access_list = object + .get_setting::(operation_state) + .await + .map_err(|e| OperationError::Internal(e.to_string()))?; + + if access_list + .0 + .iter() + .find(|user| *user == authenticated_user.deref()) + .is_some() + { + Ok(true) + } else { + Ok(false) + } + } +} + +#[async_trait::async_trait] +impl AuthorizedOperation for GetSetting { + async fn authorize( + &self, + authorize_for: &Repository, + operation_state: &StackOperationState, + ) -> Result> { + let authenticated_user = operation_state + .user + .as_ref() + .ok_or_else(|| OperationError::Operation(()))?; + + let mut object = operation_state + .giterated_backend + .get_object::(&authorize_for.to_string(), operation_state) + .await + .map_err(|e| OperationError::Internal(e.to_string()))?; + + let access_list = object + .get_setting::(operation_state) + .await + .map_err(|e| OperationError::Internal(e.to_string()))?; + + if access_list + .0 + .iter() + .find(|user| *user == authenticated_user.deref()) + .is_some() + { + Ok(true) + } else { + Ok(false) + } + } +} + +#[async_trait::async_trait] impl AuthorizedOperation for RegisterAccountRequest { async fn authorize( &self, diff --git a/giterated-stack/src/runtime.rs b/giterated-stack/src/runtime.rs new file mode 100644 index 0000000..449fc31 --- /dev/null +++ b/giterated-stack/src/runtime.rs @@ -0,0 +1,502 @@ +use std::{any::Any, collections::HashMap}; + +use giterated_models::{ + authenticated::AuthenticatedPayload, + error::OperationError, + message::GiteratedMessage, + object::{AnyObject, GiteratedObject}, + operation::{AnyOperation, AnyOperationV2, GiteratedOperation}, + settings::{AnySetting, GetSetting, SetSetting, Setting}, + value::{GetValue, GetValueV2, GiteratedObjectValue}, +}; +use tracing::trace; + +use crate::{ + GiteratedOperationHandler, ObjectMeta, ObjectOperationPair, ObjectValuePair, OperationMeta, + OperationWrapper, SettingMeta, StackOperationState, ValueMeta, +}; + +/// Temporary name for the next generation of Giterated stack +pub struct GiteratedRuntime { + operation_handlers: HashMap>, + value_getters: HashMap>, + setting_getters: HashMap>, + metadata: RuntimeMetadata, +} + +impl GiteratedRuntime { + pub fn merge_builder(&mut self, builder: RuntimeBuilder) -> &mut Self { + for (target, handler) in builder.operation_handlers { + let tree = self.get_or_create_tree(&target); + + tree.push(handler); + } + + for (target, handler) in builder.value_getters { + assert!(self.value_getters.insert(target, handler).is_none()); + } + + for (target, handler) in builder.setting_getters { + assert!(self.setting_getters.insert(target, handler).is_none()); + } + + self.metadata.append(builder.metadata); + + self + } + + fn get_or_create_tree(&mut self, target: &ObjectOperationPair) -> &mut HandlerTree { + if self.operation_handlers.contains_key(target) { + self.operation_handlers.get_mut(target).unwrap() + } else { + self.operation_handlers + .insert(target.clone(), HandlerTree::default()); + + self.operation_handlers.get_mut(target).unwrap() + } + } +} + +pub struct HandlerTree { + elements: Vec>, +} + +impl Default for HandlerTree { + fn default() -> Self { + Self { elements: vec![] } + } +} + +impl HandlerTree { + pub fn push(&mut self, handler: OperationWrapper) { + self.elements.push(handler); + } + + pub fn handle( + &self, + object: &dyn Any, + operation: Box, + state: &S, + operation_state: &StackOperationState, + ) -> Result, OperationError>> { + todo!() + } +} + +/// Stores runtime metadata for all in-use Giterated protocol types. +#[derive(Default)] +struct RuntimeMetadata { + objects: HashMap, + operations: HashMap, + values: HashMap, + settings: HashMap, +} + +/// Defines a type that is a valid Giterated runtime state. +/// +/// This allows for extraction of state in handlers, based on a +/// [`FromOperationState`] impl on (what is in this case) [`Self`]. +pub trait GiteratedRuntimeState: Send + Sync + Clone {} + +impl GiteratedRuntimeState for T {} + +#[derive(Default)] +pub struct RuntimeBuilder { + operation_handlers: HashMap>, + value_getters: HashMap>, + setting_getters: HashMap>, + metadata: RuntimeMetadata, +} + +impl RuntimeBuilder { + /// 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 + 'static, + D: GiteratedOperation + 'static, + H: GiteratedOperationHandler + 'static + Clone, + { + let object_name = handler.object_name().to_string(); + let operation_name = handler.operation_name().to_string(); + + let wrapped = OperationWrapper::new(handler); + + let pair = ObjectOperationPair { + object_name, + operation_name, + }; + + assert!(self.operation_handlers.insert(pair, wrapped).is_none()); + + self.metadata.register_operation::(); + + self + } + + /// Register a [`GiteratedObject`] type with the runtime. + /// + /// # Type Registration + /// This will register the provided object type. + pub fn object(&mut self) -> &mut Self { + self.metadata.register_object::(); + + self + } + + /// 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`] 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 value(&mut self, handler: F) -> &mut Self + where + O: GiteratedObject + 'static, + V: GiteratedObjectValue + 'static, + F: GiteratedOperationHandler, S> + Clone + 'static, + { + let object_name = handler.object_name().to_string(); + let value_name = V::value_name().to_string(); + + let wrapped = OperationWrapper::new(handler); + + assert!(self + .value_getters + .insert( + ObjectValuePair { + object_kind: object_name, + value_kind: value_name + }, + wrapped + ) + .is_none()); + + self.metadata.register_value::(); + + self + } + + /// Register a handler for [`GetSetting`] for it's associated object type. + pub fn object_settings(&mut self, handler: F) -> &mut Self + where + O: GiteratedObject + 'static, + F: GiteratedOperationHandler + Clone + 'static, + { + let object_name = handler.object_name().to_string(); + + let wrapped = OperationWrapper::new(handler); + + assert!(self.setting_getters.insert(object_name, wrapped).is_none()); + + self + } +} + +impl RuntimeMetadata { + fn register_object(&mut self) { + let object_name = O::object_name().to_string(); + + let object_meta = ObjectMeta { + name: object_name.clone(), + from_str: Box::new(|str| Ok(Box::new(O::from_str(&str).map_err(|_| ())?))), + any_is_same: Box::new(|any| any.is::()), + }; + + if self.objects.insert(object_name, object_meta).is_some() { + trace!( + "Registration of object {} overwrote previous registration.", + O::object_name() + ); + } else { + trace!("Registration of object {}.", O::object_name()) + } + } + + fn register_operation + 'static>( + &mut self, + ) { + let object_name = O::object_name().to_string(); + let operation_name = D::operation_name().to_string(); + + if self + .operations + .insert( + ObjectOperationPair { + object_name: object_name.clone(), + operation_name: operation_name.clone(), + }, + OperationMeta { + name: operation_name, + object_kind: object_name, + deserialize: Box::new(|bytes| { + Ok(Box::new(serde_json::from_slice::(bytes).unwrap()) + as Box) + }), + any_is_same: Box::new(|any_box| any_box.is::()), + serialize_success: Box::new(|any| { + let to_serialize = any.downcast::().unwrap(); + serde_json::to_vec(&to_serialize) + }), + serialize_error: Box::new(|any| { + let to_serialize = any.downcast::().unwrap(); + serde_json::to_vec(&to_serialize) + }), + }, + ) + .is_some() + { + trace!( + "Registration of object operation {}<{}> overwrote previous registration.", + D::operation_name(), + O::object_name() + ); + } else { + trace!( + "Registration of object operation {}<{}>.", + D::operation_name(), + O::object_name() + ) + } + } + + fn register_value< + O: GiteratedObject + 'static, + V: GiteratedObjectValue + 'static, + >( + &mut self, + ) { + let object_name = O::object_name().to_string(); + let value_name = V::value_name().to_string(); + + if self + .values + .insert( + ObjectValuePair { + object_kind: object_name.clone(), + value_kind: value_name.clone(), + }, + ValueMeta { + name: value_name, + deserialize: Box::new(|bytes| Ok(Box::new(serde_json::from_slice(&bytes)?))), + }, + ) + .is_some() + { + trace!( + "Registration of value <{}>::{} overwrote previous registration.", + O::object_name(), + V::value_name() + ); + } else { + trace!( + "Registration of value <{}>::{}.", + O::object_name(), + V::value_name() + ); + } + } + + fn register_setting(&mut self) { + let setting_name = S::name().to_string(); + + if self + .settings + .insert( + setting_name.clone(), + SettingMeta { + name: setting_name, + deserialize: Box::new(|bytes| Ok(Box::new(serde_json::from_slice(bytes)?))), + }, + ) + .is_some() + { + trace!( + "Registration of setting {} overwrote previous registration.", + S::name() + ); + } else { + trace!("Registration of setting {}.", S::name()); + } + } + + fn append(&mut self, other: Self) { + self.objects.extend(other.objects); + self.operations.extend(other.operations); + self.values.extend(other.values); + self.settings.extend(other.settings); + } +} + +#[async_trait::async_trait] +impl GiteratedOperationHandler for GiteratedRuntime +where + O: GiteratedObject + 'static, + D: GiteratedOperation + 'static, + S: GiteratedRuntimeState, +{ + 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, + operation_state: &StackOperationState, + ) -> Result> { + // Erase object and operation types. + let object = object as &dyn Any; + let operation = Box::new(operation) as Box; + + // We need to determine the type of the object, iterate through all known + // object types and check if the &dyn Any we have is the same type as the + // object type. + let object_type = { + let mut object_type = None; + + for (object_name, object_meta) in self.metadata.objects.iter() { + if (object_meta.any_is_same)(object) { + object_type = Some(object_name.clone()); + break; + } + } + + object_type + } + .ok_or_else(|| OperationError::Unhandled)?; + + // We need to hijack get_value, set_setting, and get_setting. + if operation.is::() { + todo!() + } else if operation.is::() { + todo!() + } else if operation.is::() { + todo!() + } + + // Resolve the operation from the known operations table. + let operation_type = { + let mut operation_type = None; + + for (target, operation_meta) in self.metadata.operations.iter() { + // Skip elements that we know will not match + if target.object_name != object_type { + continue; + } + + if (operation_meta.any_is_same)(&operation) { + operation_type = Some(target.clone()); + break; + } + } + + operation_type + } + .ok_or_else(|| OperationError::Unhandled)?; + + // Resolve the handler from our handler tree + let handler_tree = self + .operation_handlers + .get(&operation_type) + .ok_or_else(|| OperationError::Unhandled)?; + + let raw_result = handler_tree.handle(object, operation, &state, operation_state); + + // Convert the dynamic result back into its concrete type + match raw_result { + Ok(result) => Ok(*result.downcast::().unwrap()), + Err(err) => Err(match err { + OperationError::Internal(internal) => OperationError::Internal(internal), + OperationError::Operation(boxed_error) => { + OperationError::Operation(*boxed_error.downcast::().unwrap()) + } + OperationError::Unhandled => OperationError::Unhandled, + }), + } + } +} + +impl GiteratedRuntime { + /// Handles a giterated network message, returning either a raw success + /// payload or a serialized error payload. + pub async fn handle_network_message( + &self, + message: AuthenticatedPayload, + state: &S, + operation_state: &StackOperationState, + ) -> Result, OperationError>> { + let message: GiteratedMessage = message.into_message_v2(); + + // Deserialize the object, also getting the object type's name + let (object_type, object) = { + let mut result = None; + + for (object_type, object_meta) in self.metadata.objects.iter() { + if let Ok(object) = (object_meta.from_str)(&message.object.0) { + result = Some((object_type.clone(), object)); + break; + } + } + + result + } + .ok_or_else(|| OperationError::Unhandled)?; + + let target = ObjectOperationPair { + object_name: object_type, + operation_name: message.operation, + }; + + // Resolve the target operations from the handlers table + let handler = self + .operation_handlers + .get(&target) + .ok_or_else(|| OperationError::Unhandled)?; + + // Deserialize the operation + let meta = self + .metadata + .operations + .get(&target) + .ok_or_else(|| OperationError::Unhandled)?; + + let operation = + (meta.deserialize)(&message.payload.0).map_err(|_| OperationError::Unhandled)?; + + // Get the raw result of the operation, where the return values are boxed. + let raw_result = handler.handle(&object, operation, state, operation_state); + + // Deserialize the raw result for the network + match raw_result { + Ok(success) => Ok((meta.serialize_success)(success) + .map_err(|e| OperationError::Internal(e.to_string()))?), + Err(err) => Err(match err { + OperationError::Operation(failure) => OperationError::Operation( + (meta.serialize_error)(failure) + .map_err(|e| OperationError::Internal(e.to_string()))?, + ), + OperationError::Internal(internal) => OperationError::Internal(internal), + OperationError::Unhandled => OperationError::Unhandled, + }), + } + } +} diff --git a/giterated-stack/src/update.rs b/giterated-stack/src/update.rs new file mode 100644 index 0000000..efd833e --- /dev/null +++ b/giterated-stack/src/update.rs @@ -0,0 +1,194 @@ +use std::sync::Arc; + +use futures_util::{future::BoxFuture, FutureExt}; +use giterated_models::{ + object::GiteratedObject, + settings::{AnySetting, Setting}, + value::{AnyValue, GiteratedObjectValue}, +}; + +use crate::StackOperationState; + +#[async_trait::async_trait] +pub trait HandleValueUpdate> { + async fn handle_value_update( + &mut self, + object: O, + value_name: String, + value: V, + operation_state: &StackOperationState, + ) -> Result<(), ()>; +} + +#[async_trait::async_trait] +impl HandleValueUpdate for F +where + F: Fn(O, String, V, &StackOperationState) -> BoxFuture<'static, Result<(), ()>> + Send + Sync, + O: GiteratedObject + Send + Sync + 'static, + V: GiteratedObjectValue + Send + Sync + 'static, +{ + async fn handle_value_update( + &mut self, + object: O, + value_name: String, + value: V, + operation_state: &StackOperationState, + ) -> Result<(), ()> { + self(object, value_name, value, operation_state).await + } +} + +#[async_trait::async_trait] +pub trait HandleSettingUpdate { + async fn handle_setting_update( + &mut self, + object: O, + setting_name: String, + setting: S, + operation_state: &StackOperationState, + ) -> Result<(), ()>; +} + +#[async_trait::async_trait] +impl HandleSettingUpdate for F +where + F: Fn(O, String, S, &StackOperationState) -> BoxFuture<'static, Result<(), ()>> + Send + Sync, + O: GiteratedObject + Send + Sync + 'static, + S: Setting + Send + Sync + 'static, +{ + async fn handle_setting_update( + &mut self, + object: O, + setting_name: String, + setting: S, + operation_state: &StackOperationState, + ) -> Result<(), ()> { + self(object, setting_name, setting, operation_state).await + } +} + +#[async_trait::async_trait] +pub trait ValueUpdatedHandler { + async fn value_updated(&mut self, object: &O, value_name: &str, value: AnyValue<()>); +} + +#[async_trait::async_trait] +pub trait SettingUpdatedHandler { + async fn setting_updated(&mut self, object: &O, setting_name: &str, setting: AnySetting); +} + +#[derive(Debug, Clone, Hash, PartialEq, Eq)] +pub struct ValueUpdateKind { + pub object_kind: String, + pub value_name: String, +} + +#[derive(Debug, Clone, Hash, PartialEq, Eq)] +pub struct SettingUpdateKind { + pub object_kind: String, + pub setting_name: String, +} + +pub struct HandleSettingUpdatedFunction { + pub target: SettingUpdateKind, + pub function: Box< + dyn FnOnce( + String, + String, + AnySetting, + StackOperationState, + ) -> BoxFuture<'static, Result<(), ()>> + + Send + + Sync, + >, +} + +impl HandleSettingUpdatedFunction { + pub fn new< + S: Setting + Send + Sync, + T: HandleSettingUpdate + 'static + Clone + Send + Sync, + O: GiteratedObject + Send + Sync, + >( + handler: T, + setting_name: &str, + ) -> Self { + Self { + target: SettingUpdateKind { + object_kind: O::object_name().to_string(), + setting_name: setting_name.to_string(), + }, + + function: Box::new(move |object, setting_name, value, state| { + async move { + let mut handler = handler; + + let object = match O::from_str(&object) { + Ok(object) => object, + Err(_) => return Err(()), + }; + + let setting: S = serde_json::from_value(value.0).unwrap(); + + handler + .handle_setting_update(object, setting_name, setting, &state) + .await; + + Ok(()) + } + .boxed() + }), + } + } +} + +pub struct HandleValueUpdatedFunction { + pub target: ValueUpdateKind, + pub function: Box< + dyn FnOnce( + String, + String, + AnySetting, + StackOperationState, + ) -> BoxFuture<'static, Result<(), ()>> + + Send + + Sync, + >, +} + +impl HandleValueUpdatedFunction { + pub fn new< + V: GiteratedObjectValue + Send + Sync, + T: HandleValueUpdate + 'static + Clone + Send + Sync, + O: GiteratedObject + Send + Sync, + >( + handler: T, + value_name: &str, + ) -> Self { + Self { + target: ValueUpdateKind { + object_kind: O::object_name().to_string(), + value_name: value_name.to_string(), + }, + + function: Box::new(move |object, setting_name, value, state| { + async move { + let mut handler = handler; + + let object = match O::from_str(&object) { + Ok(object) => object, + Err(_) => return Err(()), + }; + + let setting: V = serde_json::from_value(value.0).unwrap(); + + handler + .handle_value_update(object, setting_name, setting, &state) + .await; + + Ok(()) + } + .boxed() + }), + } + } +}