use std::marker::PhantomData;
use super::markers::ResourceKind;
pub trait ResourceSet: Send + Sync + 'static {}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct Empty;
impl ResourceSet for Empty {}
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct Has<R: ResourceKind, Rest: ResourceSet = Empty>(PhantomData<(R, Rest)>);
impl<R: ResourceKind, Rest: ResourceSet> Default for Has<R, Rest> {
fn default() -> Self {
Has(PhantomData)
}
}
impl<R: ResourceKind, Rest: ResourceSet> std::fmt::Debug for Has<R, Rest> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Has<{}>", R::NAME)
}
}
impl<R: ResourceKind, Rest: ResourceSet> ResourceSet for Has<R, Rest> {}
pub trait Union<Other: ResourceSet>: ResourceSet {
type Output: ResourceSet;
}
impl<S: ResourceSet> Union<Empty> for S {
type Output = S;
}
impl<R: ResourceKind, Rest: ResourceSet> Union<Has<R, Rest>> for Empty {
type Output = Has<R, Rest>;
}
impl<R1: ResourceKind, Rest1: ResourceSet, R2: ResourceKind, Rest2: ResourceSet>
Union<Has<R2, Rest2>> for Has<R1, Rest1>
where
Rest1: Union<Has<R2, Rest2>>,
{
type Output = Has<R1, <Rest1 as Union<Has<R2, Rest2>>>::Output>;
}
pub trait Contains<R: ResourceKind>: ResourceSet {}
impl<R: ResourceKind, Rest: ResourceSet> Contains<R> for Has<R, Rest> {}
pub trait Subset<Super: ResourceSet>: ResourceSet {}
impl<S: ResourceSet> Subset<S> for Empty {}
impl<R: ResourceKind, Rest: ResourceSet> Subset<Has<R, Rest>> for Has<R, Empty> {}
#[cfg(test)]
mod tests {
use super::*;
use crate::effect::resource::markers::{DbRes, FileRes, TxRes};
#[test]
fn empty_set_is_zero_sized() {
assert_eq!(std::mem::size_of::<Empty>(), 0);
}
#[test]
fn has_set_is_zero_sized() {
assert_eq!(std::mem::size_of::<Has<FileRes>>(), 0);
assert_eq!(std::mem::size_of::<Has<FileRes, Has<DbRes>>>(), 0);
}
#[test]
fn debug_impl() {
let empty = Empty;
assert_eq!(format!("{:?}", empty), "Empty");
let has_file: Has<FileRes> = Has::default();
assert!(format!("{:?}", has_file).contains("File"));
}
fn _assert_resource_set<T: ResourceSet>() {}
fn _assert_contains<S: Contains<R>, R: ResourceKind>() {}
fn _assert_subset<A: Subset<B>, B: ResourceSet>() {}
#[test]
fn empty_is_resource_set() {
_assert_resource_set::<Empty>();
}
#[test]
fn has_is_resource_set() {
_assert_resource_set::<Has<FileRes>>();
_assert_resource_set::<Has<FileRes, Has<DbRes>>>();
}
#[test]
fn has_contains_its_resource() {
_assert_contains::<Has<FileRes>, FileRes>();
}
#[test]
fn empty_is_subset_of_empty() {
_assert_subset::<Empty, Empty>();
}
#[test]
fn empty_is_subset_of_has() {
_assert_subset::<Empty, Has<FileRes>>();
}
#[test]
fn single_has_is_subset_of_same() {
_assert_subset::<Has<FileRes, Empty>, Has<FileRes>>();
}
fn _assert_union<A: Union<B>, B: ResourceSet>() {}
#[test]
fn union_with_empty() {
_assert_union::<Has<FileRes>, Empty>();
_assert_union::<Empty, Has<FileRes>>();
}
#[test]
fn union_of_has() {
_assert_union::<Has<FileRes>, Has<DbRes>>();
}
fn _union_output_is_resource_set<A, B>()
where
A: Union<B>,
B: ResourceSet,
{
_assert_resource_set::<<A as Union<B>>::Output>();
}
#[test]
fn union_output_types() {
_union_output_is_resource_set::<Empty, Empty>();
_union_output_is_resource_set::<Has<FileRes>, Empty>();
_union_output_is_resource_set::<Empty, Has<DbRes>>();
_union_output_is_resource_set::<Has<FileRes>, Has<DbRes>>();
}
#[test]
fn three_resource_set() {
type ThreeRes = Has<FileRes, Has<DbRes, Has<TxRes>>>;
_assert_resource_set::<ThreeRes>();
_assert_contains::<ThreeRes, FileRes>();
}
}