Skip to content

Instantly share code, notes, and snippets.

@KeenS
Created July 2, 2018 17:15
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save KeenS/d8ef8c95110742d31c74c750ed456ecb to your computer and use it in GitHub Desktop.
Save KeenS/d8ef8c95110742d31c74c750ed456ecb to your computer and use it in GitHub Desktop.
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