use std::fmt;
use std::fmt::Debug;
use std::error::Error;
use std::ops::Deref;
use std::cmp::Ordering;
#[derive(Debug)]
pub enum CheckEnsureResult<M, A> {
Met(M),
EnsureAction(A),
}
#[derive(Debug)]
pub struct VerificationError;
impl fmt::Display for VerificationError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "verification of target state failed after it was ensured to be met")
}
}
impl Error for VerificationError {}
pub trait Meet {
type Met;
type Error;
fn meet(self) -> Result<Self::Met, Self::Error>;
}
pub trait Ensure<T>: Sized {
type EnsureAction: Meet<Met = T>;
fn check_ensure(self) -> Result<CheckEnsureResult<T, Self::EnsureAction>, <Self::EnsureAction as Meet>::Error>;
fn ensure(self) -> Result<T, <Self::EnsureAction as Meet>::Error> {
match self.check_ensure()? {
CheckEnsureResult::Met(met) => Ok(met),
CheckEnsureResult::EnsureAction(meet) => meet.meet(),
}
}
fn ensure_verify(self) -> Result<T, <Self::EnsureAction as Meet>::Error> where Self: Clone, <Self::EnsureAction as Meet>::Error: From<VerificationError> {
let verify = self.clone();
match self.check_ensure()? {
CheckEnsureResult::Met(met) => Ok(met),
CheckEnsureResult::EnsureAction(action) => {
let result = action.meet()?;
match verify.check_ensure()? {
CheckEnsureResult::Met(_met) => Ok(result),
CheckEnsureResult::EnsureAction(_action) => Err(VerificationError.into()),
}
}
}
}
}
impl<T, E, A, F> Ensure<T> for F
where F: FnOnce() -> Result<CheckEnsureResult<T, A>, E>, A: Meet<Met = T, Error = E> {
type EnsureAction = A;
fn check_ensure(self) -> Result<CheckEnsureResult<T, Self::EnsureAction>, E> {
self()
}
}
impl<T, E, F> Meet for F
where F: FnOnce() -> Result<T, E> {
type Met = T;
type Error = E;
fn meet(self) -> Result<Self::Met, Self::Error> {
self()
}
}
pub fn ensure<T, E, R, A>(ensure: R) -> Result<T, E> where R: Ensure<T, EnsureAction = A>, A: Meet<Met = T, Error = E> {
ensure.ensure()
}
pub trait External { }
pub trait ExternalState<T> where T: External {
fn invalidate_state(self) -> T;
}
impl<T> ExternalState<T> for T where T: External {
fn invalidate_state(self) -> T {
self
}
}
pub struct Present<T>(pub T) where T: External;
impl<T> ExternalState<T> for Present<T> where T: External {
fn invalidate_state(self) -> T {
self.0
}
}
pub struct Absent<T>(pub T) where T: External;
impl<T> ExternalState<T> for Absent<T> where T: External {
fn invalidate_state(self) -> T {
self.0
}
}
impl<T> Deref for Present<T> where T: External {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T> Debug for Present<T> where T: External + Debug {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("Present")
.field(&self.0)
.finish()
}
}
impl<T> PartialEq for Present<T> where T: External + PartialEq {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl<T> PartialOrd for Present<T> where T: External + PartialOrd {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.0.partial_cmp(&other.0)
}
}
impl<T> Eq for Present<T> where T: External + Eq {}
impl<T> Ord for Present<T> where T: External + Ord {
fn cmp(&self, other: &Self) -> Ordering {
self.0.cmp(&other.0)
}
}
impl<T> Deref for Absent<T> where T: External {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T> Debug for Absent<T> where T: External + Debug {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("Absent")
.field(&self.0)
.finish()
}
}
impl<T> PartialEq for Absent<T> where T: External + PartialEq {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl<T> PartialOrd for Absent<T> where T: External + PartialOrd {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.0.partial_cmp(&other.0)
}
}
impl<T> Eq for Absent<T> where T: External + Eq {}
impl<T> Ord for Absent<T> where T: External + Ord {
fn cmp(&self, other: &Self) -> Ordering {
self.0.cmp(&other.0)
}
}
pub trait Existential<T> where T: External {
type Error;
fn ensure_present(self) -> Result<Present<T>, Self::Error>;
fn ensure_absent(self) -> Result<Absent<T>, Self::Error>;
}
impl<T, E, R, PA, AA> Existential<T> for R where
R: Ensure<Present<T>, EnsureAction = PA>, PA: Meet<Met = Present<T>, Error = E>,
R: Ensure<Absent<T>, EnsureAction = AA>, AA: Meet<Met = Absent<T>, Error = E>,
T: External
{
type Error = E;
fn ensure_present(self) -> Result<Present<T>, Self::Error> {
self.ensure()
}
fn ensure_absent(self) -> Result<Absent<T>, Self::Error> {
self.ensure()
}
}
#[cfg(test)]
mod test {
use super::*;
use super::CheckEnsureResult::*;
#[test]
fn test_closure() {
fn test(met: bool) -> impl Ensure<u8, EnsureAction = impl Meet<Met = u8, Error = ()>> {
move || {
Ok(match met {
true => Met(1),
_ => EnsureAction(|| Ok(2))
})
}
}
assert_eq!(test(true).ensure(), Ok(1));
assert_eq!(test(false).ensure(), Ok(2));
assert_eq!(ensure(test(true)), Ok(1));
assert_eq!(ensure(test(false)), Ok(2));
}
struct Resource;
impl External for Resource {}
struct CreateResourceAction(Resource);
impl Meet for CreateResourceAction {
type Met = Present<Resource>;
type Error = ();
fn meet(self) -> Result<Present<Resource>, ()> {
Ok(Present(self.0))
}
}
impl Ensure<Present<Resource>> for Resource {
type EnsureAction = CreateResourceAction;
fn check_ensure(self) -> Result<CheckEnsureResult<Present<Resource>, Self::EnsureAction>, ()> {
Ok(if true {
Met(Present(self))
} else {
EnsureAction(CreateResourceAction(self))
})
}
}
struct DeleteResourceAction(Resource);
impl Meet for DeleteResourceAction {
type Met = Absent<Resource>;
type Error = ();
fn meet(self) -> Result<Absent<Resource>, ()> {
Ok(Absent(self.0))
}
}
impl Ensure<Absent<Resource>> for Resource {
type EnsureAction = DeleteResourceAction;
fn check_ensure(self) -> Result<CheckEnsureResult<Absent<Resource>, Self::EnsureAction>, ()> {
Ok(if true {
Met(Absent(self))
} else {
EnsureAction(DeleteResourceAction(self))
})
}
}
#[test]
fn test_ensure() {
let _r: Result<Present<Resource>, ()> = Resource.ensure();
let _r: Result<Absent<Resource>, ()> = Resource.ensure();
}
#[test]
fn test_existential() {
let _r: Result<Present<Resource>, ()> = Resource.ensure_present();
let _r: Result<Absent<Resource>, ()> = Resource.ensure_absent();
}
}