Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -220,15 +220,20 @@ fn derive_blanket_impl(item_trait: &ItemTrait) -> syn::Result<TokenStream> {

let method_body = if signature.asyncness.is_some() {
quote! {
#matcher::<#computer_ident>::compute_async(
// Name the provider trait so the call stays unambiguous when the
// consumer trait `CanComputeAsync` is also in scope. The `_` arguments
// are inferred, which avoids naming the HRTB-only `'__a__` lifetime.
<#matcher<#computer_ident> as AsyncComputer<_, _, _>>::compute_async(
&(),
::core::marker::PhantomData::<()>,
#args,
).await
}
} else {
quote! {
#matcher::<#computer_ident>::compute(
// As above, qualified so that an imported `CanCompute` does not make
// the call ambiguous.
<#matcher<#computer_ident> as Computer<_, _, _>>::compute(
&(),
::core::marker::PhantomData::<()>,
#args,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
//! `#[cgp_auto_dispatch]` in a module that imports the computer consumer traits.
//!
//! The generated method calls the matcher through its provider trait, so importing
//! `CanCompute` and `CanComputeAsync` beside the prelude's `Computer` and
//! `AsyncComputer` leaves the call unambiguous for a synchronous method with an
//! argument and for an async `&self` method, both dispatched over `FooBar`.
//!
//! See cgp-knowledge-base/cgp/implementation/entrypoints/cgp_auto_dispatch.md.

// Only their presence in scope matters: the generated calls must not resolve to them.
#[allow(unused_imports)]
use cgp::extra::handler::{CanCompute, CanComputeAsync};
use cgp::prelude::*;
use futures::executor::block_on;

use super::types::{Bar, Foo, FooBar};

#[cgp_auto_dispatch]
pub trait CanGreet {
fn greet(&self, name: &str) -> String;
}

impl CanGreet for Foo {
fn greet(&self, name: &str) -> String {
format!("foo greets {name}")
}
}

impl CanGreet for Bar {
fn greet(&self, name: &str) -> String {
format!("bar greets {name}")
}
}

#[cgp_auto_dispatch]
#[async_trait]
pub trait CanCall {
async fn call(&self) -> &'static str;
}

impl CanCall for Foo {
async fn call(&self) -> &'static str {
"foo"
}
}

impl CanCall for Bar {
async fn call(&self) -> &'static str {
"bar"
}
}

#[test]
fn test_consumer_traits_in_scope() {
assert_eq!(FooBar::Bar(Bar).greet("ann"), "bar greets ann");
assert_eq!(block_on(FooBar::Foo(Foo).call()), "foo");
}
3 changes: 3 additions & 0 deletions crates/tests/cgp-tests/tests/dispatching/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ pub mod auto_dispatch_async_self_mut_only;
pub mod auto_dispatch_async_self_only;
pub mod auto_dispatch_async_self_ref_only;

// `#[cgp_auto_dispatch]` beside imported `CanCompute`/`CanComputeAsync`.
pub mod auto_dispatch_consumer_traits_in_scope;

// The `UseDelegate` dispatch provider and the `UseDelegate`-table form of
// `delegate_components!` (this concept owns those snapshots).
pub mod use_delegate_getter;
Expand Down
1 change: 1 addition & 0 deletions crates/tests/cgp-tests/tests/namespaces/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ pub mod multi_param_namespace;
pub mod multi_param_open;
pub mod namespace_group;
pub mod open_dispatch;
pub mod open_later_param;
pub mod prefix_default_namespace;
pub mod redirect_lookup;

