use core::fmt;
use core::marker::PhantomData;
use super::multiplicitas::Semel;
use super::qtt::Qtt;
#[must_use = "linear handles must be explicitly released"]
pub struct ManusLinearis<T> {
resource: Option<T>,
_linear: PhantomData<Semel>,
}
impl<T> ManusLinearis<T> {
#[inline]
pub fn acquire(resource: T) -> Self {
ManusLinearis {
resource: Some(resource),
_linear: PhantomData,
}
}
#[inline]
pub fn use_ref<R, F>(&self, f: F) -> R
where
F: FnOnce(&T) -> R,
{
f(self.resource.as_ref().expect("resource already released"))
}
#[inline]
pub fn use_mut<R, F>(&mut self, f: F) -> R
where
F: FnOnce(&mut T) -> R,
{
f(self.resource.as_mut().expect("resource already released"))
}
#[inline]
pub fn release(mut self) -> T {
self.resource.take().expect("resource already released")
}
#[inline]
pub fn release_with<R, F>(self, f: F) -> R
where
F: FnOnce(T) -> R,
{
f(self.release())
}
#[inline]
pub fn map<U, F>(self, f: F) -> ManusLinearis<U>
where
F: FnOnce(T) -> U,
{
ManusLinearis::acquire(f(self.release()))
}
#[inline]
pub fn and_then<U, F>(self, f: F) -> ManusLinearis<U>
where
F: FnOnce(T) -> ManusLinearis<U>,
{
f(self.release())
}
#[inline]
pub fn is_released(&self) -> bool {
self.resource.is_none()
}
#[inline]
pub fn into_qtt(self) -> Qtt<T, Semel> {
Qtt::linear(self.release())
}
}
impl<T: fmt::Debug> fmt::Debug for ManusLinearis<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ManusLinearis")
.field("resource", &self.resource)
.finish()
}
}
pub struct ManusGuard<'a, T> {
resource: &'a mut Option<T>,
temp: Option<T>,
}
impl<'a, T> ManusGuard<'a, T> {
#[inline]
fn new(resource: &'a mut Option<T>) -> Self {
let temp = resource.take();
ManusGuard { resource, temp }
}
#[inline]
pub fn get(&self) -> &T {
self.temp.as_ref().expect("guard is empty")
}
#[inline]
pub fn get_mut(&mut self) -> &mut T {
self.temp.as_mut().expect("guard is empty")
}
#[inline]
pub fn take(mut self) -> T {
self.temp.take().expect("guard is empty")
}
}
impl<T> Drop for ManusGuard<'_, T> {
fn drop(&mut self) {
if let Some(resource) = self.temp.take() {
*self.resource = Some(resource);
}
}
}
impl<T> core::ops::Deref for ManusGuard<'_, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.get()
}
}
impl<T> core::ops::DerefMut for ManusGuard<'_, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.get_mut()
}
}
impl<T> ManusLinearis<T> {
#[inline]
pub fn guard(&mut self) -> ManusGuard<'_, T> {
ManusGuard::new(&mut self.resource)
}
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::vec;
#[test]
fn test_acquire_release() {
let handle = ManusLinearis::acquire(42);
let value = handle.release();
assert_eq!(value, 42);
}
#[test]
fn test_use_ref() {
let handle = ManusLinearis::acquire(vec![1, 2, 3]);
let len = handle.use_ref(std::vec::Vec::len);
assert_eq!(len, 3);
let _ = handle.release();
}
#[test]
fn test_use_mut() {
let mut handle = ManusLinearis::acquire(vec![1, 2, 3]);
handle.use_mut(|v| v.push(4));
let vec = handle.release();
assert_eq!(vec, vec![1, 2, 3, 4]);
}
#[test]
fn test_release_with() {
let handle = ManusLinearis::acquire(10);
let result = handle.release_with(|x| x * 2);
assert_eq!(result, 20);
}
#[test]
fn test_map() {
let handle = ManusLinearis::acquire(5);
let mapped = handle.map(|x| x * 2);
assert_eq!(mapped.release(), 10);
}
#[test]
fn test_and_then() {
let handle = ManusLinearis::acquire(5);
let chained = handle.and_then(|x| ManusLinearis::acquire(x + 10));
assert_eq!(chained.release(), 15);
}
#[test]
fn test_guard() {
let mut handle = ManusLinearis::acquire(vec![1, 2, 3]);
{
let mut guard = handle.guard();
guard.push(4);
guard.push(5);
}
let vec = handle.release();
assert_eq!(vec, vec![1, 2, 3, 4, 5]);
}
#[test]
fn test_guard_deref() {
let mut handle = ManusLinearis::acquire(42);
{
let guard = handle.guard();
assert_eq!(*guard, 42);
}
assert_eq!(handle.release(), 42);
}
#[test]
fn test_into_qtt() {
let handle = ManusLinearis::acquire(42);
let qtt = handle.into_qtt();
assert_eq!(qtt.consume(), 42);
}
#[test]
fn test_is_released() {
let handle = ManusLinearis::acquire(42);
assert!(!handle.is_released());
let _ = handle.release();
}
#[test]
fn test_debug() {
let handle = ManusLinearis::acquire(42);
let debug_str = alloc::format!("{handle:?}");
assert!(debug_str.contains("ManusLinearis"));
assert!(debug_str.contains("42"));
}
}