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::OwnedStatic, AnyTrait, Indirect, IndirectLtAny, LtTypeId, Mut, Ref, TypeNameable}, 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>;
trait MockIndirect: Send {
fn indirect(&self) -> IndirectLtAny<'_, 'static, Ref>;
fn indirect_mut(&mut self) -> IndirectLtAny<'_, 'static, Mut>;
}
impl<T: ?Sized + for<'a> TypeNameable<'a, 'static> + Send> MockIndirect for Box<T> {
fn indirect(&self) -> IndirectLtAny<'_, 'static, Ref> {
IndirectLtAny::new(self)
}
fn indirect_mut(&mut self) -> IndirectLtAny<'_, 'static, Mut> {
IndirectLtAny::new(self)
}
}
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>(
&'a mut self,
id: LtTypeId<'static>,
) -> Option<IndirectLtAny<'a, '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, OwnedStatic<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();
ctx.expect().once().returning(|_| {
let mut mock = MockBuilder::new();
mock.expect_lookup_mut().returning(|| {
let mut map = HashMap::<_, Box<dyn MockIndirect>>::new();
map.insert(
LtTypeId::of::<DynValue<'_, 'static, OwnedStatic<i32>, Blocking>>(),
Box::new(Box::new(MockValueVisitor::new()) as Box<DynValue<'_, 'static, OwnedStatic<i32>, Blocking>>)
);
map
});
mock.expect_private_build().once().returning(|| {
Ok(())
});
mock
});
let mut builder = Spin::block_on(<MockBuilder as Builder<Blocking>>::from_seed(()));
{
let visitor = <MockBuilder as Builder<Blocking>>::as_visitor(&mut builder);
let x = visitor.upcast_to_id_mut(LtTypeId::of::<DynValue<'_, '_, OwnedStatic<i32>, Blocking>>()).unwrap();
let Ok(x) = x.downcast::<DynValue<'_, '_, OwnedStatic<i32>, Blocking>>() else {
panic!();
};
Spin::block_on(x.visit(OwnedStatic(42)));
}
let x = <MockBuilder as Builder<Blocking>>::build(builder);
todo!();
}
}