use std::marker::PhantomData;
use super::sets::{Empty, ResourceSet};
use crate::effect::trait_def::Effect;
pub trait ResourceEffect: Effect {
type Acquires: ResourceSet;
type Releases: ResourceSet;
}
pub struct Tracked<Eff, Acq: ResourceSet = Empty, Rel: ResourceSet = Empty> {
inner: Eff,
_phantom: PhantomData<(Acq, Rel)>,
}
impl<Eff, Acq: ResourceSet, Rel: ResourceSet> Tracked<Eff, Acq, Rel> {
pub fn new(inner: Eff) -> Self {
Tracked {
inner,
_phantom: PhantomData,
}
}
pub fn inner(&self) -> &Eff {
&self.inner
}
pub fn into_inner(self) -> Eff {
self.inner
}
}
impl<Eff: Clone, Acq: ResourceSet, Rel: ResourceSet> Clone for Tracked<Eff, Acq, Rel> {
fn clone(&self) -> Self {
Tracked {
inner: self.inner.clone(),
_phantom: PhantomData,
}
}
}
impl<Eff, Acq: ResourceSet, Rel: ResourceSet> std::fmt::Debug for Tracked<Eff, Acq, Rel> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Tracked")
.field("inner", &"<effect>")
.field("acquires", &std::any::type_name::<Acq>())
.field("releases", &std::any::type_name::<Rel>())
.finish()
}
}
impl<Eff: Effect, Acq: ResourceSet, Rel: ResourceSet> Effect for Tracked<Eff, Acq, Rel> {
type Output = Eff::Output;
type Error = Eff::Error;
type Env = Eff::Env;
async fn run(self, env: &Self::Env) -> Result<Self::Output, Self::Error> {
self.inner.run(env).await
}
}
impl<Eff: Effect, Acq: ResourceSet, Rel: ResourceSet> ResourceEffect for Tracked<Eff, Acq, Rel> {
type Acquires = Acq;
type Releases = Rel;
}
#[cfg(test)]
mod tests {
use super::*;
use crate::effect::constructors::pure;
use crate::effect::resource::ext::ResourceEffectExt;
use crate::effect::resource::markers::FileRes;
use crate::effect::resource::sets::Has;
#[tokio::test]
async fn tracked_delegates_to_inner() {
let inner = pure::<_, String, ()>(42);
let tracked: Tracked<_, Empty, Empty> = Tracked::new(inner);
let result = tracked.run(&()).await;
assert_eq!(result, Ok(42));
}
#[tokio::test]
async fn tracked_with_resources_still_works() {
let inner = pure::<_, String, ()>(42);
let tracked: Tracked<_, Has<FileRes>, Empty> = Tracked::new(inner);
let result = tracked.run(&()).await;
assert_eq!(result, Ok(42));
}
#[test]
fn tracked_is_zero_sized_overhead() {
use std::mem::size_of;
assert_eq!(size_of::<PhantomData<(Empty, Empty)>>(), 0);
}
#[test]
fn tracked_debug_impl() {
let inner = pure::<_, String, ()>(42);
let tracked: Tracked<_, Has<FileRes>, Empty> = Tracked::new(inner);
let debug_str = format!("{:?}", tracked);
assert!(debug_str.contains("Tracked"));
assert!(debug_str.contains("<effect>"));
}
#[test]
fn tracked_inner_access() {
let inner = pure::<_, String, ()>(42);
let tracked: Tracked<_, Empty, Empty> = Tracked::new(inner);
let _ = tracked.inner();
let _ = tracked.into_inner();
}
fn _assert_resource_effect<T: ResourceEffect>() {}
fn _assert_acquires<T: ResourceEffect<Acquires = A>, A: ResourceSet>() {}
fn _assert_releases<T: ResourceEffect<Releases = R>, R: ResourceSet>() {}
#[test]
fn tracked_implements_resource_effect() {
type TrackedPure = Tracked<crate::effect::combinators::Pure<i32, String, ()>, Empty, Empty>;
_assert_resource_effect::<TrackedPure>();
}
#[test]
fn tracked_acquires_type() {
type TrackedWithAcq =
Tracked<crate::effect::combinators::Pure<i32, String, ()>, Has<FileRes>, Empty>;
_assert_acquires::<TrackedWithAcq, Has<FileRes>>();
}
#[test]
fn tracked_releases_type() {
type TrackedWithRel =
Tracked<crate::effect::combinators::Pure<i32, String, ()>, Empty, Has<FileRes>>;
_assert_releases::<TrackedWithRel, Has<FileRes>>();
}
#[tokio::test]
async fn tracked_clone_works() {
let original: Tracked<_, Has<FileRes>, Empty> = Tracked::new(pure::<_, String, ()>(42));
let cloned = original.clone();
let result1 = original.run(&()).await;
let result2 = cloned.run(&()).await;
assert_eq!(result1, Ok(42));
assert_eq!(result2, Ok(42));
}
#[test]
fn tracked_clone_preserves_type() {
let original = pure::<_, String, ()>(42).acquires::<FileRes>();
let cloned = original.clone();
fn check<T: ResourceEffect<Acquires = Has<FileRes>, Releases = Empty>>(_: T) {}
check(cloned);
}
}