|
use std::marker::PhantomData; |
|
|
|
pub trait Capability<Operation> { |
|
type Data; |
|
type Error; |
|
|
|
fn perform(&self, Operation) -> Result<Self::Data, Self::Error>; |
|
} |
|
|
|
pub trait Authority<Request> { |
|
type Operation; |
|
type Data; |
|
type Error; |
|
|
|
fn request(&self, req: Request) -> Option<Box<Capability<Self::Operation, Data = Self::Data, Error = Self::Error>>>; |
|
} |
|
|
|
macro_rules! capability { |
|
($name:ident for $type:ty, |
|
composing $({$operations:ty, $d:ty, $e:ty}),+) => { |
|
trait $name: $(Capability<$operations, Data = $d, Error = $e>+)+ {} |
|
|
|
impl $name for $type {} |
|
}; |
|
} |
|
|
|
#[derive(Clone)] |
|
pub struct SQLite { |
|
// Some connection. |
|
} |
|
|
|
struct SaveCap<Resource> { |
|
_t: PhantomData<Resource>, |
|
id: String, |
|
} |
|
|
|
struct UpdateCap<Resource> { |
|
_t: PhantomData<Resource>, |
|
id: String, |
|
} |
|
|
|
pub struct SaveCapRequest<Resource> { |
|
_t: PhantomData<Resource>, |
|
id: String, |
|
} |
|
|
|
|
|
pub struct Save<T>(pub T); |
|
pub struct Update<T>(pub T); |
|
pub struct Delete<T>(pub T); |
|
pub struct Search<T>(pub T); |
|
|
|
#[derive(Debug)] |
|
pub struct User { |
|
pub email_address: String, |
|
pub name: String, |
|
} |
|
|
|
#[derive(Debug)] |
|
pub struct Order { |
|
pub item_name: String, |
|
pub cost: u32, |
|
} |
|
|
|
pub struct FindByEmail { |
|
pub email_address: String, |
|
} |
|
|
|
impl Authority<SaveCapRequest<User>> for SQLite { |
|
type Operation = Save<User>; |
|
type Data = User; |
|
type Error = String; |
|
|
|
fn request(&self, req: SaveCapRequest<User>) -> Option<Box<Capability<Save<User>, Data = User, Error = String>>> { |
|
Some(Box::new(Save(User { |
|
email_address: "[email protected]".to_string(), |
|
name: "test user".to_string(), |
|
}))) |
|
} |
|
} |
|
|
|
impl Capability<Save<User>> for Save<User> { |
|
type Data = User; |
|
type Error = String; |
|
|
|
fn perform(&self, to_save: Save<User>) -> Result<User, String> { |
|
println!("Saved user {:?}", to_save.0); |
|
Ok(to_save.0) |
|
} |
|
} |
|
|
|
impl Capability<Update<User>> for Update<User> { |
|
type Data = User; |
|
type Error = String; |
|
|
|
fn perform(&self, to_update: Update<User>) -> Result<User, String> { |
|
println!("Updating user {:?}", to_update.0); |
|
Ok(to_update.0) |
|
} |
|
} |
|
|
|
capability!(CreateAndUpdateUsers for SQLite, |
|
composing { Save<User>, User, String }, |
|
{ Update<User>, User, String }); |
|
|
|
|
|
fn create_new_user<R: CreateAndUpdateUsers>(resource_manager: &R) -> Result<(), String> |
|
//where R: Capability<Save<User>, Error = String> |
|
{ |
|
// Load user from, e.g., a request. |
|
let user = User { |
|
email_address: "[email protected]".to_string(), |
|
name: "Test User".to_string(), |
|
}; |
|
let user = resource_manager.perform(Save(user)).unwrap(); |
|
resource_manager.perform(Update(user)).map(|_| ()) |
|
} |
|
|
|
#[cfg(test)] |
|
mod tests { |
|
use super::*; |
|
|
|
#[test] |
|
fn can_create_users() { |
|
/* |
|
let db = SQLite{}; |
|
let cap = db.request |
|
let create_result = create_new_user(&resource_manager); |
|
|
|
assert!(create_result.is_ok()); |
|
*/ |
|
} |
|
} |