use super::{
proxy_matrix::{CallArgs, Duplicate, ProxyCall, ProxyCombination},
test::TestContext,
test_name::TestName,
};
use std::{
any::{Any, TypeId},
default::Default,
ops::Deref,
sync::Arc,
};
#[derive(Debug, Clone)]
pub struct FixtureCreationError {
pub fixture_name: String,
pub error: Arc<dyn std::error::Error + Sync + Send>,
}
pub type FixtureCreationResult<T> = Result<T, FixtureCreationError>;
impl FixtureCreationError {
pub fn new<T>(fixture_name: &str, error: T) -> Self
where
T: std::error::Error + Sync + Send + 'static,
{
Self {
fixture_name: fixture_name.into(),
error: Arc::new(error),
}
}
}
pub trait FixtureProxy: Duplicate + TestName {
type Fixt: Fixture;
const SCOPE: FixtureScope;
fn setup(ctx: &mut TestContext) -> Vec<Self>
where
Self: Sized;
fn build(self) -> FixtureCreationResult<Self::Fixt>
where
Self: Sized;
}
pub trait Fixture: Deref<Target = Self::Type> {
type Type;
type Proxy: FixtureProxy<Fixt = Self>;
}
pub trait SubFixture: Fixture + 'static {}
impl<F> SubFixture for F where F: Fixture + 'static {}
#[derive(Copy, Clone)]
pub enum FixtureScope {
Once,
MatrixUnique,
Test,
Global,
}
#[derive(Default)]
pub(crate) struct FixtureRegistry {
pub fixtures: std::collections::HashMap<TypeId, Box<dyn Any>>,
}
impl FixtureRegistry {
pub(crate) fn new() -> Self {
Self {
fixtures: Default::default(),
}
}
pub(crate) fn add<B>(&mut self, value: Vec<B>)
where
B: FixtureProxy + 'static,
{
self.fixtures.insert(TypeId::of::<B>(), Box::new(value));
}
pub(crate) fn get<B>(&mut self) -> Option<Vec<B>>
where
B: FixtureProxy + 'static,
{
self.fixtures.get(&TypeId::of::<B>()).map(|a| {
let proxy = a.downcast_ref::<Vec<B>>().unwrap();
proxy.duplicate()
})
}
}
pub type TeardownFn<T> = Box<dyn Fn(&mut T) + Send + Sync>;
pub struct FixtureTeardown<T> {
value: T,
teardown: Option<TeardownFn<T>>,
}
impl<T> FixtureTeardown<T> {
pub fn new(value: T, teardown: Option<TeardownFn<T>>) -> Self {
Self { value, teardown }
}
}
impl<T> std::ops::Deref for FixtureTeardown<T> {
type Target = T;
fn deref(&self) -> &T {
&self.value
}
}
impl<T> Drop for FixtureTeardown<T> {
fn drop(&mut self) {
if let Some(t) = self.teardown.take() {
t(&mut self.value)
}
}
}
pub enum LazyValue<V, B> {
Value(SharedFixtureValue<V>),
Proxies(Option<ProxyCombination<B>>),
}
impl<V, B> From<ProxyCombination<B>> for LazyValue<V, B> {
fn from(b: ProxyCombination<B>) -> Self {
Self::Proxies(Some(b))
}
}
impl<V, B> LazyValue<V, B> {
pub fn get<F, T>(&mut self, f: F) -> FixtureCreationResult<SharedFixtureValue<V>>
where
F: Fn(CallArgs<T>) -> FixtureCreationResult<(V, Option<TeardownFn<V>>)>,
ProxyCombination<B>: ProxyCall<T>,
{
if let LazyValue::Proxies(b) = self {
let (value, teardown) = b.take().unwrap().call(f)?;
*self = LazyValue::Value(SharedFixtureValue::new(value, teardown));
};
match self {
LazyValue::Value(v) => Ok(v.clone()),
LazyValue::Proxies(_) => unreachable!(),
}
}
}
#[repr(transparent)]
#[doc(hidden)]
pub struct SharedFixtureValue<T>(Arc<FixtureTeardown<T>>);
impl<T> SharedFixtureValue<T> {
pub fn new(value: T, teardown: Option<TeardownFn<T>>) -> Self {
Self(Arc::new(FixtureTeardown { value, teardown }))
}
}
impl<T> Clone for SharedFixtureValue<T> {
fn clone(&self) -> Self {
Self(Arc::clone(&self.0))
}
}
impl<T> std::ops::Deref for SharedFixtureValue<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0
}
}