refactor: using type erasure so users can define their own primary key types instead of lookupkey
This commit is contained in:
@@ -2,8 +2,8 @@ pub use config::AdminModelConfig;
|
||||
pub use dto::AdminApp;
|
||||
pub use dto::AdminModel;
|
||||
pub use repository::{
|
||||
AdminRepository, LookupKey, RepoInfo, RepositoryContext, RepositoryInfo, RepositoryItem,
|
||||
RepositoryList, Widget,
|
||||
AdminRepository, DynAdminRepository, LookupKey, RepoInfo, RepositoryContext, RepositoryInfo,
|
||||
RepositoryItem, RepositoryList, Widget,
|
||||
};
|
||||
|
||||
mod auth {
|
||||
@@ -350,18 +350,32 @@ pub mod repository {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait PrimaryKeyType {
|
||||
fn key_as_string(&self) -> String;
|
||||
fn key_from_string(&self, s: String) -> Self;
|
||||
use std::any::Any;
|
||||
use std::fmt::Debug;
|
||||
|
||||
pub trait PrimaryKeyType: Any + Debug + Send + Sync {
|
||||
fn as_any(&self) -> &dyn Any;
|
||||
}
|
||||
|
||||
impl PrimaryKeyType for i64 {
|
||||
fn key_as_string(&self) -> String {
|
||||
self.to_string()
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
fn key_from_string(&self, s: String) -> Self {
|
||||
s.parse::<usize>().unwrap() as i64
|
||||
impl PrimaryKeyType for String {
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for Box<dyn PrimaryKeyType> {
|
||||
fn from(s: String) -> Box<dyn PrimaryKeyType> {
|
||||
if let Ok(i) = s.parse::<i64>() {
|
||||
Box::new(i)
|
||||
} else {
|
||||
Box::new(s)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -371,7 +385,7 @@ pub mod repository {
|
||||
|
||||
async fn info(&self, context: &RepositoryContext) -> RepositoryInfo;
|
||||
async fn list(&self, context: &RepositoryContext) -> RepositoryList;
|
||||
async fn get(&self, context: &RepositoryContext, id: Self::Key) -> Option<RepositoryItem>;
|
||||
async fn get(&self, context: &RepositoryContext, id: &Self::Key) -> Option<RepositoryItem>;
|
||||
async fn create(
|
||||
&mut self,
|
||||
context: &RepositoryContext,
|
||||
@@ -380,76 +394,127 @@ pub mod repository {
|
||||
async fn update(
|
||||
&mut self,
|
||||
context: &RepositoryContext,
|
||||
id: Self::Key,
|
||||
id: &Self::Key,
|
||||
data: Value,
|
||||
) -> Option<RepositoryItem>;
|
||||
async fn replace(
|
||||
&mut self,
|
||||
context: &RepositoryContext,
|
||||
id: Self::Key,
|
||||
id: &Self::Key,
|
||||
data: Value,
|
||||
) -> Option<RepositoryItem>;
|
||||
async fn delete(&mut self, context: &RepositoryContext, id: Self::Key) -> Option<Value>;
|
||||
async fn delete(&mut self, context: &RepositoryContext, id: &Self::Key) -> Option<Value>;
|
||||
}
|
||||
|
||||
use std::any::Any;
|
||||
|
||||
trait Key: Any {
|
||||
fn as_any(&self) -> &dyn Any;
|
||||
fn from_str(s: &str) -> Self
|
||||
where
|
||||
Self: Sized;
|
||||
fn to_string(&self) -> String;
|
||||
#[async_trait]
|
||||
pub trait DynAdminRepository: Send + Sync {
|
||||
async fn info(&self, context: &RepositoryContext) -> RepositoryInfo;
|
||||
async fn list(&self, context: &RepositoryContext) -> RepositoryList;
|
||||
async fn get(
|
||||
&self,
|
||||
context: &RepositoryContext,
|
||||
id: &dyn PrimaryKeyType,
|
||||
) -> Option<RepositoryItem>;
|
||||
async fn create(
|
||||
&mut self,
|
||||
context: &RepositoryContext,
|
||||
data: Value,
|
||||
) -> Option<RepositoryItem>;
|
||||
async fn update(
|
||||
&mut self,
|
||||
context: &RepositoryContext,
|
||||
id: &dyn PrimaryKeyType,
|
||||
data: Value,
|
||||
) -> Option<RepositoryItem>;
|
||||
async fn replace(
|
||||
&mut self,
|
||||
context: &RepositoryContext,
|
||||
id: &dyn PrimaryKeyType,
|
||||
data: Value,
|
||||
) -> Option<RepositoryItem>;
|
||||
async fn delete(
|
||||
&mut self,
|
||||
context: &RepositoryContext,
|
||||
id: &dyn PrimaryKeyType,
|
||||
) -> Option<Value>;
|
||||
}
|
||||
|
||||
impl<T: Any> Key for T {
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
pub struct AdminRepositoryWrapper<T: AdminRepository> {
|
||||
inner: T,
|
||||
}
|
||||
|
||||
impl Key for String {
|
||||
fn from_str(s: &str) -> Self {
|
||||
s.to_string()
|
||||
}
|
||||
|
||||
fn to_string(&self) -> String {
|
||||
self.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl Key for i64 {
|
||||
fn from_str(s: &str) -> Self {
|
||||
s.parse().expect("Invalid i64 string")
|
||||
}
|
||||
|
||||
fn to_string(&self) -> String {
|
||||
self.to_string()
|
||||
impl<T: AdminRepository> AdminRepositoryWrapper<T> {
|
||||
pub fn new(inner: T) -> Self {
|
||||
Self { inner }
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait ErasedAdminRepository: Send + Sync {
|
||||
async fn info(&self, context: &RepositoryContext) -> RepositoryInfo;
|
||||
async fn list(&self, context: &RepositoryContext) -> RepositoryList;
|
||||
async fn get(&self, context: &RepositoryContext, id: &dyn Key) -> Option<RepositoryItem>;
|
||||
impl<T: AdminRepository> DynAdminRepository for AdminRepositoryWrapper<T> {
|
||||
async fn info(&self, context: &RepositoryContext) -> RepositoryInfo {
|
||||
self.inner.info(context).await
|
||||
}
|
||||
|
||||
async fn list(&self, context: &RepositoryContext) -> RepositoryList {
|
||||
self.inner.list(context).await
|
||||
}
|
||||
|
||||
async fn get(
|
||||
&self,
|
||||
context: &RepositoryContext,
|
||||
id: &dyn PrimaryKeyType,
|
||||
) -> Option<RepositoryItem> {
|
||||
if let Some(key) = id.as_any().downcast_ref::<T::Key>() {
|
||||
self.inner.get(context, key).await
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
async fn create(
|
||||
&mut self,
|
||||
context: &RepositoryContext,
|
||||
data: Value,
|
||||
) -> Option<RepositoryItem>;
|
||||
) -> Option<RepositoryItem> {
|
||||
self.inner.create(context, data).await
|
||||
}
|
||||
|
||||
async fn update(
|
||||
&mut self,
|
||||
context: &RepositoryContext,
|
||||
id: &dyn Key,
|
||||
id: &dyn PrimaryKeyType,
|
||||
data: Value,
|
||||
) -> Option<RepositoryItem>;
|
||||
) -> Option<RepositoryItem> {
|
||||
if let Some(key) = id.as_any().downcast_ref::<T::Key>() {
|
||||
self.inner.update(context, key, data).await
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
async fn replace(
|
||||
&mut self,
|
||||
context: &RepositoryContext,
|
||||
id: &dyn Key,
|
||||
id: &dyn PrimaryKeyType,
|
||||
data: Value,
|
||||
) -> Option<RepositoryItem>;
|
||||
async fn delete(&mut self, context: &RepositoryContext, id: &dyn Key) -> Option<Value>;
|
||||
) -> Option<RepositoryItem> {
|
||||
if let Some(key) = id.as_any().downcast_ref::<T::Key>() {
|
||||
self.inner.replace(context, key, data).await
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
async fn delete(
|
||||
&mut self,
|
||||
context: &RepositoryContext,
|
||||
id: &dyn PrimaryKeyType,
|
||||
) -> Option<Value> {
|
||||
if let Some(key) = id.as_any().downcast_ref::<T::Key>() {
|
||||
self.inner.delete(context, key).await
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
use super::domain::{AdminApp, AdminModel, AdminModelConfig, AdminRepository};
|
||||
use super::domain::repository::AdminRepositoryWrapper;
|
||||
use super::domain::{AdminApp, AdminModel, AdminModelConfig, AdminRepository, DynAdminRepository};
|
||||
use crate::service::templates::Templates;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
@@ -17,7 +18,7 @@ pub struct AdminRegistry {
|
||||
base_path: String,
|
||||
apps: HashMap<String, internal::AdminApp>,
|
||||
models: HashMap<String, internal::AdminModel>,
|
||||
repositories: HashMap<String, Arc<Mutex<dyn AdminRepository>>>,
|
||||
repositories: HashMap<String, Arc<Mutex<dyn DynAdminRepository>>>,
|
||||
}
|
||||
|
||||
impl AdminRegistry {
|
||||
@@ -113,6 +114,7 @@ impl AdminRegistry {
|
||||
repository: R,
|
||||
) -> Result<(), String> {
|
||||
let model_key = self.register_model_config(model)?;
|
||||
let repository = AdminRepositoryWrapper::new(repository);
|
||||
self.repositories
|
||||
.insert(model_key, Arc::new(Mutex::new(repository)));
|
||||
Ok(())
|
||||
@@ -122,7 +124,7 @@ impl AdminRegistry {
|
||||
&self,
|
||||
app_key: &str,
|
||||
model_key: &str,
|
||||
) -> Result<Arc<Mutex<dyn AdminRepository>>, String> {
|
||||
) -> Result<Arc<Mutex<dyn DynAdminRepository>>, String> {
|
||||
let full_model_key = format!("{}.{}", app_key, model_key);
|
||||
if let Some(repo) = self.repositories.get(&full_model_key) {
|
||||
// Clone the Arc to return a reference to the repository
|
||||
|
||||
@@ -9,6 +9,7 @@ use crate::admin::domain::{AdminApp, AdminModel};
|
||||
use crate::admin::state::AdminState;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::domain::repository::PrimaryKeyType;
|
||||
use super::domain::{LookupKey, RepositoryInfo, RepositoryItem, RepositoryList};
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
@@ -157,13 +158,13 @@ pub async fn view_item_details<S: AdminState + Clone + Send + Sync + 'static>(
|
||||
let admin_model = registry
|
||||
.get_model(&app_key, &model_key)
|
||||
.expect("Admin Model not found?");
|
||||
let key: LookupKey = id.into();
|
||||
let key: Box<dyn PrimaryKeyType> = Box::new(id);
|
||||
AdminContext {
|
||||
base: base_template(&headers),
|
||||
available_apps: registry.get_apps(),
|
||||
item_info: Some(repo.info(&admin_model).await),
|
||||
item_list: repo.list(&admin_model).await,
|
||||
item: repo.get(&admin_model, key).await,
|
||||
item: repo.get(&admin_model, key.as_ref()).await,
|
||||
item_model: Some(admin_model),
|
||||
..Default::default()
|
||||
}
|
||||
@@ -257,13 +258,13 @@ pub async fn change_item<S: AdminState + Clone + Send + Sync + 'static>(
|
||||
let admin_model = registry
|
||||
.get_model(&app_key, &model_key)
|
||||
.expect("Admin Model not found?");
|
||||
let key: LookupKey = id.into();
|
||||
let key: Box<dyn PrimaryKeyType> = Box::new(id);
|
||||
AdminContext {
|
||||
base: base_template(&headers),
|
||||
available_apps: registry.get_apps(),
|
||||
item_info: Some(repo.info(&admin_model).await),
|
||||
item_list: repo.list(&admin_model).await,
|
||||
item: repo.get(&admin_model, key).await,
|
||||
item: repo.get(&admin_model, key.as_ref()).await,
|
||||
item_model: Some(admin_model),
|
||||
..Default::default()
|
||||
}
|
||||
@@ -290,9 +291,9 @@ pub async fn update_item<S: AdminState + Clone + Send + Sync + 'static>(
|
||||
let admin_model = registry
|
||||
.get_model(&app_key, &model_key)
|
||||
.expect("Admin Model not found?");
|
||||
let key: LookupKey = id.into();
|
||||
let key: Box<dyn PrimaryKeyType> = Box::new(id);
|
||||
|
||||
let result = repo.update(&admin_model, key, form).await;
|
||||
let result = repo.update(&admin_model, key.as_ref(), form).await;
|
||||
|
||||
AdminContext {
|
||||
base: base_template(&headers),
|
||||
|
||||
Reference in New Issue
Block a user