Created
July 2, 2018 17:15
-
-
Save KeenS/d8ef8c95110742d31c74c750ed456ecb to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
struct DbConn; | |
#[derive(Clone, Copy)] | |
struct User; | |
#[derive(Clone, Copy)] | |
struct Device; | |
enum Error { | |
ReadPrivilege, | |
WritePrivilege, | |
} | |
type Result<T> = ::std::result::Result<T, Error>; | |
fn get_conn() -> DbConn { | |
// get connection from context | |
DbConn | |
} | |
trait Precondition { | |
fn check(&self, conn: &DbConn) -> Result<()>; | |
} | |
struct And<P, Q>(P, Q); | |
impl<P, Q> Precondition for And<P, Q> | |
where | |
P: Precondition, | |
Q: Precondition, | |
{ | |
fn check(&self, conn: &DbConn) -> Result<()> { | |
self.0.check(conn).and_then(|()| self.1.check(conn)) | |
} | |
} | |
struct IsReadable<A, B>(A, B); | |
struct IsWritable<A, B>(A, B); | |
struct IsReadableWritable<A, B>(A, B); | |
impl Precondition for IsReadable<User, Device> { | |
fn check(&self, _conn: &DbConn) -> Result<()> { | |
// do ckeck | |
Ok(()) | |
} | |
} | |
impl Precondition for IsWritable<User, Device> { | |
fn check(&self, _conn: &DbConn) -> Result<()> { | |
// do ckeck | |
Err(Error::WritePrivilege) | |
} | |
} | |
impl Precondition for IsReadableWritable<User, Device> { | |
fn check(&self, _conn: &DbConn) -> Result<()> { | |
// 本当はAnd<IsReadable<_, _>, IsWritable<_, _>>でも可能 | |
// DBアクセスを減らすために特殊化 | |
Err(Error::WritePrivilege) | |
} | |
} | |
mod dsl { | |
use super::*; | |
pub struct IncompleteIsReadable<T>(T); | |
pub struct IncompleteIsWritable<T>(T); | |
pub struct IncompleteIsReadableWritable<T>(T); | |
pub trait IsReadableDsl { | |
type Out; | |
fn is_readable(self) -> Self::Out; | |
} | |
pub trait IsWritableDsl { | |
type Out; | |
fn is_writable(self) -> Self::Out; | |
} | |
pub trait ToDsl<To> { | |
type Out; | |
fn to(self, to: To) -> Self::Out; | |
} | |
impl IsReadableDsl for User { | |
type Out = IncompleteIsReadable<User>; | |
fn is_readable(self) -> Self::Out { | |
IncompleteIsReadable(self) | |
} | |
} | |
impl IsWritableDsl for User { | |
type Out = IncompleteIsWritable<User>; | |
fn is_writable(self) -> Self::Out { | |
IncompleteIsWritable(self) | |
} | |
} | |
impl IsWritableDsl for IncompleteIsReadable<User> { | |
type Out = IncompleteIsReadableWritable<User>; | |
fn is_writable(self) -> Self::Out { | |
IncompleteIsReadableWritable(self.0) | |
} | |
} | |
impl ToDsl<Device> for IncompleteIsReadable<User> { | |
type Out = IsReadable<User, Device>; | |
fn to(self, to: Device) -> Self::Out { | |
IsReadable(self.0, to) | |
} | |
} | |
impl ToDsl<Device> for IncompleteIsWritable<User> { | |
type Out = IsWritable<User, Device>; | |
fn to(self, to: Device) -> Self::Out { | |
IsWritable(self.0, to) | |
} | |
} | |
impl ToDsl<Device> for IncompleteIsReadableWritable<User> { | |
type Out = IsReadableWritable<User, Device>; | |
fn to(self, to: Device) -> Self::Out { | |
// ユーザインタフェースを(見た目レベルでは)変えずに内部実装を変更可能 | |
// And(IsReadable(self, to), IsWritable(self, to)) | |
IsReadableWritable(self.0, to) | |
} | |
} | |
pub(super) fn check_if<P>(cond: P) -> Result<()> | |
where | |
P: Precondition, | |
{ | |
let conn = get_conn(); | |
cond.check(&conn) | |
} | |
} | |
fn update_device(operator: User, device: Device) -> Result<Device> { | |
use dsl::*; | |
check_if( | |
// 権限チェックのDSL | |
operator.is_readable().is_writable().to(device), | |
)?; | |
// do update | |
Ok(device.clone()) | |
} | |
fn main() { | |
let user = User; | |
let device = Device; | |
let _ignore = update_device(user, device); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment