use std::any::TypeId;
use common::protocol::hint::MockHintWalker;
use treaty::{
any::TypeNameId,
effect::{
blocking::{Blocking, Spin},
Effect, Effective, ErasedEffective,
},
hkt::higher_ranked_type,
protocol::{
walker::hint::{self, Hint, HintMeta, HintProto, Meta, MetaHint, MetaKnown},
DynVisitor,
},
Flow,
};
use crate::common::{builder::MockBuilder, protocol::hint::KnownFactory};
mod common;
/// This tests for the hint protocol being able to give the known info and being able to hint.
#[test]
fn can_get_known_and_hint() {
// The protocol we will hint for.
struct MyProtocol;
// The known value for the protocol.
#[derive(Debug, PartialEq)]
struct Known(f32);
// Link the higher ranked type with the known type.
higher_ranked_type! {
impl Meta {
impl['a, 'ctx] type T['a, 'ctx] for Known = Known;
impl['a, 'ctx] type HigherRanked['a, 'ctx] for Known = Known;
}
}
#[derive(Debug, PartialEq)]
struct Hint(i32);
higher_ranked_type! {
impl Meta {
impl['a, 'ctx] type T['a, 'ctx] for Hint = Hint;
impl['a, 'ctx] type HigherRanked['a, 'ctx] for Hint = Hint;
}
}
// Enroll the protocol in the hint system.
impl HintMeta for MyProtocol {
type Known = Known;
type Hint = Hint;
type Effect = Blocking;
}
let mut mock = MockHintWalker::<MyProtocol>::new();
mock.expect_known()
.once()
.return_const((|_, Hint(hint)| Ok(Known(*hint as f32))) as KnownFactory<MyProtocol>);
mock.expect_hint()
.once()
.withf(|_visitor, Hint(hint)| *hint == 123)
.return_const(Flow::Done);
// Get the mock as a hint protocol trait object.
let walker: &mut dyn hint::Hint<MyProtocol> = &mut mock;
// We can call known to get what the walker knows about the protocol.
assert_eq!(walker.known(&Hint(42)).value(), Ok(Known(42.0)));
{
// A hint needs the visitor for the walker to walk.
let mut mock = MockBuilder::<(), (), ()>::new();
// We can call hint to "commit" to the protocol and ask the walker to use it.
assert_eq!(
walker.hint(DynVisitor(&mut mock), Hint(123)).value(),
Flow::Done
);
}
}
#[test]
fn known_can_have_temp_mutable_borrow() {
struct MyProtocol;
struct KnownHrt;
#[derive(Debug, PartialEq)]
struct Known<'a>(&'a mut String);
higher_ranked_type! {
impl Meta {
impl['a, 'ctx] type T['a, 'ctx] for KnownHrt = Known<'a>;
impl['a, 'ctx] type HigherRanked['a, 'ctx] for Known<'a> = KnownHrt;
}
}
impl HintMeta for MyProtocol {
type Known = KnownHrt;
type Hint = ();
type Effect = Blocking;
}
struct Walker<'ctx>(&'ctx mut String);
impl<'ctx> Hint<'ctx, MyProtocol> for Walker<'ctx> {
fn hint<'this, 'visitor, 'hint, 'e>(
&'this mut self,
visitor: DynVisitor<'visitor, 'ctx>,
hint: MetaHint<'hint, 'ctx, MyProtocol>,
) -> ErasedEffective<'e, Flow, Blocking>
where
'ctx: 'this + 'visitor + 'hint + 'e,
{
unreachable!()
}
fn known<'a>(
&'a mut self,
(): &'a <MyProtocol as HintMeta>::Hint,
) -> ErasedEffective<'a, Result<MetaKnown<'a, 'ctx, MyProtocol>, ()>, Blocking> {
self.0.push_str("test");
Blocking::<Spin>::ready(Ok(Known(self.0)))
}
}
let mut context = String::new();
{
let mut walker = Walker(&mut context);
// Get the mock as a hint protocol trait object.
let walker: &mut dyn Hint<MyProtocol> = &mut walker;
// We can call known to get what the walker knows about the protocol.
let mut x = String::from("test");
assert_eq!(walker.known(&()).value(), Ok(Known(&mut x)));
}
drop(context);
}
#[test]
fn known_can_have_context_borrow() {
struct MyProtocol;
struct KnownHrt;
#[derive(Debug, PartialEq)]
struct Known<'ctx>(&'ctx String);
higher_ranked_type! {
impl Meta {
impl['a, 'ctx] type T['a, 'ctx] for KnownHrt = Known<'ctx>;
impl['a, 'ctx] type HigherRanked['a, 'ctx] for Known<'ctx> = KnownHrt;
}
}
impl HintMeta for MyProtocol {
type Known = KnownHrt;
type Hint = ();
type Effect = Blocking;
}
struct Walker<'ctx>(&'ctx String);
impl<'ctx> Hint<'ctx, MyProtocol> for Walker<'ctx> {
fn hint<'this, 'visitor, 'hint, 'e>(
&'this mut self,
visitor: DynVisitor<'visitor, 'ctx>,
hint: MetaHint<'hint, 'ctx, MyProtocol>,
) -> ErasedEffective<'e, Flow, Blocking>
where
'ctx: 'this + 'visitor + 'hint + 'e,
{
unreachable!()
}
fn known<'a>(
&'a mut self,
(): &'a <MyProtocol as HintMeta>::Hint,
) -> ErasedEffective<'a, Result<MetaKnown<'a, 'ctx, MyProtocol>, ()>, Blocking> {
Blocking::<Spin>::ready(Ok(Known(self.0)))
}
}
let context = String::from("test");
let ctx = {
let mut walker = Walker(&context);
// Get the mock as a hint protocol trait object.
let walker: &mut dyn Hint<MyProtocol> = &mut walker;
// We can call known to get what the walker knows about the protocol.
let Ok(Known(y)) = walker.known(&()).value() else {
unreachable!()
};
y
};
assert_eq!(ctx, "test");
drop(context);
}
#[test]
fn hint_can_have_temp_mutable_borrow() {
struct MyProtocol;
struct HintHrt;
#[derive(Debug, PartialEq)]
struct Hint<'a>(&'a mut String);
higher_ranked_type! {
impl Meta {
impl['a, 'ctx] type T['a, 'ctx] for HintHrt = Hint<'a>;
impl['a, 'ctx] type HigherRanked['a, 'ctx] for Hint<'a> = HintHrt;
}
}
impl HintMeta for MyProtocol {
type Known = ();
type Hint = HintHrt;
type Effect = Blocking;
}
let mut mock = MockHintWalker::new();
mock.expect_hint().once().returning(|_, hint: Hint| {
hint.0.push_str("test");
Flow::Done
});
{
// Get the mock as a hint protocol trait object.
let walker: &mut dyn hint::Hint<MyProtocol> = &mut mock;
let mut visitor = MockBuilder::<(), (), ()>::new();
let mut temp = String::new();
// We can call known to get what the walker knows about the protocol.
assert_eq!(
walker
.hint(DynVisitor(&mut visitor), Hint(&mut temp))
.value(),
Flow::Done
);
assert_eq!(temp, "test");
}
}
#[test]
fn hint_can_have_context_borrow() {
struct MyProtocol;
struct HintHrt;
#[derive(Debug, PartialEq)]
struct Hint<'ctx>(&'ctx String);
higher_ranked_type! {
impl Meta {
impl['a, 'ctx] type T['a, 'ctx] for HintHrt = Hint<'ctx>;
impl['a, 'ctx] type HigherRanked['a, 'ctx] for Hint<'ctx> = HintHrt;
}
}
impl HintMeta for MyProtocol {
type Known = ();
type Hint = HintHrt;
type Effect = Blocking;
}
let mut mock = MockHintWalker::new();
mock.expect_hint().once().returning(|_, hint: Hint| {
assert_eq!(hint.0, "test");
Flow::Done
});
let context = String::from("test");
{
// Get the mock as a hint protocol trait object.
let walker: &mut dyn hint::Hint<MyProtocol> = &mut mock;
let mut visitor = MockBuilder::<(), (), ()>::new();
// We can call known to get what the walker knows about the protocol.
assert_eq!(
walker
.hint(DynVisitor(&mut visitor), Hint(&context))
.value(),
Flow::Done
);
}
drop(context);
}
#[test]
fn hint_proto() {
struct MyProtocol;
impl HintMeta for MyProtocol {
type Known = ();
type Hint = ();
type Effect = Blocking;
}
// The type id of the higher ranked type.
let id = TypeId::of::<HintProto<MyProtocol>>();
// The type id for the lifetime containing value protocol trait object.
let name_id = TypeNameId::of_lower::<dyn hint::Hint<MyProtocol> + Send + Sync>();
// They should be the same.
assert_eq!(id, name_id.into_type_id());
}