Expand Down
150 changes: 150 additions & 0 deletions crates/tests/cgp-tests/tests/namespaces/open_later_param.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
//! `open` dispatch on a component's second type parameter alone.
//!
//! The redirect appends every type parameter to the lookup path, so a lookup of
//! `CanDescribe<Code, Input>` follows `@DescriberComponent.Code.Input`. A key whose
//! first segment is a per-entry generic, `@DescriberComponent.<Code> Code.u64`,
//! matches every `Code` and dispatches on the input, which is the `open` form of a
//! `UseInputDelegate` table. The test calls one context with two codes per input
//! and checks that the input alone selects the provider. The `delegate_components!`
//! snapshot pins the generic-first-segment expansion; the component and providers
//! are incidental scaffolding.
//!
//! See cgp-knowledge-base/cgp/implementation/entrypoints/delegate_components.md.

use core::marker::PhantomData;

use cgp::prelude::*;
use cgp_macro_test_util::snapshot_delegate_components;

// Incidental: a component with a code parameter and an input parameter.
#[cgp_component(Describer)]
pub trait CanDescribe<Code, Input> {
fn describe(&self, code: PhantomData<Code>, input: Input) -> String;
}

// Incidental: one provider per input type, each generic over the code.
#[cgp_impl(new DescribeNumber)]
impl<Code> Describer<Code, u64> {
fn describe(&self, _code: PhantomData<Code>, input: u64) -> String {
format!("number {input}")
}
}

#[cgp_impl(new DescribeText)]
impl<Code> Describer<Code, String> {
fn describe(&self, _code: PhantomData<Code>, input: String) -> String {
format!("text {input}")
}
}

pub struct Short;

pub struct Long;

pub struct App;

snapshot_delegate_components! {
delegate_components! {
App {
open DescriberComponent;

@DescriberComponent.<Code> Code.u64:
DescribeNumber,
@DescriberComponent.<Code> Code.String:
DescribeText,
}
}

expand_open_later_param(output) {
insta::assert_snapshot!(output, @"
impl DelegateComponent<DescriberComponent> for App {
type Delegate = RedirectLookup<App, PathCons<DescriberComponent, Nil>>;
}
impl<__Context__, __Params__> IsProviderFor<DescriberComponent, __Context__, __Params__>
for App
where
RedirectLookup<
App,
PathCons<DescriberComponent, Nil>,
>: IsProviderFor<DescriberComponent, __Context__, __Params__>,
{}
impl<
Code,
__Wildcard__,
> DelegateComponent<
PathCons<DescriberComponent, PathCons<Code, PathCons<u64, __Wildcard__>>>,
> for App {
type Delegate = DescribeNumber;
}
impl<
Code,
__Wildcard__,
__Context__,
__Params__,
> IsProviderFor<
PathCons<DescriberComponent, PathCons<Code, PathCons<u64, __Wildcard__>>>,
__Context__,
__Params__,
> for App
where
DescribeNumber: IsProviderFor<
PathCons<DescriberComponent, PathCons<Code, PathCons<u64, __Wildcard__>>>,
__Context__,
__Params__,
>,
{}
impl<
Code,
__Wildcard__,
> DelegateComponent<
PathCons<DescriberComponent, PathCons<Code, PathCons<String, __Wildcard__>>>,
> for App {
type Delegate = DescribeText;
}
impl<
Code,
__Wildcard__,
__Context__,
__Params__,
> IsProviderFor<
PathCons<DescriberComponent, PathCons<Code, PathCons<String, __Wildcard__>>>,
__Context__,
__Params__,
> for App
where
DescribeText: IsProviderFor<
PathCons<DescriberComponent, PathCons<Code, PathCons<String, __Wildcard__>>>,
__Context__,
__Params__,
>,
{}
")
}
}

check_components! {
App {
DescriberComponent: [
(Short, u64),
(Long, u64),
(Short, String),
(Long, String),
],
}
}

#[test]
fn test_open_later_param() {
let app = App;

assert_eq!(app.describe(PhantomData::<Short>, 7), "number 7");
assert_eq!(app.describe(PhantomData::<Long>, 7), "number 7");
assert_eq!(
app.describe(PhantomData::<Short>, "hi".to_owned()),
"text hi"
);
assert_eq!(
app.describe(PhantomData::<Long>, "hi".to_owned()),
"text hi"
);
}
Loading