Implement Associated Type for Keys #3
| @ -2,8 +2,8 @@ pub use config::AdminModelConfig; | |||||||
| pub use dto::AdminApp; | pub use dto::AdminApp; | ||||||
| pub use dto::AdminModel; | pub use dto::AdminModel; | ||||||
| pub use repository::{ | pub use repository::{ | ||||||
|     AdminRepository, LookupKey, RepoInfo, RepositoryContext, RepositoryInfo, RepositoryItem, |     AdminRepository, DynAdminRepository, LookupKey, RepoInfo, RepositoryContext, RepositoryInfo, | ||||||
|     RepositoryList, Widget, |     RepositoryItem, RepositoryList, Widget, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| mod auth { | mod auth { | ||||||
| @ -350,18 +350,32 @@ pub mod repository { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub trait PrimaryKeyType { |     use std::any::Any; | ||||||
|         fn key_as_string(&self) -> String; |     use std::fmt::Debug; | ||||||
|         fn key_from_string(&self, s: String) -> Self; | 
 | ||||||
|  |     pub trait PrimaryKeyType: Any + Debug + Send + Sync { | ||||||
|  |         fn as_any(&self) -> &dyn Any; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     impl PrimaryKeyType for i64 { |     impl PrimaryKeyType for i64 { | ||||||
|         fn key_as_string(&self) -> String { |         fn as_any(&self) -> &dyn Any { | ||||||
|             self.to_string() |             self | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|         fn key_from_string(&self, s: String) -> Self { |     impl PrimaryKeyType for String { | ||||||
|             s.parse::<usize>().unwrap() as i64 |         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 info(&self, context: &RepositoryContext) -> RepositoryInfo; | ||||||
|         async fn list(&self, context: &RepositoryContext) -> RepositoryList; |         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( |         async fn create( | ||||||
|             &mut self, |             &mut self, | ||||||
|             context: &RepositoryContext, |             context: &RepositoryContext, | ||||||
| @ -380,76 +394,127 @@ pub mod repository { | |||||||
|         async fn update( |         async fn update( | ||||||
|             &mut self, |             &mut self, | ||||||
|             context: &RepositoryContext, |             context: &RepositoryContext, | ||||||
|             id: Self::Key, |             id: &Self::Key, | ||||||
|             data: Value, |             data: Value, | ||||||
|         ) -> Option<RepositoryItem>; |         ) -> Option<RepositoryItem>; | ||||||
|         async fn replace( |         async fn replace( | ||||||
|             &mut self, |             &mut self, | ||||||
|             context: &RepositoryContext, |             context: &RepositoryContext, | ||||||
|             id: Self::Key, |             id: &Self::Key, | ||||||
|             data: Value, |             data: Value, | ||||||
|         ) -> Option<RepositoryItem>; |         ) -> 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; |     #[async_trait] | ||||||
| 
 |     pub trait DynAdminRepository: Send + Sync { | ||||||
|     trait Key: Any { |         async fn info(&self, context: &RepositoryContext) -> RepositoryInfo; | ||||||
|         fn as_any(&self) -> &dyn Any; |         async fn list(&self, context: &RepositoryContext) -> RepositoryList; | ||||||
|         fn from_str(s: &str) -> Self |         async fn get( | ||||||
|         where |             &self, | ||||||
|             Self: Sized; |             context: &RepositoryContext, | ||||||
|         fn to_string(&self) -> String; |             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 { |     pub struct AdminRepositoryWrapper<T: AdminRepository> { | ||||||
|         fn as_any(&self) -> &dyn Any { |         inner: T, | ||||||
|             self |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     impl Key for String { |     impl<T: AdminRepository> AdminRepositoryWrapper<T> { | ||||||
|         fn from_str(s: &str) -> Self { |         pub fn new(inner: T) -> Self { | ||||||
|             s.to_string() |             Self { inner } | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         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() |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     #[async_trait] |     #[async_trait] | ||||||
|     pub trait ErasedAdminRepository: Send + Sync { |     impl<T: AdminRepository> DynAdminRepository for AdminRepositoryWrapper<T> { | ||||||
|         async fn info(&self, context: &RepositoryContext) -> RepositoryInfo; |         async fn info(&self, context: &RepositoryContext) -> RepositoryInfo { | ||||||
|         async fn list(&self, context: &RepositoryContext) -> RepositoryList; |             self.inner.info(context).await | ||||||
|         async fn get(&self, context: &RepositoryContext, id: &dyn Key) -> Option<RepositoryItem>; |         } | ||||||
|  | 
 | ||||||
|  |         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( |         async fn create( | ||||||
|             &mut self, |             &mut self, | ||||||
|             context: &RepositoryContext, |             context: &RepositoryContext, | ||||||
|             data: Value, |             data: Value, | ||||||
|         ) -> Option<RepositoryItem>; |         ) -> Option<RepositoryItem> { | ||||||
|  |             self.inner.create(context, data).await | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         async fn update( |         async fn update( | ||||||
|             &mut self, |             &mut self, | ||||||
|             context: &RepositoryContext, |             context: &RepositoryContext, | ||||||
|             id: &dyn Key, |             id: &dyn PrimaryKeyType, | ||||||
|             data: Value, |             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( |         async fn replace( | ||||||
|             &mut self, |             &mut self, | ||||||
|             context: &RepositoryContext, |             context: &RepositoryContext, | ||||||
|             id: &dyn Key, |             id: &dyn PrimaryKeyType, | ||||||
|             data: Value, |             data: Value, | ||||||
|         ) -> Option<RepositoryItem>; |         ) -> Option<RepositoryItem> { | ||||||
|         async fn delete(&mut self, context: &RepositoryContext, id: &dyn Key) -> Option<Value>; |             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 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 crate::service::templates::Templates; | ||||||
| use std::collections::HashMap; | use std::collections::HashMap; | ||||||
| use std::sync::Arc; | use std::sync::Arc; | ||||||
| @ -17,7 +18,7 @@ pub struct AdminRegistry { | |||||||
|     base_path: String, |     base_path: String, | ||||||
|     apps: HashMap<String, internal::AdminApp>, |     apps: HashMap<String, internal::AdminApp>, | ||||||
|     models: HashMap<String, internal::AdminModel>, |     models: HashMap<String, internal::AdminModel>, | ||||||
|     repositories: HashMap<String, Arc<Mutex<dyn AdminRepository>>>, |     repositories: HashMap<String, Arc<Mutex<dyn DynAdminRepository>>>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl AdminRegistry { | impl AdminRegistry { | ||||||
| @ -113,6 +114,7 @@ impl AdminRegistry { | |||||||
|         repository: R, |         repository: R, | ||||||
|     ) -> Result<(), String> { |     ) -> Result<(), String> { | ||||||
|         let model_key = self.register_model_config(model)?; |         let model_key = self.register_model_config(model)?; | ||||||
|  |         let repository = AdminRepositoryWrapper::new(repository); | ||||||
|         self.repositories |         self.repositories | ||||||
|             .insert(model_key, Arc::new(Mutex::new(repository))); |             .insert(model_key, Arc::new(Mutex::new(repository))); | ||||||
|         Ok(()) |         Ok(()) | ||||||
| @ -122,7 +124,7 @@ impl AdminRegistry { | |||||||
|         &self, |         &self, | ||||||
|         app_key: &str, |         app_key: &str, | ||||||
|         model_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); |         let full_model_key = format!("{}.{}", app_key, model_key); | ||||||
|         if let Some(repo) = self.repositories.get(&full_model_key) { |         if let Some(repo) = self.repositories.get(&full_model_key) { | ||||||
|             // Clone the Arc to return a reference to the repository
 |             // 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 crate::admin::state::AdminState; | ||||||
| use serde::{Deserialize, Serialize}; | use serde::{Deserialize, Serialize}; | ||||||
| 
 | 
 | ||||||
|  | use super::domain::repository::PrimaryKeyType; | ||||||
| use super::domain::{LookupKey, RepositoryInfo, RepositoryItem, RepositoryList}; | use super::domain::{LookupKey, RepositoryInfo, RepositoryItem, RepositoryList}; | ||||||
| 
 | 
 | ||||||
| #[derive(Serialize, Deserialize)] | #[derive(Serialize, Deserialize)] | ||||||
| @ -157,13 +158,13 @@ pub async fn view_item_details<S: AdminState + Clone + Send + Sync + 'static>( | |||||||
|         let admin_model = registry |         let admin_model = registry | ||||||
|             .get_model(&app_key, &model_key) |             .get_model(&app_key, &model_key) | ||||||
|             .expect("Admin Model not found?"); |             .expect("Admin Model not found?"); | ||||||
|         let key: LookupKey = id.into(); |         let key: Box<dyn PrimaryKeyType> = Box::new(id); | ||||||
|         AdminContext { |         AdminContext { | ||||||
|             base: base_template(&headers), |             base: base_template(&headers), | ||||||
|             available_apps: registry.get_apps(), |             available_apps: registry.get_apps(), | ||||||
|             item_info: Some(repo.info(&admin_model).await), |             item_info: Some(repo.info(&admin_model).await), | ||||||
|             item_list: repo.list(&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), |             item_model: Some(admin_model), | ||||||
|             ..Default::default() |             ..Default::default() | ||||||
|         } |         } | ||||||
| @ -257,13 +258,13 @@ pub async fn change_item<S: AdminState + Clone + Send + Sync + 'static>( | |||||||
|         let admin_model = registry |         let admin_model = registry | ||||||
|             .get_model(&app_key, &model_key) |             .get_model(&app_key, &model_key) | ||||||
|             .expect("Admin Model not found?"); |             .expect("Admin Model not found?"); | ||||||
|         let key: LookupKey = id.into(); |         let key: Box<dyn PrimaryKeyType> = Box::new(id); | ||||||
|         AdminContext { |         AdminContext { | ||||||
|             base: base_template(&headers), |             base: base_template(&headers), | ||||||
|             available_apps: registry.get_apps(), |             available_apps: registry.get_apps(), | ||||||
|             item_info: Some(repo.info(&admin_model).await), |             item_info: Some(repo.info(&admin_model).await), | ||||||
|             item_list: repo.list(&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), |             item_model: Some(admin_model), | ||||||
|             ..Default::default() |             ..Default::default() | ||||||
|         } |         } | ||||||
| @ -290,9 +291,9 @@ pub async fn update_item<S: AdminState + Clone + Send + Sync + 'static>( | |||||||
|         let admin_model = registry |         let admin_model = registry | ||||||
|             .get_model(&app_key, &model_key) |             .get_model(&app_key, &model_key) | ||||||
|             .expect("Admin Model not found?"); |             .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 { |         AdminContext { | ||||||
|             base: base_template(&headers), |             base: base_template(&headers), | ||||||
|  | |||||||
| @ -8,6 +8,8 @@ impl Repository {} | |||||||
| 
 | 
 | ||||||
| #[async_trait] | #[async_trait] | ||||||
| impl AdminRepository for Repository { | impl AdminRepository for Repository { | ||||||
|  |     type Key = i64; | ||||||
|  | 
 | ||||||
|     async fn info(&self, _: &RepositoryContext) -> RepositoryInfo { |     async fn info(&self, _: &RepositoryContext) -> RepositoryInfo { | ||||||
|         RepoInfo { |         RepoInfo { | ||||||
|             name: "My Empty Repository", |             name: "My Empty Repository", | ||||||
| @ -33,7 +35,7 @@ impl AdminRepository for Repository { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // GET single item.
 |     // GET single item.
 | ||||||
|     async fn get(&self, model: &RepositoryContext, id: LookupKey) -> Option<RepositoryItem> { |     async fn get(&self, model: &RepositoryContext, id: &Self::Key) -> Option<RepositoryItem> { | ||||||
|         None |         None | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -41,7 +43,7 @@ impl AdminRepository for Repository { | |||||||
|     async fn update( |     async fn update( | ||||||
|         &mut self, |         &mut self, | ||||||
|         model: &RepositoryContext, |         model: &RepositoryContext, | ||||||
|         id: LookupKey, |         id: &Self::Key, | ||||||
|         data: Value, |         data: Value, | ||||||
|     ) -> Option<RepositoryItem> { |     ) -> Option<RepositoryItem> { | ||||||
|         None |         None | ||||||
| @ -51,14 +53,14 @@ impl AdminRepository for Repository { | |||||||
|     async fn replace( |     async fn replace( | ||||||
|         &mut self, |         &mut self, | ||||||
|         model: &RepositoryContext, |         model: &RepositoryContext, | ||||||
|         id: LookupKey, |         id: &Self::Key, | ||||||
|         data: Value, |         data: Value, | ||||||
|     ) -> Option<RepositoryItem> { |     ) -> Option<RepositoryItem> { | ||||||
|         None |         None | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // DELETE single item.
 |     // DELETE single item.
 | ||||||
|     async fn delete(&mut self, _: &RepositoryContext, id: LookupKey) -> Option<Value> { |     async fn delete(&mut self, _: &RepositoryContext, id: &Self::Key) -> Option<Value> { | ||||||
|         None |         None | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -36,6 +36,8 @@ impl MyStaticRepository { | |||||||
| 
 | 
 | ||||||
| #[async_trait] | #[async_trait] | ||||||
| impl AdminRepository for MyStaticRepository { | impl AdminRepository for MyStaticRepository { | ||||||
|  |     type Key = i64; | ||||||
|  | 
 | ||||||
|     async fn info(&self, _: &RepositoryContext) -> RepositoryInfo { |     async fn info(&self, _: &RepositoryContext) -> RepositoryInfo { | ||||||
|         RepoInfo { |         RepoInfo { | ||||||
|             name: "My Static Repository", |             name: "My Static Repository", | ||||||
| @ -48,15 +50,11 @@ impl AdminRepository for MyStaticRepository { | |||||||
|         //.set_widget("name", Widget::textarea().options(&[("disabled", "true")]))
 |         //.set_widget("name", Widget::textarea().options(&[("disabled", "true")]))
 | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async fn get(&self, model: &RepositoryContext, id: LookupKey) -> Option<RepositoryItem> { |     async fn get(&self, model: &RepositoryContext, id: &Self::Key) -> Option<RepositoryItem> { | ||||||
|         if let LookupKey::Integer(id) = id { |         let id = *id as usize; | ||||||
|         let item = self.content.get(id - 1).cloned().unwrap(); |         let item = self.content.get(id - 1).cloned().unwrap(); | ||||||
|         let id = item.get("id").unwrap(); |         let id = item.get("id").unwrap(); | ||||||
|         Some(model.build_item(&*id.to_string(), item)) |         Some(model.build_item(&*id.to_string(), item)) | ||||||
|         } else { |  | ||||||
|             warn!("Got non-integer lookup key: {}", id); |  | ||||||
|             None |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async fn list(&self, model: &RepositoryContext) -> RepositoryList { |     async fn list(&self, model: &RepositoryContext) -> RepositoryList { | ||||||
| @ -94,7 +92,7 @@ impl AdminRepository for MyStaticRepository { | |||||||
|     async fn update( |     async fn update( | ||||||
|         &mut self, |         &mut self, | ||||||
|         model: &RepositoryContext, |         model: &RepositoryContext, | ||||||
|         id: LookupKey, |         id: &Self::Key, | ||||||
|         data: Value, |         data: Value, | ||||||
|     ) -> Option<RepositoryItem> { |     ) -> Option<RepositoryItem> { | ||||||
|         debug!("I would now update: {}, {}", id, data); |         debug!("I would now update: {}, {}", id, data); | ||||||
| @ -102,10 +100,7 @@ impl AdminRepository for MyStaticRepository { | |||||||
|         // First, find the index of the item to update
 |         // First, find the index of the item to update
 | ||||||
|         let item_index = self.content.iter().position(|item| { |         let item_index = self.content.iter().position(|item| { | ||||||
|             if let Some(item_id) = item.get("id") { |             if let Some(item_id) = item.get("id") { | ||||||
|                 match id { |                 item_id == id | ||||||
|                     LookupKey::Integer(i) => item_id == &i, |  | ||||||
|                     LookupKey::String(ref s) => item_id == s, |  | ||||||
|                 } |  | ||||||
|             } else { |             } else { | ||||||
|                 false |                 false | ||||||
|             } |             } | ||||||
| @ -127,21 +122,18 @@ impl AdminRepository for MyStaticRepository { | |||||||
|     async fn replace( |     async fn replace( | ||||||
|         &mut self, |         &mut self, | ||||||
|         model: &RepositoryContext, |         model: &RepositoryContext, | ||||||
|         id: LookupKey, |         id: &Self::Key, | ||||||
|         data: Value, |         data: Value, | ||||||
|     ) -> Option<RepositoryItem> { |     ) -> Option<RepositoryItem> { | ||||||
|         self.update(model, id, data).await |         self.update(model, id, data).await | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async fn delete(&mut self, _: &RepositoryContext, id: LookupKey) -> Option<Value> { |     async fn delete(&mut self, _: &RepositoryContext, id: &Self::Key) -> Option<Value> { | ||||||
|         debug!("Would delete: {}", id); |         debug!("Would delete: {}", id); | ||||||
| 
 | 
 | ||||||
|         let item_index = self.content.iter().position(|item| { |         let item_index = self.content.iter().position(|item| { | ||||||
|             if let Some(item_id) = item.get("id") { |             if let Some(item_id) = item.get("id") { | ||||||
|                 match id { |                 item_id == id | ||||||
|                     LookupKey::Integer(i) => item_id == &i, |  | ||||||
|                     LookupKey::String(ref s) => item_id == s, |  | ||||||
|                 } |  | ||||||
|             } else { |             } else { | ||||||
|                 false |                 false | ||||||
|             } |             } | ||||||
|  | |||||||
| @ -19,6 +19,8 @@ impl UserRepository { | |||||||
| 
 | 
 | ||||||
| #[async_trait] | #[async_trait] | ||||||
| impl AdminRepository for UserRepository { | impl AdminRepository for UserRepository { | ||||||
|  |     type Key = i64; | ||||||
|  | 
 | ||||||
|     async fn info(&self, _: &RepositoryContext) -> RepositoryInfo { |     async fn info(&self, _: &RepositoryContext) -> RepositoryInfo { | ||||||
|         RepoInfo { |         RepoInfo { | ||||||
|             name: "User", |             name: "User", | ||||||
| @ -29,9 +31,8 @@ impl AdminRepository for UserRepository { | |||||||
|         .into() |         .into() | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async fn get(&self, model: &RepositoryContext, id: LookupKey) -> Option<RepositoryItem> { |     async fn get(&self, model: &RepositoryContext, id: &Self::Key) -> Option<RepositoryItem> { | ||||||
|         if let LookupKey::Integer(id) = id { |         let id: i32 = *id as i32; // use try_into() instead.
 | ||||||
|             let id: i32 = id as i32; // use try_into() instead.
 |  | ||||||
|         let get_user = entity::User::find_by_id(id).one(&self.connection).await; |         let get_user = entity::User::find_by_id(id).one(&self.connection).await; | ||||||
|         match get_user { |         match get_user { | ||||||
|             Ok(get_user) => { |             Ok(get_user) => { | ||||||
| @ -47,7 +48,7 @@ impl AdminRepository for UserRepository { | |||||||
|             } |             } | ||||||
|             Err(_) => return None, |             Err(_) => return None, | ||||||
|         } |         } | ||||||
|         } | 
 | ||||||
|         None |         None | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -94,11 +95,10 @@ impl AdminRepository for UserRepository { | |||||||
|     async fn update( |     async fn update( | ||||||
|         &mut self, |         &mut self, | ||||||
|         model: &RepositoryContext, |         model: &RepositoryContext, | ||||||
|         id: LookupKey, |         id: &Self::Key, | ||||||
|         data: Value, |         data: Value, | ||||||
|     ) -> Option<RepositoryItem> { |     ) -> Option<RepositoryItem> { | ||||||
|         if let LookupKey::Integer(id) = id { |         let id: i32 = *id as i32; | ||||||
|             let id: i32 = id as i32; // use try_into() instead.
 |  | ||||||
|         let user: Option<entity::user::Model> = entity::User::find_by_id(id) |         let user: Option<entity::user::Model> = entity::User::find_by_id(id) | ||||||
|             .one(&self.connection) |             .one(&self.connection) | ||||||
|             .await |             .await | ||||||
| @ -121,22 +121,21 @@ impl AdminRepository for UserRepository { | |||||||
|             let id = user.id.to_string(); |             let id = user.id.to_string(); | ||||||
|             return Some(model.build_item(&*id, serde_json::to_value(&user).unwrap())); |             return Some(model.build_item(&*id, serde_json::to_value(&user).unwrap())); | ||||||
|         } |         } | ||||||
|         } | 
 | ||||||
|         None |         None | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async fn replace( |     async fn replace( | ||||||
|         &mut self, |         &mut self, | ||||||
|         model: &RepositoryContext, |         model: &RepositoryContext, | ||||||
|         id: LookupKey, |         id: &Self::Key, | ||||||
|         data: Value, |         data: Value, | ||||||
|     ) -> Option<RepositoryItem> { |     ) -> Option<RepositoryItem> { | ||||||
|         self.update(model, id, data).await |         self.update(model, id, data).await | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async fn delete(&mut self, _: &RepositoryContext, id: LookupKey) -> Option<Value> { |     async fn delete(&mut self, _: &RepositoryContext, id: &Self::Key) -> Option<Value> { | ||||||
|         if let LookupKey::Integer(id) = id { |         let id: i32 = *id as i32; | ||||||
|             let id: i32 = id as i32; // use try_into() instead.
 |  | ||||||
|         let user: Option<entity::user::Model> = entity::User::find_by_id(id) |         let user: Option<entity::user::Model> = entity::User::find_by_id(id) | ||||||
|             .one(&self.connection) |             .one(&self.connection) | ||||||
|             .await |             .await | ||||||
| @ -145,7 +144,7 @@ impl AdminRepository for UserRepository { | |||||||
|             let delete_result = user.delete(&self.connection).await.unwrap(); |             let delete_result = user.delete(&self.connection).await.unwrap(); | ||||||
|             debug!("deleted rows: {}", delete_result.rows_affected); |             debug!("deleted rows: {}", delete_result.rows_affected); | ||||||
|         } |         } | ||||||
|         } | 
 | ||||||
|         None |         None | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,37 +1,142 @@ | |||||||
| 
 |  | ||||||
| 
 |  | ||||||
| pub trait Repository { |  | ||||||
|     type Key; |  | ||||||
| 
 |  | ||||||
|     fn get_data(&self, key: Self::Key) -> String; |  | ||||||
|     fn set_data(&self, key: Self::Key, value: String); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| use std::any::Any; | use std::any::Any; | ||||||
|  | use std::collections::HashMap; | ||||||
|  | use std::fmt::Debug; | ||||||
| 
 | 
 | ||||||
| trait Key: Any { | // Define the Key trait with Any and Debug for type erasure and debugging
 | ||||||
|  | pub trait Key: Any + Debug { | ||||||
|     fn as_any(&self) -> &dyn Any; |     fn as_any(&self) -> &dyn Any; | ||||||
|     fn from_str(s: &str) -> Self where Self: Sized; |  | ||||||
|     fn to_string(&self) -> String; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl<T: Any> Key for T { | // Implement Key for i32
 | ||||||
|  | impl Key for i32 { | ||||||
|     fn as_any(&self) -> &dyn Any { |     fn as_any(&self) -> &dyn Any { | ||||||
|         self |         self | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // Implement Key for String
 | ||||||
| impl Key for String { | impl Key for String { | ||||||
|     fn from_str(s: &str) -> Self { |     fn as_any(&self) -> &dyn Any { | ||||||
|         s.to_string() |         self | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn to_string(&self) -> String { |  | ||||||
|         self.clone() |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub trait RepositoryTypeErased { | // Implement From<String> for i32
 | ||||||
|     fn get_data(&self, key: Key) -> String; | impl From<String> for Box<dyn Key> { | ||||||
|     fn set_data(&self, key: Key, value: String); |     fn from(s: String) -> Box<dyn Key> { | ||||||
|  |         if let Ok(i) = s.parse::<i32>() { | ||||||
|  |             Box::new(i) | ||||||
|  |         } else { | ||||||
|  |             Box::new(s) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Define the Repository trait with an associated type Key
 | ||||||
|  | pub trait Repository { | ||||||
|  |     type Key: Key; | ||||||
|  |     fn get_entry(&self, key: &Self::Key) -> String; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Define a helper trait for dynamic dispatch
 | ||||||
|  | pub trait DynRepository { | ||||||
|  |     fn get_entry(&self, key: &dyn Key) -> String; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Define a type-erased wrapper for Repository
 | ||||||
|  | pub struct RepositoryWrapper<T: Repository> { | ||||||
|  |     inner: T, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<T: Repository> RepositoryWrapper<T> { | ||||||
|  |     pub fn new(inner: T) -> Self { | ||||||
|  |         Self { inner } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<T: Repository> DynRepository for RepositoryWrapper<T> { | ||||||
|  |     fn get_entry(&self, key: &dyn Key) -> String { | ||||||
|  |         if let Some(key) = key.as_any().downcast_ref::<T::Key>() { | ||||||
|  |             self.inner.get_entry(key) | ||||||
|  |         } else { | ||||||
|  |             "Invalid Key Type".to_string() | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Implement the Repository trait for IntRepository
 | ||||||
|  | pub struct IntRepository { | ||||||
|  |     data: HashMap<i32, String>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl IntRepository { | ||||||
|  |     pub fn new() -> Self { | ||||||
|  |         let mut data = HashMap::new(); | ||||||
|  |         data.insert(1, "IntEntry1".to_string()); | ||||||
|  |         data.insert(2, "IntEntry2".to_string()); | ||||||
|  |         Self { data } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Repository for IntRepository { | ||||||
|  |     type Key = i32; | ||||||
|  | 
 | ||||||
|  |     fn get_entry(&self, key: &Self::Key) -> String { | ||||||
|  |         self.data | ||||||
|  |             .get(key) | ||||||
|  |             .unwrap_or(&"Not Found".to_string()) | ||||||
|  |             .clone() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Implement the Repository trait for StringRepository
 | ||||||
|  | pub struct StringRepository { | ||||||
|  |     data: HashMap<String, String>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl StringRepository { | ||||||
|  |     pub fn new() -> Self { | ||||||
|  |         let mut data = HashMap::new(); | ||||||
|  |         data.insert("one".to_string(), "StringEntry1".to_string()); | ||||||
|  |         data.insert("two".to_string(), "StringEntry2".to_string()); | ||||||
|  |         Self { data } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Repository for StringRepository { | ||||||
|  |     type Key = String; | ||||||
|  | 
 | ||||||
|  |     fn get_entry(&self, key: &Self::Key) -> String { | ||||||
|  |         self.data | ||||||
|  |             .get(key) | ||||||
|  |             .unwrap_or(&"Not Found".to_string()) | ||||||
|  |             .clone() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn main() { | ||||||
|  |     let int_repo = IntRepository::new(); | ||||||
|  |     let string_repo = StringRepository::new(); | ||||||
|  | 
 | ||||||
|  |     let repositories: Vec<Box<dyn DynRepository>> = vec![ | ||||||
|  |         Box::new(RepositoryWrapper::new(int_repo)), | ||||||
|  |         Box::new(RepositoryWrapper::new(string_repo)), | ||||||
|  |     ]; | ||||||
|  | 
 | ||||||
|  |     // Creating keys from strings using the From<String> implementation
 | ||||||
|  |     let keys: Vec<Box<dyn Key>> = vec![ | ||||||
|  |         Box::new(1_i32), | ||||||
|  |         Box::new("one".to_string()), | ||||||
|  |         Box::new(99_i32),                // non-existing int key
 | ||||||
|  |         Box::new("unknown".to_string()), // non-existing string key
 | ||||||
|  |         Box::from("123".to_string()),    // i32 key from string
 | ||||||
|  |         Box::from("hello".to_string()),  // String key from string
 | ||||||
|  |     ]; | ||||||
|  | 
 | ||||||
|  |     for key in keys { | ||||||
|  |         for repo in &repositories { | ||||||
|  |             let result = repo.get_entry(key.as_ref()); | ||||||
|  |             println!("Result: {}", result); | ||||||
|  |         } | ||||||
|  |     } | ||||||
| } | } | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user