diff --git a/Cargo.lock b/Cargo.lock index c8df65c..506f567 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -708,7 +708,6 @@ dependencies = [ "tokio", "tokio-tungstenite", "toml", - "tower", "tracing", "tracing-subscriber", ] @@ -717,19 +716,14 @@ dependencies = [ name = "giterated-models" version = "0.1.0" dependencies = [ - "aes-gcm", "anyhow", - "argon2", "async-trait", "base64 0.21.3", "bincode", "chrono", - "futures-util", "git2", "jsonwebtoken", - "log", "rand", - "reqwest", "rsa", "secrecy", "semver", @@ -737,12 +731,8 @@ dependencies = [ "serde_json", "sqlx", "thiserror", - "tokio", - "tokio-tungstenite", "toml", - "tower", "tracing", - "tracing-subscriber", ] [[package]] @@ -2401,23 +2391,6 @@ dependencies = [ ] [[package]] -name = "tower" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" -dependencies = [ - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "tower-layer" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" - -[[package]] name = "tower-service" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/giterated-daemon/Cargo.toml b/giterated-daemon/Cargo.toml index ada4ec8..edafcd6 100644 --- a/giterated-daemon/Cargo.toml +++ b/giterated-daemon/Cargo.toml @@ -22,7 +22,6 @@ reqwest = "*" argon2 = "*" aes-gcm = "0.10.2" semver = {version = "*", features = ["serde"]} -tower = "*" giterated-models = { path = "../giterated-models" } giterated-api = { path = "../../giterated-api" } deadpool = "*" diff --git a/giterated-daemon/src/backend/mod.rs b/giterated-daemon/src/backend/mod.rs index d5aaaeb..5ef2dc1 100644 --- a/giterated-daemon/src/backend/mod.rs +++ b/giterated-daemon/src/backend/mod.rs @@ -16,6 +16,7 @@ use giterated_models::{ repository::{ IssueLabel, Repository, RepositoryIssue, RepositorySummary, RepositoryTreeEntry, }, + settings::AnySetting, user::User, }, operation::{ @@ -25,10 +26,11 @@ use giterated_models::{ RepositoryIssuesCountRequest, RepositoryIssuesRequest, }, }, + values::AnyValue, }; #[async_trait] -pub trait RepositoryBackend: IssuesBackend { +pub trait RepositoryBackend { async fn create_repository( &mut self, user: &User, @@ -81,6 +83,14 @@ pub trait AuthBackend { #[async_trait::async_trait] pub trait UserBackend: AuthBackend { + async fn get_value(&mut self, user: &User, name: &str) -> Result, Error>; + async fn get_setting(&mut self, user: &User, name: &str) -> Result; + async fn write_setting( + &mut self, + user: &User, + name: &str, + setting: &Value, + ) -> Result<(), Error>; async fn exists(&mut self, user: &User) -> Result; } diff --git a/giterated-daemon/src/backend/user.rs b/giterated-daemon/src/backend/user.rs index cdb0d16..87afe2c 100644 --- a/giterated-daemon/src/backend/user.rs +++ b/giterated-daemon/src/backend/user.rs @@ -6,8 +6,12 @@ use aes_gcm::{aead::Aead, AeadCore, Aes256Gcm, Key, KeyInit}; use argon2::{password_hash::SaltString, Argon2, PasswordHash, PasswordHasher, PasswordVerifier}; use base64::{engine::general_purpose::STANDARD, Engine as _}; use giterated_models::{ - model::{authenticated::UserAuthenticationToken, instance::Instance, user::User}, + model::{ + authenticated::UserAuthenticationToken, instance::Instance, settings::AnySetting, + user::User, + }, operation::instance::{AuthenticationTokenRequest, RegisterAccountRequest}, + values::AnyValue, }; use rsa::{ pkcs8::{EncodePrivateKey, EncodePublicKey}, @@ -16,6 +20,7 @@ use rsa::{ }; use secrecy::ExposeSecret; +use serde_json::Value; use sqlx::PgPool; use tokio::sync::Mutex; @@ -48,6 +53,20 @@ impl UserAuth { #[async_trait::async_trait] impl UserBackend for UserAuth { + async fn get_value(&mut self, _user: &User, _name: &str) -> Result, Error> { + todo!() + } + async fn get_setting(&mut self, _user: &User, _name: &str) -> Result { + todo!() + } + async fn write_setting( + &mut self, + _user: &User, + _name: &str, + _setting: &Value, + ) -> Result<(), Error> { + todo!() + } async fn exists(&mut self, user: &User) -> Result { Ok(sqlx::query_as!( UserRow, diff --git a/giterated-daemon/src/cache_backend.rs b/giterated-daemon/src/cache_backend.rs new file mode 100644 index 0000000..b4a784f --- /dev/null +++ b/giterated-daemon/src/cache_backend.rs @@ -0,0 +1,27 @@ +use giterated_models::{ + error::OperationError, + operation::{GiteratedObject, GiteratedOperation, Object, ObjectBackend, ObjectRequestError}, +}; +use std::fmt::Debug; + +#[derive(Clone, Debug)] +pub struct CacheBackend; + +#[async_trait::async_trait] +impl ObjectBackend for CacheBackend { + async fn object_operation + Debug>( + &self, + _object: O, + _operation: D, + ) -> Result> { + // We don't handle operations with this backend + Err(OperationError::Unhandled) + } + + async fn get_object( + &self, + _object_str: &str, + ) -> Result, OperationError> { + Err(OperationError::Unhandled) + } +} diff --git a/giterated-daemon/src/connection/wrapper.rs b/giterated-daemon/src/connection/wrapper.rs index 866d890..93d0c36 100644 --- a/giterated-daemon/src/connection/wrapper.rs +++ b/giterated-daemon/src/connection/wrapper.rs @@ -5,7 +5,10 @@ use std::{ use anyhow::Error; use futures_util::{SinkExt, StreamExt}; -use giterated_models::model::instance::Instance; +use giterated_models::{ + model::{authenticated::AuthenticatedPayload, instance::Instance}, + operation::{AnyObject, AnyOperation, GiteratedMessage, ObjectBackend}, +}; use serde::Serialize; @@ -16,6 +19,7 @@ use toml::Table; use crate::{ authentication::AuthenticationTokenGranter, backend::{RepositoryBackend, SettingsBackend, UserBackend}, + database_backend::Foobackend, federation::connections::InstanceConnections, keys::PublicKeyCache, }; @@ -34,7 +38,7 @@ pub async fn connection_wrapper( instance_connections: Arc>, config: Table, ) { - let _connection_state = ConnectionState { + let connection_state = ConnectionState { socket: Arc::new(Mutex::new(socket)), connections, repository_backend, @@ -51,6 +55,42 @@ pub async fn connection_wrapper( let _handshaked = false; + loop { + let mut socket = connection_state.socket.lock().await; + let message = socket.next().await; + drop(socket); + + match message { + Some(Ok(message)) => { + let payload = match message { + Message::Binary(payload) => payload, + Message::Ping(_) => { + let mut socket = connection_state.socket.lock().await; + let _ = socket.send(Message::Pong(vec![])).await; + drop(socket); + continue; + } + Message::Close(_) => return, + _ => continue, + }; + + let message: AuthenticatedPayload = bincode::deserialize(&payload).unwrap(); + + let message: GiteratedMessage = message.into_message(); + + let backend = Foobackend {}; + + backend + .object_operation(message.object, message.payload) + .await + .unwrap(); + } + _ => { + return; + } + } + } + // loop { // let mut socket = connection_state.socket.lock().await; // let message = socket.next().await; diff --git a/giterated-daemon/src/database_backend/handler.rs b/giterated-daemon/src/database_backend/handler.rs new file mode 100644 index 0000000..2d4637b --- /dev/null +++ b/giterated-daemon/src/database_backend/handler.rs @@ -0,0 +1,238 @@ +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 + } +} + +fn test_operation( + _object: &User, + _operation: SetSetting, + _state: (), +) -> Pin>> + Send + 'static>> { + todo!() +} + +fn foo() {} + +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>> { + todo!() +} + +pub fn user_set_setting( + _object: &User, + _operation: SetSetting, + _state: DatabaseBackend, +) -> BoxFuture<'static, Result<(), OperationError>> { + todo!() +} + +pub fn repository_get_value( + _object: &Repository, + _operation: GetValue>, + _state: DatabaseBackend, +) -> BoxFuture<'static, Result, OperationError>> { + todo!() +} + +pub fn repository_get_setting( + _object: &Repository, + _operation: GetSetting, + _state: DatabaseBackend, +) -> BoxFuture<'static, Result>> { + todo!() +} + +pub fn repository_set_setting( + _object: &Repository, + _operation: SetSetting, + _state: DatabaseBackend, +) -> BoxFuture<'static, Result<(), OperationError>> { + todo!() +} + +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(serde_json::to_string(object).unwrap()), + operation, + state, + ) + .await + } else { + panic!() + } + } +} diff --git a/giterated-daemon/src/database_backend/mod.rs b/giterated-daemon/src/database_backend/mod.rs index 3fc61e8..de41251 100644 --- a/giterated-daemon/src/database_backend/mod.rs +++ b/giterated-daemon/src/database_backend/mod.rs @@ -1,16 +1,23 @@ +pub mod handler; + use std::{str::FromStr, sync::Arc}; +use futures_util::TryFutureExt; use giterated_models::{ error::OperationError, model::{instance::Instance, repository::Repository, user::User}, operation::{GiteratedObject, GiteratedOperation, Object, ObjectBackend, ObjectRequestError}, }; -use sqlx::PgPool; use std::fmt::Debug; use tokio::sync::Mutex; use crate::backend::{RepositoryBackend, UserBackend}; +use self::handler::{ + repository_get_setting, repository_get_value, repository_set_setting, user_get_setting, + user_get_value, user_set_setting, OperationHandlers, +}; + #[derive(Clone, Debug)] pub struct Foobackend {} @@ -29,34 +36,78 @@ impl ObjectBackend for Foobackend { &self, _object_str: &str, ) -> Result, OperationError> { - todo!() + Err(OperationError::Unhandled) } } /// A backend implementation which attempts to resolve data from the instance's database. #[derive(Clone)] -pub struct DatabaseBackend<'b> { - our_instance: Object<'b, Instance, Foobackend>, - user_backend: Arc>, - repository_backend: Arc>, - pool: PgPool, +pub struct DatabaseBackend { + pub(self) our_instance: Instance, + pub(self) user_backend: Arc>, + pub(self) repository_backend: Arc>, } -impl Debug for DatabaseBackend<'_> { +impl Debug for DatabaseBackend { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("DatabaseBackend").finish() } } #[async_trait::async_trait] -impl<'b> ObjectBackend for DatabaseBackend<'b> { +impl ObjectBackend for DatabaseBackend { async fn object_operation + Debug>( &self, - _object: O, - _operation: D, + object: O, + operation: D, ) -> Result> { - // We don't handle operations with this backend - Err(OperationError::Unhandled) + let serialized = + serde_json::to_value(operation).map_err(|e| OperationError::Internal(e.to_string()))?; + let object_name = object.to_string(); + + if let Ok(user) = User::from_str(&object_name) { + let mut handler = OperationHandlers::default(); + + handler + .insert(user_get_value) + .insert(user_get_setting) + .insert(user_set_setting); + + match handler + .handle( + &user, + D::operation_name(), + serde_json::from_value(serialized.clone()).unwrap(), + self.clone(), + ) + .await + { + Ok(result) => Ok(serde_json::from_slice(&result) + .map_err(|e| OperationError::Internal(e.to_string()))?), + Err(err) => match err { + OperationError::Internal(internal) => Err(OperationError::Internal(internal)), + OperationError::Unhandled => Err(OperationError::Unhandled), + OperationError::Operation(err) => Err(OperationError::Operation( + serde_json::from_slice(&err) + .map_err(|e| OperationError::Internal(e.to_string()))?, + )), + }, + } + } else if let Ok(_repository) = Repository::from_str(&object_name) { + let mut handler = OperationHandlers::default(); + + handler + .insert(repository_get_value) + .insert(repository_get_setting) + .insert(repository_set_setting); + + // handler.handle(&repository, D::operation_name(), bincode::deserialize(&serialized).unwrap(), DatabaseBackendState).await; + todo!() + } else if Instance::from_str(&object_name).is_ok() { + Err(OperationError::Unhandled) + } else { + Err(OperationError::Unhandled) + } } async fn get_object( @@ -107,3 +158,129 @@ impl<'b> ObjectBackend for DatabaseBackend<'b> { } } } + +mod test { + use std::{str::FromStr, sync::Arc}; + + use anyhow::Error; + use giterated_models::{ + model::{ + authenticated::UserAuthenticationToken, + instance::Instance, + repository::{Repository, RepositorySummary, RepositoryTreeEntry}, + settings::AnySetting, + user::User, + }, + operation::{ + instance::{ + AuthenticationTokenRequest, RegisterAccountRequest, RepositoryCreateRequest, + }, + repository::RepositoryFileInspectRequest, + GiteratedObjectValue, ObjectBackend, + }, + values::{user::DisplayName, AnyValue}, + }; + use serde_json::Value; + use tokio::sync::Mutex; + + use crate::backend::{git::GitBackendError, AuthBackend, RepositoryBackend, UserBackend}; + + use super::DatabaseBackend; + pub struct TestUserDatabaseBackend; + + #[async_trait::async_trait] + impl UserBackend for TestUserDatabaseBackend { + async fn get_value(&mut self, _user: &User, name: &str) -> Result, Error> { + assert_eq!(name, DisplayName::value_name()); + + Ok(serde_json::from_slice( + &serde_json::to_vec(&DisplayName(String::from("test"))).unwrap(), + ) + .unwrap()) + } + async fn get_setting(&mut self, _user: &User, _name: &str) -> Result { + todo!() + } + async fn write_setting( + &mut self, + _user: &User, + _name: &str, + _setting: &Value, + ) -> Result<(), Error> { + todo!() + } + async fn exists(&mut self, user: &User) -> Result { + Ok(user == &User::from_str("test_user:test.giterated.dev").unwrap()) + } + } + + #[async_trait::async_trait] + impl AuthBackend for TestUserDatabaseBackend { + async fn register( + &mut self, + _request: RegisterAccountRequest, + ) -> Result { + todo!() + } + + async fn login( + &mut self, + _source: &Instance, + _request: AuthenticationTokenRequest, + ) -> Result { + todo!() + } + } + + pub struct TestUserRepositoryBackend; + + #[async_trait::async_trait] + impl RepositoryBackend for TestUserRepositoryBackend { + async fn create_repository( + &mut self, + _user: &User, + _request: &RepositoryCreateRequest, + ) -> Result { + todo!() + } + async fn repository_file_inspect( + &mut self, + _requester: Option<&User>, + _request: &RepositoryFileInspectRequest, + ) -> Result, Error> { + todo!() + } + async fn repositories_for_user( + &mut self, + _requester: Option<&User>, + _user: &User, + ) -> Result, Error> { + todo!() + } + async fn exists(&mut self, _repository: &Repository) -> Result { + todo!() + } + } + + fn test_backend() -> DatabaseBackend { + DatabaseBackend { + our_instance: Instance::from_str("testing.giterated.dev").unwrap(), + user_backend: Arc::new(Mutex::new(TestUserDatabaseBackend)) as _, + repository_backend: Arc::new(Mutex::new(TestUserRepositoryBackend)) as _, + } + } + + #[tokio::test] + async fn test_user_get() { + let backend = test_backend(); + + let mut user = backend + .get_object::("test_user:test.giterated.dev") + .await + .expect("object should have been returned"); + + user.get::() + .await + .expect("object value should have been returned"); + } +} diff --git a/giterated-daemon/src/lib.rs b/giterated-daemon/src/lib.rs index e295cdc..2f18859 100644 --- a/giterated-daemon/src/lib.rs +++ b/giterated-daemon/src/lib.rs @@ -4,6 +4,7 @@ use semver::{Version, VersionReq}; pub mod authentication; pub mod backend; +pub mod cache_backend; pub mod connection; pub mod database_backend; pub mod federation; diff --git a/giterated-models/Cargo.toml b/giterated-models/Cargo.toml index 75674de..ee2d9c6 100644 --- a/giterated-models/Cargo.toml +++ b/giterated-models/Cargo.toml @@ -6,35 +6,25 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -tokio-tungstenite = "*" -tokio = { version = "1.32.0", features = [ "full" ] } tracing = "*" -futures-util = "*" serde = { version = "1.0.188", features = [ "derive" ]} serde_json = "1.0" -tracing-subscriber = "0.3" base64 = "0.21.3" jsonwebtoken = { version = "*", features = ["use_pem"]} -log = "*" rand = "*" rsa = {version = "0.9", features = ["sha2"]} -reqwest = "*" -argon2 = "*" -aes-gcm = "0.10.2" semver = {version = "*", features = ["serde"]} -tower = "*" bincode = "*" secrecy = { version = "0.8.0", features = ["serde"] } - +thiserror = "1" +anyhow = "1" toml = { version = "0.7" } - +# Git backend +git2 = "0.17" chrono = { version = "0.4", features = [ "serde" ] } async-trait = "0.1" # Git backend -git2 = "0.17" -thiserror = "1" -anyhow = "1" -sqlx = { version = "0.7", features = [ "runtime-tokio", "tls-native-tls", "postgres", "macros", "migrate", "chrono" ] } +sqlx = { version = "0.7", default-features = false, features = [ "macros", "chrono" ] } #uuid = { version = "1.4", features = [ "v4", "serde" ] } diff --git a/giterated-models/src/model/authenticated.rs b/giterated-models/src/model/authenticated.rs index 9989493..2706c35 100644 --- a/giterated-models/src/model/authenticated.rs +++ b/giterated-models/src/model/authenticated.rs @@ -8,9 +8,12 @@ use rsa::{ RsaPrivateKey, }; use serde::{Deserialize, Serialize}; +use serde_json::Value; use tracing::info; -use crate::operation::{GiteratedMessage, GiteratedObject, GiteratedOperation}; +use crate::operation::{ + AnyObject, AnyOperation, GiteratedMessage, GiteratedObject, GiteratedOperation, +}; use super::{instance::Instance, user::User, MessageTarget}; @@ -27,12 +30,24 @@ pub struct Authenticated> { pub message: GiteratedMessage, } -#[derive(Debug, Hash, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)] pub struct AuthenticatedPayload { pub source: Vec, + pub object: String, + pub operation: String, pub payload: Vec, } +impl AuthenticatedPayload { + pub fn into_message(self) -> GiteratedMessage { + GiteratedMessage { + object: AnyObject(self.object), + operation: self.operation, + payload: AnyOperation(serde_json::from_slice::(&self.payload).unwrap()), + } + } +} + // impl From> for AuthenticatedPayload { // fn from(mut value: Authenticated) -> Self { // let payload = bincode::serialize(&value.message).unwrap(); @@ -103,9 +118,11 @@ impl> Authenticated { } pub fn into_payload(mut self) -> AuthenticatedPayload { - let payload = bincode::serialize(&self.message).unwrap(); + let payload = bincode::serialize(&self.message.payload).unwrap(); AuthenticatedPayload { + object: self.message.object.to_string(), + operation: self.message.operation, source: self .source .drain(..) diff --git a/giterated-models/src/model/instance.rs b/giterated-models/src/model/instance.rs index f13f34d..8eb755c 100644 --- a/giterated-models/src/model/instance.rs +++ b/giterated-models/src/model/instance.rs @@ -33,7 +33,7 @@ pub struct Instance { } impl GiteratedObject for Instance { - fn object_name(&self) -> &str { + fn object_name() -> &'static str { "instance" } diff --git a/giterated-models/src/model/repository.rs b/giterated-models/src/model/repository.rs index a6087d8..9c73e1e 100644 --- a/giterated-models/src/model/repository.rs +++ b/giterated-models/src/model/repository.rs @@ -39,7 +39,7 @@ pub struct Repository { } impl GiteratedObject for Repository { - fn object_name(&self) -> &str { + fn object_name() -> &'static str { "repository" } diff --git a/giterated-models/src/model/settings.rs b/giterated-models/src/model/settings.rs index 6ca4075..30fd966 100644 --- a/giterated-models/src/model/settings.rs +++ b/giterated-models/src/model/settings.rs @@ -1,9 +1,19 @@ -use serde::{Deserialize, Serialize}; +use serde::{de::DeserializeOwned, Deserialize, Serialize}; +use serde_json::Value; -pub trait Setting: Serialize { +pub trait Setting: Serialize + DeserializeOwned { fn name() -> &'static str; } +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct AnySetting(Value); + +impl Setting for AnySetting { + fn name() -> &'static str { + "any" + } +} + #[derive(Debug, Serialize, Deserialize)] pub struct UserBio(pub String); diff --git a/giterated-models/src/model/user.rs b/giterated-models/src/model/user.rs index 0d95d4a..4de906a 100644 --- a/giterated-models/src/model/user.rs +++ b/giterated-models/src/model/user.rs @@ -35,7 +35,7 @@ pub struct User { } impl GiteratedObject for User { - fn object_name(&self) -> &str { + fn object_name() -> &'static str { "user" } diff --git a/giterated-models/src/operation/instance.rs b/giterated-models/src/operation/instance.rs index 54b1f29..141ac08 100644 --- a/giterated-models/src/operation/instance.rs +++ b/giterated-models/src/operation/instance.rs @@ -2,7 +2,7 @@ use secrecy::Secret; use serde::{Deserialize, Serialize}; use crate::{ - error::InstanceError, + error::{InstanceError, OperationError}, model::{ authenticated::UserAuthenticationToken, instance::Instance, @@ -112,24 +112,26 @@ impl Object<'_, Instance, B> { email: Option<&str>, username: &str, password: &Secret, - ) -> Result { + ) -> Result> { self.request::(RegisterAccountRequest { username: username.to_string(), email: email.map(|s| s.to_string()), password: password.clone(), }) + .await } pub async fn authentication_token( &mut self, username: &str, password: &Secret, - ) -> Result { + ) -> Result> { self.request::(AuthenticationTokenRequest { instance: self.inner.clone(), username: username.to_string(), password: password.clone(), }) + .await } pub async fn authentication_token_for( @@ -137,21 +139,23 @@ impl Object<'_, Instance, B> { instance: &Instance, username: &str, password: &Secret, - ) -> Result { + ) -> Result> { self.request::(AuthenticationTokenRequest { instance: instance.clone(), username: username.to_string(), password: password.clone(), }) + .await } pub async fn token_extension( &mut self, token: &UserAuthenticationToken, - ) -> Result, InstanceError> { + ) -> Result, OperationError> { self.request::(TokenExtensionRequest { token: token.clone(), }) + .await } pub async fn create_repository( @@ -161,7 +165,7 @@ impl Object<'_, Instance, B> { visibility: RepositoryVisibility, default_branch: &str, owner: &User, - ) -> Result { + ) -> Result> { self.request::(RepositoryCreateRequest { instance: Some(instance.clone()), name: name.to_string(), @@ -170,5 +174,6 @@ impl Object<'_, Instance, B> { default_branch: default_branch.to_string(), owner: owner.clone(), }) + .await } } diff --git a/giterated-models/src/operation/mod.rs b/giterated-models/src/operation/mod.rs index 47c59ad..5cdbaea 100644 --- a/giterated-models/src/operation/mod.rs +++ b/giterated-models/src/operation/mod.rs @@ -2,18 +2,20 @@ use std::{any::type_name, fmt::Debug, marker::PhantomData}; use anyhow::Error; use serde::{de::DeserializeOwned, Deserialize, Serialize}; +use serde_json::Value; use crate::{ error::{GetValueError, OperationError}, - model::{instance::Instance, MessageTarget}, + model::{instance::Instance, settings::Setting, MessageTarget}, + values::{GetSetting, GetSettingError, SetSetting, SetSettingError}, }; pub mod instance; pub mod repository; pub mod user; -pub trait GiteratedObject: Send + Serialize + DeserializeOwned { - fn object_name(&self) -> &str; +pub trait GiteratedObject: Send + Serialize + DeserializeOwned + ToString { + fn object_name() -> &'static str; fn from_object_str(object_str: &str) -> Result; } @@ -22,7 +24,7 @@ pub trait GiteratedOperation: Send + Serialize + Deserialize type Success: Serialize + DeserializeOwned + Send; type Failure: Serialize + DeserializeOwned + Send; - fn operation_name(&self) -> &'static str { + fn operation_name() -> &'static str { type_name::() } } @@ -73,43 +75,43 @@ impl<'b, B: ObjectBackend + Send + Sync + Clone, O: GiteratedObject> Object<'b, impl<'b, O: GiteratedObject + Clone + Debug, B: ObjectBackend + Debug + Send + Sync + Clone> Object<'b, O, B> { - pub async fn get + Send>( - &self, - ) -> Result> { - let operation: GetValue = GetValue { - value_name: V::value_name().to_string(), - _marker: PhantomData, - }; - - let _message: GiteratedMessage = GiteratedMessage { - object: self.inner.clone(), - operation: operation.operation_name().to_string(), - payload: operation, - }; - - todo!() - } - - pub fn request + Debug>( - &mut self, - request: R, - ) -> Result { - self.backend.object_operation(self.inner.clone(), request); - - todo!() - } + // pub async fn get + Send>( + // &self, + // ) -> Result> { + // let operation: GetValue = GetValue { + // value_name: V::value_name().to_string(), + // _marker: PhantomData, + // }; + + // let _message: GiteratedMessage = GiteratedMessage { + // object: self.inner.clone(), + // operation: operation.operation_name().to_string(), + // payload: operation, + // }; + + // todo!() + // } + + // pub fn request + Debug>( + // &mut self, + // request: R, + // ) -> Result { + // self.backend.object_operation(self.inner.clone(), request); + + // todo!() + // } } -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct GetValue { - value_name: String, + pub value_name: String, _marker: PhantomData, } impl + Send> GiteratedOperation for GetValue { - fn operation_name(&self) -> &'static str { + fn operation_name() -> &'static str { "get_value" } type Success = V; @@ -124,6 +126,21 @@ pub struct GiteratedMessage> { pub payload: V, } +impl GiteratedMessage { + pub fn try_into>( + &self, + ) -> Result, ()> { + let object = O::from_object_str(&self.object.0).map_err(|_| ())?; + let payload = serde_json::from_value::(self.payload.0.clone()).map_err(|_| ())?; + + Ok(GiteratedMessage { + object, + operation: self.operation.clone(), + payload, + }) + } +} + impl> MessageTarget for GiteratedMessage {} impl + Debug, O: GiteratedObject + Debug> Debug @@ -157,23 +174,74 @@ pub enum ObjectRequestError { } #[derive(Clone, Debug, Serialize, Deserialize)] -pub struct AnyObject(Vec); +#[repr(transparent)] +pub struct AnyObject(pub String); impl GiteratedObject for AnyObject { - fn object_name(&self) -> &str { + fn object_name() -> &'static str { "any" } fn from_object_str(object_str: &str) -> Result { - Ok(Self(Vec::from(object_str.as_bytes()))) + Ok(Self(object_str.to_string())) + } +} + +impl ToString for AnyObject { + fn to_string(&self) -> String { + self.0.to_string() } } #[derive(Clone, Debug, Serialize, Deserialize)] -pub struct AnyOperation(Vec); +#[serde(transparent)] +#[repr(transparent)] +pub struct AnyOperation(pub Value); impl GiteratedOperation for AnyOperation { type Success = Vec; type Failure = Vec; } + +impl<'b, O: GiteratedObject + Clone + Debug, B: ObjectBackend> Object<'b, O, B> { + pub async fn get + Send + Debug>( + &mut self, + ) -> Result> { + self.request(GetValue { + value_name: V::value_name().to_string(), + _marker: PhantomData, + }) + .await + } + + pub async fn get_setting( + &mut self, + ) -> Result> { + self.request(GetSetting { + setting_name: S::name().to_string(), + _marker: PhantomData, + }) + .await + } + + pub async fn set_setting( + &mut self, + setting: S, + ) -> Result<(), OperationError> { + self.request(SetSetting { + setting_name: S::name().to_string(), + value: setting, + }) + .await + } + + pub async fn request + Debug>( + &mut self, + request: R, + ) -> Result> { + self.backend + .object_operation(self.inner.clone(), request) + .await + } +} diff --git a/giterated-models/src/operation/repository.rs b/giterated-models/src/operation/repository.rs index d38c8a8..e3ccf10 100644 --- a/giterated-models/src/operation/repository.rs +++ b/giterated-models/src/operation/repository.rs @@ -1,7 +1,7 @@ use serde::{Deserialize, Serialize}; use crate::{ - error::RepositoryError, + error::{OperationError, RepositoryError}, model::repository::{IssueLabel, Repository, RepositoryIssue, RepositoryTreeEntry}, }; @@ -78,24 +78,32 @@ impl GiteratedOperation for RepositoryFileInspectRequest { } impl Object<'_, Repository, B> { - pub async fn issues_count(&mut self) -> Result { + pub async fn issues_count(&mut self) -> Result> { self.request::(RepositoryIssuesCountRequest) + .await } - pub async fn issue_labels(&mut self) -> Result, RepositoryError> { + pub async fn issue_labels( + &mut self, + ) -> Result, OperationError> { self.request::(RepositoryIssueLabelsRequest) + .await } - pub async fn issues(&mut self) -> Result, RepositoryError> { + pub async fn issues( + &mut self, + ) -> Result, OperationError> { self.request::(RepositoryIssuesRequest) + .await } pub async fn inspect_files( &mut self, entry: &RepositoryTreeEntry, - ) -> Result, RepositoryError> { + ) -> Result, OperationError> { self.request::(RepositoryFileInspectRequest { path: entry.clone(), }) + .await } } diff --git a/giterated-models/src/operation/user.rs b/giterated-models/src/operation/user.rs index 48c71d6..f7a16b9 100644 --- a/giterated-models/src/operation/user.rs +++ b/giterated-models/src/operation/user.rs @@ -1,7 +1,7 @@ use serde::{Deserialize, Serialize}; use crate::{ - error::UserError, + error::{OperationError, UserError}, model::{instance::Instance, repository::Repository, user::User}, }; @@ -22,10 +22,11 @@ impl Object<'_, User, B> { pub async fn repositories( &mut self, instance: &Instance, - ) -> Result, UserError> { + ) -> Result, OperationError> { self.request::(UserRepositoriesRequest { instance: instance.clone(), user: self.inner.clone(), }) + .await } } diff --git a/giterated-models/src/values/mod.rs b/giterated-models/src/values/mod.rs index e741f77..d05d323 100644 --- a/giterated-models/src/values/mod.rs +++ b/giterated-models/src/values/mod.rs @@ -1,3 +1,79 @@ +use std::{fmt::Debug, marker::PhantomData}; + +use serde::{de::DeserializeOwned, Deserialize, Serialize}; +use serde_json::Value; +use thiserror::Error; + +use crate::{ + model::settings::Setting, + operation::{GiteratedObject, GiteratedObjectValue, GiteratedOperation}, +}; + pub mod instance; pub mod repository; pub mod user; + +// #[derive(Serialize, Deserialize)] +// pub struct GetRequest { +// value_name: String, +// _marker: PhantomData, +// } + +// impl + Send> GiteratedOperation +// for GetRequest +// { +// type Success = V; + +// type Failure = GetValueError; +// } + +// #[derive(Error, Debug, Serialize, Deserialize)] +// pub enum GetValueError {} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct GetSetting { + pub setting_name: String, + pub _marker: PhantomData, +} + +impl GiteratedOperation + for GetSetting +{ + type Success = S; + + type Failure = GetSettingError; +} + +#[derive(Error, Debug, Serialize, Deserialize)] +pub enum GetSettingError {} +#[derive(Serialize, Deserialize, Debug, Clone)] +#[serde(bound(deserialize = "S: Setting"))] +pub struct SetSetting { + pub setting_name: String, + pub value: S, +} + +impl GiteratedOperation for SetSetting { + type Success = (); + + type Failure = SetSettingError; +} + +#[derive(Error, Debug, Serialize, Deserialize)] +pub enum SetSettingError {} + +#[derive(Debug, Clone, Deserialize, Serialize)] +#[serde(transparent)] +pub struct AnyValue { + value: Value, + #[serde(skip)] + _marker: PhantomData, +} + +impl GiteratedObjectValue for AnyValue { + type Object = O; + + fn value_name() -> &'static str { + todo!() + } +}