use std::{convert, mem};
use dope_sys::memory;
use crate::{
Resources,
driver::{life::native::create, storage},
};
pub struct Cleanup(State<Resources, create::Failure>);
impl Cleanup {
pub(in crate::driver) fn new<S: Into<Resources>>(state: S) -> Self {
Self(State::new(state))
}
pub fn try_close(
&mut self,
report: impl FnMut(storage::Failure),
) -> Result<storage::Failure, storage::Failure> {
self.0
.try_close(report, storage::Failure::from, storage::Failure::from)
}
pub fn primary(&self) -> storage::Failure {
self.0.primary(storage::Failure::from)
}
pub fn cleanup(&self) -> Option<storage::Failure> {
self.0.failure(storage::Failure::from)
}
}
pub(super) enum State<R, P> {
Owner(R),
Primary(P),
}
impl<R, P> State<R, P> {
pub(super) fn new<S: Into<R>>(state: S) -> Self {
Self::Owner(state.into())
}
}
impl<R: memory::Cleanup<P>, P: Copy> State<R, P> {
pub(super) fn try_close<T, E>(
&mut self,
mut report: impl FnMut(E),
primary: impl FnOnce(P) -> T,
failure: impl Fn(R::Error) -> E,
) -> Result<T, E> {
let retained = self.primary(convert::identity);
match mem::replace(self, Self::Primary(retained)) {
Self::Primary(value) => Ok(primary(value)),
Self::Owner(owner) => match owner.try_close(|error| report(failure(error))) {
Ok(value) => {
*self = Self::Primary(value);
Ok(primary(value))
}
Err(owner) => {
let error = owner.failure();
*self = Self::Owner(owner);
Err(failure(error))
}
},
}
}
pub(super) fn primary<T>(&self, map: impl FnOnce(P) -> T) -> T {
map(match self {
Self::Owner(owner) => owner.primary(),
Self::Primary(primary) => *primary,
})
}
pub(super) fn failure<E>(&self, map: impl FnOnce(R::Error) -> E) -> Option<E> {
match self {
Self::Owner(owner) => Some(map(owner.failure())),
Self::Primary(_) => None,
}
}
}