pub mod builders;
use crate::{
effect::{Effect, Future},
protocol::Visitor,
};
/// A buildable type.
pub trait Build<'ctx, M, E: Effect<'ctx>>: BuilderTypes<Value = Self> + Send {
/// The builder that can be used to build a value of `Self`.
type Builder: Builder<'ctx, E, Seed = Self::Seed, Error = Self::Error, Value = Self>;
}
pub trait BuilderTypes {
type Seed: Send;
/// Error that can happen during filling the builder with data.
type Error: Send;
/// Type to be built.
type Value: Send;
}
/// Builder for a type.
///
/// The `'ctx` lifetime is some lifetime that is longer than the walker.
/// As such, the built value can borrow from other data with a `'ctx` lifetimes.
///
/// A builder allows creating a value of a type [`Self::Value`].
/// The way to use a builder is as follows.
/// - Call [`Default::default()`] to create an instance of the builder.
/// - Call [`Self::as_visitor()`] and give it to a walker's
/// [`walk()`][crate::walk::Walker::walk]. The walker will then fill
/// the builder with data from it's walk.
/// - Call [`Self::build()`] to finish building the value and get any errors
/// that happened during filling it with data.
pub trait Builder<'ctx, E: Effect<'ctx>>: BuilderTypes + Sized + Send {
fn from_seed<'a>(seed: Self::Seed) -> Future<'a, 'ctx, Self, E>
where
Self: 'a;
/// Finish the value.
///
/// If an error happened with the builder during the walk
/// it will be reported here.
fn build<'a>(self) -> Future<'a, 'ctx, Result<Self::Value, Self::Error>, E>
where
Self: 'a;
/// Get the builder as a visitor that a walker can use.
///
/// This is expected to just be `self`.
fn as_visitor(&mut self) -> Visitor<'_, 'ctx>;
}
#[cfg(test)]
pub mod test {
use std::{
collections::HashMap,
sync::{Mutex, MutexGuard, PoisonError},
};
use crate::{
any::{
static_wrapper::{DynOwnedStatic, OwnedStatic},
AnyTrait, Boxed, Indirect, IndirectLtAny, LtTypeId, MaybeSized, Mut, Ref, TypeName,
},
effect::{BlockOn, Blocking, Spin},
protocol::visitor::value::{DynValue, Value},
Flow,
};
use super::*;
use mockall::mock;
use mockall::predicate::eq;
pub mod mock {
use std::collections::HashMap;
use super::*;
mock! {
pub Builder<Seed: 'static, Value: 'static, Error: 'static> {
pub fn private_from_seed(seed: Seed) -> Self;
pub fn private_build(self) -> Result<Value, Error>;
// pub fn private_upcast_to_id(&self, id: LtTypeId<'static>) -> Option<Box<dyn for<'a> MockIndirect>>;
// pub fn private_upcast_to_id_mut(&mut self, id: LtTypeId<'static>) -> Option<Box<dyn for<'a> MockIndirect<'a, Mut>>>;
pub fn lookup(&self) -> &HashMap<LtTypeId<'static>, Box<dyn MockIndirect>>;
pub fn lookup_mut(&mut self) -> &mut HashMap<LtTypeId<'static>, Box<dyn MockIndirect>>;
}
}
}
pub type MockBuilder<Seed = (), Value = (), Error = ()> = mock::MockBuilder<Seed, Value, Error>;
pub trait MockIndirect: Send {
fn indirect(&self) -> IndirectLtAny<'_, 'static, Ref>;
fn indirect_mut<'a: 'b, 'b>(&'a mut self) -> IndirectLtAny<'b, 'static, Mut>;
}
impl MockIndirect for MockValueVisitor {
fn indirect(&self) -> IndirectLtAny<'_, 'static, Ref> {
IndirectLtAny::new::<DynValue<'static, DynOwnedStatic<'static, i32>, Blocking>>(
self as _,
)
}
fn indirect_mut<'a: 'b, 'b>(&'a mut self) -> IndirectLtAny<'b, 'static, Mut> {
IndirectLtAny::<'b, 'static, Mut>::new::<
DynValue<'static, DynOwnedStatic<'static, i32>, Blocking>,
>(self as _)
}
}
// pub struct IndirectBox<'a, 'ctx>(IndirectLtAny<'a, 'ctx, Boxed>);
//
// impl MockIndirect for IndirectBox
// {
// fn indirect<'a>(&'a self) -> IndirectLtAny<'a, 'static, Ref> {
// // let x: &MaybeSized::T<'a, 'static, T> = &*self.0;
// // IndirectLtAny::<'a, 'static, Ref>::new::<T>(x)
// todo!()
// }
//
// fn indirect_mut<'a>(&'a mut self) -> IndirectLtAny<'a, 'static, Mut> {
// let x: &'a mut MaybeSized::T<'static, 'static, T> = &mut *self.0;
// IndirectLtAny::<'static, 'static, Mut>::new::<T>(x)
// }
// }
impl<Seed: Send, Value: Send, Error: Send> BuilderTypes for MockBuilder<Seed, Value, Error> {
type Seed = Seed;
type Error = Error;
type Value = Value;
}
impl<Seed, Value, Error> MockBuilder<Seed, Value, Error> {
pub fn lock_context<'a>() -> MutexGuard<'a, ()> {
static LOCK: Mutex<()> = Mutex::new(());
LOCK.lock().unwrap_or_else(PoisonError::into_inner)
}
}
impl<Seed: Send, Value: Send, Error: Send, E: Effect<'static>> Builder<'static, E>
for MockBuilder<Seed, Value, Error>
{
fn from_seed<'a>(seed: Self::Seed) -> Future<'a, 'static, Self, E>
where
Self: 'a,
{
E::ready(Self::private_from_seed(seed))
}
fn build<'a>(self) -> Future<'a, 'static, Result<Self::Value, Self::Error>, E>
where
Self: 'a,
{
E::ready(self.private_build())
}
fn as_visitor(&mut self) -> Visitor<'_, 'static> {
self
}
}
impl<Seed, Value, Error> AnyTrait<'static> for MockBuilder<Seed, Value, Error> {
fn upcast_to_id<'a>(
&'a self,
id: LtTypeId<'static>,
) -> Option<IndirectLtAny<'a, 'static, Ref>>
where
'static: 'a,
{
self.lookup().get(&id).map(|x| x.indirect())
}
fn upcast_to_id_mut<'a: 'b, 'b>(
&'a mut self,
id: LtTypeId<'static>,
) -> Option<IndirectLtAny<'b, 'static, Mut>>
where
'static: 'a,
{
self.lookup_mut().get_mut(&id).map(|x| x.indirect_mut())
}
}
mock! {
ValueVisitor {
fn private_visit(&mut self, value: OwnedStatic<i32>) -> Flow;
}
}
impl<'ctx, E: Effect<'ctx>> Value<'ctx, DynOwnedStatic<'ctx, i32>, E> for MockValueVisitor {
fn visit<'a>(&'a mut self, value: OwnedStatic<i32>) -> Future<'a, 'ctx, Flow, E> {
E::ready(self.private_visit(value))
}
}
#[test]
fn demo2() {
let _lock = MockBuilder::<(), (), ()>::lock_context();
let ctx = MockBuilder::<(), (), ()>::private_from_seed_context();
// Expect one mock builder to be made from the from_seed static method;
ctx.expect().once().returning(|_| {
let mut mock = MockBuilder::new();
// Expect that we will lookup one trait on the visitor.
mock.expect_lookup_mut().once().returning(|| {
let mut map = HashMap::<LtTypeId<'static>, Box<dyn MockIndirect>>::new();
let mut mock = MockValueVisitor::new();
// Expect that we will pass the value 42 to the visitor.
mock.expect_private_visit()
.once()
.with(eq(OwnedStatic(42)))
.return_const(Flow::Done);
// This mock will be for the value protocol.
map.insert(
LtTypeId::of::<DynValue<'static, DynOwnedStatic<'static, i32>, Blocking>>(),
Box::new(mock),
);
map
});
mock.expect_private_build().once().returning(|| Ok(()));
mock
});
// Create a mock builder using the static from_seed method.
let mut builder = Spin::block_on(<MockBuilder as Builder<Blocking>>::from_seed(()));
{
// Get the mock builder as a visitor.
let visitor = <MockBuilder as Builder<Blocking>>::as_visitor(&mut builder);
// Upcast the mock visitor into a trait object for the value protocol.
// This resolves to one of the values in the hashmap above and not the builder itself.
let x = visitor
.upcast_to_id_mut(LtTypeId::of::<
DynValue<'_, DynOwnedStatic<'_, i32>, Blocking>,
>())
.unwrap();
// Finish the upcast by downcasting to the trait object.
let Ok(x) = x.downcast::<DynValue<'_, DynOwnedStatic<'_, i32>, Blocking>>() else {
panic!();
};
// Use the visit method on the mock visitor.
assert_eq!(Spin::block_on(x.visit(OwnedStatic(42))), Flow::Done);
}
// Finish building the mock builder.
assert_eq!(
Spin::block_on(<MockBuilder as Builder<Blocking>>::build(builder)).unwrap(),
()
);
}
}