diff --git a/Cargo.lock b/Cargo.lock index 2fa9c205..aacb84e3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -239,7 +239,9 @@ version = "0.7.0" dependencies = [ "cgp", "cgp-macro-core", + "cgp-macro-lib", "cgp-macro-test-util", + "insta", "proc-macro2", "quote", "syn", @@ -267,6 +269,21 @@ dependencies = [ "cgp-core", ] +[[package]] +name = "cgp-test-crate-a" +version = "0.7.0" +dependencies = [ + "cgp", +] + +[[package]] +name = "cgp-test-crate-b" +version = "0.7.0" +dependencies = [ + "cgp", + "cgp-test-crate-a", +] + [[package]] name = "cgp-tests" version = "0.7.0" diff --git a/Cargo.toml b/Cargo.toml index 7a3f0b0b..4e206d33 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,6 +38,8 @@ members = [ "crates/tests/cgp-tests", "crates/tests/cgp-macro-tests", + "crates/tests/cgp-test-crate-a", + "crates/tests/cgp-test-crate-b", ] [workspace.package] @@ -74,6 +76,9 @@ cgp-macro = { version = "0.7.0", path = "./crates/macros/cgp-m cgp-macro-core = { version = "0.7.0", path = "./crates/macros/cgp-macro-core" } cgp-macro-lib = { version = "0.7.0", path = "./crates/macros/cgp-macro-lib" } cgp-macro-test-util = { version = "0.7.0", path = "./crates/macros/cgp-macro-test-util" } + +cgp-test-crate-a = { version = "0.7.0", path = "./crates/tests/cgp-test-crate-a" } +cgp-test-crate-b = { version = "0.7.0", path = "./crates/tests/cgp-test-crate-b" } cgp-macro-test-util-lib = { version = "0.7.0", path = "./crates/macros/cgp-macro-test-util-lib" } cgp-extra-macro = { version = "0.7.0", path = "./crates/macros/cgp-extra-macro" } cgp-extra-macro-lib = { version = "0.7.0", path = "./crates/macros/cgp-extra-macro-lib" } diff --git a/crates/tests/CLAUDE.md b/crates/tests/CLAUDE.md new file mode 100644 index 00000000..2f417ee3 --- /dev/null +++ b/crates/tests/CLAUDE.md @@ -0,0 +1,153 @@ +# CLAUDE.md — maintaining the CGP test suite + +This file governs the test crates under `crates/tests`. Read it before adding, +moving, or refactoring any test here. Invoke the `/cgp` skill first — every test +in this tree is CGP code, and the skill is the authoritative source for CGP +semantics and vocabulary. + +The test suite has three jobs, split across crates: + +- **`cgp-tests`** is the main suite: realistic example code that must **compile and + run**. A passing test is often just successful compilation, because much of CGP + is compile-time wiring. This is where behavior is verified and where the + user-facing macros are exercised end-to-end. +- **`cgp-macro-tests`** tests the **internals** of the CGP macros by calling the + functions in `cgp-macro-core` directly (parsers, AST types), and is the home for + **failure cases** — inputs CGP should reject, and cases where a macro currently + emits invalid or wrong code. +- **`cgp-test-crate-a` / `cgp-test-crate-b`** are auxiliary packages for + **cross-crate** behavior: whether a downstream crate can extend a namespace or + provide a provider for a component defined elsewhere, under Rust's coherence and + orphan rules. + +## Organize by concept, not by construct + +Group tests by the **CGP concept or feature** under test, never by the macro that +happens to appear. A single construct such as `delegate_components!` serves many +concepts — basic delegation, `open` dispatch, namespace headers, `UseDelegate` +tables — so a bucket named after the construct mixes unrelated concerns and hides +what is actually being verified. Name each group for the concept: `basic_delegation`, +`abstract_types`, `implicit_arguments`, `namespaces`, `higher_order_providers`, and +so on. + +The right granularity is driven by the feature, its implementation complexity, and +how many cases are needed to cover it exhaustively — **not** by mirroring the +concept documents under `docs/concepts/`. The names may coincide, but the split is +chosen for coverage. **When a category accumulates too many test cases to stay +coherent, split it into finer categories** rather than letting it sprawl; prefer +splitting early. + +## A test target is a "sub-crate" + +Each concept is one **integration test target**, which Cargo compiles as its own +crate — so each concept has its own coherence scope, exactly like a separate crate. +A target is two things: + +- an **entrypoint file** `tests/_tests.rs` — the `_tests` suffix marks it + as the target root; it carries a module doc comment, `#![allow(dead_code)]` when + the target is mostly compile-time wiring, and a single `pub mod ;`; +- a **module directory** `tests//` — the clean concept name — whose + `mod.rs` lists the unit-test modules, one `pub mod` per file. + +`basic_delegation` is the reference implementation of this layout — copy its shape +when adding a concept. + +## One unit test per file + +Put each unit test in its own `.rs` file under the concept directory, and make the +file **self-contained**: define its own components, providers, and context types at +module scope. Do **not** separate unrelated units with `#[test]` functions or nested +`mod`s inside one file. CGP tests are dominated by type-level constructs and +compile-time wiring that live at module scope and cannot be isolated by a function +boundary; separate files are the only reliable isolation within a target. A file may +still contain a `#[test]` fn for its runtime assertions, plus the module-scope items +that test exercises. + +## Explain what each test covers + +Open every test file with a brief comment stating **what behavior it exercises**, +and annotate individual tricky cases inline. Where it helps a reader, link to the +owning reference document (for example `// see docs/reference/macros/cgp_impl.md`). +Tests link **to** the documentation; the documentation never links back to a test +(per `docs/CLAUDE.md`). + +## Use macro snapshots sparingly + +`cgp-macro-test-util` provides `snapshot_*!` macros (`snapshot_cgp_component!`, +`snapshot_cgp_impl!`, `snapshot_delegate_components!`, …). Each **emits the real +generated code** into the module *and* generates a `#[test]` that asserts a +pretty-printed inline `insta` snapshot of it — so adding or removing a snapshot +never changes the compile/runtime coverage, only the golden assertion. Always keep +the snapshot string **inline** in the file (`@"…"`). + +The rule for when to snapshot: **snapshot a macro only in the concept target that +owns that macro's feature; everywhere else invoke the macro plainly.** Concretely, +each macro has one canonical full-expansion snapshot (plus snapshots for its +genuinely distinct variants) in its owning target, and nowhere else: + +| Macro | Owning target(s) | +| --- | --- | +| `#[cgp_component]` | `basic_delegation` (+ generic variant in `generic_components`) | +| `#[cgp_impl]` | `basic_delegation` (+ `higher_order_providers`, `implicit_arguments` variants) | +| `#[cgp_type]` | `abstract_types` | +| `#[cgp_getter]` / `#[cgp_auto_getter]` | `getters` | +| `#[cgp_fn]` | `implicit_arguments`, `impl_side_dependencies` | +| `delegate_components!` | `basic_delegation` (basic), `namespaces` (open/namespace), `dispatching` (`UseDelegate`) | +| `check_components!` / `delegate_and_check_components!` | `checking` | +| `cgp_namespace!` | `namespaces` | +| `#[blanket_trait]` | `blanket_traits` | +| `#[derive(HasField)]` / `HasFields` / `CgpData` | `field_access` / `extensible_records` / `extensible_variants` | + +When a file uses one of these macros as **incidental scaffolding** — a +`#[cgp_component]` needed to set up a `delegate_components!` test, say — write the +plain macro, not the snapshot form. The expansion is already pinned in the owning +target, and a redundant snapshot only adds golden output that breaks on unrelated +macro changes. + +## Adding a failure case (in `cgp-macro-tests`) + +CGP will have corner cases it does not yet handle. Do **not** try to fix them inline +while refactoring; capture them as failing-behavior tests instead, in a dedicated +failure-case target: + +- **Input that should be rejected** — assert the `cgp-macro-core` parser rejects it, + using the `assert_rejects` helper pattern (see `ident_with_type_params`). +- **A macro that emits invalid Rust** — capture the expanded code as an `insta` + inline snapshot (the snapshot is a *string*, so it compiles even though the code + would not), and add a code comment explaining **why** the output is wrong and + **what the correct output should be**. + +Every failure case must also be recorded in the reference document that owns the +construct, under its `## Known issues` section (the heading `docs/CLAUDE.md` +mandates), describing the behavior without referring to the test. Put a link from +the test's comment to that reference document. + +## Keep the docs in sync + +This suite is one of the four views of CGP's truth, alongside the macro +implementation in `cgp-macro-core`, the reference documents in `docs/reference`, and +the `/cgp` skill (see `docs/CLAUDE.md`). When a test reveals or pins a behavior +worth documenting, update the reference document to explain that behavior directly — +without referring to the test. When you move a test that a reference document's +`## Source` section links to, update the link in the same change. + +## Running the suite + +``` +cargo nextest run -p cgp-tests # the main suite +cargo nextest run -p cgp-macro-tests # macro internals + failures +cargo nextest run --workspace # everything + +cargo insta test -p cgp-tests --review # review snapshot diffs +cargo insta test -p cgp-tests --accept # accept intended snapshot changes +``` + +A snapshot test that fails prints a diff of the generated code; accept it with +`cargo insta` only after confirming the change is intended. + +## Migration status + +The suite was reorganized from a by-construct layout to this by-concept layout. As +categories grow, keep splitting them per the rule above, and keep expanding failure +coverage in `cgp-macro-tests` and cross-crate coverage in the `cgp-test-crate-*` +packages — these were established with representative cases and are meant to grow. diff --git a/crates/tests/README.md b/crates/tests/README.md new file mode 100644 index 00000000..71e72372 --- /dev/null +++ b/crates/tests/README.md @@ -0,0 +1,72 @@ +# CGP test suite + +This directory holds the test suite for Context-Generic Programming. The tests +are organized **by CGP concept** — basic delegation, abstract types, implicit +arguments, namespaces, and so on — rather than by the macro that implements each +concept, because a single macro (for example `delegate_components!`) serves many +concepts at once. If you are maintaining or extending the suite, read +[CLAUDE.md](CLAUDE.md) first; it is the authoritative guide to the conventions. +This README is the map. + +## The crates + +The suite is split into three kinds of crate, each with a distinct job. + +**`cgp-tests`** is the main suite: realistic example code that must compile and +run. Because much of CGP is compile-time wiring, a test here often passes simply +by compiling. It is also where the user-facing macros are exercised end-to-end and +where the canonical macro-expansion snapshots live. + +**`cgp-macro-tests`** tests the macro internals directly against `cgp-macro-core` +(the parsers and AST types), and is the home for **failure cases** — inputs CGP +should reject, and cases where a macro currently emits invalid code. + +**`cgp-test-crate-a`** and **`cgp-test-crate-b`** are auxiliary packages for +**cross-crate** behavior. Crate A defines components, a provider, and a namespaced +component; crate B (a downstream crate) wires them, supplies its own provider for a +foreign component, and participates in crate A's namespace — showing that CGP stays +within Rust's coherence and orphan rules across crate boundaries. + +## How the tests are laid out + +Inside `cgp-tests`, each concept is one **integration test target**, which Cargo +compiles as its own crate (its own coherence scope). A target is an entrypoint file +`tests/_tests.rs` plus a module directory `tests//` holding one +`.rs` file per unit test. Each unit-test file is self-contained: it defines its own +components, providers, and context types at module scope, so the type-level wiring +of one test never leaks into another. `tests/basic_delegation/` is the reference +example of this layout. + +The concept targets currently cover: basic delegation, impl-side dependencies, +implicit arguments, higher-order providers, generic components, abstract types, +getters, field access, extensible records, extensible variants, checking, +dispatching, namespaces, handlers, monadic handlers, async and Send bounds, and +blanket traits. This set grows and subdivides over time — when a concept +accumulates too many cases to stay coherent, it is split into finer targets. + +`cgp-macro-tests` follows the same target/`_tests.rs` shape: `ident_with_type_params` +for parser corner cases, and the failure-case targets `parser_rejections` and +`invalid_expansion`. + +## Snapshots + +Many tests assert the exact code a macro generates, using the `snapshot_*!` macros +from `cgp-macro-test-util`. Each such macro emits the real generated code into the +module **and** generates a `#[test]` asserting a pretty-printed inline `insta` +snapshot of it. Snapshots are used deliberately: a macro's expansion is snapshotted +only in the concept target that owns that macro's feature, and written plainly +everywhere else (see [CLAUDE.md](CLAUDE.md) for the ownership rules). + +## Running the tests + +``` +cargo nextest run -p cgp-tests # the main suite +cargo nextest run -p cgp-macro-tests # macro internals + failure cases +cargo nextest run --workspace # everything, including the aux crates + +cargo insta test -p cgp-tests --review # review snapshot diffs interactively +cargo insta test -p cgp-tests --accept # accept intended snapshot changes +``` + +When a snapshot test fails it prints a diff of the generated code; accept the new +output with `cargo insta` only after confirming the change is intended. diff --git a/crates/tests/cgp-macro-tests/Cargo.toml b/crates/tests/cgp-macro-tests/Cargo.toml index 58d584bc..56de4c4e 100644 --- a/crates/tests/cgp-macro-tests/Cargo.toml +++ b/crates/tests/cgp-macro-tests/Cargo.toml @@ -11,9 +11,11 @@ keywords = { workspace = true } [dependencies] cgp = { workspace = true } cgp-macro-test-util = { workspace = true } +insta = { version = "1.48.0" } [dev-dependencies] cgp-macro-core = { workspace = true } +cgp-macro-lib = { workspace = true } syn = { version = "2.0.95" } quote = { version = "1.0.38" } proc-macro2 = { version = "1.0.92" } diff --git a/crates/tests/cgp-macro-tests/tests/ident_with_type_params.rs b/crates/tests/cgp-macro-tests/tests/ident_with_type_params.rs deleted file mode 100644 index b8a9f0d1..00000000 --- a/crates/tests/cgp-macro-tests/tests/ident_with_type_params.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod ident_with_type_params_tests; diff --git a/crates/tests/cgp-macro-tests/tests/ident_with_type_params_tests/mod.rs b/crates/tests/cgp-macro-tests/tests/ident_with_type_params/mod.rs similarity index 100% rename from crates/tests/cgp-macro-tests/tests/ident_with_type_params_tests/mod.rs rename to crates/tests/cgp-macro-tests/tests/ident_with_type_params/mod.rs diff --git a/crates/tests/cgp-macro-tests/tests/ident_with_type_params_tests/new_ident_with_type_args.rs b/crates/tests/cgp-macro-tests/tests/ident_with_type_params/new_ident_with_type_args.rs similarity index 100% rename from crates/tests/cgp-macro-tests/tests/ident_with_type_params_tests/new_ident_with_type_args.rs rename to crates/tests/cgp-macro-tests/tests/ident_with_type_params/new_ident_with_type_args.rs diff --git a/crates/tests/cgp-macro-tests/tests/ident_with_type_params_tests/new_ident_with_type_generics.rs b/crates/tests/cgp-macro-tests/tests/ident_with_type_params/new_ident_with_type_generics.rs similarity index 100% rename from crates/tests/cgp-macro-tests/tests/ident_with_type_params_tests/new_ident_with_type_generics.rs rename to crates/tests/cgp-macro-tests/tests/ident_with_type_params/new_ident_with_type_generics.rs diff --git a/crates/tests/cgp-macro-tests/tests/ident_with_type_params_tests/path_with_type_args.rs b/crates/tests/cgp-macro-tests/tests/ident_with_type_params/path_with_type_args.rs similarity index 100% rename from crates/tests/cgp-macro-tests/tests/ident_with_type_params_tests/path_with_type_args.rs rename to crates/tests/cgp-macro-tests/tests/ident_with_type_params/path_with_type_args.rs diff --git a/crates/tests/cgp-macro-tests/tests/ident_with_type_params_tests.rs b/crates/tests/cgp-macro-tests/tests/ident_with_type_params_tests.rs new file mode 100644 index 00000000..ecf3a01e --- /dev/null +++ b/crates/tests/cgp-macro-tests/tests/ident_with_type_params_tests.rs @@ -0,0 +1,8 @@ +//! Entrypoint for parser corner-case tests of the `IdentWithTypeArgs` / +//! `IdentWithTypeGenerics` / `PathWithTypeArgs` grammars in `cgp-macro-core`. +//! +//! These call the `cgp-macro-core` parsers directly to pin what they accept, +//! reject, and round-trip. +#![allow(dead_code)] + +pub mod ident_with_type_params; diff --git a/crates/tests/cgp-macro-tests/tests/invalid_expansion/mod.rs b/crates/tests/cgp-macro-tests/tests/invalid_expansion/mod.rs new file mode 100644 index 00000000..28b00919 --- /dev/null +++ b/crates/tests/cgp-macro-tests/tests/invalid_expansion/mod.rs @@ -0,0 +1,7 @@ +//! Failure cases where a CGP macro emits invalid or incorrect Rust. +//! +//! See the entrypoint `invalid_expansion_tests.rs` for the pattern to follow when +//! adding a case. This module is intentionally empty until the first genuine +//! invalid-expansion case is captured; keeping the target in place means the +//! harness is ready and a future agent only has to add a `pub mod ;` line +//! and the snapshot file. diff --git a/crates/tests/cgp-macro-tests/tests/invalid_expansion_tests.rs b/crates/tests/cgp-macro-tests/tests/invalid_expansion_tests.rs new file mode 100644 index 00000000..27ca7251 --- /dev/null +++ b/crates/tests/cgp-macro-tests/tests/invalid_expansion_tests.rs @@ -0,0 +1,21 @@ +//! Entrypoint for the `invalid_expansion` failure-case target. +//! +//! This target captures cases where a CGP macro currently emits **invalid or +//! wrong Rust** — code that a user would reasonably expect to work but that the +//! macro mishandles. Because the captured expansion is stored as a *string* +//! `insta` snapshot, the test compiles even though the code it describes would +//! not. +//! +//! To add a case: +//! 1. produce the expansion by calling the matching `cgp-macro-lib` entrypoint, +//! pretty-printing it (see `cgp-macro-test-util-lib`'s `pretty_format`), and +//! asserting it against an inline `insta` snapshot; +//! 2. add a code comment explaining **why** the output is wrong and **what the +//! correct output should be**; +//! 3. record the limitation in the owning reference document's `## Known issues` +//! section (per docs/CLAUDE.md), and link from the test to that document. +//! +//! No cases are enumerated yet; see crates/tests/CLAUDE.md ("Migration status"). +#![allow(dead_code)] + +pub mod invalid_expansion; diff --git a/crates/tests/cgp-macro-tests/tests/parser_rejections/cgp_component.rs b/crates/tests/cgp-macro-tests/tests/parser_rejections/cgp_component.rs new file mode 100644 index 00000000..84d83f07 --- /dev/null +++ b/crates/tests/cgp-macro-tests/tests/parser_rejections/cgp_component.rs @@ -0,0 +1,21 @@ +//! `#[cgp_component]` must be applied to a trait; applying it to another item +//! is rejected at parse time. +//! +//! See docs/reference/macros/cgp_component.md. + +use quote::quote; + +use super::assert_macro_rejects; + +#[test] +fn rejects_non_trait_item() { + // A struct is not a trait, so the consumer-trait parser rejects it. + assert_macro_rejects("cgp_component on a struct", || { + cgp_macro_lib::cgp_component( + quote!(FooProvider), + quote!( + pub struct NotATrait; + ), + ) + }); +} diff --git a/crates/tests/cgp-macro-tests/tests/parser_rejections/mod.rs b/crates/tests/cgp-macro-tests/tests/parser_rejections/mod.rs new file mode 100644 index 00000000..56b4a9b1 --- /dev/null +++ b/crates/tests/cgp-macro-tests/tests/parser_rejections/mod.rs @@ -0,0 +1,25 @@ +//! Failure cases: inputs the CGP macros must reject. +//! +//! A rejection test drives a `cgp-macro-lib` entrypoint (or a `cgp-macro-core` +//! parser) with an invalid input and asserts it returns `Err` rather than +//! producing tokens. This is how we pin down which code CGP deliberately refuses, +//! and catch regressions where a macro starts accepting something it should not. +//! +//! To add a case: +//! 1. call the entrypoint, e.g. `cgp_macro_lib::cgp_component(attr, body)`; +//! 2. assert the result is `Err` with [`assert_macro_rejects`]; +//! 3. if the rejection corresponds to a documented limitation, note it in the +//! owning reference document's `## Known issues` section and link to it here. + +use proc_macro2::TokenStream; + +/// Assert that a macro entrypoint rejects its input. `run` is the entrypoint call +/// (for example `|| cgp_macro_lib::cgp_component(attr.clone(), body.clone())`). +#[track_caller] +pub fn assert_macro_rejects(label: &str, run: impl FnOnce() -> syn::Result) { + if let Ok(tokens) = run() { + panic!("expected `{label}` to be rejected, but it expanded to:\n{tokens}"); + } +} + +pub mod cgp_component; diff --git a/crates/tests/cgp-macro-tests/tests/parser_rejections_tests.rs b/crates/tests/cgp-macro-tests/tests/parser_rejections_tests.rs new file mode 100644 index 00000000..5d782c34 --- /dev/null +++ b/crates/tests/cgp-macro-tests/tests/parser_rejections_tests.rs @@ -0,0 +1,11 @@ +//! Entrypoint for the `parser_rejections` failure-case target. +//! +//! This target collects inputs that the CGP macros must **reject** — malformed +//! syntax, or forms CGP deliberately disallows. Each case asserts that the +//! relevant `cgp-macro-core` parser (or a `cgp-macro-lib` entrypoint) returns an +//! error rather than silently accepting the input. +//! +//! See crates/tests/CLAUDE.md ("Adding a failure case"). +#![allow(dead_code)] + +pub mod parser_rejections; diff --git a/crates/tests/cgp-test-crate-a/Cargo.toml b/crates/tests/cgp-test-crate-a/Cargo.toml new file mode 100644 index 00000000..37b68444 --- /dev/null +++ b/crates/tests/cgp-test-crate-a/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "cgp-test-crate-a" +version = "0.7.0" +edition = { workspace = true } +license = { workspace = true } +repository = { workspace = true } +authors = { workspace = true } +rust-version = { workspace = true } +keywords = { workspace = true } + +[dependencies] +cgp = { workspace = true } diff --git a/crates/tests/cgp-test-crate-a/src/lib.rs b/crates/tests/cgp-test-crate-a/src/lib.rs new file mode 100644 index 00000000..b4a1eb7c --- /dev/null +++ b/crates/tests/cgp-test-crate-a/src/lib.rs @@ -0,0 +1,54 @@ +//! Upstream crate for cross-crate CGP coherence tests. +//! +//! This crate defines a getter capability, a component with a provider, and a +//! component registered under the `@app` namespace. The downstream crate +//! `cgp-test-crate-b` wires these onto its own contexts, supplies its own +//! provider for the foreign `Greeter` component, and participates in the `@app` +//! namespace — exercising that CGP's two-trait split stays within Rust's +//! coherence and orphan rules across crate boundaries. +//! +//! See crates/tests/CLAUDE.md and docs/concepts/coherence.md. + +use cgp::prelude::*; + +/// A published field accessor. Any context with a `name` field gains it through +/// the blanket `#[cgp_auto_getter]` impl, with no wiring required. +#[cgp_auto_getter] +pub trait HasName { + fn name(&self) -> &str; +} + +/// A component whose provider a downstream context can wire — or replace with its +/// own provider. +#[cgp_component(Greeter)] +pub trait CanGreet { + fn greet(&self) -> String; +} + +#[cgp_impl(new GreetHello)] +impl Greeter +where + Self: HasName, +{ + fn greet(&self) -> String { + format!("Hello, {}!", self.name()) + } +} + +/// A component registered under the `@app` namespace. A downstream context wires +/// it through `delegate_components! { … namespace DefaultNamespace; @app.…: … }`. +#[cgp_component(Announcer)] +#[prefix(@app in DefaultNamespace)] +pub trait CanAnnounce { + fn announce(&self) -> String; +} + +#[cgp_impl(new AnnounceLoudly)] +impl Announcer +where + Self: HasName, +{ + fn announce(&self) -> String { + format!("ANNOUNCEMENT from {}!", self.name()) + } +} diff --git a/crates/tests/cgp-test-crate-b/Cargo.toml b/crates/tests/cgp-test-crate-b/Cargo.toml new file mode 100644 index 00000000..f798c5f4 --- /dev/null +++ b/crates/tests/cgp-test-crate-b/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "cgp-test-crate-b" +version = "0.7.0" +edition = { workspace = true } +license = { workspace = true } +repository = { workspace = true } +authors = { workspace = true } +rust-version = { workspace = true } +keywords = { workspace = true } + +[dependencies] +cgp = { workspace = true } +cgp-test-crate-a = { workspace = true } diff --git a/crates/tests/cgp-test-crate-b/src/lib.rs b/crates/tests/cgp-test-crate-b/src/lib.rs new file mode 100644 index 00000000..9b9fc422 --- /dev/null +++ b/crates/tests/cgp-test-crate-b/src/lib.rs @@ -0,0 +1,99 @@ +//! Downstream crate for cross-crate CGP coherence tests. +//! +//! Everything here consumes the CGP surface defined in `cgp-test-crate-a`, +//! demonstrating three cross-crate abilities that Rust's coherence rules would +//! otherwise make awkward: +//! +//! 1. wiring a foreign component to a foreign provider on a local context; +//! 2. defining a *local* provider for a *foreign* provider trait (orphan-safe, +//! because the provider struct is local) and wiring a context to it; +//! 3. participating in a namespace declared upstream. +//! +//! See crates/tests/CLAUDE.md and docs/concepts/coherence.md. + +use cgp::prelude::*; +use cgp_test_crate_a::{AnnounceLoudly, AnnouncerComponent, GreetHello, Greeter, GreeterComponent}; + +/// (1) A local context wires the foreign `Greeter` component to the foreign +/// `GreetHello` provider. `GreetHello` needs `HasName`, satisfied by the `name` +/// field through crate-a's auto getter. +#[derive(HasField)] +pub struct Person { + pub name: String, +} + +delegate_components! { + Person { + GreeterComponent: GreetHello, + } +} + +/// (2) A provider defined in *this* crate for the foreign `Greeter` provider +/// trait. This is allowed because `GreetGoodbye` is local, even though `Greeter` +/// is not — the coherence win that CGP's provider structs are built for. +#[cgp_impl(new GreetGoodbye)] +impl Greeter +where + Self: cgp_test_crate_a::HasName, +{ + fn greet(&self) -> String { + format!("Goodbye, {}!", self.name()) + } +} + +#[derive(HasField)] +pub struct FormalPerson { + pub name: String, +} + +delegate_components! { + FormalPerson { + GreeterComponent: GreetGoodbye, + } +} + +/// (3) A local context participates in crate-a's `@app` namespace, wiring the +/// upstream `Announcer` component through the namespace path. +#[derive(HasField)] +pub struct Broadcaster { + pub name: String, +} + +delegate_components! { + Broadcaster { + namespace DefaultNamespace; + + @app.AnnouncerComponent: AnnounceLoudly, + } +} + +#[cfg(test)] +mod tests { + use cgp_test_crate_a::{CanAnnounce, CanGreet}; + + use super::*; + + #[test] + fn wire_foreign_provider() { + let person = Person { + name: "John".to_owned(), + }; + assert_eq!(person.greet(), "Hello, John!"); + } + + #[test] + fn local_provider_for_foreign_component() { + let person = FormalPerson { + name: "John".to_owned(), + }; + assert_eq!(person.greet(), "Goodbye, John!"); + } + + #[test] + fn participate_in_upstream_namespace() { + let broadcaster = Broadcaster { + name: "John".to_owned(), + }; + assert_eq!(broadcaster.announce(), "ANNOUNCEMENT from John!"); + } +} diff --git a/crates/tests/cgp-tests/src/lib.rs b/crates/tests/cgp-tests/src/lib.rs index 3c4e0340..82863e00 100644 --- a/crates/tests/cgp-tests/src/lib.rs +++ b/crates/tests/cgp-tests/src/lib.rs @@ -1,4 +1,7 @@ -#[cfg(test)] -pub mod tests; - -pub mod namespaces; +//! Main CGP test suite. +//! +//! All tests live in the integration targets under `tests/`, organized by CGP +//! concept (one target per concept, one file per unit test). This library crate +//! holds only shared support code; today there is none, so it is intentionally +//! empty. See `../README.md` and `../CLAUDE.md` for the organization and the +//! rules for maintaining the suite. diff --git a/crates/tests/cgp-tests/src/namespaces/mod.rs b/crates/tests/cgp-tests/src/namespaces/mod.rs deleted file mode 100644 index c8002a89..00000000 --- a/crates/tests/cgp-tests/src/namespaces/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod default_impls; -pub mod extended; diff --git a/crates/tests/cgp-tests/src/tests/async/mod.rs b/crates/tests/cgp-tests/src/tests/async/mod.rs deleted file mode 100644 index e40b0655..00000000 --- a/crates/tests/cgp-tests/src/tests/async/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod spawn; diff --git a/crates/tests/cgp-tests/src/tests/async/spawn.rs b/crates/tests/cgp-tests/src/tests/async/spawn.rs deleted file mode 100644 index 9921b3ba..00000000 --- a/crates/tests/cgp-tests/src/tests/async/spawn.rs +++ /dev/null @@ -1,890 +0,0 @@ -/** - This test demonstrates how something like tokio::spawn can be used without - us needing to annotate Future: Send everywhere in our code. - - The key is to implement a proxy SendRunner on the concrete context, which - would verify that all abstract types are safe to be sent in the produced - futures. - - This workaround is needed until the Return Type Notation (RTN) feature in - Rust is stabilized. -*/ -use core::convert::Infallible; -use core::future::Future; - -use cgp::core::error::ErrorTypeProviderComponent; -use cgp::extra::run::{ - CanRun, CanSendRun, Runner, RunnerComponent, SendRunner, SendRunnerComponent, -}; -use cgp::prelude::*; -use cgp_macro_test_util::{ - snapshot_cgp_component, snapshot_cgp_new_provider, snapshot_cgp_provider, snapshot_cgp_type, - snapshot_delegate_components, -}; -use futures::executor::block_on; - -// A dummy spawn function that has the same signature as tokio::spawn, -// requiring the Future to implement Send + 'static. -fn dummy_spawn(_future: F) -where - F: Future + Send + 'static, - F::Output: Send + 'static, -{ -} - -// The abstract types and interfaces do not contain explicit Send bounds - -snapshot_cgp_type! { - #[cgp_type] - pub trait HasFooType { - type Foo; - } - - expand_has_foo_type(output) { - insta::assert_snapshot!(output, @" - pub trait HasFooType { - type Foo; - } - impl<__Context__> HasFooType for __Context__ - where - __Context__: FooTypeProvider<__Context__>, - { - type Foo = <__Context__ as FooTypeProvider<__Context__>>::Foo; - } - pub trait FooTypeProvider< - __Context__, - >: IsProviderFor { - type Foo; - } - impl<__Provider__, __Context__> FooTypeProvider<__Context__> for __Provider__ - where - __Provider__: DelegateComponent - + IsProviderFor, - <__Provider__ as DelegateComponent< - FooTypeProviderComponent, - >>::Delegate: FooTypeProvider<__Context__>, - { - type Foo = <<__Provider__ as DelegateComponent< - FooTypeProviderComponent, - >>::Delegate as FooTypeProvider<__Context__>>::Foo; - } - pub struct FooTypeProviderComponent; - impl<__Context__> FooTypeProvider<__Context__> for UseContext - where - __Context__: HasFooType, - { - type Foo = <__Context__ as HasFooType>::Foo; - } - impl<__Context__> IsProviderFor for UseContext - where - __Context__: HasFooType, - {} - impl<__Context__, __Components__, __Path__> FooTypeProvider<__Context__> - for RedirectLookup<__Components__, __Path__> - where - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent< - __Path__, - >>::Delegate: FooTypeProvider<__Context__>, - { - type Foo = <<__Components__ as DelegateComponent< - __Path__, - >>::Delegate as FooTypeProvider<__Context__>>::Foo; - } - impl< - __Context__, - __Components__, - __Path__, - > IsProviderFor - for RedirectLookup<__Components__, __Path__> - where - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent< - __Path__, - >>::Delegate: IsProviderFor - + FooTypeProvider<__Context__>, - {} - impl FooTypeProvider<__Context__> for UseType { - type Foo = Foo; - } - impl IsProviderFor - for UseType {} - impl<__Provider__, Foo, __Context__> FooTypeProvider<__Context__> - for WithProvider<__Provider__> - where - __Provider__: TypeProvider<__Context__, FooTypeProviderComponent, Type = Foo>, - { - type Foo = Foo; - } - impl< - __Provider__, - Foo, - __Context__, - > IsProviderFor for WithProvider<__Provider__> - where - __Provider__: TypeProvider<__Context__, FooTypeProviderComponent, Type = Foo>, - {} - ") - } -} - -snapshot_cgp_type! { - #[cgp_type] - pub trait HasBarType { - type Bar; - } - - expand_has_bar_type(output) { - insta::assert_snapshot!(output, @" - pub trait HasBarType { - type Bar; - } - impl<__Context__> HasBarType for __Context__ - where - __Context__: BarTypeProvider<__Context__>, - { - type Bar = <__Context__ as BarTypeProvider<__Context__>>::Bar; - } - pub trait BarTypeProvider< - __Context__, - >: IsProviderFor { - type Bar; - } - impl<__Provider__, __Context__> BarTypeProvider<__Context__> for __Provider__ - where - __Provider__: DelegateComponent - + IsProviderFor, - <__Provider__ as DelegateComponent< - BarTypeProviderComponent, - >>::Delegate: BarTypeProvider<__Context__>, - { - type Bar = <<__Provider__ as DelegateComponent< - BarTypeProviderComponent, - >>::Delegate as BarTypeProvider<__Context__>>::Bar; - } - pub struct BarTypeProviderComponent; - impl<__Context__> BarTypeProvider<__Context__> for UseContext - where - __Context__: HasBarType, - { - type Bar = <__Context__ as HasBarType>::Bar; - } - impl<__Context__> IsProviderFor for UseContext - where - __Context__: HasBarType, - {} - impl<__Context__, __Components__, __Path__> BarTypeProvider<__Context__> - for RedirectLookup<__Components__, __Path__> - where - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent< - __Path__, - >>::Delegate: BarTypeProvider<__Context__>, - { - type Bar = <<__Components__ as DelegateComponent< - __Path__, - >>::Delegate as BarTypeProvider<__Context__>>::Bar; - } - impl< - __Context__, - __Components__, - __Path__, - > IsProviderFor - for RedirectLookup<__Components__, __Path__> - where - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent< - __Path__, - >>::Delegate: IsProviderFor - + BarTypeProvider<__Context__>, - {} - impl BarTypeProvider<__Context__> for UseType { - type Bar = Bar; - } - impl IsProviderFor - for UseType {} - impl<__Provider__, Bar, __Context__> BarTypeProvider<__Context__> - for WithProvider<__Provider__> - where - __Provider__: TypeProvider<__Context__, BarTypeProviderComponent, Type = Bar>, - { - type Bar = Bar; - } - impl< - __Provider__, - Bar, - __Context__, - > IsProviderFor for WithProvider<__Provider__> - where - __Provider__: TypeProvider<__Context__, BarTypeProviderComponent, Type = Bar>, - {} - ") - } -} - -snapshot_cgp_component! { - #[cgp_component(FooFetcher)] - #[async_trait] - pub trait CanFetchFoo: HasFooType + HasErrorType { - async fn fetch_foo(&self) -> Result; - } - - expand_can_fetch_foo(output) { - insta::assert_snapshot!(output, @" - #[async_trait] - pub trait CanFetchFoo: HasFooType + HasErrorType { - async fn fetch_foo(&self) -> Result; - } - #[async_trait] - impl<__Context__> CanFetchFoo for __Context__ - where - __Context__: HasFooType + HasErrorType, - __Context__: FooFetcher<__Context__>, - { - async fn fetch_foo(&self) -> Result { - __Context__::fetch_foo(self).await - } - } - #[async_trait] - pub trait FooFetcher<__Context__>: IsProviderFor - where - __Context__: HasFooType + HasErrorType, - { - async fn fetch_foo( - __context__: &__Context__, - ) -> Result<__Context__::Foo, __Context__::Error>; - } - #[async_trait] - impl<__Provider__, __Context__> FooFetcher<__Context__> for __Provider__ - where - __Context__: HasFooType + HasErrorType, - __Provider__: DelegateComponent - + IsProviderFor, - <__Provider__ as DelegateComponent< - FooFetcherComponent, - >>::Delegate: FooFetcher<__Context__>, - { - async fn fetch_foo( - __context__: &__Context__, - ) -> Result<__Context__::Foo, __Context__::Error> { - <__Provider__ as DelegateComponent< - FooFetcherComponent, - >>::Delegate::fetch_foo(__context__) - .await - } - } - pub struct FooFetcherComponent; - #[async_trait] - impl<__Context__> FooFetcher<__Context__> for UseContext - where - __Context__: HasFooType + HasErrorType, - __Context__: CanFetchFoo, - { - async fn fetch_foo( - __context__: &__Context__, - ) -> Result<__Context__::Foo, __Context__::Error> { - __Context__::fetch_foo(__context__).await - } - } - impl<__Context__> IsProviderFor for UseContext - where - __Context__: HasFooType + HasErrorType, - __Context__: CanFetchFoo, - {} - #[async_trait] - impl<__Context__, __Components__, __Path__> FooFetcher<__Context__> - for RedirectLookup<__Components__, __Path__> - where - __Context__: HasFooType + HasErrorType, - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent<__Path__>>::Delegate: FooFetcher<__Context__>, - { - async fn fetch_foo( - __context__: &__Context__, - ) -> Result<__Context__::Foo, __Context__::Error> { - <__Components__ as DelegateComponent<__Path__>>::Delegate::fetch_foo(__context__) - .await - } - } - impl< - __Context__, - __Components__, - __Path__, - > IsProviderFor - for RedirectLookup<__Components__, __Path__> - where - __Context__: HasFooType + HasErrorType, - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent< - __Path__, - >>::Delegate: IsProviderFor - + FooFetcher<__Context__>, - {} - ") - } -} - -snapshot_cgp_component! { - #[cgp_component(BarFetcher)] - #[async_trait] - pub trait CanFetchBar: HasBarType + HasErrorType { - async fn fetch_bar(&self) -> Result; - } - - expand_can_fetch_bar(output) { - insta::assert_snapshot!(output, @" - #[async_trait] - pub trait CanFetchBar: HasBarType + HasErrorType { - async fn fetch_bar(&self) -> Result; - } - #[async_trait] - impl<__Context__> CanFetchBar for __Context__ - where - __Context__: HasBarType + HasErrorType, - __Context__: BarFetcher<__Context__>, - { - async fn fetch_bar(&self) -> Result { - __Context__::fetch_bar(self).await - } - } - #[async_trait] - pub trait BarFetcher<__Context__>: IsProviderFor - where - __Context__: HasBarType + HasErrorType, - { - async fn fetch_bar( - __context__: &__Context__, - ) -> Result<__Context__::Bar, __Context__::Error>; - } - #[async_trait] - impl<__Provider__, __Context__> BarFetcher<__Context__> for __Provider__ - where - __Context__: HasBarType + HasErrorType, - __Provider__: DelegateComponent - + IsProviderFor, - <__Provider__ as DelegateComponent< - BarFetcherComponent, - >>::Delegate: BarFetcher<__Context__>, - { - async fn fetch_bar( - __context__: &__Context__, - ) -> Result<__Context__::Bar, __Context__::Error> { - <__Provider__ as DelegateComponent< - BarFetcherComponent, - >>::Delegate::fetch_bar(__context__) - .await - } - } - pub struct BarFetcherComponent; - #[async_trait] - impl<__Context__> BarFetcher<__Context__> for UseContext - where - __Context__: HasBarType + HasErrorType, - __Context__: CanFetchBar, - { - async fn fetch_bar( - __context__: &__Context__, - ) -> Result<__Context__::Bar, __Context__::Error> { - __Context__::fetch_bar(__context__).await - } - } - impl<__Context__> IsProviderFor for UseContext - where - __Context__: HasBarType + HasErrorType, - __Context__: CanFetchBar, - {} - #[async_trait] - impl<__Context__, __Components__, __Path__> BarFetcher<__Context__> - for RedirectLookup<__Components__, __Path__> - where - __Context__: HasBarType + HasErrorType, - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent<__Path__>>::Delegate: BarFetcher<__Context__>, - { - async fn fetch_bar( - __context__: &__Context__, - ) -> Result<__Context__::Bar, __Context__::Error> { - <__Components__ as DelegateComponent<__Path__>>::Delegate::fetch_bar(__context__) - .await - } - } - impl< - __Context__, - __Components__, - __Path__, - > IsProviderFor - for RedirectLookup<__Components__, __Path__> - where - __Context__: HasBarType + HasErrorType, - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent< - __Path__, - >>::Delegate: IsProviderFor - + BarFetcher<__Context__>, - {} - ") - } -} - -snapshot_cgp_component! { - #[cgp_component(FooBarRunner)] - #[async_trait] - pub trait CanRunFooBar: HasFooType + HasBarType + HasErrorType { - async fn run_foo_bar(&self, foo: &Self::Foo, bar: &Self::Bar) -> Result<(), Self::Error>; - } - - expand_can_run_foo_bar(output) { - insta::assert_snapshot!(output, @" - #[async_trait] - pub trait CanRunFooBar: HasFooType + HasBarType + HasErrorType { - async fn run_foo_bar( - &self, - foo: &Self::Foo, - bar: &Self::Bar, - ) -> Result<(), Self::Error>; - } - #[async_trait] - impl<__Context__> CanRunFooBar for __Context__ - where - __Context__: HasFooType + HasBarType + HasErrorType, - __Context__: FooBarRunner<__Context__>, - { - async fn run_foo_bar( - &self, - foo: &Self::Foo, - bar: &Self::Bar, - ) -> Result<(), Self::Error> { - __Context__::run_foo_bar(self, foo, bar).await - } - } - #[async_trait] - pub trait FooBarRunner< - __Context__, - >: IsProviderFor - where - __Context__: HasFooType + HasBarType + HasErrorType, - { - async fn run_foo_bar( - __context__: &__Context__, - foo: &__Context__::Foo, - bar: &__Context__::Bar, - ) -> Result<(), __Context__::Error>; - } - #[async_trait] - impl<__Provider__, __Context__> FooBarRunner<__Context__> for __Provider__ - where - __Context__: HasFooType + HasBarType + HasErrorType, - __Provider__: DelegateComponent - + IsProviderFor, - <__Provider__ as DelegateComponent< - FooBarRunnerComponent, - >>::Delegate: FooBarRunner<__Context__>, - { - async fn run_foo_bar( - __context__: &__Context__, - foo: &__Context__::Foo, - bar: &__Context__::Bar, - ) -> Result<(), __Context__::Error> { - <__Provider__ as DelegateComponent< - FooBarRunnerComponent, - >>::Delegate::run_foo_bar(__context__, foo, bar) - .await - } - } - pub struct FooBarRunnerComponent; - #[async_trait] - impl<__Context__> FooBarRunner<__Context__> for UseContext - where - __Context__: HasFooType + HasBarType + HasErrorType, - __Context__: CanRunFooBar, - { - async fn run_foo_bar( - __context__: &__Context__, - foo: &__Context__::Foo, - bar: &__Context__::Bar, - ) -> Result<(), __Context__::Error> { - __Context__::run_foo_bar(__context__, foo, bar).await - } - } - impl<__Context__> IsProviderFor for UseContext - where - __Context__: HasFooType + HasBarType + HasErrorType, - __Context__: CanRunFooBar, - {} - #[async_trait] - impl<__Context__, __Components__, __Path__> FooBarRunner<__Context__> - for RedirectLookup<__Components__, __Path__> - where - __Context__: HasFooType + HasBarType + HasErrorType, - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent<__Path__>>::Delegate: FooBarRunner<__Context__>, - { - async fn run_foo_bar( - __context__: &__Context__, - foo: &__Context__::Foo, - bar: &__Context__::Bar, - ) -> Result<(), __Context__::Error> { - <__Components__ as DelegateComponent< - __Path__, - >>::Delegate::run_foo_bar(__context__, foo, bar) - .await - } - } - impl< - __Context__, - __Components__, - __Path__, - > IsProviderFor - for RedirectLookup<__Components__, __Path__> - where - __Context__: HasFooType + HasBarType + HasErrorType, - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent< - __Path__, - >>::Delegate: IsProviderFor - + FooBarRunner<__Context__>, - {} - ") - } -} - -// Abstract providers can be implemented without Send bounds - -snapshot_cgp_new_provider! { - #[cgp_new_provider(RunnerComponent)] - impl Runner for RunWithFooBar - where - Context: CanFetchFoo + CanFetchBar + CanRunFooBar, - { - async fn run(context: &Context, _code: PhantomData) -> Result<(), Context::Error> { - let foo = context.fetch_foo().await?; - let bar = context.fetch_bar().await?; - - context.run_foo_bar(&foo, &bar).await?; - - Ok(()) - } - } - - expand_run_with_foo_bar(output) { - insta::assert_snapshot!(output, @" - impl Runner for RunWithFooBar - where - Context: CanFetchFoo + CanFetchBar + CanRunFooBar, - { - async fn run( - context: &Context, - _code: PhantomData, - ) -> Result<(), Context::Error> { - let foo = context.fetch_foo().await?; - let bar = context.fetch_bar().await?; - context.run_foo_bar(&foo, &bar).await?; - Ok(()) - } - } - impl IsProviderFor for RunWithFooBar - where - Context: CanFetchFoo + CanFetchBar + CanRunFooBar, - {} - pub struct RunWithFooBar; - ") - } -} - -snapshot_cgp_new_provider! { - #[cgp_new_provider(RunnerComponent)] - impl Runner for SpawnAndRun - where - Context: 'static + Send + Clone + CanSendRun, - { - async fn run(context: &Context, _code: PhantomData) -> Result<(), Context::Error> { - let context = context.clone(); - - dummy_spawn(async move { - let _ = context.send_run(PhantomData).await; - }); - - Ok(()) - } - } - - expand_spawn_and_run(output) { - insta::assert_snapshot!(output, @" - impl Runner for SpawnAndRun - where - Context: 'static + Send + Clone + CanSendRun, - { - async fn run( - context: &Context, - _code: PhantomData, - ) -> Result<(), Context::Error> { - let context = context.clone(); - dummy_spawn(async move { - let _ = context.send_run(PhantomData).await; - }); - Ok(()) - } - } - impl IsProviderFor - for SpawnAndRun - where - Context: 'static + Send + Clone + CanSendRun, - {} - pub struct SpawnAndRun(pub ::core::marker::PhantomData<(InCode)>); - ") - } -} - -snapshot_cgp_new_provider! { - #[cgp_new_provider] - impl FooFetcher for DummyFetchFoo - where - Context: HasFooType + HasErrorType, - { - async fn fetch_foo(_context: &Context) -> Result { - Ok(Default::default()) - } - } - - expand_dummy_fetch_foo(output) { - insta::assert_snapshot!(output, @" - impl FooFetcher for DummyFetchFoo - where - Context: HasFooType + HasErrorType, - { - async fn fetch_foo(_context: &Context) -> Result { - Ok(Default::default()) - } - } - impl IsProviderFor for DummyFetchFoo - where - Context: HasFooType + HasErrorType, - {} - pub struct DummyFetchFoo; - ") - } -} - -snapshot_cgp_new_provider! { - #[cgp_new_provider] - impl BarFetcher for DummyFetchBar - where - Context: HasBarType + HasErrorType, - { - async fn fetch_bar(_context: &Context) -> Result { - Ok(Default::default()) - } - } - - expand_dummy_fetch_bar(output) { - insta::assert_snapshot!(output, @" - impl BarFetcher for DummyFetchBar - where - Context: HasBarType + HasErrorType, - { - async fn fetch_bar(_context: &Context) -> Result { - Ok(Default::default()) - } - } - impl IsProviderFor for DummyFetchBar - where - Context: HasBarType + HasErrorType, - {} - pub struct DummyFetchBar; - ") - } -} - -snapshot_cgp_new_provider! { - #[cgp_new_provider] - impl FooBarRunner for DummyRunFoobar - where - Context: HasFooType + HasBarType + HasErrorType, - { - async fn run_foo_bar( - _context: &Context, - _foo: &Context::Foo, - _bar: &Context::Bar, - ) -> Result<(), Context::Error> { - Ok(()) - } - } - - expand_dummy_run_foo_bar(output) { - insta::assert_snapshot!(output, @" - impl FooBarRunner for DummyRunFoobar - where - Context: HasFooType + HasBarType + HasErrorType, - { - async fn run_foo_bar( - _context: &Context, - _foo: &Context::Foo, - _bar: &Context::Bar, - ) -> Result<(), Context::Error> { - Ok(()) - } - } - impl IsProviderFor for DummyRunFoobar - where - Context: HasFooType + HasBarType + HasErrorType, - {} - pub struct DummyRunFoobar; - ") - } -} - -// An example App context that has Send-safe implementationsS - -#[derive(Clone)] -pub struct App; - -pub struct ActionA; -pub struct ActionB; - -snapshot_delegate_components! { - delegate_components! { - App { - ErrorTypeProviderComponent: - UseType, - [ - FooTypeProviderComponent, - BarTypeProviderComponent, - ]: - UseType<()>, - FooFetcherComponent: - DummyFetchFoo, - BarFetcherComponent: - DummyFetchBar, - FooBarRunnerComponent: - DummyRunFoobar, - RunnerComponent: - UseDelegate, - }>, - } - } - - expand_app(output) { - insta::assert_snapshot!(output, @" - pub struct AppRunnerComponents; - impl DelegateComponent for App { - type Delegate = UseType; - } - impl< - __Context__, - __Params__, - > IsProviderFor for App - where - UseType< - Infallible, - >: IsProviderFor, - {} - impl DelegateComponent for App { - type Delegate = UseType<()>; - } - impl< - __Context__, - __Params__, - > IsProviderFor for App - where - UseType<()>: IsProviderFor, - {} - impl DelegateComponent for App { - type Delegate = UseType<()>; - } - impl< - __Context__, - __Params__, - > IsProviderFor for App - where - UseType<()>: IsProviderFor, - {} - impl DelegateComponent for App { - type Delegate = DummyFetchFoo; - } - impl<__Context__, __Params__> IsProviderFor - for App - where - DummyFetchFoo: IsProviderFor, - {} - impl DelegateComponent for App { - type Delegate = DummyFetchBar; - } - impl<__Context__, __Params__> IsProviderFor - for App - where - DummyFetchBar: IsProviderFor, - {} - impl DelegateComponent for App { - type Delegate = DummyRunFoobar; - } - impl< - __Context__, - __Params__, - > IsProviderFor for App - where - DummyRunFoobar: IsProviderFor, - {} - impl DelegateComponent for App { - type Delegate = UseDelegate; - } - impl<__Context__, __Params__> IsProviderFor - for App - where - UseDelegate< - AppRunnerComponents, - >: IsProviderFor, - {} - impl DelegateComponent for AppRunnerComponents { - type Delegate = RunWithFooBar; - } - impl<__Context__, __Params__> IsProviderFor - for AppRunnerComponents - where - RunWithFooBar: IsProviderFor, - {} - impl DelegateComponent for AppRunnerComponents { - type Delegate = SpawnAndRun; - } - impl<__Context__, __Params__> IsProviderFor - for AppRunnerComponents - where - SpawnAndRun: IsProviderFor, - {} - ") - } -} - -// Explicit implementation of SendRunner for App, by forwarding the -// call to Runner that is implemented by `RunWithFooBar`. -// With the concrete context known, the Send bound can be found in the concrete future. - -snapshot_cgp_provider! { - #[cgp_provider] - impl SendRunner for App { - async fn send_run(context: &App, code: PhantomData) -> Result<(), Infallible> { - context.run(code).await - } - } - - expand_app_send_runner(output) { - insta::assert_snapshot!(output, @" - impl SendRunner for App { - async fn send_run( - context: &App, - code: PhantomData, - ) -> Result<(), Infallible> { - context.run(code).await - } - } - impl IsProviderFor for App {} - ") - } -} - -#[test] -pub fn test_async_spawn() { - let app = App; - block_on(app.run(PhantomData::)).unwrap(); -} diff --git a/crates/tests/cgp-tests/src/tests/blanket_trait.rs b/crates/tests/cgp-tests/src/tests/blanket_trait.rs deleted file mode 100644 index d23e8caa..00000000 --- a/crates/tests/cgp-tests/src/tests/blanket_trait.rs +++ /dev/null @@ -1,170 +0,0 @@ -#![allow(dead_code)] - -mod basic_blanket_trait { - use cgp_macro_test_util::snapshot_blanket_trait; - - pub trait Foo {} - pub trait Bar {} - - snapshot_blanket_trait! { - #[blanket_trait] - pub trait FooBar: Foo + Bar {} - - expand_foo_bar(output) { - insta::assert_snapshot!(output, @" - pub trait FooBar: Foo + Bar {} - impl<__Context__> FooBar for __Context__ - where - __Context__: Foo + Bar, - {} - ") - } - } - - pub struct Context; - - impl Foo for Context {} - impl Bar for Context {} - - pub trait CanUseFooBar: FooBar {} - impl CanUseFooBar for Context {} -} - -mod blanket_trait_with_method { - use cgp_macro_test_util::snapshot_blanket_trait; - - pub trait Foo { - fn foo(&self); - } - pub trait Bar { - fn bar(&self); - } - - snapshot_blanket_trait! { - #[blanket_trait] - pub trait FooBar: Foo + Bar { - fn foo_bar(&self) { - self.foo(); - self.bar(); - } - } - - expand_foo_bar(output) { - insta::assert_snapshot!(output, @" - pub trait FooBar: Foo + Bar { - fn foo_bar(&self) { - self.foo(); - self.bar(); - } - } - impl<__Context__> FooBar for __Context__ - where - __Context__: Foo + Bar, - { - fn foo_bar(&self) { - self.foo(); - self.bar(); - } - } - ") - } - } - - pub struct Context; - - impl Foo for Context { - fn foo(&self) {} - } - - impl Bar for Context { - fn bar(&self) {} - } - - pub trait CanUseFooBar: FooBar {} - impl CanUseFooBar for Context {} -} - -mod blanket_trait_with_associated_type_without_constraints { - use cgp_macro_test_util::snapshot_blanket_trait; - - pub trait HasFooTypeAt { - type Foo; - } - - pub struct Bar; - - snapshot_blanket_trait! { - #[blanket_trait] - pub trait HasFooTypeAtBar: HasFooTypeAt { - type FooBar; - } - - expand_foo_bar(output) { - insta::assert_snapshot!(output, @" - pub trait HasFooTypeAtBar: HasFooTypeAt { - type FooBar; - } - impl<__Context__, FooBar> HasFooTypeAtBar for __Context__ - where - __Context__: HasFooTypeAt, - { - type FooBar = FooBar; - } - ") - } - } - - pub struct Context; - pub struct FooBar; - - impl HasFooTypeAt for Context { - type Foo = FooBar; - } - - pub trait CanUseFooTypeAtBar: HasFooTypeAtBar {} - impl CanUseFooTypeAtBar for Context {} -} - -mod blanket_trait_with_associated_type_and_constraints { - use cgp_macro_test_util::snapshot_blanket_trait; - - pub trait HasFooTypeAt { - type Foo; - } - - pub struct Bar; - - snapshot_blanket_trait! { - #[blanket_trait] - pub trait HasFooTypeAtBar: HasFooTypeAt { - type FooBar: Clone; - } - - expand_foo_bar(output) { - insta::assert_snapshot!(output, @" - pub trait HasFooTypeAtBar: HasFooTypeAt { - type FooBar: Clone; - } - impl<__Context__, FooBar> HasFooTypeAtBar for __Context__ - where - __Context__: HasFooTypeAt, - FooBar: Clone, - { - type FooBar = FooBar; - } - ") - } - } - - pub struct Context; - - #[derive(Clone)] - pub struct FooBar; - - impl HasFooTypeAt for Context { - type Foo = FooBar; - } - - pub trait CanUseFooTypeAtBar: HasFooTypeAtBar {} - impl CanUseFooTypeAtBar for Context {} -} diff --git a/crates/tests/cgp-tests/src/tests/check_components.rs b/crates/tests/cgp-tests/src/tests/check_components.rs deleted file mode 100644 index ca8a84d6..00000000 --- a/crates/tests/cgp-tests/src/tests/check_components.rs +++ /dev/null @@ -1,1452 +0,0 @@ -#![allow(dead_code)] - -mod basic_check_components { - use core::marker::PhantomData; - - use cgp::prelude::*; - use cgp_macro_test_util::{ - snapshot_cgp_getter, snapshot_cgp_type, snapshot_check_components, - snapshot_delegate_and_check_components, - }; - - snapshot_cgp_type! { - #[cgp_type] - pub trait HasFooType { - type Foo; - } - - expand_has_foo_type(output) { - insta::assert_snapshot!(output, @" - pub trait HasFooType { - type Foo; - } - impl<__Context__> HasFooType for __Context__ - where - __Context__: FooTypeProvider<__Context__>, - { - type Foo = <__Context__ as FooTypeProvider<__Context__>>::Foo; - } - pub trait FooTypeProvider< - __Context__, - >: IsProviderFor { - type Foo; - } - impl<__Provider__, __Context__> FooTypeProvider<__Context__> for __Provider__ - where - __Provider__: DelegateComponent - + IsProviderFor, - <__Provider__ as DelegateComponent< - FooTypeProviderComponent, - >>::Delegate: FooTypeProvider<__Context__>, - { - type Foo = <<__Provider__ as DelegateComponent< - FooTypeProviderComponent, - >>::Delegate as FooTypeProvider<__Context__>>::Foo; - } - pub struct FooTypeProviderComponent; - impl<__Context__> FooTypeProvider<__Context__> for UseContext - where - __Context__: HasFooType, - { - type Foo = <__Context__ as HasFooType>::Foo; - } - impl<__Context__> IsProviderFor for UseContext - where - __Context__: HasFooType, - {} - impl<__Context__, __Components__, __Path__> FooTypeProvider<__Context__> - for RedirectLookup<__Components__, __Path__> - where - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent< - __Path__, - >>::Delegate: FooTypeProvider<__Context__>, - { - type Foo = <<__Components__ as DelegateComponent< - __Path__, - >>::Delegate as FooTypeProvider<__Context__>>::Foo; - } - impl< - __Context__, - __Components__, - __Path__, - > IsProviderFor - for RedirectLookup<__Components__, __Path__> - where - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent< - __Path__, - >>::Delegate: IsProviderFor - + FooTypeProvider<__Context__>, - {} - impl FooTypeProvider<__Context__> for UseType { - type Foo = Foo; - } - impl IsProviderFor - for UseType {} - impl<__Provider__, Foo, __Context__> FooTypeProvider<__Context__> - for WithProvider<__Provider__> - where - __Provider__: TypeProvider<__Context__, FooTypeProviderComponent, Type = Foo>, - { - type Foo = Foo; - } - impl< - __Provider__, - Foo, - __Context__, - > IsProviderFor for WithProvider<__Provider__> - where - __Provider__: TypeProvider<__Context__, FooTypeProviderComponent, Type = Foo>, - {} - ") - } - } - - snapshot_cgp_type! { - #[cgp_type] - pub trait HasBarType { - type Bar; - } - - expand_has_bar_type(output) { - insta::assert_snapshot!(output, @" - pub trait HasBarType { - type Bar; - } - impl<__Context__> HasBarType for __Context__ - where - __Context__: BarTypeProvider<__Context__>, - { - type Bar = <__Context__ as BarTypeProvider<__Context__>>::Bar; - } - pub trait BarTypeProvider< - __Context__, - >: IsProviderFor { - type Bar; - } - impl<__Provider__, __Context__> BarTypeProvider<__Context__> for __Provider__ - where - __Provider__: DelegateComponent - + IsProviderFor, - <__Provider__ as DelegateComponent< - BarTypeProviderComponent, - >>::Delegate: BarTypeProvider<__Context__>, - { - type Bar = <<__Provider__ as DelegateComponent< - BarTypeProviderComponent, - >>::Delegate as BarTypeProvider<__Context__>>::Bar; - } - pub struct BarTypeProviderComponent; - impl<__Context__> BarTypeProvider<__Context__> for UseContext - where - __Context__: HasBarType, - { - type Bar = <__Context__ as HasBarType>::Bar; - } - impl<__Context__> IsProviderFor for UseContext - where - __Context__: HasBarType, - {} - impl<__Context__, __Components__, __Path__> BarTypeProvider<__Context__> - for RedirectLookup<__Components__, __Path__> - where - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent< - __Path__, - >>::Delegate: BarTypeProvider<__Context__>, - { - type Bar = <<__Components__ as DelegateComponent< - __Path__, - >>::Delegate as BarTypeProvider<__Context__>>::Bar; - } - impl< - __Context__, - __Components__, - __Path__, - > IsProviderFor - for RedirectLookup<__Components__, __Path__> - where - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent< - __Path__, - >>::Delegate: IsProviderFor - + BarTypeProvider<__Context__>, - {} - impl BarTypeProvider<__Context__> for UseType { - type Bar = Bar; - } - impl IsProviderFor - for UseType {} - impl<__Provider__, Bar, __Context__> BarTypeProvider<__Context__> - for WithProvider<__Provider__> - where - __Provider__: TypeProvider<__Context__, BarTypeProviderComponent, Type = Bar>, - { - type Bar = Bar; - } - impl< - __Provider__, - Bar, - __Context__, - > IsProviderFor for WithProvider<__Provider__> - where - __Provider__: TypeProvider<__Context__, BarTypeProviderComponent, Type = Bar>, - {} - ") - } - } - - snapshot_cgp_getter! { - #[cgp_getter { - provider: FooGetterAt, - }] - pub trait HasFooAt: HasFooType { - fn foo(&self, _tag: PhantomData) -> &Self::Foo; - } - - expand_has_foo_at(output) { - insta::assert_snapshot!(output, @" - pub trait HasFooAt: HasFooType { - fn foo(&self, _tag: PhantomData) -> &Self::Foo; - } - impl<__Context__, I> HasFooAt for __Context__ - where - __Context__: HasFooType, - __Context__: FooGetterAt<__Context__, I>, - { - fn foo(&self, _tag: PhantomData) -> &Self::Foo { - __Context__::foo(self, _tag) - } - } - pub trait FooGetterAt< - __Context__, - I, - >: IsProviderFor - where - __Context__: HasFooType, - { - fn foo(__context__: &__Context__, _tag: PhantomData) -> &__Context__::Foo; - } - impl<__Provider__, __Context__, I> FooGetterAt<__Context__, I> for __Provider__ - where - __Context__: HasFooType, - __Provider__: DelegateComponent - + IsProviderFor, - <__Provider__ as DelegateComponent< - FooGetterAtComponent, - >>::Delegate: FooGetterAt<__Context__, I>, - { - fn foo(__context__: &__Context__, _tag: PhantomData) -> &__Context__::Foo { - <__Provider__ as DelegateComponent< - FooGetterAtComponent, - >>::Delegate::foo(__context__, _tag) - } - } - pub struct FooGetterAtComponent; - impl<__Context__, I> FooGetterAt<__Context__, I> for UseContext - where - __Context__: HasFooType, - __Context__: HasFooAt, - { - fn foo(__context__: &__Context__, _tag: PhantomData) -> &__Context__::Foo { - __Context__::foo(__context__, _tag) - } - } - impl<__Context__, I> IsProviderFor for UseContext - where - __Context__: HasFooType, - __Context__: HasFooAt, - {} - impl<__Context__, I, __Components__, __Path__> FooGetterAt<__Context__, I> - for RedirectLookup<__Components__, __Path__> - where - __Context__: HasFooType, - __Path__: ConcatPath>, - __Components__: DelegateComponent< - <__Path__ as ConcatPath>>::Output, - >, - <__Components__ as DelegateComponent< - <__Path__ as ConcatPath>>::Output, - >>::Delegate: FooGetterAt<__Context__, I>, - { - fn foo(__context__: &__Context__, _tag: PhantomData) -> &__Context__::Foo { - <__Components__ as DelegateComponent< - <__Path__ as ConcatPath>>::Output, - >>::Delegate::foo(__context__, _tag) - } - } - impl< - __Context__, - I, - __Components__, - __Path__, - > IsProviderFor - for RedirectLookup<__Components__, __Path__> - where - __Context__: HasFooType, - __Path__: ConcatPath>, - __Components__: DelegateComponent< - <__Path__ as ConcatPath>>::Output, - >, - <__Components__ as DelegateComponent< - <__Path__ as ConcatPath>>::Output, - >>::Delegate: IsProviderFor - + FooGetterAt<__Context__, I>, - {} - impl<__Context__, I> FooGetterAt<__Context__, I> for UseFields - where - __Context__: HasFooType, - __Context__: HasField< - Symbol<3, Chars<'f', Chars<'o', Chars<'o', Nil>>>>, - Value = __Context__::Foo, - >, - { - fn foo(__context__: &__Context__, _phantom: PhantomData) -> &__Context__::Foo { - __context__ - .get_field( - ::core::marker::PhantomData::< - Symbol<3, Chars<'f', Chars<'o', Chars<'o', Nil>>>>, - >, - ) - } - } - impl<__Context__, I> IsProviderFor for UseFields - where - __Context__: HasFooType, - __Context__: HasField< - Symbol<3, Chars<'f', Chars<'o', Chars<'o', Nil>>>>, - Value = __Context__::Foo, - >, - {} - impl<__Context__, I, __Tag__> FooGetterAt<__Context__, I> for UseField<__Tag__> - where - __Context__: HasFooType, - __Context__: HasField<__Tag__, Value = __Context__::Foo>, - { - fn foo(__context__: &__Context__, _phantom: PhantomData) -> &__Context__::Foo { - __context__.get_field(::core::marker::PhantomData::<__Tag__>) - } - } - impl<__Context__, I, __Tag__> IsProviderFor - for UseField<__Tag__> - where - __Context__: HasFooType, - __Context__: HasField<__Tag__, Value = __Context__::Foo>, - {} - impl<__Context__, I, __Provider__> FooGetterAt<__Context__, I> - for WithProvider<__Provider__> - where - __Context__: HasFooType, - __Provider__: FieldGetter< - __Context__, - FooGetterAtComponent, - Value = __Context__::Foo, - >, - { - fn foo(__context__: &__Context__, _phantom: PhantomData) -> &__Context__::Foo { - __Provider__::get_field( - __context__, - ::core::marker::PhantomData::, - ) - } - } - impl<__Context__, I, __Provider__> IsProviderFor - for WithProvider<__Provider__> - where - __Context__: HasFooType, - __Provider__: FieldGetter< - __Context__, - FooGetterAtComponent, - Value = __Context__::Foo, - >, - {} - ") - } - } - - snapshot_cgp_getter! { - #[cgp_getter { - provider: BarGetterAt, - }] - pub trait HasBarAt: HasBarType { - fn foo(&self, _tag: PhantomData<(I, J)>) -> &Self::Bar; - } - - expand_has_bar_at(output) { - insta::assert_snapshot!(output, @" - pub trait HasBarAt: HasBarType { - fn foo(&self, _tag: PhantomData<(I, J)>) -> &Self::Bar; - } - impl<__Context__, I, J> HasBarAt for __Context__ - where - __Context__: HasBarType, - __Context__: BarGetterAt<__Context__, I, J>, - { - fn foo(&self, _tag: PhantomData<(I, J)>) -> &Self::Bar { - __Context__::foo(self, _tag) - } - } - pub trait BarGetterAt< - __Context__, - I, - J, - >: IsProviderFor - where - __Context__: HasBarType, - { - fn foo(__context__: &__Context__, _tag: PhantomData<(I, J)>) -> &__Context__::Bar; - } - impl<__Provider__, __Context__, I, J> BarGetterAt<__Context__, I, J> for __Provider__ - where - __Context__: HasBarType, - __Provider__: DelegateComponent - + IsProviderFor, - <__Provider__ as DelegateComponent< - BarGetterAtComponent, - >>::Delegate: BarGetterAt<__Context__, I, J>, - { - fn foo(__context__: &__Context__, _tag: PhantomData<(I, J)>) -> &__Context__::Bar { - <__Provider__ as DelegateComponent< - BarGetterAtComponent, - >>::Delegate::foo(__context__, _tag) - } - } - pub struct BarGetterAtComponent; - impl<__Context__, I, J> BarGetterAt<__Context__, I, J> for UseContext - where - __Context__: HasBarType, - __Context__: HasBarAt, - { - fn foo(__context__: &__Context__, _tag: PhantomData<(I, J)>) -> &__Context__::Bar { - __Context__::foo(__context__, _tag) - } - } - impl<__Context__, I, J> IsProviderFor - for UseContext - where - __Context__: HasBarType, - __Context__: HasBarAt, - {} - impl<__Context__, I, J, __Components__, __Path__> BarGetterAt<__Context__, I, J> - for RedirectLookup<__Components__, __Path__> - where - __Context__: HasBarType, - __Path__: ConcatPath>>, - __Components__: DelegateComponent< - <__Path__ as ConcatPath>>>::Output, - >, - <__Components__ as DelegateComponent< - <__Path__ as ConcatPath>>>::Output, - >>::Delegate: BarGetterAt<__Context__, I, J>, - { - fn foo(__context__: &__Context__, _tag: PhantomData<(I, J)>) -> &__Context__::Bar { - <__Components__ as DelegateComponent< - <__Path__ as ConcatPath>>>::Output, - >>::Delegate::foo(__context__, _tag) - } - } - impl< - __Context__, - I, - J, - __Components__, - __Path__, - > IsProviderFor - for RedirectLookup<__Components__, __Path__> - where - __Context__: HasBarType, - __Path__: ConcatPath>>, - __Components__: DelegateComponent< - <__Path__ as ConcatPath>>>::Output, - >, - <__Components__ as DelegateComponent< - <__Path__ as ConcatPath>>>::Output, - >>::Delegate: IsProviderFor - + BarGetterAt<__Context__, I, J>, - {} - impl<__Context__, I, J> BarGetterAt<__Context__, I, J> for UseFields - where - __Context__: HasBarType, - __Context__: HasField< - Symbol<3, Chars<'f', Chars<'o', Chars<'o', Nil>>>>, - Value = __Context__::Bar, - >, - { - fn foo( - __context__: &__Context__, - _phantom: PhantomData<(I, J)>, - ) -> &__Context__::Bar { - __context__ - .get_field( - ::core::marker::PhantomData::< - Symbol<3, Chars<'f', Chars<'o', Chars<'o', Nil>>>>, - >, - ) - } - } - impl<__Context__, I, J> IsProviderFor - for UseFields - where - __Context__: HasBarType, - __Context__: HasField< - Symbol<3, Chars<'f', Chars<'o', Chars<'o', Nil>>>>, - Value = __Context__::Bar, - >, - {} - impl<__Context__, I, J, __Tag__> BarGetterAt<__Context__, I, J> for UseField<__Tag__> - where - __Context__: HasBarType, - __Context__: HasField<__Tag__, Value = __Context__::Bar>, - { - fn foo( - __context__: &__Context__, - _phantom: PhantomData<(I, J)>, - ) -> &__Context__::Bar { - __context__.get_field(::core::marker::PhantomData::<__Tag__>) - } - } - impl<__Context__, I, J, __Tag__> IsProviderFor - for UseField<__Tag__> - where - __Context__: HasBarType, - __Context__: HasField<__Tag__, Value = __Context__::Bar>, - {} - impl<__Context__, I, J, __Provider__> BarGetterAt<__Context__, I, J> - for WithProvider<__Provider__> - where - __Context__: HasBarType, - __Provider__: FieldGetter< - __Context__, - BarGetterAtComponent, - Value = __Context__::Bar, - >, - { - fn foo( - __context__: &__Context__, - _phantom: PhantomData<(I, J)>, - ) -> &__Context__::Bar { - __Provider__::get_field( - __context__, - ::core::marker::PhantomData::, - ) - } - } - impl< - __Context__, - I, - J, - __Provider__, - > IsProviderFor for WithProvider<__Provider__> - where - __Context__: HasBarType, - __Provider__: FieldGetter< - __Context__, - BarGetterAtComponent, - Value = __Context__::Bar, - >, - {} - ") - } - } - - #[derive(HasField)] - pub struct Context { - pub dummy: (), - pub extra_dummy: (), - } - - snapshot_delegate_and_check_components! { - delegate_and_check_components! { - Context { - [ - FooTypeProviderComponent, - BarTypeProviderComponent, - ]: - UseType<()>, - - #[check_params( - (Index<5>, Index<6>), - (Index<7>, Index<8>), - )] - [ - #[check_params( - Index<0>, - Index<1>, - )] - FooGetterAtComponent, - - #[check_params( - (Index<0>, Index<1>), - (Index<1>, Index<0>), - )] - BarGetterAtComponent, - ]: - UseField, - } - } - - expand_context(output) { - insta::assert_snapshot!(output, @r#" - impl DelegateComponent for Context { - type Delegate = UseType<()>; - } - impl< - __Context__, - __Params__, - > IsProviderFor for Context - where - UseType<()>: IsProviderFor, - {} - impl DelegateComponent for Context { - type Delegate = UseType<()>; - } - impl< - __Context__, - __Params__, - > IsProviderFor for Context - where - UseType<()>: IsProviderFor, - {} - impl DelegateComponent for Context { - type Delegate = UseField; - } - impl< - __Context__, - __Params__, - > IsProviderFor for Context - where - UseField< - Symbol!("dummy"), - >: IsProviderFor, - {} - impl DelegateComponent for Context { - type Delegate = UseField; - } - impl< - __Context__, - __Params__, - > IsProviderFor for Context - where - UseField< - Symbol!("dummy"), - >: IsProviderFor, - {} - trait __CanUseContext< - __Component__, - __Params__: ?Sized, - >: CanUseComponent<__Component__, __Params__> {} - impl __CanUseContext for Context {} - impl __CanUseContext for Context {} - impl __CanUseContext, Index<6>)> for Context {} - impl __CanUseContext, Index<8>)> for Context {} - impl __CanUseContext> for Context {} - impl __CanUseContext> for Context {} - impl __CanUseContext, Index<6>)> for Context {} - impl __CanUseContext, Index<8>)> for Context {} - impl __CanUseContext, Index<1>)> for Context {} - impl __CanUseContext, Index<0>)> for Context {} - "#) - } - } - - snapshot_check_components! { - check_components! { - #[check_trait(CanUseContext)] - Context { - FooTypeProviderComponent, - BarTypeProviderComponent, - FooGetterAtComponent: [ - Index<0>, - Index<1>, - ], - FooGetterAtComponent: - Index<3>, - } - - #[check_trait(CanUseContext2)] - Context { - BarGetterAtComponent: [ - (Index<0>, Index<1>), - (Index<1>, Index<0>), - ], - BarGetterAtComponent: - (Index<3>, Index<4>), - [ - FooGetterAtComponent, - BarGetterAtComponent, - ]: [ - (Index<5>, Index<6>), - (Index<7>, Index<8>), - ] - } - - #[check_trait(CanUseDummyField)] - #[check_providers( - UseField, - UseField, - )] - Context { - FooGetterAtComponent: [ - Index<0>, - Index<1>, - ], - FooGetterAtComponent: - Index<3>, - BarGetterAtComponent: [ - (Index<0>, Index<1>), - (Index<1>, Index<0>), - ], - BarGetterAtComponent: - (Index<3>, Index<4>), - [ - FooGetterAtComponent, - BarGetterAtComponent, - ]: [ - (Index<5>, Index<6>), - (Index<7>, Index<8>), - ] - } - } - - expand_check_context(output) { - insta::assert_snapshot!(output, @r#" - trait CanUseContext< - __Component__, - __Params__: ?Sized, - >: CanUseComponent<__Component__, __Params__> {} - impl CanUseContext for Context {} - impl CanUseContext for Context {} - impl CanUseContext> for Context {} - impl CanUseContext> for Context {} - impl CanUseContext> for Context {} - trait CanUseContext2< - __Component__, - __Params__: ?Sized, - >: CanUseComponent<__Component__, __Params__> {} - impl CanUseContext2, Index<1>)> for Context {} - impl CanUseContext2, Index<0>)> for Context {} - impl CanUseContext2, Index<4>)> for Context {} - impl CanUseContext2, Index<6>)> for Context {} - impl CanUseContext2, Index<8>)> for Context {} - impl CanUseContext2, Index<6>)> for Context {} - impl CanUseContext2, Index<8>)> for Context {} - trait CanUseDummyField< - __Component__, - __Params__: ?Sized, - >: IsProviderFor<__Component__, Context, __Params__> {} - impl CanUseDummyField> for UseField {} - impl CanUseDummyField> - for UseField {} - impl CanUseDummyField> for UseField {} - impl CanUseDummyField> - for UseField {} - impl CanUseDummyField> for UseField {} - impl CanUseDummyField> - for UseField {} - impl CanUseDummyField, Index<1>)> - for UseField {} - impl CanUseDummyField, Index<1>)> - for UseField {} - impl CanUseDummyField, Index<0>)> - for UseField {} - impl CanUseDummyField, Index<0>)> - for UseField {} - impl CanUseDummyField, Index<4>)> - for UseField {} - impl CanUseDummyField, Index<4>)> - for UseField {} - impl CanUseDummyField, Index<6>)> - for UseField {} - impl CanUseDummyField, Index<6>)> - for UseField {} - impl CanUseDummyField, Index<8>)> - for UseField {} - impl CanUseDummyField, Index<8>)> - for UseField {} - impl CanUseDummyField, Index<6>)> - for UseField {} - impl CanUseDummyField, Index<6>)> - for UseField {} - impl CanUseDummyField, Index<8>)> - for UseField {} - impl CanUseDummyField, Index<8>)> - for UseField {} - "#) - } - } -} - -mod generic_check_components { - use core::marker::PhantomData; - - use cgp::prelude::*; - use cgp_macro_test_util::{ - snapshot_cgp_getter, snapshot_cgp_type, snapshot_check_components, - snapshot_delegate_components, - }; - - snapshot_cgp_type! { - #[cgp_type] - pub trait HasFooType { - type Foo; - } - - expand_has_foo_type(output) { - insta::assert_snapshot!(output, @" - pub trait HasFooType { - type Foo; - } - impl<__Context__> HasFooType for __Context__ - where - __Context__: FooTypeProvider<__Context__>, - { - type Foo = <__Context__ as FooTypeProvider<__Context__>>::Foo; - } - pub trait FooTypeProvider< - __Context__, - >: IsProviderFor { - type Foo; - } - impl<__Provider__, __Context__> FooTypeProvider<__Context__> for __Provider__ - where - __Provider__: DelegateComponent - + IsProviderFor, - <__Provider__ as DelegateComponent< - FooTypeProviderComponent, - >>::Delegate: FooTypeProvider<__Context__>, - { - type Foo = <<__Provider__ as DelegateComponent< - FooTypeProviderComponent, - >>::Delegate as FooTypeProvider<__Context__>>::Foo; - } - pub struct FooTypeProviderComponent; - impl<__Context__> FooTypeProvider<__Context__> for UseContext - where - __Context__: HasFooType, - { - type Foo = <__Context__ as HasFooType>::Foo; - } - impl<__Context__> IsProviderFor for UseContext - where - __Context__: HasFooType, - {} - impl<__Context__, __Components__, __Path__> FooTypeProvider<__Context__> - for RedirectLookup<__Components__, __Path__> - where - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent< - __Path__, - >>::Delegate: FooTypeProvider<__Context__>, - { - type Foo = <<__Components__ as DelegateComponent< - __Path__, - >>::Delegate as FooTypeProvider<__Context__>>::Foo; - } - impl< - __Context__, - __Components__, - __Path__, - > IsProviderFor - for RedirectLookup<__Components__, __Path__> - where - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent< - __Path__, - >>::Delegate: IsProviderFor - + FooTypeProvider<__Context__>, - {} - impl FooTypeProvider<__Context__> for UseType { - type Foo = Foo; - } - impl IsProviderFor - for UseType {} - impl<__Provider__, Foo, __Context__> FooTypeProvider<__Context__> - for WithProvider<__Provider__> - where - __Provider__: TypeProvider<__Context__, FooTypeProviderComponent, Type = Foo>, - { - type Foo = Foo; - } - impl< - __Provider__, - Foo, - __Context__, - > IsProviderFor for WithProvider<__Provider__> - where - __Provider__: TypeProvider<__Context__, FooTypeProviderComponent, Type = Foo>, - {} - ") - } - } - - snapshot_cgp_type! { - #[cgp_type] - pub trait HasBarType { - type Bar; - } - - expand_has_bar_type(output) { - insta::assert_snapshot!(output, @" - pub trait HasBarType { - type Bar; - } - impl<__Context__> HasBarType for __Context__ - where - __Context__: BarTypeProvider<__Context__>, - { - type Bar = <__Context__ as BarTypeProvider<__Context__>>::Bar; - } - pub trait BarTypeProvider< - __Context__, - >: IsProviderFor { - type Bar; - } - impl<__Provider__, __Context__> BarTypeProvider<__Context__> for __Provider__ - where - __Provider__: DelegateComponent - + IsProviderFor, - <__Provider__ as DelegateComponent< - BarTypeProviderComponent, - >>::Delegate: BarTypeProvider<__Context__>, - { - type Bar = <<__Provider__ as DelegateComponent< - BarTypeProviderComponent, - >>::Delegate as BarTypeProvider<__Context__>>::Bar; - } - pub struct BarTypeProviderComponent; - impl<__Context__> BarTypeProvider<__Context__> for UseContext - where - __Context__: HasBarType, - { - type Bar = <__Context__ as HasBarType>::Bar; - } - impl<__Context__> IsProviderFor for UseContext - where - __Context__: HasBarType, - {} - impl<__Context__, __Components__, __Path__> BarTypeProvider<__Context__> - for RedirectLookup<__Components__, __Path__> - where - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent< - __Path__, - >>::Delegate: BarTypeProvider<__Context__>, - { - type Bar = <<__Components__ as DelegateComponent< - __Path__, - >>::Delegate as BarTypeProvider<__Context__>>::Bar; - } - impl< - __Context__, - __Components__, - __Path__, - > IsProviderFor - for RedirectLookup<__Components__, __Path__> - where - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent< - __Path__, - >>::Delegate: IsProviderFor - + BarTypeProvider<__Context__>, - {} - impl BarTypeProvider<__Context__> for UseType { - type Bar = Bar; - } - impl IsProviderFor - for UseType {} - impl<__Provider__, Bar, __Context__> BarTypeProvider<__Context__> - for WithProvider<__Provider__> - where - __Provider__: TypeProvider<__Context__, BarTypeProviderComponent, Type = Bar>, - { - type Bar = Bar; - } - impl< - __Provider__, - Bar, - __Context__, - > IsProviderFor for WithProvider<__Provider__> - where - __Provider__: TypeProvider<__Context__, BarTypeProviderComponent, Type = Bar>, - {} - ") - } - } - - snapshot_cgp_getter! { - #[cgp_getter { - provider: FooGetterAt, - }] - pub trait HasFooAt: HasFooType { - fn foo(&self, _tag: PhantomData) -> &Self::Foo; - } - - expand_has_foo_at(output) { - insta::assert_snapshot!(output, @" - pub trait HasFooAt: HasFooType { - fn foo(&self, _tag: PhantomData) -> &Self::Foo; - } - impl<__Context__, I: Clone> HasFooAt for __Context__ - where - __Context__: HasFooType, - __Context__: FooGetterAt<__Context__, I>, - { - fn foo(&self, _tag: PhantomData) -> &Self::Foo { - __Context__::foo(self, _tag) - } - } - pub trait FooGetterAt< - __Context__, - I: Clone, - >: IsProviderFor - where - __Context__: HasFooType, - { - fn foo(__context__: &__Context__, _tag: PhantomData) -> &__Context__::Foo; - } - impl<__Provider__, __Context__, I: Clone> FooGetterAt<__Context__, I> for __Provider__ - where - __Context__: HasFooType, - __Provider__: DelegateComponent - + IsProviderFor, - <__Provider__ as DelegateComponent< - FooGetterAtComponent, - >>::Delegate: FooGetterAt<__Context__, I>, - { - fn foo(__context__: &__Context__, _tag: PhantomData) -> &__Context__::Foo { - <__Provider__ as DelegateComponent< - FooGetterAtComponent, - >>::Delegate::foo(__context__, _tag) - } - } - pub struct FooGetterAtComponent; - impl<__Context__, I: Clone> FooGetterAt<__Context__, I> for UseContext - where - __Context__: HasFooType, - __Context__: HasFooAt, - { - fn foo(__context__: &__Context__, _tag: PhantomData) -> &__Context__::Foo { - __Context__::foo(__context__, _tag) - } - } - impl<__Context__, I: Clone> IsProviderFor - for UseContext - where - __Context__: HasFooType, - __Context__: HasFooAt, - {} - impl<__Context__, I: Clone, __Components__, __Path__> FooGetterAt<__Context__, I> - for RedirectLookup<__Components__, __Path__> - where - __Context__: HasFooType, - __Path__: ConcatPath>, - __Components__: DelegateComponent< - <__Path__ as ConcatPath>>::Output, - >, - <__Components__ as DelegateComponent< - <__Path__ as ConcatPath>>::Output, - >>::Delegate: FooGetterAt<__Context__, I>, - { - fn foo(__context__: &__Context__, _tag: PhantomData) -> &__Context__::Foo { - <__Components__ as DelegateComponent< - <__Path__ as ConcatPath>>::Output, - >>::Delegate::foo(__context__, _tag) - } - } - impl< - __Context__, - I: Clone, - __Components__, - __Path__, - > IsProviderFor - for RedirectLookup<__Components__, __Path__> - where - __Context__: HasFooType, - __Path__: ConcatPath>, - __Components__: DelegateComponent< - <__Path__ as ConcatPath>>::Output, - >, - <__Components__ as DelegateComponent< - <__Path__ as ConcatPath>>::Output, - >>::Delegate: IsProviderFor - + FooGetterAt<__Context__, I>, - {} - impl<__Context__, I: Clone> FooGetterAt<__Context__, I> for UseFields - where - __Context__: HasFooType, - __Context__: HasField< - Symbol<3, Chars<'f', Chars<'o', Chars<'o', Nil>>>>, - Value = __Context__::Foo, - >, - { - fn foo(__context__: &__Context__, _phantom: PhantomData) -> &__Context__::Foo { - __context__ - .get_field( - ::core::marker::PhantomData::< - Symbol<3, Chars<'f', Chars<'o', Chars<'o', Nil>>>>, - >, - ) - } - } - impl<__Context__, I: Clone> IsProviderFor - for UseFields - where - __Context__: HasFooType, - __Context__: HasField< - Symbol<3, Chars<'f', Chars<'o', Chars<'o', Nil>>>>, - Value = __Context__::Foo, - >, - {} - impl<__Context__, I: Clone, __Tag__> FooGetterAt<__Context__, I> for UseField<__Tag__> - where - __Context__: HasFooType, - __Context__: HasField<__Tag__, Value = __Context__::Foo>, - { - fn foo(__context__: &__Context__, _phantom: PhantomData) -> &__Context__::Foo { - __context__.get_field(::core::marker::PhantomData::<__Tag__>) - } - } - impl< - __Context__, - I: Clone, - __Tag__, - > IsProviderFor for UseField<__Tag__> - where - __Context__: HasFooType, - __Context__: HasField<__Tag__, Value = __Context__::Foo>, - {} - impl<__Context__, I: Clone, __Provider__> FooGetterAt<__Context__, I> - for WithProvider<__Provider__> - where - __Context__: HasFooType, - __Provider__: FieldGetter< - __Context__, - FooGetterAtComponent, - Value = __Context__::Foo, - >, - { - fn foo(__context__: &__Context__, _phantom: PhantomData) -> &__Context__::Foo { - __Provider__::get_field( - __context__, - ::core::marker::PhantomData::, - ) - } - } - impl< - __Context__, - I: Clone, - __Provider__, - > IsProviderFor for WithProvider<__Provider__> - where - __Context__: HasFooType, - __Provider__: FieldGetter< - __Context__, - FooGetterAtComponent, - Value = __Context__::Foo, - >, - {} - ") - } - } - - snapshot_cgp_getter! { - #[cgp_getter { - name: BarGetterAtComponent, - provider: BarGetterAt, - }] - pub trait HasBarAt: HasBarType { - fn foo(&self, _tag: PhantomData<(I, J)>) -> &Self::Bar; - } - - expand_has_bar_at(output) { - insta::assert_snapshot!(output, @" - pub trait HasBarAt: HasBarType { - fn foo(&self, _tag: PhantomData<(I, J)>) -> &Self::Bar; - } - impl<__Context__, I: Clone, J> HasBarAt for __Context__ - where - __Context__: HasBarType, - __Context__: BarGetterAt<__Context__, I, J>, - { - fn foo(&self, _tag: PhantomData<(I, J)>) -> &Self::Bar { - __Context__::foo(self, _tag) - } - } - pub trait BarGetterAt< - __Context__, - I: Clone, - J, - >: IsProviderFor, __Context__, (I, J)> - where - __Context__: HasBarType, - { - fn foo(__context__: &__Context__, _tag: PhantomData<(I, J)>) -> &__Context__::Bar; - } - impl<__Provider__, __Context__, I: Clone, J> BarGetterAt<__Context__, I, J> - for __Provider__ - where - __Context__: HasBarType, - __Provider__: DelegateComponent> - + IsProviderFor, __Context__, (I, J)>, - <__Provider__ as DelegateComponent< - BarGetterAtComponent, - >>::Delegate: BarGetterAt<__Context__, I, J>, - { - fn foo(__context__: &__Context__, _tag: PhantomData<(I, J)>) -> &__Context__::Bar { - <__Provider__ as DelegateComponent< - BarGetterAtComponent, - >>::Delegate::foo(__context__, _tag) - } - } - pub struct BarGetterAtComponent(pub ::core::marker::PhantomData<(I)>); - impl<__Context__, I: Clone, J> BarGetterAt<__Context__, I, J> for UseContext - where - __Context__: HasBarType, - __Context__: HasBarAt, - { - fn foo(__context__: &__Context__, _tag: PhantomData<(I, J)>) -> &__Context__::Bar { - __Context__::foo(__context__, _tag) - } - } - impl< - __Context__, - I: Clone, - J, - > IsProviderFor, __Context__, (I, J)> for UseContext - where - __Context__: HasBarType, - __Context__: HasBarAt, - {} - impl<__Context__, I: Clone, J, __Components__, __Path__> BarGetterAt<__Context__, I, J> - for RedirectLookup<__Components__, __Path__> - where - __Context__: HasBarType, - __Path__: ConcatPath>>, - __Components__: DelegateComponent< - <__Path__ as ConcatPath>>>::Output, - >, - <__Components__ as DelegateComponent< - <__Path__ as ConcatPath>>>::Output, - >>::Delegate: BarGetterAt<__Context__, I, J>, - { - fn foo(__context__: &__Context__, _tag: PhantomData<(I, J)>) -> &__Context__::Bar { - <__Components__ as DelegateComponent< - <__Path__ as ConcatPath>>>::Output, - >>::Delegate::foo(__context__, _tag) - } - } - impl< - __Context__, - I: Clone, - J, - __Components__, - __Path__, - > IsProviderFor, __Context__, (I, J)> - for RedirectLookup<__Components__, __Path__> - where - __Context__: HasBarType, - __Path__: ConcatPath>>, - __Components__: DelegateComponent< - <__Path__ as ConcatPath>>>::Output, - >, - <__Components__ as DelegateComponent< - <__Path__ as ConcatPath>>>::Output, - >>::Delegate: IsProviderFor, __Context__, (I, J)> - + BarGetterAt<__Context__, I, J>, - {} - impl<__Context__, I: Clone, J> BarGetterAt<__Context__, I, J> for UseFields - where - __Context__: HasBarType, - __Context__: HasField< - Symbol<3, Chars<'f', Chars<'o', Chars<'o', Nil>>>>, - Value = __Context__::Bar, - >, - { - fn foo( - __context__: &__Context__, - _phantom: PhantomData<(I, J)>, - ) -> &__Context__::Bar { - __context__ - .get_field( - ::core::marker::PhantomData::< - Symbol<3, Chars<'f', Chars<'o', Chars<'o', Nil>>>>, - >, - ) - } - } - impl< - __Context__, - I: Clone, - J, - > IsProviderFor, __Context__, (I, J)> for UseFields - where - __Context__: HasBarType, - __Context__: HasField< - Symbol<3, Chars<'f', Chars<'o', Chars<'o', Nil>>>>, - Value = __Context__::Bar, - >, - {} - impl<__Context__, I: Clone, J, __Tag__> BarGetterAt<__Context__, I, J> - for UseField<__Tag__> - where - __Context__: HasBarType, - __Context__: HasField<__Tag__, Value = __Context__::Bar>, - { - fn foo( - __context__: &__Context__, - _phantom: PhantomData<(I, J)>, - ) -> &__Context__::Bar { - __context__.get_field(::core::marker::PhantomData::<__Tag__>) - } - } - impl< - __Context__, - I: Clone, - J, - __Tag__, - > IsProviderFor, __Context__, (I, J)> for UseField<__Tag__> - where - __Context__: HasBarType, - __Context__: HasField<__Tag__, Value = __Context__::Bar>, - {} - impl<__Context__, I: Clone, J, __Provider__> BarGetterAt<__Context__, I, J> - for WithProvider<__Provider__> - where - __Context__: HasBarType, - __Provider__: FieldGetter< - __Context__, - BarGetterAtComponent, - Value = __Context__::Bar, - >, - { - fn foo( - __context__: &__Context__, - _phantom: PhantomData<(I, J)>, - ) -> &__Context__::Bar { - __Provider__::get_field( - __context__, - ::core::marker::PhantomData::>, - ) - } - } - impl< - __Context__, - I: Clone, - J, - __Provider__, - > IsProviderFor, __Context__, (I, J)> - for WithProvider<__Provider__> - where - __Context__: HasBarType, - __Provider__: FieldGetter< - __Context__, - BarGetterAtComponent, - Value = __Context__::Bar, - >, - {} - ") - } - } - - #[derive(HasField)] - pub struct Context { - pub dummy: (), - } - - snapshot_delegate_components! { - delegate_components! { - Context { - [ - FooTypeProviderComponent, - BarTypeProviderComponent, - ]: - UseType<()>, - [ - FooGetterAtComponent, - BarGetterAtComponent, - ]: - UseField, - } - } - - expand_context(output) { - insta::assert_snapshot!(output, @r#" - impl DelegateComponent for Context { - type Delegate = UseType<()>; - } - impl< - __Context__, - __Params__, - > IsProviderFor for Context - where - UseType<()>: IsProviderFor, - {} - impl DelegateComponent for Context { - type Delegate = UseType<()>; - } - impl< - __Context__, - __Params__, - > IsProviderFor for Context - where - UseType<()>: IsProviderFor, - {} - impl DelegateComponent for Context { - type Delegate = UseField; - } - impl< - __Context__, - __Params__, - > IsProviderFor for Context - where - UseField< - Symbol!("dummy"), - >: IsProviderFor, - {} - impl DelegateComponent> for Context { - type Delegate = UseField; - } - impl< - I, - __Context__, - __Params__, - > IsProviderFor, __Context__, __Params__> for Context - where - UseField< - Symbol!("dummy"), - >: IsProviderFor, __Context__, __Params__>, - {} - "#) - } - } - - snapshot_check_components! { - check_components! { - <'a, I> Context - where - I: Clone, - { - FooGetterAtComponent: &'a I, - BarGetterAtComponent: (I, &'a Index<0>), - } - } - - expand_check_context(output) { - insta::assert_snapshot!(output, @" - trait __CheckContext< - __Component__, - __Params__: ?Sized, - >: CanUseComponent<__Component__, __Params__> {} - impl<'a, I> __CheckContext for Context - where - I: Clone, - {} - impl<'a, I> __CheckContext, (I, &'a Index<0>)> for Context - where - I: Clone, - {} - ") - } - } -} diff --git a/crates/tests/cgp-tests/src/tests/compose.rs b/crates/tests/cgp-tests/src/tests/compose.rs deleted file mode 100644 index 79eac769..00000000 --- a/crates/tests/cgp-tests/src/tests/compose.rs +++ /dev/null @@ -1,166 +0,0 @@ -use core::fmt::Display; - -use cgp::extra::handler::{ComputerRef, ComputerRefComponent}; -use cgp::prelude::*; -use cgp_macro_test_util::snapshot_cgp_new_provider; - -pub fn first_name_to_string(context: &Context) -> String -where - Context: HasField, -{ - context.get_field(PhantomData).to_string() -} - -pub fn last_name_to_string(context: &Context) -> String -where - Context: HasField, -{ - context.get_field(PhantomData).to_string() -} - -pub fn full_name_to_string(context: &Context) -> String -where - Context: HasField - + HasField, -{ - let composed = concate_outputs(first_name_to_string, last_name_to_string); - composed(context) -} - -pub fn concate_outputs( - fn_a: impl Fn(&Context) -> String, - fn_b: impl Fn(&Context) -> String, -) -> impl Fn(&Context) -> String { - move |context| format!("{} {}", fn_a(context), fn_b(context)) -} - -snapshot_cgp_new_provider! { - #[cgp_new_provider] - impl ComputerRef for FirstNameToString - where - Context: HasField, - { - type Output = String; - - fn compute_ref(context: &Context, _code: PhantomData, _input: &Input) -> String { - context.get_field(PhantomData).to_string() - } - } - - expand_first_name_to_string(output) { - insta::assert_snapshot!(output, @r#" - impl ComputerRef for FirstNameToString - where - Context: HasField, - { - type Output = String; - fn compute_ref( - context: &Context, - _code: PhantomData, - _input: &Input, - ) -> String { - context.get_field(PhantomData).to_string() - } - } - impl IsProviderFor - for FirstNameToString - where - Context: HasField, - {} - pub struct FirstNameToString; - "#) - } -} - -snapshot_cgp_new_provider! { - #[cgp_new_provider] - impl ComputerRef for LastNameToString - where - Context: HasField, - { - type Output = String; - - fn compute_ref(context: &Context, _code: PhantomData, _input: &Input) -> String { - context.get_field(PhantomData).to_string() - } - } - - expand_last_name_to_string(output) { - insta::assert_snapshot!(output, @r#" - impl ComputerRef for LastNameToString - where - Context: HasField, - { - type Output = String; - fn compute_ref( - context: &Context, - _code: PhantomData, - _input: &Input, - ) -> String { - context.get_field(PhantomData).to_string() - } - } - impl IsProviderFor - for LastNameToString - where - Context: HasField, - {} - pub struct LastNameToString; - "#) - } -} - -snapshot_cgp_new_provider! { - #[cgp_new_provider] - impl ComputerRef - for ConcatOutputs - where - ProviderA: ComputerRef, - ProviderB: ComputerRef, - { - type Output = String; - - fn compute_ref(context: &Context, code: PhantomData, input: &Input) -> String { - let output_a = ProviderA::compute_ref(context, code, input); - let output_b = ProviderB::compute_ref(context, code, input); - format!("{output_a} {output_b}") - } - } - - expand_concat_outputs(output) { - insta::assert_snapshot!(output, @r#" - impl ComputerRef - for ConcatOutputs - where - ProviderA: ComputerRef, - ProviderB: ComputerRef, - { - type Output = String; - fn compute_ref(context: &Context, code: PhantomData, input: &Input) -> String { - let output_a = ProviderA::compute_ref(context, code, input); - let output_b = ProviderB::compute_ref(context, code, input); - format!("{output_a} {output_b}") - } - } - impl< - Context, - Code, - Input, - ProviderA, - ProviderB, - > IsProviderFor - for ConcatOutputs - where - ProviderA: IsProviderFor - + ComputerRef, - ProviderB: IsProviderFor - + ComputerRef, - {} - pub struct ConcatOutputs( - pub ::core::marker::PhantomData<(ProviderA, ProviderB)>, - ); - "#) - } -} - -pub type FullNameToString = ConcatOutputs; diff --git a/crates/tests/cgp-tests/src/tests/delegate_and_check_components.rs b/crates/tests/cgp-tests/src/tests/delegate_and_check_components.rs deleted file mode 100644 index 30cb57f3..00000000 --- a/crates/tests/cgp-tests/src/tests/delegate_and_check_components.rs +++ /dev/null @@ -1,605 +0,0 @@ -#![allow(dead_code)] - -mod basic_delegate_and_check_components { - use cgp::prelude::*; - use cgp_macro_test_util::{ - snapshot_cgp_getter, snapshot_cgp_type, snapshot_delegate_and_check_components, - }; - - snapshot_cgp_type! { - #[cgp_type] - pub trait HasNameType { - type Name; - } - - expand_has_name_type(output) { - insta::assert_snapshot!(output, @" - pub trait HasNameType { - type Name; - } - impl<__Context__> HasNameType for __Context__ - where - __Context__: NameTypeProvider<__Context__>, - { - type Name = <__Context__ as NameTypeProvider<__Context__>>::Name; - } - pub trait NameTypeProvider< - __Context__, - >: IsProviderFor { - type Name; - } - impl<__Provider__, __Context__> NameTypeProvider<__Context__> for __Provider__ - where - __Provider__: DelegateComponent - + IsProviderFor, - <__Provider__ as DelegateComponent< - NameTypeProviderComponent, - >>::Delegate: NameTypeProvider<__Context__>, - { - type Name = <<__Provider__ as DelegateComponent< - NameTypeProviderComponent, - >>::Delegate as NameTypeProvider<__Context__>>::Name; - } - pub struct NameTypeProviderComponent; - impl<__Context__> NameTypeProvider<__Context__> for UseContext - where - __Context__: HasNameType, - { - type Name = <__Context__ as HasNameType>::Name; - } - impl<__Context__> IsProviderFor - for UseContext - where - __Context__: HasNameType, - {} - impl<__Context__, __Components__, __Path__> NameTypeProvider<__Context__> - for RedirectLookup<__Components__, __Path__> - where - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent< - __Path__, - >>::Delegate: NameTypeProvider<__Context__>, - { - type Name = <<__Components__ as DelegateComponent< - __Path__, - >>::Delegate as NameTypeProvider<__Context__>>::Name; - } - impl< - __Context__, - __Components__, - __Path__, - > IsProviderFor - for RedirectLookup<__Components__, __Path__> - where - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent< - __Path__, - >>::Delegate: IsProviderFor - + NameTypeProvider<__Context__>, - {} - impl NameTypeProvider<__Context__> for UseType { - type Name = Name; - } - impl IsProviderFor - for UseType {} - impl<__Provider__, Name, __Context__> NameTypeProvider<__Context__> - for WithProvider<__Provider__> - where - __Provider__: TypeProvider<__Context__, NameTypeProviderComponent, Type = Name>, - { - type Name = Name; - } - impl< - __Provider__, - Name, - __Context__, - > IsProviderFor - for WithProvider<__Provider__> - where - __Provider__: TypeProvider<__Context__, NameTypeProviderComponent, Type = Name>, - {} - ") - } - } - - snapshot_cgp_getter! { - #[cgp_getter] - pub trait HasName: HasNameType { - fn name(&self) -> &Self::Name; - } - - expand_has_name(output) { - insta::assert_snapshot!(output, @" - pub trait HasName: HasNameType { - fn name(&self) -> &Self::Name; - } - impl<__Context__> HasName for __Context__ - where - __Context__: HasNameType, - __Context__: NameGetter<__Context__>, - { - fn name(&self) -> &Self::Name { - __Context__::name(self) - } - } - pub trait NameGetter<__Context__>: IsProviderFor - where - __Context__: HasNameType, - { - fn name(__context__: &__Context__) -> &__Context__::Name; - } - impl<__Provider__, __Context__> NameGetter<__Context__> for __Provider__ - where - __Context__: HasNameType, - __Provider__: DelegateComponent - + IsProviderFor, - <__Provider__ as DelegateComponent< - NameGetterComponent, - >>::Delegate: NameGetter<__Context__>, - { - fn name(__context__: &__Context__) -> &__Context__::Name { - <__Provider__ as DelegateComponent< - NameGetterComponent, - >>::Delegate::name(__context__) - } - } - pub struct NameGetterComponent; - impl<__Context__> NameGetter<__Context__> for UseContext - where - __Context__: HasNameType, - __Context__: HasName, - { - fn name(__context__: &__Context__) -> &__Context__::Name { - __Context__::name(__context__) - } - } - impl<__Context__> IsProviderFor for UseContext - where - __Context__: HasNameType, - __Context__: HasName, - {} - impl<__Context__, __Components__, __Path__> NameGetter<__Context__> - for RedirectLookup<__Components__, __Path__> - where - __Context__: HasNameType, - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent<__Path__>>::Delegate: NameGetter<__Context__>, - { - fn name(__context__: &__Context__) -> &__Context__::Name { - <__Components__ as DelegateComponent<__Path__>>::Delegate::name(__context__) - } - } - impl< - __Context__, - __Components__, - __Path__, - > IsProviderFor - for RedirectLookup<__Components__, __Path__> - where - __Context__: HasNameType, - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent< - __Path__, - >>::Delegate: IsProviderFor - + NameGetter<__Context__>, - {} - impl<__Context__> NameGetter<__Context__> for UseFields - where - __Context__: HasNameType, - __Context__: HasField< - Symbol<4, Chars<'n', Chars<'a', Chars<'m', Chars<'e', Nil>>>>>, - Value = __Context__::Name, - >, - { - fn name(__context__: &__Context__) -> &__Context__::Name { - __context__ - .get_field( - ::core::marker::PhantomData::< - Symbol<4, Chars<'n', Chars<'a', Chars<'m', Chars<'e', Nil>>>>>, - >, - ) - } - } - impl<__Context__> IsProviderFor for UseFields - where - __Context__: HasNameType, - __Context__: HasField< - Symbol<4, Chars<'n', Chars<'a', Chars<'m', Chars<'e', Nil>>>>>, - Value = __Context__::Name, - >, - {} - impl<__Context__, __Tag__> NameGetter<__Context__> for UseField<__Tag__> - where - __Context__: HasNameType, - __Context__: HasField<__Tag__, Value = __Context__::Name>, - { - fn name(__context__: &__Context__) -> &__Context__::Name { - __context__.get_field(::core::marker::PhantomData::<__Tag__>) - } - } - impl<__Context__, __Tag__> IsProviderFor - for UseField<__Tag__> - where - __Context__: HasNameType, - __Context__: HasField<__Tag__, Value = __Context__::Name>, - {} - impl<__Context__, __Provider__> NameGetter<__Context__> for WithProvider<__Provider__> - where - __Context__: HasNameType, - __Provider__: FieldGetter< - __Context__, - NameGetterComponent, - Value = __Context__::Name, - >, - { - fn name(__context__: &__Context__) -> &__Context__::Name { - __Provider__::get_field( - __context__, - ::core::marker::PhantomData::, - ) - } - } - impl<__Context__, __Provider__> IsProviderFor - for WithProvider<__Provider__> - where - __Context__: HasNameType, - __Provider__: FieldGetter< - __Context__, - NameGetterComponent, - Value = __Context__::Name, - >, - {} - ") - } - } - - #[derive(HasField)] - pub struct MyContext { - pub name: String, - } - - snapshot_delegate_and_check_components! { - delegate_and_check_components! { - #[check_trait(CheckMyContext)] - MyContext { - NameTypeProviderComponent: UseType, - NameGetterComponent: UseField, - } - } - - expand_my_context(output) { - insta::assert_snapshot!(output, @r#" - impl DelegateComponent for MyContext { - type Delegate = UseType; - } - impl< - __Context__, - __Params__, - > IsProviderFor for MyContext - where - UseType: IsProviderFor, - {} - impl DelegateComponent for MyContext { - type Delegate = UseField; - } - impl<__Context__, __Params__> IsProviderFor - for MyContext - where - UseField< - Symbol!("name"), - >: IsProviderFor, - {} - trait CheckMyContext< - __Component__, - __Params__: ?Sized, - >: CanUseComponent<__Component__, __Params__> {} - impl CheckMyContext for MyContext {} - impl CheckMyContext for MyContext {} - "#) - } - } -} - -mod generic_delegate_and_check_components { - use cgp::prelude::*; - use cgp_macro_test_util::{ - snapshot_cgp_getter, snapshot_cgp_type, snapshot_delegate_and_check_components, - }; - - snapshot_cgp_type! { - #[cgp_type] - pub trait HasNameType { - type Name; - } - - expand_has_name_type(output) { - insta::assert_snapshot!(output, @" - pub trait HasNameType { - type Name; - } - impl<__Context__> HasNameType for __Context__ - where - __Context__: NameTypeProvider<__Context__>, - { - type Name = <__Context__ as NameTypeProvider<__Context__>>::Name; - } - pub trait NameTypeProvider< - __Context__, - >: IsProviderFor { - type Name; - } - impl<__Provider__, __Context__> NameTypeProvider<__Context__> for __Provider__ - where - __Provider__: DelegateComponent - + IsProviderFor, - <__Provider__ as DelegateComponent< - NameTypeProviderComponent, - >>::Delegate: NameTypeProvider<__Context__>, - { - type Name = <<__Provider__ as DelegateComponent< - NameTypeProviderComponent, - >>::Delegate as NameTypeProvider<__Context__>>::Name; - } - pub struct NameTypeProviderComponent; - impl<__Context__> NameTypeProvider<__Context__> for UseContext - where - __Context__: HasNameType, - { - type Name = <__Context__ as HasNameType>::Name; - } - impl<__Context__> IsProviderFor - for UseContext - where - __Context__: HasNameType, - {} - impl<__Context__, __Components__, __Path__> NameTypeProvider<__Context__> - for RedirectLookup<__Components__, __Path__> - where - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent< - __Path__, - >>::Delegate: NameTypeProvider<__Context__>, - { - type Name = <<__Components__ as DelegateComponent< - __Path__, - >>::Delegate as NameTypeProvider<__Context__>>::Name; - } - impl< - __Context__, - __Components__, - __Path__, - > IsProviderFor - for RedirectLookup<__Components__, __Path__> - where - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent< - __Path__, - >>::Delegate: IsProviderFor - + NameTypeProvider<__Context__>, - {} - impl NameTypeProvider<__Context__> for UseType { - type Name = Name; - } - impl IsProviderFor - for UseType {} - impl<__Provider__, Name, __Context__> NameTypeProvider<__Context__> - for WithProvider<__Provider__> - where - __Provider__: TypeProvider<__Context__, NameTypeProviderComponent, Type = Name>, - { - type Name = Name; - } - impl< - __Provider__, - Name, - __Context__, - > IsProviderFor - for WithProvider<__Provider__> - where - __Provider__: TypeProvider<__Context__, NameTypeProviderComponent, Type = Name>, - {} - ") - } - } - - snapshot_cgp_getter! { - #[cgp_getter] - pub trait HasName: HasNameType { - fn name(&self) -> &Self::Name; - } - - expand_has_name(output) { - insta::assert_snapshot!(output, @" - pub trait HasName: HasNameType { - fn name(&self) -> &Self::Name; - } - impl<__Context__> HasName for __Context__ - where - __Context__: HasNameType, - __Context__: NameGetter<__Context__>, - { - fn name(&self) -> &Self::Name { - __Context__::name(self) - } - } - pub trait NameGetter<__Context__>: IsProviderFor - where - __Context__: HasNameType, - { - fn name(__context__: &__Context__) -> &__Context__::Name; - } - impl<__Provider__, __Context__> NameGetter<__Context__> for __Provider__ - where - __Context__: HasNameType, - __Provider__: DelegateComponent - + IsProviderFor, - <__Provider__ as DelegateComponent< - NameGetterComponent, - >>::Delegate: NameGetter<__Context__>, - { - fn name(__context__: &__Context__) -> &__Context__::Name { - <__Provider__ as DelegateComponent< - NameGetterComponent, - >>::Delegate::name(__context__) - } - } - pub struct NameGetterComponent; - impl<__Context__> NameGetter<__Context__> for UseContext - where - __Context__: HasNameType, - __Context__: HasName, - { - fn name(__context__: &__Context__) -> &__Context__::Name { - __Context__::name(__context__) - } - } - impl<__Context__> IsProviderFor for UseContext - where - __Context__: HasNameType, - __Context__: HasName, - {} - impl<__Context__, __Components__, __Path__> NameGetter<__Context__> - for RedirectLookup<__Components__, __Path__> - where - __Context__: HasNameType, - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent<__Path__>>::Delegate: NameGetter<__Context__>, - { - fn name(__context__: &__Context__) -> &__Context__::Name { - <__Components__ as DelegateComponent<__Path__>>::Delegate::name(__context__) - } - } - impl< - __Context__, - __Components__, - __Path__, - > IsProviderFor - for RedirectLookup<__Components__, __Path__> - where - __Context__: HasNameType, - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent< - __Path__, - >>::Delegate: IsProviderFor - + NameGetter<__Context__>, - {} - impl<__Context__> NameGetter<__Context__> for UseFields - where - __Context__: HasNameType, - __Context__: HasField< - Symbol<4, Chars<'n', Chars<'a', Chars<'m', Chars<'e', Nil>>>>>, - Value = __Context__::Name, - >, - { - fn name(__context__: &__Context__) -> &__Context__::Name { - __context__ - .get_field( - ::core::marker::PhantomData::< - Symbol<4, Chars<'n', Chars<'a', Chars<'m', Chars<'e', Nil>>>>>, - >, - ) - } - } - impl<__Context__> IsProviderFor for UseFields - where - __Context__: HasNameType, - __Context__: HasField< - Symbol<4, Chars<'n', Chars<'a', Chars<'m', Chars<'e', Nil>>>>>, - Value = __Context__::Name, - >, - {} - impl<__Context__, __Tag__> NameGetter<__Context__> for UseField<__Tag__> - where - __Context__: HasNameType, - __Context__: HasField<__Tag__, Value = __Context__::Name>, - { - fn name(__context__: &__Context__) -> &__Context__::Name { - __context__.get_field(::core::marker::PhantomData::<__Tag__>) - } - } - impl<__Context__, __Tag__> IsProviderFor - for UseField<__Tag__> - where - __Context__: HasNameType, - __Context__: HasField<__Tag__, Value = __Context__::Name>, - {} - impl<__Context__, __Provider__> NameGetter<__Context__> for WithProvider<__Provider__> - where - __Context__: HasNameType, - __Provider__: FieldGetter< - __Context__, - NameGetterComponent, - Value = __Context__::Name, - >, - { - fn name(__context__: &__Context__) -> &__Context__::Name { - __Provider__::get_field( - __context__, - ::core::marker::PhantomData::, - ) - } - } - impl<__Context__, __Provider__> IsProviderFor - for WithProvider<__Provider__> - where - __Context__: HasNameType, - __Provider__: FieldGetter< - __Context__, - NameGetterComponent, - Value = __Context__::Name, - >, - {} - ") - } - } - - #[derive(HasField)] - pub struct MyContext { - pub name: T, - } - - snapshot_delegate_and_check_components! { - delegate_and_check_components! { - - MyContext { - NameTypeProviderComponent: UseType, - NameGetterComponent: UseField, - } - } - - expand_my_context(output) { - insta::assert_snapshot!(output, @r#" - impl DelegateComponent for MyContext { - type Delegate = UseType; - } - impl< - T, - __Context__, - __Params__, - > IsProviderFor for MyContext - where - UseType: IsProviderFor, - {} - impl DelegateComponent for MyContext { - type Delegate = UseField; - } - impl< - T, - __Context__, - __Params__, - > IsProviderFor for MyContext - where - UseField< - Symbol!("name"), - >: IsProviderFor, - {} - trait __CanUseMyContext< - __Component__, - __Params__: ?Sized, - >: CanUseComponent<__Component__, __Params__> {} - impl __CanUseMyContext for MyContext {} - impl __CanUseMyContext for MyContext {} - "#) - } - } -} diff --git a/crates/tests/cgp-tests/src/tests/mod.rs b/crates/tests/cgp-tests/src/tests/mod.rs deleted file mode 100644 index e9213554..00000000 --- a/crates/tests/cgp-tests/src/tests/mod.rs +++ /dev/null @@ -1,8 +0,0 @@ -pub mod r#async; -pub mod blanket_trait; -pub mod check_components; -pub mod compose; -pub mod delegate_and_check_components; -pub mod monad; -pub mod symbol; -pub mod use_delegate; diff --git a/crates/tests/cgp-tests/src/tests/monad/mod.rs b/crates/tests/cgp-tests/src/tests/monad/mod.rs deleted file mode 100644 index 61826bc1..00000000 --- a/crates/tests/cgp-tests/src/tests/monad/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod err; -pub mod ok; -pub mod ok_err_trans; diff --git a/crates/tests/cgp-tests/src/tests/use_delegate/mod.rs b/crates/tests/cgp-tests/src/tests/use_delegate/mod.rs deleted file mode 100644 index 84abcd1e..00000000 --- a/crates/tests/cgp-tests/src/tests/use_delegate/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod getter; diff --git a/crates/tests/cgp-tests/tests/abstract_types/cgp_type_bounded.rs b/crates/tests/cgp-tests/tests/abstract_types/cgp_type_bounded.rs new file mode 100644 index 00000000..bfd04d0d --- /dev/null +++ b/crates/tests/cgp-tests/tests/abstract_types/cgp_type_bounded.rs @@ -0,0 +1,122 @@ +//! `#[cgp_type]` where the abstract type is bounded by *another* abstract-type +//! component (`type Types: HasScalarType`). +//! +//! This variant shows the bound propagating onto the generated `UseType` +//! and `WithProvider` impls as `where Types: HasScalarType`, so a context may only +//! wire a concrete `Types` that itself implements `HasScalarType`. The prerequisite +//! `HasScalarType` is written with a plain `#[cgp_type]` (its simple expansion is +//! pinned in `cgp_type_macro`). +//! +//! See docs/reference/macros/cgp_type.md and docs/concepts/abstract-types.md. + +use cgp::prelude::*; +use cgp_macro_test_util::snapshot_cgp_type; + +#[cgp_type] +pub trait HasScalarType { + type Scalar; +} + +snapshot_cgp_type! { + #[cgp_type] + pub trait HasTypes { + type Types: HasScalarType; + } + + expand_has_types(output) { + insta::assert_snapshot!(output, @" + pub trait HasTypes { + type Types: HasScalarType; + } + impl<__Context__> HasTypes for __Context__ + where + __Context__: TypesTypeProvider<__Context__>, + { + type Types = <__Context__ as TypesTypeProvider<__Context__>>::Types; + } + pub trait TypesTypeProvider< + __Context__, + >: IsProviderFor { + type Types: HasScalarType; + } + impl<__Provider__, __Context__> TypesTypeProvider<__Context__> for __Provider__ + where + __Provider__: DelegateComponent + + IsProviderFor, + <__Provider__ as DelegateComponent< + TypesTypeProviderComponent, + >>::Delegate: TypesTypeProvider<__Context__>, + { + type Types = <<__Provider__ as DelegateComponent< + TypesTypeProviderComponent, + >>::Delegate as TypesTypeProvider<__Context__>>::Types; + } + pub struct TypesTypeProviderComponent; + impl<__Context__> TypesTypeProvider<__Context__> for UseContext + where + __Context__: HasTypes, + { + type Types = <__Context__ as HasTypes>::Types; + } + impl<__Context__> IsProviderFor + for UseContext + where + __Context__: HasTypes, + {} + impl<__Context__, __Components__, __Path__> TypesTypeProvider<__Context__> + for RedirectLookup<__Components__, __Path__> + where + __Components__: DelegateComponent<__Path__>, + <__Components__ as DelegateComponent< + __Path__, + >>::Delegate: TypesTypeProvider<__Context__>, + { + type Types = <<__Components__ as DelegateComponent< + __Path__, + >>::Delegate as TypesTypeProvider<__Context__>>::Types; + } + impl< + __Context__, + __Components__, + __Path__, + > IsProviderFor + for RedirectLookup<__Components__, __Path__> + where + __Components__: DelegateComponent<__Path__>, + <__Components__ as DelegateComponent< + __Path__, + >>::Delegate: IsProviderFor + + TypesTypeProvider<__Context__>, + {} + impl TypesTypeProvider<__Context__> for UseType + where + Types: HasScalarType, + { + type Types = Types; + } + impl IsProviderFor + for UseType + where + Types: HasScalarType, + {} + impl<__Provider__, Types, __Context__> TypesTypeProvider<__Context__> + for WithProvider<__Provider__> + where + Types: HasScalarType, + __Provider__: TypeProvider<__Context__, TypesTypeProviderComponent, Type = Types>, + { + type Types = Types; + } + impl< + __Provider__, + Types, + __Context__, + > IsProviderFor + for WithProvider<__Provider__> + where + Types: HasScalarType, + __Provider__: TypeProvider<__Context__, TypesTypeProviderComponent, Type = Types>, + {} + ") + } +} diff --git a/crates/tests/cgp-tests/tests/component_tests/abstract_types/foreign.rs b/crates/tests/cgp-tests/tests/abstract_types/cgp_type_macro.rs similarity index 70% rename from crates/tests/cgp-tests/tests/component_tests/abstract_types/foreign.rs rename to crates/tests/cgp-tests/tests/abstract_types/cgp_type_macro.rs index eb97e401..f39255a7 100644 --- a/crates/tests/cgp-tests/tests/component_tests/abstract_types/foreign.rs +++ b/crates/tests/cgp-tests/tests/abstract_types/cgp_type_macro.rs @@ -1,9 +1,17 @@ -use std::convert::Infallible; -use std::ops::Mul; +//! Canonical expansion of `#[cgp_type]` for a simple abstract-type trait. +//! +//! `#[cgp_type]` is the dedicated macro for an abstract-type component: like +//! `#[cgp_component]` it emits the consumer trait, the provider trait (here the +//! provider name defaults to the type name plus `TypeProvider`), the `…Component` +//! marker, and the routing blanket impls — but it *additionally* emits the +//! `UseType` blanket impl (and the `WithProvider` bridge), which is what lets +//! a context fix the abstract type by wiring the component to `UseType`. +//! This is the reference snapshot for that expansion; other files reuse +//! `#[cgp_type]` plainly. +//! +//! See docs/reference/macros/cgp_type.md and docs/concepts/abstract-types.md. -use cgp::core::error::ErrorTypeProviderComponent; -use cgp::prelude::*; -use cgp_macro_test_util::{snapshot_cgp_type, snapshot_check_components}; +use cgp_macro_test_util::snapshot_cgp_type; snapshot_cgp_type! { #[cgp_type] @@ -100,59 +108,3 @@ snapshot_cgp_type! { ") } } - -#[cgp_component(AreaCalculator)] -#[use_type(@Types::HasScalarType::Scalar, HasErrorType::Error)] -pub trait CanCalculateArea { - fn area(&self) -> Result; -} - -#[cgp_impl(new RectangleArea)] -#[use_type(@Types::HasScalarType::Scalar, HasErrorType::Error)] -impl AreaCalculator -where - Scalar: Mul + Copy, -{ - fn area(&self, #[implicit] width: Scalar, #[implicit] height: Scalar) -> Result { - Ok(width * height) - } -} - -#[derive(HasField)] -pub struct Rectangle { - pub width: f64, - pub height: f64, -} - -pub struct Types; - -impl HasScalarType for Types { - type Scalar = f64; -} - -delegate_components! { - Rectangle { - ErrorTypeProviderComponent: - UseType, - AreaCalculatorComponent: - RectangleArea, - } -} - -snapshot_check_components! { - check_components! { - Rectangle { - AreaCalculatorComponent: Types, - } - } - - expand_check_rectangle(output) { - insta::assert_snapshot!(output, @" - trait __CheckRectangle< - __Component__, - __Params__: ?Sized, - >: CanUseComponent<__Component__, __Params__> {} - impl __CheckRectangle for Rectangle {} - ") - } -} diff --git a/crates/tests/cgp-tests/tests/component_tests/abstract_types/self_referential.rs b/crates/tests/cgp-tests/tests/abstract_types/cgp_type_self_referential.rs similarity index 84% rename from crates/tests/cgp-tests/tests/component_tests/abstract_types/self_referential.rs rename to crates/tests/cgp-tests/tests/abstract_types/cgp_type_self_referential.rs index a99144ac..3f364db4 100644 --- a/crates/tests/cgp-tests/tests/component_tests/abstract_types/self_referential.rs +++ b/crates/tests/cgp-tests/tests/abstract_types/cgp_type_self_referential.rs @@ -1,7 +1,19 @@ +//! `#[cgp_type]` where the abstract type carries a *self-referential* bound +//! (`type Scalar: Mul + Clone`). +//! +//! This is a distinct `#[cgp_type]` expansion variant: the bound is threaded onto +//! the provider trait's associated type and, notably, onto the generated +//! `UseType` and `WithProvider` impls as a `where Scalar: Mul + Clone` clause (with `Self::Scalar` rewritten to the free `Scalar` +//! parameter). The `delegate_components!` wiring and its check are incidental +//! scaffolding and use the plain macros. +//! +//! See docs/reference/macros/cgp_type.md and docs/concepts/abstract-types.md. + use core::ops::Mul; use cgp::prelude::*; -use cgp_macro_test_util::{snapshot_cgp_type, snapshot_check_components}; +use cgp_macro_test_util::snapshot_cgp_type; snapshot_cgp_type! { #[cgp_type] @@ -116,20 +128,8 @@ delegate_components! { } } -snapshot_check_components! { - check_components! { - App { - ScalarTypeProviderComponent, - } - } - - expand_check_app(output) { - insta::assert_snapshot!(output, @" - trait __CheckApp< - __Component__, - __Params__: ?Sized, - >: CanUseComponent<__Component__, __Params__> {} - impl __CheckApp for App {} - ") +check_components! { + App { + ScalarTypeProviderComponent, } } diff --git a/crates/tests/cgp-tests/tests/component_tests/cgp_component/sized.rs b/crates/tests/cgp-tests/tests/abstract_types/cgp_type_unsized.rs similarity index 86% rename from crates/tests/cgp-tests/tests/component_tests/cgp_component/sized.rs rename to crates/tests/cgp-tests/tests/abstract_types/cgp_type_unsized.rs index 746e1189..bf4af064 100644 --- a/crates/tests/cgp-tests/tests/component_tests/cgp_component/sized.rs +++ b/crates/tests/cgp-tests/tests/abstract_types/cgp_type_unsized.rs @@ -1,3 +1,15 @@ +//! `#[cgp_type]` with an unsized (`T: ?Sized`) generic parameter and a custom +//! provider name. +//! +//! This variant exercises two things at once: the `#[cgp_type(ProvideFooType)]` +//! argument overriding the default `…TypeProvider` name, and a `?Sized` generic +//! parameter, which the expansion threads through every generated item (the +//! provider trait's `Params` tuple becomes `(T)` and the `RedirectLookup` impls +//! extend the path with `PathCons`). A trailing `#[cgp_getter]` that +//! depends on the abstract type is incidental scaffolding and is written plainly. +//! +//! See docs/reference/macros/cgp_type.md and docs/concepts/abstract-types.md. + use cgp::prelude::*; use cgp_macro_test_util::snapshot_cgp_type; diff --git a/crates/tests/cgp-tests/tests/abstract_types/extend_component.rs b/crates/tests/cgp-tests/tests/abstract_types/extend_component.rs new file mode 100644 index 00000000..04186e9a --- /dev/null +++ b/crates/tests/cgp-tests/tests/abstract_types/extend_component.rs @@ -0,0 +1,60 @@ +//! The `#[extend(...)]` + `Self::Scalar` alternative to `#[use_type]` for a +//! `#[cgp_component]`/`#[cgp_impl]` that names abstract types. +//! +//! Instead of importing and rewriting the aliases, `#[extend(HasScalarType, +//! HasErrorType)]` only adds the abstract-type components as supertraits, and the +//! signatures name the types the long way as `Self::Scalar` / `Self::Error`. The +//! provider mirrors this with `#[uses(HasScalarType, HasErrorType)]`. This is the +//! more verbose counterpart to `use_type_component`; `#[use_type]` is the +//! preferred form. Wiring/check and `#[cgp_type]` are incidental scaffolding here. +//! +//! See docs/reference/attributes/use_type.md and docs/concepts/abstract-types.md. + +use std::convert::Infallible; +use std::ops::Mul; + +use cgp::core::error::ErrorTypeProviderComponent; +use cgp::prelude::*; + +#[cgp_type] +pub trait HasScalarType { + type Scalar; +} + +#[cgp_component(AreaCalculator)] +#[extend(HasScalarType, HasErrorType)] +pub trait CanCalculateArea { + fn area(&self) -> Result; +} + +#[cgp_impl(new RectangleArea)] +#[uses(HasScalarType, HasErrorType)] +impl AreaCalculator +where + Self::Scalar: Mul + Copy, +{ + fn area( + &self, + #[implicit] width: Self::Scalar, + #[implicit] height: Self::Scalar, + ) -> Result { + Ok(width * height) + } +} + +#[derive(HasField)] +pub struct Rectangle { + pub width: f64, + pub height: f64, +} + +delegate_and_check_components! { + Rectangle { + ErrorTypeProviderComponent: + UseType, + ScalarTypeProviderComponent: + UseType, + AreaCalculatorComponent: + RectangleArea, + } +} diff --git a/crates/tests/cgp-tests/tests/abstract_types/mod.rs b/crates/tests/cgp-tests/tests/abstract_types/mod.rs new file mode 100644 index 00000000..d326ecc0 --- /dev/null +++ b/crates/tests/cgp-tests/tests/abstract_types/mod.rs @@ -0,0 +1,32 @@ +//! One unit test per file. Each file is self-contained: it defines its own +//! abstract-type components, providers, and context types at module scope so that +//! the type-level wiring of one test never leaks into another. + +// Canonical `#[cgp_type]` macro-expansion snapshots (this concept owns them): the +// full expansion plus its genuinely distinct variants — a self-referential +// associated-type bound, an unsized generic parameter with a custom provider +// name, and an associated type bounded by another abstract-type component. +pub mod cgp_type_bounded; +pub mod cgp_type_macro; +pub mod cgp_type_self_referential; +pub mod cgp_type_unsized; + +// The `#[use_type]` attribute (and the `#[extend]` alternative) importing an +// abstract type into a `#[cgp_component]`/`#[cgp_impl]`. +pub mod extend_component; +pub mod use_type_component; +pub mod use_type_foreign; +pub mod use_type_generic_param; + +// The `#[use_type]` attribute rewriting abstract types inside `#[cgp_fn]`: the +// bare alias, alias renaming, type-equality bounds, and foreign/nested type +// sources. These keep the `#[cgp_fn]` snapshot because the abstract-type rewrite +// is the point (the `#[cgp_fn]` expansion itself is owned by `implicit_arguments`). +pub mod use_type_fn_alias; +pub mod use_type_fn_equality; +pub mod use_type_fn_equality_cross_trait; +pub mod use_type_fn_extend; +pub mod use_type_fn_foreign; +pub mod use_type_fn_foreign_equality; +pub mod use_type_fn_foreign_equality_cross_trait; +pub mod use_type_fn_nested_foreign; diff --git a/crates/tests/cgp-tests/tests/abstract_types/use_type_component.rs b/crates/tests/cgp-tests/tests/abstract_types/use_type_component.rs new file mode 100644 index 00000000..68aaf651 --- /dev/null +++ b/crates/tests/cgp-tests/tests/abstract_types/use_type_component.rs @@ -0,0 +1,59 @@ +//! Importing abstract types into a `#[cgp_component]` and `#[cgp_impl]` with +//! `#[use_type(...)]`, and fixing them on a context with `UseType`. +//! +//! `#[use_type(HasScalarType::Scalar, HasErrorType::Error)]` adds the two +//! abstract-type components as supertraits/dependencies and rewrites the bare +//! aliases `Scalar` and `Error` to `::Scalar` / +//! `::Error`, so the signatures read without any `Self::` +//! qualification. The `Rectangle` context then fixes both types by wiring the +//! respective components to `UseType` / `UseType`. The +//! `#[cgp_type]` scaffolding and the delegation/check are incidental here and use +//! the plain macros; the abstract-type expansion and wiring are owned elsewhere. +//! +//! See docs/reference/attributes/use_type.md, docs/reference/providers/use_type.md, +//! and docs/concepts/abstract-types.md. + +use std::convert::Infallible; +use std::ops::Mul; + +use cgp::core::error::ErrorTypeProviderComponent; +use cgp::prelude::*; + +#[cgp_type] +pub trait HasScalarType { + type Scalar; +} + +#[cgp_component(AreaCalculator)] +#[use_type(HasScalarType::Scalar, HasErrorType::Error)] +pub trait CanCalculateArea { + fn area(&self) -> Result; +} + +#[cgp_impl(new RectangleArea)] +#[use_type(HasScalarType::Scalar, HasErrorType::Error)] +impl AreaCalculator +where + Scalar: Mul + Copy, +{ + fn area(&self, #[implicit] width: Scalar, #[implicit] height: Scalar) -> Result { + Ok(width * height) + } +} + +#[derive(HasField)] +pub struct Rectangle { + pub width: f64, + pub height: f64, +} + +delegate_and_check_components! { + Rectangle { + ErrorTypeProviderComponent: + UseType, + ScalarTypeProviderComponent: + UseType, + AreaCalculatorComponent: + RectangleArea, + } +} diff --git a/crates/tests/cgp-tests/tests/abstract_types/use_type_fn_alias.rs b/crates/tests/cgp-tests/tests/abstract_types/use_type_fn_alias.rs new file mode 100644 index 00000000..5ca4f3eb --- /dev/null +++ b/crates/tests/cgp-tests/tests/abstract_types/use_type_fn_alias.rs @@ -0,0 +1,92 @@ +//! `#[use_type]` with a *local alias* in a `#[cgp_fn]`: +//! `#[use_type(HasScalarType::{Scalar as S})]`. +//! +//! The `{Scalar as S}` form imports the abstract type under the short name `S`, +//! which the function body and `where` clause then use freely; the expansion +//! rewrites every `S` to `::Scalar`. This is the +//! `#[use_type]` counterpart to `use_type_fn_extend`. The `#[cgp_fn]` snapshot is +//! kept because the abstract-type rewrite is the point; `#[cgp_type]` scaffolding +//! is written plainly. +//! +//! See docs/reference/attributes/use_type.md and docs/concepts/abstract-types.md. + +use std::ops::Mul; + +use cgp::prelude::*; +use cgp_macro_test_util::snapshot_cgp_fn; + +#[cgp_type] +pub trait HasScalarType { + type Scalar; +} + +snapshot_cgp_fn! { + #[cgp_fn] + #[use_type(HasScalarType::{Scalar as S})] + pub fn rectangle_area(&self, #[implicit] width: S, #[implicit] height: S) -> S + where + S: Mul + Copy, + { + let res: S = width * height; + res + } + + expand_rectangle_area(output) { + insta::assert_snapshot!(output, @" + pub trait RectangleArea: HasScalarType { + fn rectangle_area(&self) -> ::Scalar; + } + impl<__Context__> RectangleArea for __Context__ + where + ::Scalar: Mul::Scalar> + + Copy, + Self: HasField< + Symbol<5, Chars<'w', Chars<'i', Chars<'d', Chars<'t', Chars<'h', Nil>>>>>>, + Value = ::Scalar, + > + + HasField< + Symbol< + 6, + Chars< + 'h', + Chars<'e', Chars<'i', Chars<'g', Chars<'h', Chars<'t', Nil>>>>>, + >, + >, + Value = ::Scalar, + >, + Self: HasScalarType, + { + fn rectangle_area(&self) -> ::Scalar { + let width: ::Scalar = self + .get_field( + ::core::marker::PhantomData::< + Symbol< + 5, + Chars<'w', Chars<'i', Chars<'d', Chars<'t', Chars<'h', Nil>>>>>, + >, + >, + ) + .clone(); + let height: ::Scalar = self + .get_field( + ::core::marker::PhantomData::< + Symbol< + 6, + Chars< + 'h', + Chars< + 'e', + Chars<'i', Chars<'g', Chars<'h', Chars<'t', Nil>>>>, + >, + >, + >, + >, + ) + .clone(); + let res: ::Scalar = width * height; + res + } + } + ") + } +} diff --git a/crates/tests/cgp-tests/tests/abstract_types/use_type_fn_equality.rs b/crates/tests/cgp-tests/tests/abstract_types/use_type_fn_equality.rs new file mode 100644 index 00000000..b12e8395 --- /dev/null +++ b/crates/tests/cgp-tests/tests/abstract_types/use_type_fn_equality.rs @@ -0,0 +1,84 @@ +//! `#[use_type]` with a *type-equality* bound in a `#[cgp_fn]`: +//! `#[use_type(HasScalarType::{Scalar = f64})]`. +//! +//! The `{Scalar = f64}` form both imports `Scalar` (rewriting the bare alias to +//! `::Scalar`) and pins it to a concrete type by adding +//! `Self: HasScalarType` to the impl's `where` clause, so the body +//! may treat the value as an `f64` while the signature still speaks the abstract +//! alias. The `#[cgp_fn]` snapshot is kept for the rewrite; `#[cgp_type]` is plain. +//! +//! See docs/reference/attributes/use_type.md and docs/concepts/abstract-types.md. + +use cgp::prelude::*; +use cgp_macro_test_util::snapshot_cgp_fn; + +#[cgp_type] +pub trait HasScalarType { + type Scalar; +} + +snapshot_cgp_fn! { + #[cgp_fn] + #[use_type(HasScalarType::{Scalar = f64})] + pub fn rectangle_area(&self, #[implicit] width: Scalar, #[implicit] height: Scalar) -> Scalar { + let res: f64 = width * height; + res + } + + expand_rectangle_area(output) { + insta::assert_snapshot!(output, @" + pub trait RectangleArea: HasScalarType { + fn rectangle_area(&self) -> ::Scalar; + } + impl<__Context__> RectangleArea for __Context__ + where + Self: HasField< + Symbol<5, Chars<'w', Chars<'i', Chars<'d', Chars<'t', Chars<'h', Nil>>>>>>, + Value = ::Scalar, + > + + HasField< + Symbol< + 6, + Chars< + 'h', + Chars<'e', Chars<'i', Chars<'g', Chars<'h', Chars<'t', Nil>>>>>, + >, + >, + Value = ::Scalar, + >, + Self: HasScalarType, + { + fn rectangle_area(&self) -> ::Scalar { + let width: ::Scalar = self + .get_field( + ::core::marker::PhantomData::< + Symbol< + 5, + Chars<'w', Chars<'i', Chars<'d', Chars<'t', Chars<'h', Nil>>>>>, + >, + >, + ) + .clone(); + let height: ::Scalar = self + .get_field( + ::core::marker::PhantomData::< + Symbol< + 6, + Chars< + 'h', + Chars< + 'e', + Chars<'i', Chars<'g', Chars<'h', Chars<'t', Nil>>>>, + >, + >, + >, + >, + ) + .clone(); + let res: f64 = width * height; + res + } + } + ") + } +} diff --git a/crates/tests/cgp-tests/tests/abstract_types/use_type_fn_equality_cross_trait.rs b/crates/tests/cgp-tests/tests/abstract_types/use_type_fn_equality_cross_trait.rs new file mode 100644 index 00000000..ff502459 --- /dev/null +++ b/crates/tests/cgp-tests/tests/abstract_types/use_type_fn_equality_cross_trait.rs @@ -0,0 +1,134 @@ +//! `#[use_type]` type-equality *across two traits* in `#[cgp_fn]`: +//! `#[use_type(HasBarType::{Bar as Baz = Foo}, HasFooType::Foo)]`. +//! +//! The `{Bar as Baz = Foo}` form imports `HasBarType::Bar` under the alias `Baz` +//! and equates it to the `Foo` alias imported from `HasFooType`, generating a +//! `Self: HasBarType::Foo>` bound. The consequence +//! pinned here: the `Ord + Clone` bounds declared on `HasFooType::Foo` become +//! visible for both aliases, while the `Display` bound on `HasBarType::Bar` is +//! hidden — because the two are the same type. `do_foo`/`do_bar` are the +//! dependencies `return_foo_or_bar` imports with `#[uses]`. All three `#[cgp_fn]` +//! snapshots are kept because the abstract-type rewrite is the point. +//! +//! See docs/reference/attributes/use_type.md and docs/concepts/abstract-types.md. + +use std::fmt::Display; + +use cgp_macro_test_util::snapshot_cgp_fn; + +pub trait HasFooType { + // The `Ord + Clone` bounds are visible to both `Foo` and `Bar` because of `Bar = Foo` below + type Foo: Ord + Clone; +} + +pub trait HasBarType { + // The `Display` bounds are hidden because of `Bar = Foo` below + type Bar: Display; +} + +snapshot_cgp_fn! { + #[cgp_fn] + #[use_type(HasFooType::Foo)] + pub fn do_foo(&self) -> Foo { + todo!() + } + + expand_do_foo(output) { + insta::assert_snapshot!(output, @" + pub trait DoFoo: HasFooType { + fn do_foo(&self) -> ::Foo; + } + impl<__Context__> DoFoo for __Context__ + where + Self: HasFooType, + { + fn do_foo(&self) -> ::Foo { + todo!() + } + } + ") + } +} + +snapshot_cgp_fn! { + #[cgp_fn] + #[use_type(HasBarType::Bar)] + pub fn do_bar(&self) -> Bar { + todo!() + } + + expand_do_bar(output) { + insta::assert_snapshot!(output, @" + pub trait DoBar: HasBarType { + fn do_bar(&self) -> ::Bar; + } + impl<__Context__> DoBar for __Context__ + where + Self: HasBarType, + { + fn do_bar(&self) -> ::Bar { + todo!() + } + } + ") + } +} + +snapshot_cgp_fn! { + #[cgp_fn] + #[use_type(HasBarType::{Bar as Baz = Foo}, HasFooType::Foo)] + #[uses(DoFoo, DoBar)] + fn return_foo_or_bar(&self, flag: bool, #[implicit] foo: &Foo, #[implicit] bar: &Baz) -> Foo { + if flag { + let res: Foo = self.do_foo(); + if &res < foo { res } else { foo.clone() } + } else { + let res: Baz = self.do_bar(); + if &res < bar { res } else { bar.clone() } + } + } + + expand_return_foo_or_bar(output) { + insta::assert_snapshot!(output, @" + trait ReturnFooOrBar: HasBarType + HasFooType { + fn return_foo_or_bar(&self, flag: bool) -> ::Foo; + } + impl<__Context__> ReturnFooOrBar for __Context__ + where + Self: DoFoo + DoBar, + Self: HasField< + Symbol<3, Chars<'f', Chars<'o', Chars<'o', Nil>>>>, + Value = ::Foo, + > + + HasField< + Symbol<3, Chars<'b', Chars<'a', Chars<'r', Nil>>>>, + Value = ::Bar, + >, + Self: HasBarType::Foo>, + Self: HasFooType, + { + fn return_foo_or_bar(&self, flag: bool) -> ::Foo { + let foo: &::Foo = self + .get_field( + ::core::marker::PhantomData::< + Symbol<3, Chars<'f', Chars<'o', Chars<'o', Nil>>>>, + >, + ); + let bar: &::Bar = self + .get_field( + ::core::marker::PhantomData::< + Symbol<3, Chars<'b', Chars<'a', Chars<'r', Nil>>>>, + >, + ); + if flag { + let res: ::Foo = self.do_foo(); + if &res < foo { res } else { foo.clone() } + } else { + let res: ::Bar = self.do_bar(); + if &res < bar { res } else { bar.clone() } + } + } + } + ") + } +} diff --git a/crates/tests/cgp-tests/tests/abstract_types/use_type_fn_extend.rs b/crates/tests/cgp-tests/tests/abstract_types/use_type_fn_extend.rs new file mode 100644 index 00000000..3b1324dc --- /dev/null +++ b/crates/tests/cgp-tests/tests/abstract_types/use_type_fn_extend.rs @@ -0,0 +1,128 @@ +//! Abstract types in a `#[cgp_fn]` via `#[extend(HasScalarType)]` + `Self::Scalar`. +//! +//! `#[cgp_fn]`'s own `where` clauses are impl-side dependencies, so a supertrait +//! is added with `#[extend]`; the abstract type is then named the long way as +//! `Self::Scalar`. This is the `#[extend]` counterpart to `use_type_fn_alias` +//! (which uses `#[use_type]` and the bare alias). The `#[cgp_fn]` snapshot is kept +//! because the point is how the abstract type flows into the generated trait/impl; +//! the `#[cgp_fn]` expansion mechanics themselves are owned by `implicit_arguments`. +//! Two runtime contexts (`f32`/`f64`) confirm the generic result. +//! +//! See docs/reference/attributes/use_type.md, docs/reference/macros/cgp_fn.md, +//! and docs/concepts/abstract-types.md. + +use core::f64; +use std::ops::Mul; + +use cgp::prelude::*; +use cgp_macro_test_util::snapshot_cgp_fn; + +#[cgp_type] +pub trait HasScalarType { + type Scalar: Mul + Copy; +} + +snapshot_cgp_fn! { + #[cgp_fn] + #[extend(HasScalarType)] + pub fn rectangle_area( + &self, + #[implicit] width: Self::Scalar, + #[implicit] height: Self::Scalar, + ) -> Self::Scalar { + width * height + } + + expand_rectangle_area(output) { + insta::assert_snapshot!(output, @" + pub trait RectangleArea: HasScalarType { + fn rectangle_area(&self) -> Self::Scalar; + } + impl<__Context__> RectangleArea for __Context__ + where + Self: HasScalarType, + Self: HasField< + Symbol<5, Chars<'w', Chars<'i', Chars<'d', Chars<'t', Chars<'h', Nil>>>>>>, + Value = Self::Scalar, + > + + HasField< + Symbol< + 6, + Chars< + 'h', + Chars<'e', Chars<'i', Chars<'g', Chars<'h', Chars<'t', Nil>>>>>, + >, + >, + Value = Self::Scalar, + >, + { + fn rectangle_area(&self) -> Self::Scalar { + let width: Self::Scalar = self + .get_field( + ::core::marker::PhantomData::< + Symbol< + 5, + Chars<'w', Chars<'i', Chars<'d', Chars<'t', Chars<'h', Nil>>>>>, + >, + >, + ) + .clone(); + let height: Self::Scalar = self + .get_field( + ::core::marker::PhantomData::< + Symbol< + 6, + Chars< + 'h', + Chars< + 'e', + Chars<'i', Chars<'g', Chars<'h', Chars<'t', Nil>>>>, + >, + >, + >, + >, + ) + .clone(); + width * height + } + } + ") + } +} + +#[derive(HasField)] +pub struct F32Rectangle { + pub width: f32, + pub height: f32, +} + +impl HasScalarType for F32Rectangle { + type Scalar = f32; +} + +#[derive(HasField)] +pub struct F64Rectangle { + pub width: f64, + pub height: f64, +} + +impl HasScalarType for F64Rectangle { + type Scalar = f64; +} + +#[test] +fn test_rectangle_area() { + let f32_rectangle = F32Rectangle { + width: 3.0, + height: 4.0, + }; + + assert_eq!(f32_rectangle.rectangle_area(), 12.0); + + let f64_rectangle = F64Rectangle { + width: 3.0, + height: 4.0, + }; + + assert_eq!(f64_rectangle.rectangle_area(), 12.0); +} diff --git a/crates/tests/cgp-tests/tests/abstract_types/use_type_fn_foreign.rs b/crates/tests/cgp-tests/tests/abstract_types/use_type_fn_foreign.rs new file mode 100644 index 00000000..19362dd1 --- /dev/null +++ b/crates/tests/cgp-tests/tests/abstract_types/use_type_fn_foreign.rs @@ -0,0 +1,111 @@ +//! `#[use_type]` importing an abstract type from a *foreign generic parameter* in +//! a `#[cgp_fn]`: `#[use_type(@Types::HasScalarType::Scalar)]`. +//! +//! The function is generic over `Types: HasScalarType`, and the `@Types::` prefix +//! resolves `Scalar` against that parameter, rewriting the bare alias to +//! `::Scalar` throughout — the generated trait becomes +//! `RectangleArea`. `CheckRectangle` is a compile-time +//! assertion that `Rectangle` implements the generated trait for the concrete +//! `Types`. The `#[cgp_fn]` snapshot is kept for the rewrite; `#[cgp_type]` is plain. +//! +//! See docs/reference/attributes/use_type.md and docs/concepts/abstract-types.md. + +use std::ops::Mul; + +use cgp::prelude::*; +use cgp_macro_test_util::snapshot_cgp_fn; + +#[cgp_type] +pub trait HasScalarType { + type Scalar; +} + +snapshot_cgp_fn! { + #[cgp_fn] + #[use_type(@Types::HasScalarType::Scalar)] + pub fn rectangle_area( + &self, + #[implicit] width: Scalar, + #[implicit] height: Scalar, + ) -> Scalar + where + Scalar: Mul + Copy, + { + let res: Scalar = width * height; + res + } + + expand_rectangle_area(output) { + insta::assert_snapshot!(output, @" + pub trait RectangleArea { + fn rectangle_area(&self) -> ::Scalar; + } + impl<__Context__, Types: HasScalarType> RectangleArea for __Context__ + where + ::Scalar: Mul::Scalar> + + Copy, + Self: HasField< + Symbol<5, Chars<'w', Chars<'i', Chars<'d', Chars<'t', Chars<'h', Nil>>>>>>, + Value = ::Scalar, + > + + HasField< + Symbol< + 6, + Chars< + 'h', + Chars<'e', Chars<'i', Chars<'g', Chars<'h', Chars<'t', Nil>>>>>, + >, + >, + Value = ::Scalar, + >, + Types: HasScalarType, + { + fn rectangle_area(&self) -> ::Scalar { + let width: ::Scalar = self + .get_field( + ::core::marker::PhantomData::< + Symbol< + 5, + Chars<'w', Chars<'i', Chars<'d', Chars<'t', Chars<'h', Nil>>>>>, + >, + >, + ) + .clone(); + let height: ::Scalar = self + .get_field( + ::core::marker::PhantomData::< + Symbol< + 6, + Chars< + 'h', + Chars< + 'e', + Chars<'i', Chars<'g', Chars<'h', Chars<'t', Nil>>>>, + >, + >, + >, + >, + ) + .clone(); + let res: ::Scalar = width * height; + res + } + } + ") + } +} + +pub struct Types; + +impl HasScalarType for Types { + type Scalar = f64; +} + +#[derive(HasField)] +pub struct Rectangle { + pub width: f64, + pub height: f64, +} + +pub trait CheckRectangle: RectangleArea {} +impl CheckRectangle for Rectangle {} diff --git a/crates/tests/cgp-tests/tests/abstract_types/use_type_fn_foreign_equality.rs b/crates/tests/cgp-tests/tests/abstract_types/use_type_fn_foreign_equality.rs new file mode 100644 index 00000000..2a4112e8 --- /dev/null +++ b/crates/tests/cgp-tests/tests/abstract_types/use_type_fn_foreign_equality.rs @@ -0,0 +1,95 @@ +//! `#[use_type]` chaining an imported associated type into a *nested* foreign type +//! source, with type-equality, in a `#[cgp_fn]`: +//! `#[use_type(HasTypes::Types, @Types::HasScalarType::{Scalar = f64})]`. +//! +//! `HasTypes::Types` imports the abstract `Types` from `Self`, then +//! `@Types::HasScalarType::{Scalar = f64}` resolves `Scalar` against *that* +//! imported type and pins it to `f64`, so the bare `Scalar` alias rewrites to the +//! two-hop `<::Types as HasScalarType>::Scalar` and the impl +//! gains `::Types: HasScalarType`. The `#[cgp_fn]` +//! snapshot is kept for the rewrite; both `#[cgp_type]` traits are written plainly. +//! +//! See docs/reference/attributes/use_type.md and docs/concepts/abstract-types.md. + +use cgp::prelude::*; +use cgp_macro_test_util::snapshot_cgp_fn; + +#[cgp_type] +pub trait HasScalarType { + type Scalar; +} + +#[cgp_type] +pub trait HasTypes { + type Types: HasScalarType; +} + +snapshot_cgp_fn! { + #[cgp_fn] + #[use_type( + HasTypes::Types, + @Types::HasScalarType::{Scalar = f64}, + )] + pub fn rectangle_area(&self, #[implicit] width: Scalar, #[implicit] height: Scalar) -> Scalar { + let res: f64 = width * height; + res + } + + expand_rectangle_area(output) { + insta::assert_snapshot!(output, @" + pub trait RectangleArea: HasTypes { + fn rectangle_area(&self) -> <::Types as HasScalarType>::Scalar; + } + impl<__Context__> RectangleArea for __Context__ + where + Self: HasField< + Symbol<5, Chars<'w', Chars<'i', Chars<'d', Chars<'t', Chars<'h', Nil>>>>>>, + Value = <::Types as HasScalarType>::Scalar, + > + + HasField< + Symbol< + 6, + Chars< + 'h', + Chars<'e', Chars<'i', Chars<'g', Chars<'h', Chars<'t', Nil>>>>>, + >, + >, + Value = <::Types as HasScalarType>::Scalar, + >, + Self: HasTypes, + ::Types: HasScalarType, + { + fn rectangle_area(&self) -> <::Types as HasScalarType>::Scalar { + let width: <::Types as HasScalarType>::Scalar = self + .get_field( + ::core::marker::PhantomData::< + Symbol< + 5, + Chars<'w', Chars<'i', Chars<'d', Chars<'t', Chars<'h', Nil>>>>>, + >, + >, + ) + .clone(); + let height: <::Types as HasScalarType>::Scalar = self + .get_field( + ::core::marker::PhantomData::< + Symbol< + 6, + Chars< + 'h', + Chars< + 'e', + Chars<'i', Chars<'g', Chars<'h', Chars<'t', Nil>>>>, + >, + >, + >, + >, + ) + .clone(); + let res: f64 = width * height; + res + } + } + ") + } +} diff --git a/crates/tests/cgp-tests/tests/abstract_types/use_type_fn_foreign_equality_cross_trait.rs b/crates/tests/cgp-tests/tests/abstract_types/use_type_fn_foreign_equality_cross_trait.rs new file mode 100644 index 00000000..7df0e3da --- /dev/null +++ b/crates/tests/cgp-tests/tests/abstract_types/use_type_fn_foreign_equality_cross_trait.rs @@ -0,0 +1,137 @@ +//! `#[use_type]` reaching through a *nested foreign* associated type across traits +//! in `#[cgp_fn]`: `#[use_type(HasBarType::Bar, @Bar::HasFooType::Foo)]` and the +//! equality form `@Bar::HasFooType::{Foo as BarFoo = Foo}`. +//! +//! Here `HasBarType::Bar` itself implements `HasFooType`, so `@Bar::HasFooType::Foo` +//! resolves the alias to the two-hop `<::Bar as HasFooType>::Foo` +//! and adds `::Bar: HasFooType` to the impl. The final function +//! equates that nested type to `Self`'s own `Foo` (`{Foo as BarFoo = Foo}`), +//! generating a `HasFooType::Foo>` bound on the nested +//! type. `do_foo`/`do_bar` are the dependencies imported by `return_foo_or_bar`. +//! All three `#[cgp_fn]` snapshots are kept because the abstract-type rewrite is +//! the point. +//! +//! See docs/reference/attributes/use_type.md and docs/concepts/abstract-types.md. + +use cgp_macro_test_util::snapshot_cgp_fn; + +pub trait HasFooType { + type Foo: Ord + Clone; +} + +pub trait HasBarType { + type Bar: HasFooType; +} + +snapshot_cgp_fn! { + #[cgp_fn] + #[use_type(HasFooType::Foo)] + pub fn do_foo(&self) -> Foo { + todo!() + } + + expand_do_foo(output) { + insta::assert_snapshot!(output, @" + pub trait DoFoo: HasFooType { + fn do_foo(&self) -> ::Foo; + } + impl<__Context__> DoFoo for __Context__ + where + Self: HasFooType, + { + fn do_foo(&self) -> ::Foo { + todo!() + } + } + ") + } +} + +snapshot_cgp_fn! { + #[cgp_fn] + #[use_type(HasBarType::Bar, @Bar::HasFooType::Foo)] + pub fn do_bar(&self) -> Foo { + todo!() + } + + expand_do_bar(output) { + insta::assert_snapshot!(output, @" + pub trait DoBar: HasBarType { + fn do_bar(&self) -> <::Bar as HasFooType>::Foo; + } + impl<__Context__> DoBar for __Context__ + where + Self: HasBarType, + ::Bar: HasFooType, + { + fn do_bar(&self) -> <::Bar as HasFooType>::Foo { + todo!() + } + } + ") + } +} + +snapshot_cgp_fn! { + #[cgp_fn] + #[use_type( + HasFooType::Foo, + HasBarType::Bar, + @Bar::HasFooType::{Foo as BarFoo = Foo}, + )] + #[uses(DoFoo, DoBar)] + fn return_foo_or_bar(&self, flag: bool, #[implicit] foo: &Foo, #[implicit] bar: &BarFoo) -> Foo { + if flag { + let res: Foo = self.do_foo(); + if &res < foo { res } else { foo.clone() } + } else { + let res: BarFoo = self.do_bar(); + if &res < bar { res } else { bar.clone() } + } + } + + expand_return_foo_or_bar(output) { + insta::assert_snapshot!(output, @" + trait ReturnFooOrBar: HasFooType + HasBarType { + fn return_foo_or_bar(&self, flag: bool) -> ::Foo; + } + impl<__Context__> ReturnFooOrBar for __Context__ + where + Self: DoFoo + DoBar, + Self: HasField< + Symbol<3, Chars<'f', Chars<'o', Chars<'o', Nil>>>>, + Value = ::Foo, + > + + HasField< + Symbol<3, Chars<'b', Chars<'a', Chars<'r', Nil>>>>, + Value = <::Bar as HasFooType>::Foo, + >, + Self: HasFooType, + Self: HasBarType, + ::Bar: HasFooType::Foo>, + { + fn return_foo_or_bar(&self, flag: bool) -> ::Foo { + let foo: &::Foo = self + .get_field( + ::core::marker::PhantomData::< + Symbol<3, Chars<'f', Chars<'o', Chars<'o', Nil>>>>, + >, + ); + let bar: &<::Bar as HasFooType>::Foo = self + .get_field( + ::core::marker::PhantomData::< + Symbol<3, Chars<'b', Chars<'a', Chars<'r', Nil>>>>, + >, + ); + if flag { + let res: ::Foo = self.do_foo(); + if &res < foo { res } else { foo.clone() } + } else { + let res: <::Bar as HasFooType>::Foo = self.do_bar(); + if &res < bar { res } else { bar.clone() } + } + } + } + ") + } +} diff --git a/crates/tests/cgp-tests/tests/abstract_types/use_type_fn_nested_foreign.rs b/crates/tests/cgp-tests/tests/abstract_types/use_type_fn_nested_foreign.rs new file mode 100644 index 00000000..f2cab1f4 --- /dev/null +++ b/crates/tests/cgp-tests/tests/abstract_types/use_type_fn_nested_foreign.rs @@ -0,0 +1,131 @@ +//! `#[use_type]` reaching a nested foreign type in `#[cgp_fn]` combined with +//! `#[extend_where]`: `#[use_type(HasTypes::Types, @Types::HasScalarType::Scalar)]` +//! plus `#[extend_where(Types: HasScalarType)]`. +//! +//! `HasTypes::Types` imports the abstract `Types`, then `@Types::HasScalarType::Scalar` +//! resolves `Scalar` against it, so the bare alias rewrites to the two-hop +//! `<::Types as HasScalarType>::Scalar`. Unlike the equality +//! variant, `Types` is *not* pinned to a concrete type, so `#[extend_where(...)]` +//! adds the `::Types: HasScalarType` bound to the generated trait +//! definition (the aliased `Types` in the attribute is likewise rewritten). The +//! `#[cgp_fn]` snapshot is kept for the rewrite; both `#[cgp_type]` traits are plain. +//! `CheckRectangle` asserts the concrete `Rectangle` implements the generated trait. +//! +//! See docs/reference/attributes/use_type.md and docs/concepts/abstract-types.md. + +use std::ops::Mul; + +use cgp::prelude::*; +use cgp_macro_test_util::snapshot_cgp_fn; + +#[cgp_type] +pub trait HasScalarType { + type Scalar; +} + +#[cgp_type] +pub trait HasTypes { + type Types; +} + +snapshot_cgp_fn! { + #[cgp_fn] + #[use_type(HasTypes::Types, @Types::HasScalarType::Scalar)] + #[extend_where(Types: HasScalarType)] + pub fn rectangle_area(&self, #[implicit] width: Scalar, #[implicit] height: Scalar) -> Scalar + where + Scalar: Mul + Copy, + { + let res: Scalar = width * height; + res + } + + expand_rectangle_area(output) { + insta::assert_snapshot!(output, @" + pub trait RectangleArea: HasTypes + where + ::Types: HasScalarType, + { + fn rectangle_area(&self) -> <::Types as HasScalarType>::Scalar; + } + impl<__Context__> RectangleArea for __Context__ + where + <::Types as HasScalarType>::Scalar: Mul< + Output = <::Types as HasScalarType>::Scalar, + > + Copy, + ::Types: HasScalarType, + Self: HasField< + Symbol<5, Chars<'w', Chars<'i', Chars<'d', Chars<'t', Chars<'h', Nil>>>>>>, + Value = <::Types as HasScalarType>::Scalar, + > + + HasField< + Symbol< + 6, + Chars< + 'h', + Chars<'e', Chars<'i', Chars<'g', Chars<'h', Chars<'t', Nil>>>>>, + >, + >, + Value = <::Types as HasScalarType>::Scalar, + >, + Self: HasTypes, + ::Types: HasScalarType, + { + fn rectangle_area(&self) -> <::Types as HasScalarType>::Scalar { + let width: <::Types as HasScalarType>::Scalar = self + .get_field( + ::core::marker::PhantomData::< + Symbol< + 5, + Chars<'w', Chars<'i', Chars<'d', Chars<'t', Chars<'h', Nil>>>>>, + >, + >, + ) + .clone(); + let height: <::Types as HasScalarType>::Scalar = self + .get_field( + ::core::marker::PhantomData::< + Symbol< + 6, + Chars< + 'h', + Chars< + 'e', + Chars<'i', Chars<'g', Chars<'h', Chars<'t', Nil>>>>, + >, + >, + >, + >, + ) + .clone(); + let res: <::Types as HasScalarType>::Scalar = width * height; + res + } + } + ") + } +} + +pub struct MyTypes; + +impl HasScalarType for MyTypes { + type Scalar = f64; +} + +#[derive(HasField)] +pub struct Rectangle { + pub width: f64, + pub height: f64, +} + +impl HasTypes for Rectangle { + type Types = MyTypes; +} + +pub trait CheckRectangle: RectangleArea +where + Self::Types: HasScalarType, +{ +} + +impl CheckRectangle for Rectangle {} diff --git a/crates/tests/cgp-tests/tests/abstract_types/use_type_foreign.rs b/crates/tests/cgp-tests/tests/abstract_types/use_type_foreign.rs new file mode 100644 index 00000000..d5f97f7e --- /dev/null +++ b/crates/tests/cgp-tests/tests/abstract_types/use_type_foreign.rs @@ -0,0 +1,67 @@ +//! Importing an abstract type from a *foreign* type parameter with the +//! `#[use_type(@Types::HasScalarType::Scalar)]` form on a generic component. +//! +//! When the abstract type lives on a generic parameter of the component rather +//! than on `Self`, the `@Types::` prefix tells `#[use_type]` to resolve `Scalar` +//! against that parameter, rewriting the bare alias to +//! `::Scalar`. The `Error` type is still resolved against +//! `Self` via `HasErrorType::Error`. `Types` is a standalone type that supplies +//! the concrete scalar, while `Rectangle` supplies the fields and error type. +//! `#[cgp_type]`, wiring, and check are incidental and use the plain macros. +//! +//! See docs/reference/attributes/use_type.md and docs/concepts/abstract-types.md. + +use std::convert::Infallible; +use std::ops::Mul; + +use cgp::core::error::ErrorTypeProviderComponent; +use cgp::prelude::*; + +#[cgp_type] +pub trait HasScalarType { + type Scalar; +} + +#[cgp_component(AreaCalculator)] +#[use_type(@Types::HasScalarType::Scalar, HasErrorType::Error)] +pub trait CanCalculateArea { + fn area(&self) -> Result; +} + +#[cgp_impl(new RectangleArea)] +#[use_type(@Types::HasScalarType::Scalar, HasErrorType::Error)] +impl AreaCalculator +where + Scalar: Mul + Copy, +{ + fn area(&self, #[implicit] width: Scalar, #[implicit] height: Scalar) -> Result { + Ok(width * height) + } +} + +#[derive(HasField)] +pub struct Rectangle { + pub width: f64, + pub height: f64, +} + +pub struct Types; + +impl HasScalarType for Types { + type Scalar = f64; +} + +delegate_components! { + Rectangle { + ErrorTypeProviderComponent: + UseType, + AreaCalculatorComponent: + RectangleArea, + } +} + +check_components! { + Rectangle { + AreaCalculatorComponent: Types, + } +} diff --git a/crates/tests/cgp-tests/tests/abstract_types/use_type_generic_param.rs b/crates/tests/cgp-tests/tests/abstract_types/use_type_generic_param.rs new file mode 100644 index 00000000..3b7f50f7 --- /dev/null +++ b/crates/tests/cgp-tests/tests/abstract_types/use_type_generic_param.rs @@ -0,0 +1,27 @@ +//! `#[use_type]` when the imported alias collides with a *generic parameter name* +//! of the trait being implemented. +//! +//! The component `Foo` is implemented for the concrete parameter +//! `FooProvider`, where `Error` is the trait's generic parameter — but the +//! same name `Error` is also imported via `#[use_type(HasErrorType::Error)]`. This +//! test pins that the abstract-type import is desugared correctly into +//! `Context::Error` (the imported associated type) rather than shadowing the +//! generic parameter — i.e. `Self::Error` resolution stays distinct from the +//! `Error` type argument. +//! +//! See docs/reference/attributes/use_type.md and docs/concepts/abstract-types.md. + +use cgp::prelude::*; + +#[cgp_component(FooProvider)] +pub trait Foo { + fn foo(&self, value: &T); +} + +// The `Error` parameter in `FooProvider` must desugar into `Context::Error` +// (the abstract error type imported by `#[use_type]`), not `Self::Error`. +#[cgp_impl(new FooError)] +#[use_type(HasErrorType::Error)] +impl FooProvider { + fn foo(&self, _value: &Error) {} +} diff --git a/crates/tests/cgp-tests/tests/abstract_types_tests.rs b/crates/tests/cgp-tests/tests/abstract_types_tests.rs new file mode 100644 index 00000000..f0707e8e --- /dev/null +++ b/crates/tests/cgp-tests/tests/abstract_types_tests.rs @@ -0,0 +1,17 @@ +//! Entrypoint for the `abstract_types` concept. +//! +//! Covers CGP's abstract-type feature end to end: the `#[cgp_type]` macro (an +//! abstract-type component whose expansion additionally emits the `UseType` +//! blanket impl), the `UseType` provider that fixes the type by wiring, and +//! the `#[use_type(...)]` attribute that imports an abstract type into a +//! `#[cgp_component]`/`#[cgp_impl]`/`#[cgp_fn]` and rewrites the bare alias +//! (`Scalar`, `Error`) to its fully-qualified associated-type form. This concept +//! owns the canonical `#[cgp_type]` macro-expansion snapshots and the +//! abstract-type-rewriting snapshots for `#[use_type]`. +//! +//! See docs/reference/macros/cgp_type.md, docs/reference/attributes/use_type.md, +//! docs/reference/providers/use_type.md, and docs/concepts/abstract-types.md. +#![allow(dead_code)] +#![allow(clippy::disallowed_names)] + +pub mod abstract_types; diff --git a/crates/tests/cgp-tests/tests/cgp_fn_tests/async.rs b/crates/tests/cgp-tests/tests/async_and_send/cgp_fn_async.rs similarity index 68% rename from crates/tests/cgp-tests/tests/cgp_fn_tests/async.rs rename to crates/tests/cgp-tests/tests/async_and_send/cgp_fn_async.rs index b3647e80..ee33678f 100644 --- a/crates/tests/cgp-tests/tests/cgp_fn_tests/async.rs +++ b/crates/tests/cgp-tests/tests/async_and_send/cgp_fn_async.rs @@ -1,3 +1,13 @@ +//! Canonical expansion of an async `#[cgp_fn]`. +//! +//! `#[async_trait]` combined with `#[cgp_fn]` turns an `async fn` into an async +//! blanket-impl trait: the generated trait method is `async fn`, both the trait +//! and its blanket impl carry `#[async_trait]`, and `#[implicit]` arguments are +//! still pulled from the context's fields. This is the async variant of the +//! `#[cgp_fn]` expansion, which the `async_and_send` concept owns. +//! +//! See docs/reference/macros/async_trait.md. + use cgp::prelude::*; use cgp_macro_test_util::snapshot_cgp_fn; diff --git a/crates/tests/cgp-tests/tests/async_and_send/mod.rs b/crates/tests/cgp-tests/tests/async_and_send/mod.rs new file mode 100644 index 00000000..dfacf513 --- /dev/null +++ b/crates/tests/cgp-tests/tests/async_and_send/mod.rs @@ -0,0 +1,13 @@ +//! One unit test per file. Each file is self-contained: it defines its own +//! components, providers, and context types at module scope so that the +//! type-level wiring of one test never leaks into another. + +// A runtime test showing that a future can be spawned onto an executor that +// requires `Future: Send + 'static`, even though the abstract types and +// interfaces carry no explicit `Send` bounds. The Send bound is recovered by a +// proxy `SendRunner` impl on the concrete context. +pub mod spawn; + +// Async `#[cgp_fn]` expansion: this concept owns the async variant of the +// `#[cgp_fn]` macro snapshot (the `#[async_trait]` handling is the feature). +pub mod cgp_fn_async; diff --git a/crates/tests/cgp-tests/tests/async_and_send/spawn.rs b/crates/tests/cgp-tests/tests/async_and_send/spawn.rs new file mode 100644 index 00000000..d14a9f13 --- /dev/null +++ b/crates/tests/cgp-tests/tests/async_and_send/spawn.rs @@ -0,0 +1,177 @@ +//! Spawning an async CGP future onto an executor that requires +//! `Future: Send + 'static`, without annotating `Send` throughout the abstract +//! code. +//! +//! The abstract types (`HasFooType`, `HasBarType`) and the async interfaces +//! (`CanFetchFoo`, `CanFetchBar`, `CanRunFooBar`) carry no explicit `Send` +//! bounds. A future built from them can still be handed to a `tokio::spawn`-like +//! function (here `dummy_spawn`, which demands `Future: Send + 'static`) by +//! implementing a proxy `SendRunner` on the *concrete* context: once the context +//! is concrete, the compiler can prove `Send` for the produced future. This is +//! the workaround CGP uses until Rust's Return Type Notation (RTN) is stabilized. +//! +//! See docs/reference/components/runner.md and docs/concepts/send-bounds.md. + +use core::convert::Infallible; +use core::future::Future; + +use cgp::core::error::ErrorTypeProviderComponent; +use cgp::extra::run::{ + CanRun, CanSendRun, Runner, RunnerComponent, SendRunner, SendRunnerComponent, +}; +use cgp::prelude::*; +use futures::executor::block_on; + +// A dummy spawn function that has the same signature as tokio::spawn, +// requiring the Future to implement Send + 'static. +fn dummy_spawn(_future: F) +where + F: Future + Send + 'static, + F::Output: Send + 'static, +{ +} + +// The abstract types and interfaces do not contain explicit Send bounds + +#[cgp_type] +pub trait HasFooType { + type Foo; +} + +#[cgp_type] +pub trait HasBarType { + type Bar; +} + +#[cgp_component(FooFetcher)] +#[async_trait] +pub trait CanFetchFoo: HasFooType + HasErrorType { + async fn fetch_foo(&self) -> Result; +} + +#[cgp_component(BarFetcher)] +#[async_trait] +pub trait CanFetchBar: HasBarType + HasErrorType { + async fn fetch_bar(&self) -> Result; +} + +#[cgp_component(FooBarRunner)] +#[async_trait] +pub trait CanRunFooBar: HasFooType + HasBarType + HasErrorType { + async fn run_foo_bar(&self, foo: &Self::Foo, bar: &Self::Bar) -> Result<(), Self::Error>; +} + +// Abstract providers can be implemented without Send bounds + +#[cgp_new_provider(RunnerComponent)] +impl Runner for RunWithFooBar +where + Context: CanFetchFoo + CanFetchBar + CanRunFooBar, +{ + async fn run(context: &Context, _code: PhantomData) -> Result<(), Context::Error> { + let foo = context.fetch_foo().await?; + let bar = context.fetch_bar().await?; + + context.run_foo_bar(&foo, &bar).await?; + + Ok(()) + } +} + +#[cgp_new_provider(RunnerComponent)] +impl Runner for SpawnAndRun +where + Context: 'static + Send + Clone + CanSendRun, +{ + async fn run(context: &Context, _code: PhantomData) -> Result<(), Context::Error> { + let context = context.clone(); + + dummy_spawn(async move { + let _ = context.send_run(PhantomData).await; + }); + + Ok(()) + } +} + +#[cgp_new_provider] +impl FooFetcher for DummyFetchFoo +where + Context: HasFooType + HasErrorType, +{ + async fn fetch_foo(_context: &Context) -> Result { + Ok(Default::default()) + } +} + +#[cgp_new_provider] +impl BarFetcher for DummyFetchBar +where + Context: HasBarType + HasErrorType, +{ + async fn fetch_bar(_context: &Context) -> Result { + Ok(Default::default()) + } +} + +#[cgp_new_provider] +impl FooBarRunner for DummyRunFoobar +where + Context: HasFooType + HasBarType + HasErrorType, +{ + async fn run_foo_bar( + _context: &Context, + _foo: &Context::Foo, + _bar: &Context::Bar, + ) -> Result<(), Context::Error> { + Ok(()) + } +} + +// An example App context that has Send-safe implementations + +#[derive(Clone)] +pub struct App; + +pub struct ActionA; +pub struct ActionB; + +delegate_components! { + App { + ErrorTypeProviderComponent: + UseType, + [ + FooTypeProviderComponent, + BarTypeProviderComponent, + ]: + UseType<()>, + FooFetcherComponent: + DummyFetchFoo, + BarFetcherComponent: + DummyFetchBar, + FooBarRunnerComponent: + DummyRunFoobar, + RunnerComponent: + UseDelegate, + }>, + } +} + +// Explicit implementation of SendRunner for App, by forwarding the +// call to Runner that is implemented by `RunWithFooBar`. +// With the concrete context known, the Send bound can be found in the concrete future. + +#[cgp_provider] +impl SendRunner for App { + async fn send_run(context: &App, code: PhantomData) -> Result<(), Infallible> { + context.run(code).await + } +} + +#[test] +pub fn test_async_spawn() { + let app = App; + block_on(app.run(PhantomData::)).unwrap(); +} diff --git a/crates/tests/cgp-tests/tests/async_and_send_tests.rs b/crates/tests/cgp-tests/tests/async_and_send_tests.rs new file mode 100644 index 00000000..e4d9d15a --- /dev/null +++ b/crates/tests/cgp-tests/tests/async_and_send_tests.rs @@ -0,0 +1,15 @@ +//! Entrypoint for the `async_and_send` concept. +//! +//! Covers CGP's async support and its `Send`-bound story: `#[async_trait]` on +//! `#[cgp_component]` traits, `#[cgp_fn]`, and providers/handlers; the runner +//! components (`Runner`/`CanRun`, `SendRunner`/`CanSendRun`); spawning futures +//! onto an executor that demands `Future: Send + 'static`; and how a concrete +//! context recovers the `Send` bound through a proxy `SendRunner` impl without +//! annotating `Send` throughout the abstract code. +//! +//! See docs/reference/macros/async_trait.md, docs/reference/components/runner.md, +//! and docs/concepts/send-bounds.md. +#![allow(dead_code)] +#![allow(clippy::disallowed_names)] + +pub mod async_and_send; diff --git a/crates/tests/cgp-tests/tests/component_tests/cgp_impl/basic.rs b/crates/tests/cgp-tests/tests/basic_delegation/component_macro.rs similarity index 56% rename from crates/tests/cgp-tests/tests/component_tests/cgp_impl/basic.rs rename to crates/tests/cgp-tests/tests/basic_delegation/component_macro.rs index b2ee00ef..54f98ef4 100644 --- a/crates/tests/cgp-tests/tests/component_tests/cgp_impl/basic.rs +++ b/crates/tests/cgp-tests/tests/basic_delegation/component_macro.rs @@ -1,4 +1,15 @@ -use cgp_macro_test_util::{snapshot_cgp_auto_getter, snapshot_cgp_component, snapshot_cgp_impl}; +//! Canonical expansion of `#[cgp_component]` for a simple consumer trait. +//! +//! `#[cgp_component]` turns one consumer trait into the whole component bundle: +//! the consumer trait itself, the provider trait (with `Self` moved to a leading +//! `__Context__` parameter), the `…Component` marker, and the blanket impls that +//! route between them (plus the `UseContext` and `RedirectLookup` providers). +//! This is the reference snapshot for that expansion; other concepts reuse +//! `#[cgp_component]` without re-snapshotting it. +//! +//! See docs/reference/macros/cgp_component.md. + +use cgp_macro_test_util::snapshot_cgp_component; snapshot_cgp_component! { #[cgp_component(FooProvider)] @@ -79,87 +90,3 @@ snapshot_cgp_component! { ") } } - -snapshot_cgp_auto_getter! { - #[cgp_auto_getter] - pub trait HasName { - fn name(&self) -> &str; - } - - expand_has_name(output) { - insta::assert_snapshot!(output, @" - pub trait HasName { - fn name(&self) -> &str; - } - impl<__Context__> HasName for __Context__ - where - __Context__: HasField< - Symbol<4, Chars<'n', Chars<'a', Chars<'m', Chars<'e', Nil>>>>>, - Value = String, - >, - { - fn name(&self) -> &str { - self.get_field( - ::core::marker::PhantomData::< - Symbol<4, Chars<'n', Chars<'a', Chars<'m', Chars<'e', Nil>>>>>, - >, - ) - .as_str() - } - } - ") - } -} - -snapshot_cgp_impl! { - #[cgp_impl(new ValueToString)] - impl FooProvider for Context { - fn foo(&self, value: u32) -> String { - value.to_string() - } - } - - expand_value_to_string(output) { - insta::assert_snapshot!(output, @" - impl FooProvider for ValueToString { - fn foo(__context__: &Context, value: u32) -> String { - value.to_string() - } - } - impl IsProviderFor for ValueToString {} - pub struct ValueToString; - ") - } -} - -pub mod inner { - use core::fmt::Display; - - use cgp::prelude::*; - - use super::{FooProvider, FooProviderComponent, HasName}; - - #[cgp_impl(new WithNamePrefix)] - impl FooProvider for Context - where - Context: HasName, - { - fn foo(&self, value: u32) -> String { - format!("{}: {}", self.name(), value) - } - } - - pub struct Foo { - pub tag: Tag, - } - - #[cgp_impl(new WithFooTag: FooProviderComponent)] - impl FooProvider for Foo - where - Tag: Display, - { - fn foo(&self, value: u32) -> String { - format!("{}: {}", self.tag, value) - } - } -} diff --git a/crates/tests/cgp-tests/tests/component_tests/consumer_delegate/generics.rs b/crates/tests/cgp-tests/tests/basic_delegation/consumer_delegate_generic.rs similarity index 67% rename from crates/tests/cgp-tests/tests/component_tests/consumer_delegate/generics.rs rename to crates/tests/cgp-tests/tests/basic_delegation/consumer_delegate_generic.rs index d5fbe9a7..9eddf898 100644 --- a/crates/tests/cgp-tests/tests/component_tests/consumer_delegate/generics.rs +++ b/crates/tests/cgp-tests/tests/basic_delegation/consumer_delegate_generic.rs @@ -1,3 +1,12 @@ +//! A generic getter component can be wired for one type argument and hand-impl'd +//! for another on the same context. +//! +//! `HasValue` is generic; `App` wires `ValueGetterComponent` to a +//! field while implementing `HasValue` directly. Each type argument resolves +//! independently. +//! +//! See docs/reference/macros/cgp_getter.md. + use cgp::prelude::*; #[cgp_getter { diff --git a/crates/tests/cgp-tests/tests/component_tests/consumer_delegate/basic.rs b/crates/tests/cgp-tests/tests/basic_delegation/consumer_delegate_getter.rs similarity index 61% rename from crates/tests/cgp-tests/tests/component_tests/consumer_delegate/basic.rs rename to crates/tests/cgp-tests/tests/basic_delegation/consumer_delegate_getter.rs index 16b3f56c..6a84f235 100644 --- a/crates/tests/cgp-tests/tests/component_tests/consumer_delegate/basic.rs +++ b/crates/tests/cgp-tests/tests/basic_delegation/consumer_delegate_getter.rs @@ -1,3 +1,12 @@ +//! A context can satisfy some components by wiring and others by a direct impl, +//! mixing the two freely. +//! +//! Here `App` wires `HasName` to `UseField` through `delegate_components!` while +//! implementing `HasCount` by hand — CGP consumer traits are ordinary traits, so +//! both routes coexist on the same context. +//! +//! See docs/concepts/consumer-and-provider-traits.md. + use cgp::prelude::*; #[cgp_getter] diff --git a/crates/tests/cgp-tests/tests/component_tests/cgp_component/default_impl.rs b/crates/tests/cgp-tests/tests/basic_delegation/default_methods.rs similarity index 86% rename from crates/tests/cgp-tests/tests/component_tests/cgp_component/default_impl.rs rename to crates/tests/cgp-tests/tests/basic_delegation/default_methods.rs index 971edfe2..a6511957 100644 --- a/crates/tests/cgp-tests/tests/component_tests/cgp_component/default_impl.rs +++ b/crates/tests/cgp-tests/tests/basic_delegation/default_methods.rs @@ -1,3 +1,13 @@ +//! A `#[cgp_component]` trait may carry a default method body and a supertrait. +//! +//! The default body is copied into the generated provider trait, so a provider +//! declared with an empty `#[cgp_impl]` (here `UseDefault`) inherits it. This is +//! the distinct component-expansion variant with a supertrait bound and a default +//! method, kept alongside the plain snapshot in `component_macro`. +//! +//! See docs/reference/macros/cgp_component.md and +//! docs/reference/attributes/extend.md. + use cgp::prelude::*; use cgp_macro_test_util::snapshot_cgp_component; @@ -101,6 +111,7 @@ snapshot_cgp_component! { pub struct UseDefault; +// Empty provider impls pick up the default method bodies from the provider trait. #[cgp_impl(UseDefault)] impl NameGetter for Context {} diff --git a/crates/tests/cgp-tests/tests/basic_delegation/delegate_array_key.rs b/crates/tests/cgp-tests/tests/basic_delegation/delegate_array_key.rs new file mode 100644 index 00000000..e25ede51 --- /dev/null +++ b/crates/tests/cgp-tests/tests/basic_delegation/delegate_array_key.rs @@ -0,0 +1,69 @@ +//! `delegate_components!` array-key form: several component keys mapping to one +//! provider in a single entry. +//! +//! An array key `[A, B]: Provider` expands to one `DelegateComponent` + +//! `IsProviderFor` impl pair per key, all pointing at the same provider. +//! +//! See docs/reference/macros/delegate_components.md. + +use cgp::prelude::DelegateComponent; +use cgp_macro_test_util::snapshot_delegate_components; + +pub struct FooKey; +pub struct FooValue; +pub struct BarKey; +pub struct BarValue; +pub struct BazKey; + +pub struct Components; + +snapshot_delegate_components! { + delegate_components! { + Components { + FooKey: FooValue, + [ + BarKey, + BazKey, + ]: + BarValue, + } + } + + expand_components(output) { + insta::assert_snapshot!(output, @" + impl DelegateComponent for Components { + type Delegate = FooValue; + } + impl<__Context__, __Params__> IsProviderFor + for Components + where + FooValue: IsProviderFor, + {} + impl DelegateComponent for Components { + type Delegate = BarValue; + } + impl<__Context__, __Params__> IsProviderFor + for Components + where + BarValue: IsProviderFor, + {} + impl DelegateComponent for Components { + type Delegate = BarValue; + } + impl<__Context__, __Params__> IsProviderFor + for Components + where + BarValue: IsProviderFor, + {} + ") + } +} + +pub trait CheckDelegates: + DelegateComponent + + DelegateComponent + + DelegateComponent +{ +} + +impl CheckDelegates for Components {} diff --git a/crates/tests/cgp-tests/tests/component_tests/delegate_components/direct.rs b/crates/tests/cgp-tests/tests/basic_delegation/delegate_components_macro.rs similarity index 78% rename from crates/tests/cgp-tests/tests/component_tests/delegate_components/direct.rs rename to crates/tests/cgp-tests/tests/basic_delegation/delegate_components_macro.rs index 158ec76c..1c8ad9c8 100644 --- a/crates/tests/cgp-tests/tests/component_tests/delegate_components/direct.rs +++ b/crates/tests/cgp-tests/tests/basic_delegation/delegate_components_macro.rs @@ -1,3 +1,13 @@ +//! Canonical expansion of `delegate_components!` for a standalone provider table. +//! +//! `delegate_components!` builds the type-level wiring table: for each entry it +//! emits a `DelegateComponent` impl (mapping a component key to its provider) and +//! an `IsProviderFor` impl that forwards the provider's dependencies. This file +//! owns the reference snapshot, including the `->` forwarding form that delegates +//! to another table's entry. +//! +//! See docs/reference/macros/delegate_components.md. + use cgp::prelude::*; use cgp_macro_test_util::snapshot_delegate_components; @@ -32,6 +42,8 @@ snapshot_delegate_components! { } } +// The `->` form forwards a key to another table's delegate instead of assigning +// a provider directly. snapshot_delegate_components! { delegate_components! { new BarComponents { @@ -71,6 +83,7 @@ snapshot_delegate_components! { } } +// Compile-time proof that the table resolves to the expected delegates. pub trait CheckBarDelegates: DelegateComponent, Delegate = FooComponents> + DelegateComponent, Delegate = String> diff --git a/crates/tests/cgp-tests/tests/basic_delegation/delegate_generic_nested_value.rs b/crates/tests/cgp-tests/tests/basic_delegation/delegate_generic_nested_value.rs new file mode 100644 index 00000000..603f2fe1 --- /dev/null +++ b/crates/tests/cgp-tests/tests/basic_delegation/delegate_generic_nested_value.rs @@ -0,0 +1,39 @@ +//! A nested `UseDelegate` value may introduce its own generics via a per-entry +//! `` list. +//! +//! ` BarKey: UseDelegate { … }>` threads the entry generic +//! `T` through both the outer key and the inner generated table struct. +//! +//! See docs/reference/macros/delegate_components.md. + +use core::marker::PhantomData; + +use cgp::core::component::UseDelegate; +use cgp::prelude::*; + +pub struct FooKey; +pub struct FooValue; +pub struct BarKey(pub PhantomData); +pub struct BazKey; +pub struct BazValue(pub PhantomData); + +delegate_components! { + new MyComponents { + FooKey: FooValue, + BarKey: UseDelegate { + BazKey: BazValue, + }>, + } +} + +pub trait CheckDelegates: + DelegateComponent + + DelegateComponent, Delegate = UseDelegate>> +{ +} + +impl CheckDelegates for MyComponents {} + +pub trait CheckInnerDelegates: DelegateComponent> {} + +impl CheckInnerDelegates for BarValue {} diff --git a/crates/tests/cgp-tests/tests/basic_delegation/delegate_generic_table.rs b/crates/tests/cgp-tests/tests/basic_delegation/delegate_generic_table.rs new file mode 100644 index 00000000..29d9d5d6 --- /dev/null +++ b/crates/tests/cgp-tests/tests/basic_delegation/delegate_generic_table.rs @@ -0,0 +1,85 @@ +//! `delegate_components!` with a leading generic list, wiring a whole family of +//! keys at once. +//! +//! The `<'a, T1: Clone>` header lifts generics (and their bounds) onto every +//! generated impl, and an entry key may introduce its own extra generics +//! (` BazKey`). +//! +//! See docs/reference/macros/delegate_components.md. + +use core::marker::PhantomData; + +use cgp::prelude::DelegateComponent; +use cgp_macro_test_util::snapshot_delegate_components; + +pub struct FooKey(pub PhantomData); +pub struct FooValue; +pub struct BarKey<'a, T>(pub PhantomData<(&'a (), T)>); +pub struct BarValue(pub PhantomData); +pub struct BazKey(pub PhantomData<(T1, T2)>); + +pub struct Components; + +snapshot_delegate_components! { + delegate_components! { + <'a, T1: Clone> + Components { + FooKey: FooValue, + [ + BarKey<'a, T1>, + BazKey, + ]: + BarValue, + } + } + expand_components(output) { + insta::assert_snapshot!(output, @" + impl<'a, T1: Clone> DelegateComponent> for Components { + type Delegate = FooValue; + } + impl< + 'a, + T1: Clone, + __Context__, + __Params__, + > IsProviderFor, __Context__, __Params__> for Components + where + FooValue: IsProviderFor, __Context__, __Params__>, + {} + impl<'a, T1: Clone> DelegateComponent> for Components { + type Delegate = BarValue; + } + impl< + 'a, + T1: Clone, + __Context__, + __Params__, + > IsProviderFor, __Context__, __Params__> for Components + where + BarValue: IsProviderFor, __Context__, __Params__>, + {} + impl<'a, T1: Clone, T2> DelegateComponent> for Components { + type Delegate = BarValue; + } + impl< + 'a, + T1: Clone, + T2, + __Context__, + __Params__, + > IsProviderFor, __Context__, __Params__> for Components + where + BarValue: IsProviderFor, __Context__, __Params__>, + {} + ") + } +} + +pub trait CheckDelegates<'a, T1, T2>: + DelegateComponent, Delegate = FooValue> + + DelegateComponent, Delegate = BarValue> + + DelegateComponent, Delegate = BarValue> +{ +} + +impl CheckDelegates<'_, T1, T2> for Components where T1: Clone {} diff --git a/crates/tests/cgp-tests/tests/basic_delegation/delegate_nested_use_delegate.rs b/crates/tests/cgp-tests/tests/basic_delegation/delegate_nested_use_delegate.rs new file mode 100644 index 00000000..78d68230 --- /dev/null +++ b/crates/tests/cgp-tests/tests/basic_delegation/delegate_nested_use_delegate.rs @@ -0,0 +1,37 @@ +//! A table entry value may itself be a nested `UseDelegate` table. +//! +//! This builds a two-level dispatch table inline: `BarKey` delegates to +//! `UseDelegate`, and `BarValue` is its own table keyed by `BazKey`. +//! +//! See docs/reference/macros/delegate_components.md and +//! docs/reference/providers/use_delegate.md. + +use cgp::core::component::UseDelegate; +use cgp::prelude::*; + +pub struct FooKey; +pub struct FooValue; +pub struct BarKey; +pub struct BazKey; +pub struct BazValue; + +delegate_components! { + new MyComponents { + FooKey: FooValue, + BarKey: UseDelegate, + } +} + +pub trait CheckDelegates: + DelegateComponent + + DelegateComponent> +{ +} + +impl CheckDelegates for MyComponents {} + +pub trait CheckInnerDelegates: DelegateComponent {} + +impl CheckInnerDelegates for BarValue {} diff --git a/crates/tests/cgp-tests/tests/basic_delegation/delegate_new_array_key.rs b/crates/tests/cgp-tests/tests/basic_delegation/delegate_new_array_key.rs new file mode 100644 index 00000000..fe492836 --- /dev/null +++ b/crates/tests/cgp-tests/tests/basic_delegation/delegate_new_array_key.rs @@ -0,0 +1,28 @@ +//! An array key and a nested `new` table combine in one entry. +//! +//! Several keys share one nested `UseDelegate` value. +//! This is a compile-time check that the array-key plus nested-`new` forms parse +//! and expand together. +//! +//! See docs/reference/macros/delegate_components.md. + +use cgp::core::component::UseDelegate; +use cgp::prelude::*; + +pub struct FooKey; +pub struct BarKey; +pub struct BazKey; + +delegate_components! { + new MyComponents { + [ + FooKey, + BarKey, + BazKey, + ]: + UseDelegate, + } +} diff --git a/crates/tests/cgp-tests/tests/basic_delegation/delegate_new_generic_struct.rs b/crates/tests/cgp-tests/tests/basic_delegation/delegate_new_generic_struct.rs new file mode 100644 index 00000000..22e7fd45 --- /dev/null +++ b/crates/tests/cgp-tests/tests/basic_delegation/delegate_new_generic_struct.rs @@ -0,0 +1,31 @@ +//! `new` with a leading generic list declares a generic provider-table struct. +//! +//! `delegate_components! { new MyComponents { … } }` defines +//! `struct MyComponents` and wires a family of tables at once. +//! +//! See docs/reference/macros/delegate_components.md. + +use core::marker::PhantomData; + +use cgp::prelude::*; + +pub struct FooKey(PhantomData); +pub struct FooValue; +pub struct BarKey; +pub struct BarValue(PhantomData); + +delegate_components! { + + new MyComponents { + FooKey: FooValue, + BarKey: BarValue, + } +} + +pub trait CheckDelegates: + DelegateComponent, Delegate = FooValue> + + DelegateComponent> +{ +} + +impl CheckDelegates for MyComponents {} diff --git a/crates/tests/cgp-tests/tests/basic_delegation/delegate_new_struct.rs b/crates/tests/cgp-tests/tests/basic_delegation/delegate_new_struct.rs new file mode 100644 index 00000000..906d2c91 --- /dev/null +++ b/crates/tests/cgp-tests/tests/basic_delegation/delegate_new_struct.rs @@ -0,0 +1,29 @@ +//! The `new` keyword in `delegate_components!` declares the provider-table struct +//! as part of the wiring. +//! +//! `delegate_components! { new MyComponents { … } }` defines `struct MyComponents` +//! and its `DelegateComponent` impls together. This is a compile-time check: the +//! `CheckDelegates` bound proves the table resolves as written. +//! +//! See docs/reference/macros/delegate_components.md. + +use cgp::prelude::*; + +pub struct FooKey; +pub struct FooValue; +pub struct BarKey; +pub struct BarValue; + +delegate_components! { + new MyComponents { + FooKey: FooValue, + BarKey: BarValue, + } +} + +pub trait CheckDelegates: + DelegateComponent + DelegateComponent +{ +} + +impl CheckDelegates for MyComponents {} diff --git a/crates/tests/cgp-tests/tests/basic_delegation/impl_self.rs b/crates/tests/cgp-tests/tests/basic_delegation/impl_self.rs new file mode 100644 index 00000000..c5a8a956 --- /dev/null +++ b/crates/tests/cgp-tests/tests/basic_delegation/impl_self.rs @@ -0,0 +1,50 @@ +//! `#[cgp_impl(Self)]` implements the consumer trait directly on the context +//! rather than on a separate provider struct. +//! +//! With `Self` as the provider name and `#[use_provider]` to borrow another +//! provider's behavior, a context can implement its own consumer trait by +//! forwarding to a reusable provider. This is the direct-impl end of the +//! modularity spectrum, where no wiring table is involved. +//! +//! See docs/reference/macros/cgp_impl.md and +//! docs/reference/attributes/use_provider.md. + +use cgp::prelude::*; + +#[cgp_component(AreaCalculator)] +pub trait CanCalculateArea { + fn area(&self) -> f64; +} + +#[cgp_impl(new RectangleArea)] +impl AreaCalculator { + fn area(&self, #[implicit] width: f64, #[implicit] height: f64) -> f64 { + width * height + } +} + +#[derive(HasField)] +pub struct Rectangle { + pub width: f64, + pub height: f64, +} + +// `impl CanCalculateArea for Rectangle`, written in provider style: `Self` is the +// context, and `RectangleArea` supplies the actual computation. +#[cgp_impl(Self)] +#[use_provider(RectangleArea: AreaCalculator)] +impl CanCalculateArea for Rectangle { + fn area(&self) -> f64 { + RectangleArea::area(self) + } +} + +#[test] +fn test_impl_self() { + let rectangle = Rectangle { + width: 3.0, + height: 4.0, + }; + + assert_eq!(rectangle.area(), 12.0); +} diff --git a/crates/tests/cgp-tests/tests/basic_delegation/mod.rs b/crates/tests/cgp-tests/tests/basic_delegation/mod.rs new file mode 100644 index 00000000..85672a66 --- /dev/null +++ b/crates/tests/cgp-tests/tests/basic_delegation/mod.rs @@ -0,0 +1,24 @@ +//! One unit test per file. Each file is self-contained: it defines its own +//! components, providers, and context types at module scope so that the +//! type-level wiring of one test never leaks into another. + +// The macro-expansion snapshots that this concept owns: the canonical +// `#[cgp_component]`, `#[cgp_impl]`, and `delegate_components!` output. +pub mod component_macro; +pub mod delegate_array_key; +pub mod delegate_components_macro; +pub mod delegate_generic_table; +pub mod provider_macro; + +// Behavioral wiring: a consumer trait becomes usable once its component is wired. +pub mod consumer_delegate_generic; +pub mod consumer_delegate_getter; +pub mod default_methods; +pub mod impl_self; + +// `delegate_components!` shape variants (compile-time checks only). +pub mod delegate_generic_nested_value; +pub mod delegate_nested_use_delegate; +pub mod delegate_new_array_key; +pub mod delegate_new_generic_struct; +pub mod delegate_new_struct; diff --git a/crates/tests/cgp-tests/tests/basic_delegation/provider_macro.rs b/crates/tests/cgp-tests/tests/basic_delegation/provider_macro.rs new file mode 100644 index 00000000..b6930f5f --- /dev/null +++ b/crates/tests/cgp-tests/tests/basic_delegation/provider_macro.rs @@ -0,0 +1,54 @@ +//! Canonical expansion of `#[cgp_impl]`, the preferred way to write a provider. +//! +//! `#[cgp_impl(new Name)]` lets a provider be written in consumer-style syntax — +//! keeping `self` and the consumer method signatures — and rewrites it into the +//! provider-trait shape, declaring the provider struct and its `IsProviderFor` +//! impl. This file owns that reference snapshot; the component it targets is +//! written with a plain `#[cgp_component]` (already snapshotted in +//! `component_macro`), and a context wires the provider and calls it. +//! +//! See docs/reference/macros/cgp_impl.md. + +use cgp::prelude::*; +use cgp_macro_test_util::snapshot_cgp_impl; + +#[cgp_component(FooProvider)] +pub trait CanDoFoo { + fn foo(&self, value: u32) -> String; +} + +snapshot_cgp_impl! { + #[cgp_impl(new ValueToString)] + impl FooProvider for Context { + fn foo(&self, value: u32) -> String { + value.to_string() + } + } + + expand_value_to_string(output) { + insta::assert_snapshot!(output, @" + impl FooProvider for ValueToString { + fn foo(__context__: &Context, value: u32) -> String { + value.to_string() + } + } + impl IsProviderFor for ValueToString {} + pub struct ValueToString; + ") + } +} + +pub struct App; + +delegate_components! { + App { + FooProviderComponent: ValueToString, + } +} + +#[test] +fn test_provider_wiring() { + // Once `App` delegates `FooProviderComponent` to `ValueToString`, it + // implements the `CanDoFoo` consumer trait. + assert_eq!(App.foo(42), "42"); +} diff --git a/crates/tests/cgp-tests/tests/basic_delegation_tests.rs b/crates/tests/cgp-tests/tests/basic_delegation_tests.rs new file mode 100644 index 00000000..a7abdf1b --- /dev/null +++ b/crates/tests/cgp-tests/tests/basic_delegation_tests.rs @@ -0,0 +1,12 @@ +//! Entrypoint for the `basic_delegation` concept. +//! +//! Covers the fundamentals of the CGP component pattern: defining a +//! consumer/provider component pair with `#[cgp_component]`, writing providers +//! with `#[cgp_impl]`, wiring a context to providers with `delegate_components!`, +//! and implementing a consumer trait directly on a context. +//! +//! See docs/concepts/consumer-and-provider-traits.md and +//! docs/reference/macros/delegate_components.md. +#![allow(dead_code)] + +pub mod basic_delegation; diff --git a/crates/tests/cgp-tests/tests/blanket_traits/associated_type.rs b/crates/tests/cgp-tests/tests/blanket_traits/associated_type.rs new file mode 100644 index 00000000..1b0d166e --- /dev/null +++ b/crates/tests/cgp-tests/tests/blanket_traits/associated_type.rs @@ -0,0 +1,48 @@ +//! A `#[blanket_trait]` that declares its own associated type and ties it to a +//! supertrait's associated type via an equality bound (`Foo = Self::FooBar`). +//! The macro lifts the local associated type into an extra generic parameter on +//! the blanket impl and rewrites the supertrait bound to name it, so the context +//! only needs to implement the underlying `HasFooTypeAt`. +//! +//! Snapshot variant: blanket trait re-exporting a supertrait associated type, +//! with no bound on the local associated type. +//! See docs/reference/macros/blanket_trait.md. + +use cgp_macro_test_util::snapshot_blanket_trait; + +pub trait HasFooTypeAt { + type Foo; +} + +pub struct Bar; + +snapshot_blanket_trait! { + #[blanket_trait] + pub trait HasFooTypeAtBar: HasFooTypeAt { + type FooBar; + } + + expand_foo_bar(output) { + insta::assert_snapshot!(output, @" + pub trait HasFooTypeAtBar: HasFooTypeAt { + type FooBar; + } + impl<__Context__, FooBar> HasFooTypeAtBar for __Context__ + where + __Context__: HasFooTypeAt, + { + type FooBar = FooBar; + } + ") + } +} + +pub struct Context; +pub struct FooBar; + +impl HasFooTypeAt for Context { + type Foo = FooBar; +} + +pub trait CanUseFooTypeAtBar: HasFooTypeAtBar {} +impl CanUseFooTypeAtBar for Context {} diff --git a/crates/tests/cgp-tests/tests/blanket_traits/associated_type_bounded.rs b/crates/tests/cgp-tests/tests/blanket_traits/associated_type_bounded.rs new file mode 100644 index 00000000..68208445 --- /dev/null +++ b/crates/tests/cgp-tests/tests/blanket_traits/associated_type_bounded.rs @@ -0,0 +1,50 @@ +//! Like `associated_type`, but the local associated type carries a bound +//! (`type FooBar: Clone`). The macro moves that bound onto the lifted generic +//! parameter in the blanket impl's `where` clause (`FooBar: Clone`), so the +//! blanket impl only applies when the underlying associated type is `Clone`. +//! +//! Snapshot variant: blanket trait re-exporting a supertrait associated type +//! with a constraint on the local associated type. +//! See docs/reference/macros/blanket_trait.md. + +use cgp_macro_test_util::snapshot_blanket_trait; + +pub trait HasFooTypeAt { + type Foo; +} + +pub struct Bar; + +snapshot_blanket_trait! { + #[blanket_trait] + pub trait HasFooTypeAtBar: HasFooTypeAt { + type FooBar: Clone; + } + + expand_foo_bar(output) { + insta::assert_snapshot!(output, @" + pub trait HasFooTypeAtBar: HasFooTypeAt { + type FooBar: Clone; + } + impl<__Context__, FooBar> HasFooTypeAtBar for __Context__ + where + __Context__: HasFooTypeAt, + FooBar: Clone, + { + type FooBar = FooBar; + } + ") + } +} + +pub struct Context; + +#[derive(Clone)] +pub struct FooBar; + +impl HasFooTypeAt for Context { + type Foo = FooBar; +} + +pub trait CanUseFooTypeAtBar: HasFooTypeAtBar {} +impl CanUseFooTypeAtBar for Context {} diff --git a/crates/tests/cgp-tests/tests/blanket_traits/basic.rs b/crates/tests/cgp-tests/tests/blanket_traits/basic.rs new file mode 100644 index 00000000..5c88d969 --- /dev/null +++ b/crates/tests/cgp-tests/tests/blanket_traits/basic.rs @@ -0,0 +1,35 @@ +//! The simplest `#[blanket_trait]`: a trait with only supertrait bounds and no +//! body. `#[blanket_trait]` emits the trait unchanged and adds a blanket impl +//! for every `__Context__` that satisfies the supertraits, so any context that +//! is both `Foo` and `Bar` automatically gets `FooBar`. +//! +//! This is the canonical, minimal expansion snapshot for `#[blanket_trait]`. +//! See docs/reference/macros/blanket_trait.md. + +use cgp_macro_test_util::snapshot_blanket_trait; + +pub trait Foo {} +pub trait Bar {} + +snapshot_blanket_trait! { + #[blanket_trait] + pub trait FooBar: Foo + Bar {} + + expand_foo_bar(output) { + insta::assert_snapshot!(output, @" + pub trait FooBar: Foo + Bar {} + impl<__Context__> FooBar for __Context__ + where + __Context__: Foo + Bar, + {} + ") + } +} + +pub struct Context; + +impl Foo for Context {} +impl Bar for Context {} + +pub trait CanUseFooBar: FooBar {} +impl CanUseFooBar for Context {} diff --git a/crates/tests/cgp-tests/tests/blanket_traits/mod.rs b/crates/tests/cgp-tests/tests/blanket_traits/mod.rs new file mode 100644 index 00000000..7b03871e --- /dev/null +++ b/crates/tests/cgp-tests/tests/blanket_traits/mod.rs @@ -0,0 +1,18 @@ +//! One unit test per file. Each file is self-contained: it defines its own +//! traits and context types at module scope so that the type-level wiring of +//! one test never leaks into another. +//! +//! These files own the canonical `#[blanket_trait]` expansion snapshots, one +//! per genuinely distinct variant of the macro. + +// Plain supertrait-only blanket trait. +pub mod basic; + +// Blanket trait carrying a default method. +pub mod with_method; + +// Blanket trait re-exporting an associated type from a supertrait. +pub mod associated_type; + +// Same, but the associated type carries a bound (`: Clone`). +pub mod associated_type_bounded; diff --git a/crates/tests/cgp-tests/tests/blanket_traits/with_method.rs b/crates/tests/cgp-tests/tests/blanket_traits/with_method.rs new file mode 100644 index 00000000..b09988ad --- /dev/null +++ b/crates/tests/cgp-tests/tests/blanket_traits/with_method.rs @@ -0,0 +1,58 @@ +//! A `#[blanket_trait]` carrying a default method. The default method is copied +//! verbatim into the blanket impl, so every qualifying context inherits the +//! method body, which delegates to the supertrait methods (`foo`/`bar`). +//! +//! Snapshot variant: blanket trait with a default method body. +//! See docs/reference/macros/blanket_trait.md. + +use cgp_macro_test_util::snapshot_blanket_trait; + +pub trait Foo { + fn foo(&self); +} +pub trait Bar { + fn bar(&self); +} + +snapshot_blanket_trait! { + #[blanket_trait] + pub trait FooBar: Foo + Bar { + fn foo_bar(&self) { + self.foo(); + self.bar(); + } + } + + expand_foo_bar(output) { + insta::assert_snapshot!(output, @" + pub trait FooBar: Foo + Bar { + fn foo_bar(&self) { + self.foo(); + self.bar(); + } + } + impl<__Context__> FooBar for __Context__ + where + __Context__: Foo + Bar, + { + fn foo_bar(&self) { + self.foo(); + self.bar(); + } + } + ") + } +} + +pub struct Context; + +impl Foo for Context { + fn foo(&self) {} +} + +impl Bar for Context { + fn bar(&self) {} +} + +pub trait CanUseFooBar: FooBar {} +impl CanUseFooBar for Context {} diff --git a/crates/tests/cgp-tests/tests/blanket_traits_tests.rs b/crates/tests/cgp-tests/tests/blanket_traits_tests.rs new file mode 100644 index 00000000..c9f2ace5 --- /dev/null +++ b/crates/tests/cgp-tests/tests/blanket_traits_tests.rs @@ -0,0 +1,12 @@ +//! Entrypoint for the `blanket_traits` concept. +//! +//! Covers the `#[blanket_trait]` macro, which turns a trait with supertrait +//! bounds (and optional default methods / associated types) into an extension +//! trait plus a blanket impl that applies to every context satisfying those +//! bounds. This concept OWNS the canonical `#[blanket_trait]` expansion +//! snapshots. +//! +//! See docs/reference/macros/blanket_trait.md. +#![allow(dead_code)] + +pub mod blanket_traits; diff --git a/crates/tests/cgp-tests/tests/cgp_fn.rs b/crates/tests/cgp-tests/tests/cgp_fn.rs deleted file mode 100644 index 5e7cd854..00000000 --- a/crates/tests/cgp-tests/tests/cgp_fn.rs +++ /dev/null @@ -1,3 +0,0 @@ -#![allow(clippy::disallowed_names)] - -pub mod cgp_fn_tests; diff --git a/crates/tests/cgp-tests/tests/cgp_fn_tests/extend.rs b/crates/tests/cgp-tests/tests/cgp_fn_tests/extend.rs deleted file mode 100644 index abbb729e..00000000 --- a/crates/tests/cgp-tests/tests/cgp_fn_tests/extend.rs +++ /dev/null @@ -1,185 +0,0 @@ -use std::ops::Mul; - -use cgp::prelude::*; -use cgp_macro_test_util::{snapshot_cgp_fn, snapshot_cgp_type}; - -snapshot_cgp_type! { - #[cgp_type] - pub trait HasScalarType { - type Scalar; - } - - expand_has_scalar_type(output) { - insta::assert_snapshot!(output, @" - pub trait HasScalarType { - type Scalar; - } - impl<__Context__> HasScalarType for __Context__ - where - __Context__: ScalarTypeProvider<__Context__>, - { - type Scalar = <__Context__ as ScalarTypeProvider<__Context__>>::Scalar; - } - pub trait ScalarTypeProvider< - __Context__, - >: IsProviderFor { - type Scalar; - } - impl<__Provider__, __Context__> ScalarTypeProvider<__Context__> for __Provider__ - where - __Provider__: DelegateComponent - + IsProviderFor, - <__Provider__ as DelegateComponent< - ScalarTypeProviderComponent, - >>::Delegate: ScalarTypeProvider<__Context__>, - { - type Scalar = <<__Provider__ as DelegateComponent< - ScalarTypeProviderComponent, - >>::Delegate as ScalarTypeProvider<__Context__>>::Scalar; - } - pub struct ScalarTypeProviderComponent; - impl<__Context__> ScalarTypeProvider<__Context__> for UseContext - where - __Context__: HasScalarType, - { - type Scalar = <__Context__ as HasScalarType>::Scalar; - } - impl<__Context__> IsProviderFor - for UseContext - where - __Context__: HasScalarType, - {} - impl<__Context__, __Components__, __Path__> ScalarTypeProvider<__Context__> - for RedirectLookup<__Components__, __Path__> - where - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent< - __Path__, - >>::Delegate: ScalarTypeProvider<__Context__>, - { - type Scalar = <<__Components__ as DelegateComponent< - __Path__, - >>::Delegate as ScalarTypeProvider<__Context__>>::Scalar; - } - impl< - __Context__, - __Components__, - __Path__, - > IsProviderFor - for RedirectLookup<__Components__, __Path__> - where - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent< - __Path__, - >>::Delegate: IsProviderFor - + ScalarTypeProvider<__Context__>, - {} - impl ScalarTypeProvider<__Context__> for UseType { - type Scalar = Scalar; - } - impl IsProviderFor - for UseType {} - impl<__Provider__, Scalar, __Context__> ScalarTypeProvider<__Context__> - for WithProvider<__Provider__> - where - __Provider__: TypeProvider<__Context__, ScalarTypeProviderComponent, Type = Scalar>, - { - type Scalar = Scalar; - } - impl< - __Provider__, - Scalar, - __Context__, - > IsProviderFor - for WithProvider<__Provider__> - where - __Provider__: TypeProvider<__Context__, ScalarTypeProviderComponent, Type = Scalar>, - {} - ") - } -} - -snapshot_cgp_fn! { - #[cgp_fn] - #[extend(HasScalarType)] - pub fn rectangle_area( - &self, - #[implicit] width: Self::Scalar, - #[implicit] height: Self::Scalar, - ) -> Self::Scalar - where - Self::Scalar: Mul + Copy, - { - width * height - } - - expand_rectangle_area(output) { - insta::assert_snapshot!(output, @" - pub trait RectangleArea: HasScalarType { - fn rectangle_area(&self) -> Self::Scalar; - } - impl<__Context__> RectangleArea for __Context__ - where - Self::Scalar: Mul + Copy, - Self: HasScalarType, - Self: HasField< - Symbol<5, Chars<'w', Chars<'i', Chars<'d', Chars<'t', Chars<'h', Nil>>>>>>, - Value = Self::Scalar, - > - + HasField< - Symbol< - 6, - Chars< - 'h', - Chars<'e', Chars<'i', Chars<'g', Chars<'h', Chars<'t', Nil>>>>>, - >, - >, - Value = Self::Scalar, - >, - { - fn rectangle_area(&self) -> Self::Scalar { - let width: Self::Scalar = self - .get_field( - ::core::marker::PhantomData::< - Symbol< - 5, - Chars<'w', Chars<'i', Chars<'d', Chars<'t', Chars<'h', Nil>>>>>, - >, - >, - ) - .clone(); - let height: Self::Scalar = self - .get_field( - ::core::marker::PhantomData::< - Symbol< - 6, - Chars< - 'h', - Chars< - 'e', - Chars<'i', Chars<'g', Chars<'h', Chars<'t', Nil>>>>, - >, - >, - >, - >, - ) - .clone(); - width * height - } - } - ") - } -} - -#[derive(HasField)] -pub struct Rectangle { - pub width: f64, - pub height: f64, -} - -impl HasScalarType for Rectangle { - type Scalar = f64; -} - -pub trait CheckRectangle: RectangleArea {} -impl CheckRectangle for Rectangle {} diff --git a/crates/tests/cgp-tests/tests/cgp_fn_tests/foreign_type.rs b/crates/tests/cgp-tests/tests/cgp_fn_tests/foreign_type.rs deleted file mode 100644 index 0fa7e5d8..00000000 --- a/crates/tests/cgp-tests/tests/cgp_fn_tests/foreign_type.rs +++ /dev/null @@ -1,190 +0,0 @@ -use std::ops::Mul; - -use cgp::prelude::*; -use cgp_macro_test_util::{snapshot_cgp_fn, snapshot_cgp_type}; - -snapshot_cgp_type! { - #[cgp_type] - pub trait HasScalarType { - type Scalar; - } - - expand_has_scalar_type(output) { - insta::assert_snapshot!(output, @" - pub trait HasScalarType { - type Scalar; - } - impl<__Context__> HasScalarType for __Context__ - where - __Context__: ScalarTypeProvider<__Context__>, - { - type Scalar = <__Context__ as ScalarTypeProvider<__Context__>>::Scalar; - } - pub trait ScalarTypeProvider< - __Context__, - >: IsProviderFor { - type Scalar; - } - impl<__Provider__, __Context__> ScalarTypeProvider<__Context__> for __Provider__ - where - __Provider__: DelegateComponent - + IsProviderFor, - <__Provider__ as DelegateComponent< - ScalarTypeProviderComponent, - >>::Delegate: ScalarTypeProvider<__Context__>, - { - type Scalar = <<__Provider__ as DelegateComponent< - ScalarTypeProviderComponent, - >>::Delegate as ScalarTypeProvider<__Context__>>::Scalar; - } - pub struct ScalarTypeProviderComponent; - impl<__Context__> ScalarTypeProvider<__Context__> for UseContext - where - __Context__: HasScalarType, - { - type Scalar = <__Context__ as HasScalarType>::Scalar; - } - impl<__Context__> IsProviderFor - for UseContext - where - __Context__: HasScalarType, - {} - impl<__Context__, __Components__, __Path__> ScalarTypeProvider<__Context__> - for RedirectLookup<__Components__, __Path__> - where - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent< - __Path__, - >>::Delegate: ScalarTypeProvider<__Context__>, - { - type Scalar = <<__Components__ as DelegateComponent< - __Path__, - >>::Delegate as ScalarTypeProvider<__Context__>>::Scalar; - } - impl< - __Context__, - __Components__, - __Path__, - > IsProviderFor - for RedirectLookup<__Components__, __Path__> - where - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent< - __Path__, - >>::Delegate: IsProviderFor - + ScalarTypeProvider<__Context__>, - {} - impl ScalarTypeProvider<__Context__> for UseType { - type Scalar = Scalar; - } - impl IsProviderFor - for UseType {} - impl<__Provider__, Scalar, __Context__> ScalarTypeProvider<__Context__> - for WithProvider<__Provider__> - where - __Provider__: TypeProvider<__Context__, ScalarTypeProviderComponent, Type = Scalar>, - { - type Scalar = Scalar; - } - impl< - __Provider__, - Scalar, - __Context__, - > IsProviderFor - for WithProvider<__Provider__> - where - __Provider__: TypeProvider<__Context__, ScalarTypeProviderComponent, Type = Scalar>, - {} - ") - } -} - -snapshot_cgp_fn! { - #[cgp_fn] - #[use_type(@Types::HasScalarType::Scalar)] - pub fn rectangle_area( - &self, - #[implicit] width: Scalar, - #[implicit] height: Scalar, - ) -> Scalar - where - Scalar: Mul + Copy, - { - let res: Scalar = width * height; - res - } - - expand_rectangle_area(output) { - insta::assert_snapshot!(output, @" - pub trait RectangleArea { - fn rectangle_area(&self) -> ::Scalar; - } - impl<__Context__, Types: HasScalarType> RectangleArea for __Context__ - where - ::Scalar: Mul::Scalar> - + Copy, - Self: HasField< - Symbol<5, Chars<'w', Chars<'i', Chars<'d', Chars<'t', Chars<'h', Nil>>>>>>, - Value = ::Scalar, - > - + HasField< - Symbol< - 6, - Chars< - 'h', - Chars<'e', Chars<'i', Chars<'g', Chars<'h', Chars<'t', Nil>>>>>, - >, - >, - Value = ::Scalar, - >, - Types: HasScalarType, - { - fn rectangle_area(&self) -> ::Scalar { - let width: ::Scalar = self - .get_field( - ::core::marker::PhantomData::< - Symbol< - 5, - Chars<'w', Chars<'i', Chars<'d', Chars<'t', Chars<'h', Nil>>>>>, - >, - >, - ) - .clone(); - let height: ::Scalar = self - .get_field( - ::core::marker::PhantomData::< - Symbol< - 6, - Chars< - 'h', - Chars< - 'e', - Chars<'i', Chars<'g', Chars<'h', Chars<'t', Nil>>>>, - >, - >, - >, - >, - ) - .clone(); - let res: ::Scalar = width * height; - res - } - } - ") - } -} - -pub struct Types; - -impl HasScalarType for Types { - type Scalar = f64; -} - -#[derive(HasField)] -pub struct Rectangle { - pub width: f64, - pub height: f64, -} - -pub trait CheckRectangle: RectangleArea {} -impl CheckRectangle for Rectangle {} diff --git a/crates/tests/cgp-tests/tests/cgp_fn_tests/foreign_type_equality.rs b/crates/tests/cgp-tests/tests/cgp_fn_tests/foreign_type_equality.rs deleted file mode 100644 index 2b8dde10..00000000 --- a/crates/tests/cgp-tests/tests/cgp_fn_tests/foreign_type_equality.rs +++ /dev/null @@ -1,392 +0,0 @@ -use cgp_macro_test_util::{snapshot_cgp_fn, snapshot_cgp_type}; - -snapshot_cgp_type! { - #[cgp_type] - pub trait HasScalarType { - type Scalar; - } - - expand_has_scalar_type(output) { - insta::assert_snapshot!(output, @" - pub trait HasScalarType { - type Scalar; - } - impl<__Context__> HasScalarType for __Context__ - where - __Context__: ScalarTypeProvider<__Context__>, - { - type Scalar = <__Context__ as ScalarTypeProvider<__Context__>>::Scalar; - } - pub trait ScalarTypeProvider< - __Context__, - >: IsProviderFor { - type Scalar; - } - impl<__Provider__, __Context__> ScalarTypeProvider<__Context__> for __Provider__ - where - __Provider__: DelegateComponent - + IsProviderFor, - <__Provider__ as DelegateComponent< - ScalarTypeProviderComponent, - >>::Delegate: ScalarTypeProvider<__Context__>, - { - type Scalar = <<__Provider__ as DelegateComponent< - ScalarTypeProviderComponent, - >>::Delegate as ScalarTypeProvider<__Context__>>::Scalar; - } - pub struct ScalarTypeProviderComponent; - impl<__Context__> ScalarTypeProvider<__Context__> for UseContext - where - __Context__: HasScalarType, - { - type Scalar = <__Context__ as HasScalarType>::Scalar; - } - impl<__Context__> IsProviderFor - for UseContext - where - __Context__: HasScalarType, - {} - impl<__Context__, __Components__, __Path__> ScalarTypeProvider<__Context__> - for RedirectLookup<__Components__, __Path__> - where - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent< - __Path__, - >>::Delegate: ScalarTypeProvider<__Context__>, - { - type Scalar = <<__Components__ as DelegateComponent< - __Path__, - >>::Delegate as ScalarTypeProvider<__Context__>>::Scalar; - } - impl< - __Context__, - __Components__, - __Path__, - > IsProviderFor - for RedirectLookup<__Components__, __Path__> - where - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent< - __Path__, - >>::Delegate: IsProviderFor - + ScalarTypeProvider<__Context__>, - {} - impl ScalarTypeProvider<__Context__> for UseType { - type Scalar = Scalar; - } - impl IsProviderFor - for UseType {} - impl<__Provider__, Scalar, __Context__> ScalarTypeProvider<__Context__> - for WithProvider<__Provider__> - where - __Provider__: TypeProvider<__Context__, ScalarTypeProviderComponent, Type = Scalar>, - { - type Scalar = Scalar; - } - impl< - __Provider__, - Scalar, - __Context__, - > IsProviderFor - for WithProvider<__Provider__> - where - __Provider__: TypeProvider<__Context__, ScalarTypeProviderComponent, Type = Scalar>, - {} - ") - } -} - -snapshot_cgp_type! { - #[cgp_type] - pub trait HasTypes { - type Types: HasScalarType; - } - - expand_has_types(output) { - insta::assert_snapshot!(output, @" - pub trait HasTypes { - type Types: HasScalarType; - } - impl<__Context__> HasTypes for __Context__ - where - __Context__: TypesTypeProvider<__Context__>, - { - type Types = <__Context__ as TypesTypeProvider<__Context__>>::Types; - } - pub trait TypesTypeProvider< - __Context__, - >: IsProviderFor { - type Types: HasScalarType; - } - impl<__Provider__, __Context__> TypesTypeProvider<__Context__> for __Provider__ - where - __Provider__: DelegateComponent - + IsProviderFor, - <__Provider__ as DelegateComponent< - TypesTypeProviderComponent, - >>::Delegate: TypesTypeProvider<__Context__>, - { - type Types = <<__Provider__ as DelegateComponent< - TypesTypeProviderComponent, - >>::Delegate as TypesTypeProvider<__Context__>>::Types; - } - pub struct TypesTypeProviderComponent; - impl<__Context__> TypesTypeProvider<__Context__> for UseContext - where - __Context__: HasTypes, - { - type Types = <__Context__ as HasTypes>::Types; - } - impl<__Context__> IsProviderFor - for UseContext - where - __Context__: HasTypes, - {} - impl<__Context__, __Components__, __Path__> TypesTypeProvider<__Context__> - for RedirectLookup<__Components__, __Path__> - where - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent< - __Path__, - >>::Delegate: TypesTypeProvider<__Context__>, - { - type Types = <<__Components__ as DelegateComponent< - __Path__, - >>::Delegate as TypesTypeProvider<__Context__>>::Types; - } - impl< - __Context__, - __Components__, - __Path__, - > IsProviderFor - for RedirectLookup<__Components__, __Path__> - where - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent< - __Path__, - >>::Delegate: IsProviderFor - + TypesTypeProvider<__Context__>, - {} - impl TypesTypeProvider<__Context__> for UseType - where - Types: HasScalarType, - { - type Types = Types; - } - impl IsProviderFor - for UseType - where - Types: HasScalarType, - {} - impl<__Provider__, Types, __Context__> TypesTypeProvider<__Context__> - for WithProvider<__Provider__> - where - Types: HasScalarType, - __Provider__: TypeProvider<__Context__, TypesTypeProviderComponent, Type = Types>, - { - type Types = Types; - } - impl< - __Provider__, - Types, - __Context__, - > IsProviderFor - for WithProvider<__Provider__> - where - Types: HasScalarType, - __Provider__: TypeProvider<__Context__, TypesTypeProviderComponent, Type = Types>, - {} - ") - } -} - -snapshot_cgp_fn! { - #[cgp_fn] - #[use_type( - HasTypes::Types, - @Types::HasScalarType::{Scalar = f64}, - )] - pub fn rectangle_area(&self, #[implicit] width: Scalar, #[implicit] height: Scalar) -> Scalar { - let res: f64 = width * height; - res - } - - expand_rectangle_area(output) { - insta::assert_snapshot!(output, @" - pub trait RectangleArea: HasTypes { - fn rectangle_area(&self) -> <::Types as HasScalarType>::Scalar; - } - impl<__Context__> RectangleArea for __Context__ - where - Self: HasField< - Symbol<5, Chars<'w', Chars<'i', Chars<'d', Chars<'t', Chars<'h', Nil>>>>>>, - Value = <::Types as HasScalarType>::Scalar, - > - + HasField< - Symbol< - 6, - Chars< - 'h', - Chars<'e', Chars<'i', Chars<'g', Chars<'h', Chars<'t', Nil>>>>>, - >, - >, - Value = <::Types as HasScalarType>::Scalar, - >, - Self: HasTypes, - ::Types: HasScalarType, - { - fn rectangle_area(&self) -> <::Types as HasScalarType>::Scalar { - let width: <::Types as HasScalarType>::Scalar = self - .get_field( - ::core::marker::PhantomData::< - Symbol< - 5, - Chars<'w', Chars<'i', Chars<'d', Chars<'t', Chars<'h', Nil>>>>>, - >, - >, - ) - .clone(); - let height: <::Types as HasScalarType>::Scalar = self - .get_field( - ::core::marker::PhantomData::< - Symbol< - 6, - Chars< - 'h', - Chars< - 'e', - Chars<'i', Chars<'g', Chars<'h', Chars<'t', Nil>>>>, - >, - >, - >, - >, - ) - .clone(); - let res: f64 = width * height; - res - } - } - ") - } -} - -pub trait HasFooType { - type Foo: Ord + Clone; -} - -pub trait HasBarType { - type Bar: HasFooType; -} - -snapshot_cgp_fn! { - #[cgp_fn] - #[use_type(HasFooType::Foo)] - pub fn do_foo(&self) -> Foo { - todo!() - } - - expand_do_foo(output) { - insta::assert_snapshot!(output, @" - pub trait DoFoo: HasFooType { - fn do_foo(&self) -> ::Foo; - } - impl<__Context__> DoFoo for __Context__ - where - Self: HasFooType, - { - fn do_foo(&self) -> ::Foo { - todo!() - } - } - ") - } -} - -snapshot_cgp_fn! { - #[cgp_fn] - #[use_type(HasBarType::Bar, @Bar::HasFooType::Foo)] - pub fn do_bar(&self) -> Foo { - todo!() - } - - expand_do_bar(output) { - insta::assert_snapshot!(output, @" - pub trait DoBar: HasBarType { - fn do_bar(&self) -> <::Bar as HasFooType>::Foo; - } - impl<__Context__> DoBar for __Context__ - where - Self: HasBarType, - ::Bar: HasFooType, - { - fn do_bar(&self) -> <::Bar as HasFooType>::Foo { - todo!() - } - } - ") - } -} - -snapshot_cgp_fn! { - #[cgp_fn] - #[use_type( - HasFooType::Foo, - HasBarType::Bar, - @Bar::HasFooType::{Foo as BarFoo = Foo}, - )] - #[uses(DoFoo, DoBar)] - fn return_foo_or_bar(&self, flag: bool, #[implicit] foo: &Foo, #[implicit] bar: &BarFoo) -> Foo { - if flag { - let res: Foo = self.do_foo(); - if &res < foo { res } else { foo.clone() } - } else { - let res: BarFoo = self.do_bar(); - if &res < bar { res } else { bar.clone() } - } - } - - expand_return_foo_or_bar(output) { - insta::assert_snapshot!(output, @" - trait ReturnFooOrBar: HasFooType + HasBarType { - fn return_foo_or_bar(&self, flag: bool) -> ::Foo; - } - impl<__Context__> ReturnFooOrBar for __Context__ - where - Self: DoFoo + DoBar, - Self: HasField< - Symbol<3, Chars<'f', Chars<'o', Chars<'o', Nil>>>>, - Value = ::Foo, - > - + HasField< - Symbol<3, Chars<'b', Chars<'a', Chars<'r', Nil>>>>, - Value = <::Bar as HasFooType>::Foo, - >, - Self: HasFooType, - Self: HasBarType, - ::Bar: HasFooType::Foo>, - { - fn return_foo_or_bar(&self, flag: bool) -> ::Foo { - let foo: &::Foo = self - .get_field( - ::core::marker::PhantomData::< - Symbol<3, Chars<'f', Chars<'o', Chars<'o', Nil>>>>, - >, - ); - let bar: &<::Bar as HasFooType>::Foo = self - .get_field( - ::core::marker::PhantomData::< - Symbol<3, Chars<'b', Chars<'a', Chars<'r', Nil>>>>, - >, - ); - if flag { - let res: ::Foo = self.do_foo(); - if &res < foo { res } else { foo.clone() } - } else { - let res: <::Bar as HasFooType>::Foo = self.do_bar(); - if &res < bar { res } else { bar.clone() } - } - } - } - ") - } -} diff --git a/crates/tests/cgp-tests/tests/cgp_fn_tests/mod.rs b/crates/tests/cgp-tests/tests/cgp_fn_tests/mod.rs deleted file mode 100644 index e4c5804e..00000000 --- a/crates/tests/cgp-tests/tests/cgp_fn_tests/mod.rs +++ /dev/null @@ -1,16 +0,0 @@ -pub mod r#async; -pub mod basic; -pub mod call; -pub mod extend; -pub mod foreign_type; -pub mod foreign_type_equality; -pub mod generics; -pub mod impl_generics; -pub mod multi; -pub mod mutable; -pub mod nested_foreign_type; -pub mod type_equality; -pub mod use_provider; -pub mod use_type; -pub mod use_type_alias; -pub mod uses; diff --git a/crates/tests/cgp-tests/tests/cgp_fn_tests/nested_foreign_type.rs b/crates/tests/cgp-tests/tests/cgp_fn_tests/nested_foreign_type.rs deleted file mode 100644 index 0fc3bb9c..00000000 --- a/crates/tests/cgp-tests/tests/cgp_fn_tests/nested_foreign_type.rs +++ /dev/null @@ -1,298 +0,0 @@ -use std::ops::Mul; - -use cgp::prelude::*; -use cgp_macro_test_util::{snapshot_cgp_fn, snapshot_cgp_type}; - -snapshot_cgp_type! { - #[cgp_type] - pub trait HasScalarType { - type Scalar; - } - - expand_has_scalar_type(output) { - insta::assert_snapshot!(output, @" - pub trait HasScalarType { - type Scalar; - } - impl<__Context__> HasScalarType for __Context__ - where - __Context__: ScalarTypeProvider<__Context__>, - { - type Scalar = <__Context__ as ScalarTypeProvider<__Context__>>::Scalar; - } - pub trait ScalarTypeProvider< - __Context__, - >: IsProviderFor { - type Scalar; - } - impl<__Provider__, __Context__> ScalarTypeProvider<__Context__> for __Provider__ - where - __Provider__: DelegateComponent - + IsProviderFor, - <__Provider__ as DelegateComponent< - ScalarTypeProviderComponent, - >>::Delegate: ScalarTypeProvider<__Context__>, - { - type Scalar = <<__Provider__ as DelegateComponent< - ScalarTypeProviderComponent, - >>::Delegate as ScalarTypeProvider<__Context__>>::Scalar; - } - pub struct ScalarTypeProviderComponent; - impl<__Context__> ScalarTypeProvider<__Context__> for UseContext - where - __Context__: HasScalarType, - { - type Scalar = <__Context__ as HasScalarType>::Scalar; - } - impl<__Context__> IsProviderFor - for UseContext - where - __Context__: HasScalarType, - {} - impl<__Context__, __Components__, __Path__> ScalarTypeProvider<__Context__> - for RedirectLookup<__Components__, __Path__> - where - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent< - __Path__, - >>::Delegate: ScalarTypeProvider<__Context__>, - { - type Scalar = <<__Components__ as DelegateComponent< - __Path__, - >>::Delegate as ScalarTypeProvider<__Context__>>::Scalar; - } - impl< - __Context__, - __Components__, - __Path__, - > IsProviderFor - for RedirectLookup<__Components__, __Path__> - where - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent< - __Path__, - >>::Delegate: IsProviderFor - + ScalarTypeProvider<__Context__>, - {} - impl ScalarTypeProvider<__Context__> for UseType { - type Scalar = Scalar; - } - impl IsProviderFor - for UseType {} - impl<__Provider__, Scalar, __Context__> ScalarTypeProvider<__Context__> - for WithProvider<__Provider__> - where - __Provider__: TypeProvider<__Context__, ScalarTypeProviderComponent, Type = Scalar>, - { - type Scalar = Scalar; - } - impl< - __Provider__, - Scalar, - __Context__, - > IsProviderFor - for WithProvider<__Provider__> - where - __Provider__: TypeProvider<__Context__, ScalarTypeProviderComponent, Type = Scalar>, - {} - ") - } -} - -snapshot_cgp_type! { - #[cgp_type] - pub trait HasTypes { - type Types; - } - - expand_has_types(output) { - insta::assert_snapshot!(output, @" - pub trait HasTypes { - type Types; - } - impl<__Context__> HasTypes for __Context__ - where - __Context__: TypesTypeProvider<__Context__>, - { - type Types = <__Context__ as TypesTypeProvider<__Context__>>::Types; - } - pub trait TypesTypeProvider< - __Context__, - >: IsProviderFor { - type Types; - } - impl<__Provider__, __Context__> TypesTypeProvider<__Context__> for __Provider__ - where - __Provider__: DelegateComponent - + IsProviderFor, - <__Provider__ as DelegateComponent< - TypesTypeProviderComponent, - >>::Delegate: TypesTypeProvider<__Context__>, - { - type Types = <<__Provider__ as DelegateComponent< - TypesTypeProviderComponent, - >>::Delegate as TypesTypeProvider<__Context__>>::Types; - } - pub struct TypesTypeProviderComponent; - impl<__Context__> TypesTypeProvider<__Context__> for UseContext - where - __Context__: HasTypes, - { - type Types = <__Context__ as HasTypes>::Types; - } - impl<__Context__> IsProviderFor - for UseContext - where - __Context__: HasTypes, - {} - impl<__Context__, __Components__, __Path__> TypesTypeProvider<__Context__> - for RedirectLookup<__Components__, __Path__> - where - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent< - __Path__, - >>::Delegate: TypesTypeProvider<__Context__>, - { - type Types = <<__Components__ as DelegateComponent< - __Path__, - >>::Delegate as TypesTypeProvider<__Context__>>::Types; - } - impl< - __Context__, - __Components__, - __Path__, - > IsProviderFor - for RedirectLookup<__Components__, __Path__> - where - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent< - __Path__, - >>::Delegate: IsProviderFor - + TypesTypeProvider<__Context__>, - {} - impl TypesTypeProvider<__Context__> for UseType { - type Types = Types; - } - impl IsProviderFor - for UseType {} - impl<__Provider__, Types, __Context__> TypesTypeProvider<__Context__> - for WithProvider<__Provider__> - where - __Provider__: TypeProvider<__Context__, TypesTypeProviderComponent, Type = Types>, - { - type Types = Types; - } - impl< - __Provider__, - Types, - __Context__, - > IsProviderFor - for WithProvider<__Provider__> - where - __Provider__: TypeProvider<__Context__, TypesTypeProviderComponent, Type = Types>, - {} - ") - } -} - -snapshot_cgp_fn! { - #[cgp_fn] - #[use_type(HasTypes::Types, @Types::HasScalarType::Scalar)] - #[extend_where(Types: HasScalarType)] - pub fn rectangle_area(&self, #[implicit] width: Scalar, #[implicit] height: Scalar) -> Scalar - where - Scalar: Mul + Copy, - { - let res: Scalar = width * height; - res - } - - expand_rectangle_area(output) { - insta::assert_snapshot!(output, @" - pub trait RectangleArea: HasTypes - where - ::Types: HasScalarType, - { - fn rectangle_area(&self) -> <::Types as HasScalarType>::Scalar; - } - impl<__Context__> RectangleArea for __Context__ - where - <::Types as HasScalarType>::Scalar: Mul< - Output = <::Types as HasScalarType>::Scalar, - > + Copy, - ::Types: HasScalarType, - Self: HasField< - Symbol<5, Chars<'w', Chars<'i', Chars<'d', Chars<'t', Chars<'h', Nil>>>>>>, - Value = <::Types as HasScalarType>::Scalar, - > - + HasField< - Symbol< - 6, - Chars< - 'h', - Chars<'e', Chars<'i', Chars<'g', Chars<'h', Chars<'t', Nil>>>>>, - >, - >, - Value = <::Types as HasScalarType>::Scalar, - >, - Self: HasTypes, - ::Types: HasScalarType, - { - fn rectangle_area(&self) -> <::Types as HasScalarType>::Scalar { - let width: <::Types as HasScalarType>::Scalar = self - .get_field( - ::core::marker::PhantomData::< - Symbol< - 5, - Chars<'w', Chars<'i', Chars<'d', Chars<'t', Chars<'h', Nil>>>>>, - >, - >, - ) - .clone(); - let height: <::Types as HasScalarType>::Scalar = self - .get_field( - ::core::marker::PhantomData::< - Symbol< - 6, - Chars< - 'h', - Chars< - 'e', - Chars<'i', Chars<'g', Chars<'h', Chars<'t', Nil>>>>, - >, - >, - >, - >, - ) - .clone(); - let res: <::Types as HasScalarType>::Scalar = width * height; - res - } - } - ") - } -} - -pub struct MyTypes; - -impl HasScalarType for MyTypes { - type Scalar = f64; -} - -#[derive(HasField)] -pub struct Rectangle { - pub width: f64, - pub height: f64, -} - -impl HasTypes for Rectangle { - type Types = MyTypes; -} - -pub trait CheckRectangle: RectangleArea -where - Self::Types: HasScalarType, -{ -} - -impl CheckRectangle for Rectangle {} diff --git a/crates/tests/cgp-tests/tests/cgp_fn_tests/type_equality.rs b/crates/tests/cgp-tests/tests/cgp_fn_tests/type_equality.rs deleted file mode 100644 index 0e226cec..00000000 --- a/crates/tests/cgp-tests/tests/cgp_fn_tests/type_equality.rs +++ /dev/null @@ -1,282 +0,0 @@ -use std::fmt::Display; - -use cgp_macro_test_util::{snapshot_cgp_fn, snapshot_cgp_type}; - -snapshot_cgp_type! { - #[cgp_type] - pub trait HasScalarType { - type Scalar; - } - - expand_has_scalar_type(output) { - insta::assert_snapshot!(output, @" - pub trait HasScalarType { - type Scalar; - } - impl<__Context__> HasScalarType for __Context__ - where - __Context__: ScalarTypeProvider<__Context__>, - { - type Scalar = <__Context__ as ScalarTypeProvider<__Context__>>::Scalar; - } - pub trait ScalarTypeProvider< - __Context__, - >: IsProviderFor { - type Scalar; - } - impl<__Provider__, __Context__> ScalarTypeProvider<__Context__> for __Provider__ - where - __Provider__: DelegateComponent - + IsProviderFor, - <__Provider__ as DelegateComponent< - ScalarTypeProviderComponent, - >>::Delegate: ScalarTypeProvider<__Context__>, - { - type Scalar = <<__Provider__ as DelegateComponent< - ScalarTypeProviderComponent, - >>::Delegate as ScalarTypeProvider<__Context__>>::Scalar; - } - pub struct ScalarTypeProviderComponent; - impl<__Context__> ScalarTypeProvider<__Context__> for UseContext - where - __Context__: HasScalarType, - { - type Scalar = <__Context__ as HasScalarType>::Scalar; - } - impl<__Context__> IsProviderFor - for UseContext - where - __Context__: HasScalarType, - {} - impl<__Context__, __Components__, __Path__> ScalarTypeProvider<__Context__> - for RedirectLookup<__Components__, __Path__> - where - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent< - __Path__, - >>::Delegate: ScalarTypeProvider<__Context__>, - { - type Scalar = <<__Components__ as DelegateComponent< - __Path__, - >>::Delegate as ScalarTypeProvider<__Context__>>::Scalar; - } - impl< - __Context__, - __Components__, - __Path__, - > IsProviderFor - for RedirectLookup<__Components__, __Path__> - where - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent< - __Path__, - >>::Delegate: IsProviderFor - + ScalarTypeProvider<__Context__>, - {} - impl ScalarTypeProvider<__Context__> for UseType { - type Scalar = Scalar; - } - impl IsProviderFor - for UseType {} - impl<__Provider__, Scalar, __Context__> ScalarTypeProvider<__Context__> - for WithProvider<__Provider__> - where - __Provider__: TypeProvider<__Context__, ScalarTypeProviderComponent, Type = Scalar>, - { - type Scalar = Scalar; - } - impl< - __Provider__, - Scalar, - __Context__, - > IsProviderFor - for WithProvider<__Provider__> - where - __Provider__: TypeProvider<__Context__, ScalarTypeProviderComponent, Type = Scalar>, - {} - ") - } -} - -snapshot_cgp_fn! { - #[cgp_fn] - #[use_type(HasScalarType::{Scalar = f64})] - pub fn rectangle_area(&self, #[implicit] width: Scalar, #[implicit] height: Scalar) -> Scalar { - let res: f64 = width * height; - res - } - - expand_rectangle_area(output) { - insta::assert_snapshot!(output, @" - pub trait RectangleArea: HasScalarType { - fn rectangle_area(&self) -> ::Scalar; - } - impl<__Context__> RectangleArea for __Context__ - where - Self: HasField< - Symbol<5, Chars<'w', Chars<'i', Chars<'d', Chars<'t', Chars<'h', Nil>>>>>>, - Value = ::Scalar, - > - + HasField< - Symbol< - 6, - Chars< - 'h', - Chars<'e', Chars<'i', Chars<'g', Chars<'h', Chars<'t', Nil>>>>>, - >, - >, - Value = ::Scalar, - >, - Self: HasScalarType, - { - fn rectangle_area(&self) -> ::Scalar { - let width: ::Scalar = self - .get_field( - ::core::marker::PhantomData::< - Symbol< - 5, - Chars<'w', Chars<'i', Chars<'d', Chars<'t', Chars<'h', Nil>>>>>, - >, - >, - ) - .clone(); - let height: ::Scalar = self - .get_field( - ::core::marker::PhantomData::< - Symbol< - 6, - Chars< - 'h', - Chars< - 'e', - Chars<'i', Chars<'g', Chars<'h', Chars<'t', Nil>>>>, - >, - >, - >, - >, - ) - .clone(); - let res: f64 = width * height; - res - } - } - ") - } -} - -pub trait HasFooType { - // The `Ord + Clone` bounds are visible to both `Foo` and `Bar` because of `Bar = Foo` below - type Foo: Ord + Clone; -} - -pub trait HasBarType { - // The `Display` bounds are hidden because of `Bar = Foo` below - type Bar: Display; -} - -snapshot_cgp_fn! { - #[cgp_fn] - #[use_type(HasFooType::Foo)] - pub fn do_foo(&self) -> Foo { - todo!() - } - - expand_do_foo(output) { - insta::assert_snapshot!(output, @" - pub trait DoFoo: HasFooType { - fn do_foo(&self) -> ::Foo; - } - impl<__Context__> DoFoo for __Context__ - where - Self: HasFooType, - { - fn do_foo(&self) -> ::Foo { - todo!() - } - } - ") - } -} - -snapshot_cgp_fn! { - #[cgp_fn] - #[use_type(HasBarType::Bar)] - pub fn do_bar(&self) -> Bar { - todo!() - } - - expand_do_bar(output) { - insta::assert_snapshot!(output, @" - pub trait DoBar: HasBarType { - fn do_bar(&self) -> ::Bar; - } - impl<__Context__> DoBar for __Context__ - where - Self: HasBarType, - { - fn do_bar(&self) -> ::Bar { - todo!() - } - } - ") - } -} - -snapshot_cgp_fn! { - #[cgp_fn] - #[use_type(HasBarType::{Bar as Baz = Foo}, HasFooType::Foo)] - #[uses(DoFoo, DoBar)] - fn return_foo_or_bar(&self, flag: bool, #[implicit] foo: &Foo, #[implicit] bar: &Baz) -> Foo { - if flag { - let res: Foo = self.do_foo(); - if &res < foo { res } else { foo.clone() } - } else { - let res: Baz = self.do_bar(); - if &res < bar { res } else { bar.clone() } - } - } - - expand_return_foo_or_bar(output) { - insta::assert_snapshot!(output, @" - trait ReturnFooOrBar: HasBarType + HasFooType { - fn return_foo_or_bar(&self, flag: bool) -> ::Foo; - } - impl<__Context__> ReturnFooOrBar for __Context__ - where - Self: DoFoo + DoBar, - Self: HasField< - Symbol<3, Chars<'f', Chars<'o', Chars<'o', Nil>>>>, - Value = ::Foo, - > - + HasField< - Symbol<3, Chars<'b', Chars<'a', Chars<'r', Nil>>>>, - Value = ::Bar, - >, - Self: HasBarType::Foo>, - Self: HasFooType, - { - fn return_foo_or_bar(&self, flag: bool) -> ::Foo { - let foo: &::Foo = self - .get_field( - ::core::marker::PhantomData::< - Symbol<3, Chars<'f', Chars<'o', Chars<'o', Nil>>>>, - >, - ); - let bar: &::Bar = self - .get_field( - ::core::marker::PhantomData::< - Symbol<3, Chars<'b', Chars<'a', Chars<'r', Nil>>>>, - >, - ); - if flag { - let res: ::Foo = self.do_foo(); - if &res < foo { res } else { foo.clone() } - } else { - let res: ::Bar = self.do_bar(); - if &res < bar { res } else { bar.clone() } - } - } - } - ") - } -} diff --git a/crates/tests/cgp-tests/tests/cgp_fn_tests/use_provider.rs b/crates/tests/cgp-tests/tests/cgp_fn_tests/use_provider.rs deleted file mode 100644 index e44eaaff..00000000 --- a/crates/tests/cgp-tests/tests/cgp_fn_tests/use_provider.rs +++ /dev/null @@ -1,202 +0,0 @@ -use cgp::prelude::*; -use cgp_macro_test_util::{snapshot_cgp_component, snapshot_cgp_fn, snapshot_cgp_impl}; - -snapshot_cgp_component! { - #[cgp_component(AreaCalculator)] - pub trait CanCalculateArea { - fn area(&self) -> f64; - } - - expand_can_calculate_area(output) { - insta::assert_snapshot!(output, @" - pub trait CanCalculateArea { - fn area(&self) -> f64; - } - impl<__Context__> CanCalculateArea for __Context__ - where - __Context__: AreaCalculator<__Context__>, - { - fn area(&self) -> f64 { - __Context__::area(self) - } - } - pub trait AreaCalculator< - __Context__, - >: IsProviderFor { - fn area(__context__: &__Context__) -> f64; - } - impl<__Provider__, __Context__> AreaCalculator<__Context__> for __Provider__ - where - __Provider__: DelegateComponent - + IsProviderFor, - <__Provider__ as DelegateComponent< - AreaCalculatorComponent, - >>::Delegate: AreaCalculator<__Context__>, - { - fn area(__context__: &__Context__) -> f64 { - <__Provider__ as DelegateComponent< - AreaCalculatorComponent, - >>::Delegate::area(__context__) - } - } - pub struct AreaCalculatorComponent; - impl<__Context__> AreaCalculator<__Context__> for UseContext - where - __Context__: CanCalculateArea, - { - fn area(__context__: &__Context__) -> f64 { - __Context__::area(__context__) - } - } - impl<__Context__> IsProviderFor for UseContext - where - __Context__: CanCalculateArea, - {} - impl<__Context__, __Components__, __Path__> AreaCalculator<__Context__> - for RedirectLookup<__Components__, __Path__> - where - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent< - __Path__, - >>::Delegate: AreaCalculator<__Context__>, - { - fn area(__context__: &__Context__) -> f64 { - <__Components__ as DelegateComponent<__Path__>>::Delegate::area(__context__) - } - } - impl< - __Context__, - __Components__, - __Path__, - > IsProviderFor - for RedirectLookup<__Components__, __Path__> - where - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent< - __Path__, - >>::Delegate: IsProviderFor - + AreaCalculator<__Context__>, - {} - ") - } -} - -snapshot_cgp_impl! { - #[cgp_impl(new RectangleAreaCalculator)] - impl AreaCalculator { - fn area(&self, #[implicit] width: f64, #[implicit] height: f64) -> f64 { - width * height - } - } - - expand_rectangle_area_calculator(output) { - insta::assert_snapshot!(output, @" - impl<__Context__> AreaCalculator<__Context__> for RectangleAreaCalculator - where - __Context__: HasField< - Symbol<5, Chars<'w', Chars<'i', Chars<'d', Chars<'t', Chars<'h', Nil>>>>>>, - Value = f64, - > - + HasField< - Symbol< - 6, - Chars< - 'h', - Chars<'e', Chars<'i', Chars<'g', Chars<'h', Chars<'t', Nil>>>>>, - >, - >, - Value = f64, - >, - { - fn area(__context__: &__Context__) -> f64 { - let width: f64 = __context__ - .get_field( - ::core::marker::PhantomData::< - Symbol< - 5, - Chars<'w', Chars<'i', Chars<'d', Chars<'t', Chars<'h', Nil>>>>>, - >, - >, - ) - .clone(); - let height: f64 = __context__ - .get_field( - ::core::marker::PhantomData::< - Symbol< - 6, - Chars< - 'h', - Chars< - 'e', - Chars<'i', Chars<'g', Chars<'h', Chars<'t', Nil>>>>, - >, - >, - >, - >, - ) - .clone(); - width * height - } - } - impl<__Context__> IsProviderFor - for RectangleAreaCalculator - where - __Context__: HasField< - Symbol<5, Chars<'w', Chars<'i', Chars<'d', Chars<'t', Chars<'h', Nil>>>>>>, - Value = f64, - > - + HasField< - Symbol< - 6, - Chars< - 'h', - Chars<'e', Chars<'i', Chars<'g', Chars<'h', Chars<'t', Nil>>>>>, - >, - >, - Value = f64, - >, - {} - pub struct RectangleAreaCalculator; - ") - } -} - -snapshot_cgp_fn! { - #[cgp_fn] - #[use_provider(RectangleAreaCalculator: AreaCalculator)] - fn rectangle_area(&self) -> f64 { - RectangleAreaCalculator::area(self) - } - - expand_rectangle_area(output) { - insta::assert_snapshot!(output, @" - trait RectangleArea { - fn rectangle_area(&self) -> f64; - } - impl<__Context__> RectangleArea for __Context__ - where - RectangleAreaCalculator: AreaCalculator, - { - fn rectangle_area(&self) -> f64 { - RectangleAreaCalculator::area(self) - } - } - ") - } -} - -#[derive(HasField)] -pub struct Rectangle { - pub width: f64, - pub height: f64, -} - -#[test] -fn test_use_provider() { - let rectangle = Rectangle { - width: 3.0, - height: 4.0, - }; - - assert_eq!(rectangle.rectangle_area(), 12.0); -} diff --git a/crates/tests/cgp-tests/tests/cgp_fn_tests/use_type.rs b/crates/tests/cgp-tests/tests/cgp_fn_tests/use_type.rs deleted file mode 100644 index 307f7f6b..00000000 --- a/crates/tests/cgp-tests/tests/cgp_fn_tests/use_type.rs +++ /dev/null @@ -1,214 +0,0 @@ -use core::f64; -use std::ops::Mul; - -use cgp::prelude::*; -use cgp_macro_test_util::{snapshot_cgp_fn, snapshot_cgp_type}; - -snapshot_cgp_type! { - #[cgp_type] - pub trait HasScalarType { - type Scalar: Mul + Copy; - } - - expand_has_scalar_type(output) { - insta::assert_snapshot!(output, @" - pub trait HasScalarType { - type Scalar: Mul + Copy; - } - impl<__Context__> HasScalarType for __Context__ - where - __Context__: ScalarTypeProvider<__Context__>, - { - type Scalar = <__Context__ as ScalarTypeProvider<__Context__>>::Scalar; - } - pub trait ScalarTypeProvider< - __Context__, - >: IsProviderFor { - type Scalar: Mul + Copy; - } - impl<__Provider__, __Context__> ScalarTypeProvider<__Context__> for __Provider__ - where - __Provider__: DelegateComponent - + IsProviderFor, - <__Provider__ as DelegateComponent< - ScalarTypeProviderComponent, - >>::Delegate: ScalarTypeProvider<__Context__>, - { - type Scalar = <<__Provider__ as DelegateComponent< - ScalarTypeProviderComponent, - >>::Delegate as ScalarTypeProvider<__Context__>>::Scalar; - } - pub struct ScalarTypeProviderComponent; - impl<__Context__> ScalarTypeProvider<__Context__> for UseContext - where - __Context__: HasScalarType, - { - type Scalar = <__Context__ as HasScalarType>::Scalar; - } - impl<__Context__> IsProviderFor - for UseContext - where - __Context__: HasScalarType, - {} - impl<__Context__, __Components__, __Path__> ScalarTypeProvider<__Context__> - for RedirectLookup<__Components__, __Path__> - where - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent< - __Path__, - >>::Delegate: ScalarTypeProvider<__Context__>, - { - type Scalar = <<__Components__ as DelegateComponent< - __Path__, - >>::Delegate as ScalarTypeProvider<__Context__>>::Scalar; - } - impl< - __Context__, - __Components__, - __Path__, - > IsProviderFor - for RedirectLookup<__Components__, __Path__> - where - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent< - __Path__, - >>::Delegate: IsProviderFor - + ScalarTypeProvider<__Context__>, - {} - impl ScalarTypeProvider<__Context__> for UseType - where - Scalar: Mul + Copy, - { - type Scalar = Scalar; - } - impl IsProviderFor - for UseType - where - Scalar: Mul + Copy, - {} - impl<__Provider__, Scalar, __Context__> ScalarTypeProvider<__Context__> - for WithProvider<__Provider__> - where - Scalar: Mul + Copy, - __Provider__: TypeProvider<__Context__, ScalarTypeProviderComponent, Type = Scalar>, - { - type Scalar = Scalar; - } - impl< - __Provider__, - Scalar, - __Context__, - > IsProviderFor - for WithProvider<__Provider__> - where - Scalar: Mul + Copy, - __Provider__: TypeProvider<__Context__, ScalarTypeProviderComponent, Type = Scalar>, - {} - ") - } -} - -snapshot_cgp_fn! { - #[cgp_fn] - #[extend(HasScalarType)] - pub fn rectangle_area( - &self, - #[implicit] width: Self::Scalar, - #[implicit] height: Self::Scalar, - ) -> Self::Scalar { - width * height - } - - expand_rectangle_area(output) { - insta::assert_snapshot!(output, @" - pub trait RectangleArea: HasScalarType { - fn rectangle_area(&self) -> Self::Scalar; - } - impl<__Context__> RectangleArea for __Context__ - where - Self: HasScalarType, - Self: HasField< - Symbol<5, Chars<'w', Chars<'i', Chars<'d', Chars<'t', Chars<'h', Nil>>>>>>, - Value = Self::Scalar, - > - + HasField< - Symbol< - 6, - Chars< - 'h', - Chars<'e', Chars<'i', Chars<'g', Chars<'h', Chars<'t', Nil>>>>>, - >, - >, - Value = Self::Scalar, - >, - { - fn rectangle_area(&self) -> Self::Scalar { - let width: Self::Scalar = self - .get_field( - ::core::marker::PhantomData::< - Symbol< - 5, - Chars<'w', Chars<'i', Chars<'d', Chars<'t', Chars<'h', Nil>>>>>, - >, - >, - ) - .clone(); - let height: Self::Scalar = self - .get_field( - ::core::marker::PhantomData::< - Symbol< - 6, - Chars< - 'h', - Chars< - 'e', - Chars<'i', Chars<'g', Chars<'h', Chars<'t', Nil>>>>, - >, - >, - >, - >, - ) - .clone(); - width * height - } - } - ") - } -} - -#[derive(HasField)] -pub struct F32Rectangle { - pub width: f32, - pub height: f32, -} - -impl HasScalarType for F32Rectangle { - type Scalar = f32; -} - -#[derive(HasField)] -pub struct F64Rectangle { - pub width: f64, - pub height: f64, -} - -impl HasScalarType for F64Rectangle { - type Scalar = f64; -} - -#[test] -fn test_rectangle_area() { - let f32_rectangle = F32Rectangle { - width: 3.0, - height: 4.0, - }; - - assert_eq!(f32_rectangle.rectangle_area(), 12.0); - - let f64_rectangle = F64Rectangle { - width: 3.0, - height: 4.0, - }; - - assert_eq!(f64_rectangle.rectangle_area(), 12.0); -} diff --git a/crates/tests/cgp-tests/tests/cgp_fn_tests/use_type_alias.rs b/crates/tests/cgp-tests/tests/cgp_fn_tests/use_type_alias.rs deleted file mode 100644 index b813d18a..00000000 --- a/crates/tests/cgp-tests/tests/cgp_fn_tests/use_type_alias.rs +++ /dev/null @@ -1,170 +0,0 @@ -use std::ops::Mul; - -use cgp_macro_test_util::{snapshot_cgp_fn, snapshot_cgp_type}; - -snapshot_cgp_type! { - #[cgp_type] - pub trait HasScalarType { - type Scalar; - } - - expand_has_scalar_type(output) { - insta::assert_snapshot!(output, @" - pub trait HasScalarType { - type Scalar; - } - impl<__Context__> HasScalarType for __Context__ - where - __Context__: ScalarTypeProvider<__Context__>, - { - type Scalar = <__Context__ as ScalarTypeProvider<__Context__>>::Scalar; - } - pub trait ScalarTypeProvider< - __Context__, - >: IsProviderFor { - type Scalar; - } - impl<__Provider__, __Context__> ScalarTypeProvider<__Context__> for __Provider__ - where - __Provider__: DelegateComponent - + IsProviderFor, - <__Provider__ as DelegateComponent< - ScalarTypeProviderComponent, - >>::Delegate: ScalarTypeProvider<__Context__>, - { - type Scalar = <<__Provider__ as DelegateComponent< - ScalarTypeProviderComponent, - >>::Delegate as ScalarTypeProvider<__Context__>>::Scalar; - } - pub struct ScalarTypeProviderComponent; - impl<__Context__> ScalarTypeProvider<__Context__> for UseContext - where - __Context__: HasScalarType, - { - type Scalar = <__Context__ as HasScalarType>::Scalar; - } - impl<__Context__> IsProviderFor - for UseContext - where - __Context__: HasScalarType, - {} - impl<__Context__, __Components__, __Path__> ScalarTypeProvider<__Context__> - for RedirectLookup<__Components__, __Path__> - where - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent< - __Path__, - >>::Delegate: ScalarTypeProvider<__Context__>, - { - type Scalar = <<__Components__ as DelegateComponent< - __Path__, - >>::Delegate as ScalarTypeProvider<__Context__>>::Scalar; - } - impl< - __Context__, - __Components__, - __Path__, - > IsProviderFor - for RedirectLookup<__Components__, __Path__> - where - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent< - __Path__, - >>::Delegate: IsProviderFor - + ScalarTypeProvider<__Context__>, - {} - impl ScalarTypeProvider<__Context__> for UseType { - type Scalar = Scalar; - } - impl IsProviderFor - for UseType {} - impl<__Provider__, Scalar, __Context__> ScalarTypeProvider<__Context__> - for WithProvider<__Provider__> - where - __Provider__: TypeProvider<__Context__, ScalarTypeProviderComponent, Type = Scalar>, - { - type Scalar = Scalar; - } - impl< - __Provider__, - Scalar, - __Context__, - > IsProviderFor - for WithProvider<__Provider__> - where - __Provider__: TypeProvider<__Context__, ScalarTypeProviderComponent, Type = Scalar>, - {} - ") - } -} - -snapshot_cgp_fn! { - #[cgp_fn] - #[use_type(HasScalarType::{Scalar as S})] - pub fn rectangle_area(&self, #[implicit] width: S, #[implicit] height: S) -> S - where - S: Mul + Copy, - { - let res: S = width * height; - res - } - - expand_rectangle_area(output) { - insta::assert_snapshot!(output, @" - pub trait RectangleArea: HasScalarType { - fn rectangle_area(&self) -> ::Scalar; - } - impl<__Context__> RectangleArea for __Context__ - where - ::Scalar: Mul::Scalar> - + Copy, - Self: HasField< - Symbol<5, Chars<'w', Chars<'i', Chars<'d', Chars<'t', Chars<'h', Nil>>>>>>, - Value = ::Scalar, - > - + HasField< - Symbol< - 6, - Chars< - 'h', - Chars<'e', Chars<'i', Chars<'g', Chars<'h', Chars<'t', Nil>>>>>, - >, - >, - Value = ::Scalar, - >, - Self: HasScalarType, - { - fn rectangle_area(&self) -> ::Scalar { - let width: ::Scalar = self - .get_field( - ::core::marker::PhantomData::< - Symbol< - 5, - Chars<'w', Chars<'i', Chars<'d', Chars<'t', Chars<'h', Nil>>>>>, - >, - >, - ) - .clone(); - let height: ::Scalar = self - .get_field( - ::core::marker::PhantomData::< - Symbol< - 6, - Chars< - 'h', - Chars< - 'e', - Chars<'i', Chars<'g', Chars<'h', Chars<'t', Nil>>>>, - >, - >, - >, - >, - ) - .clone(); - let res: ::Scalar = width * height; - res - } - } - ") - } -} diff --git a/crates/tests/cgp-tests/tests/checking/check_generic.rs b/crates/tests/cgp-tests/tests/checking/check_generic.rs new file mode 100644 index 00000000..0d35ea58 --- /dev/null +++ b/crates/tests/cgp-tests/tests/checking/check_generic.rs @@ -0,0 +1,91 @@ +//! `check_components!` on a generic context with lifetimes and a `where` clause: +//! the leading generic list (`<'a, I>`) and the trailing `where I: Clone` are +//! carried onto the generated check trait's impls, and a check entry may use a +//! generic parameter as its component's parameter (`Component: &'a I`) and target a +//! component that is itself generic (`BarGetterAtComponent`). This concept owns +//! the macro's expansion snapshot. +//! +//! See docs/reference/macros/check_components.md and +//! docs/reference/traits/can_use_component.md. + +use core::marker::PhantomData; + +use cgp::prelude::*; +use cgp_macro_test_util::snapshot_check_components; + +#[cgp_type] +pub trait HasFooType { + type Foo; +} + +#[cgp_type] +pub trait HasBarType { + type Bar; +} + +#[cgp_getter { + provider: FooGetterAt, +}] +pub trait HasFooAt: HasFooType { + fn foo(&self, _tag: PhantomData) -> &Self::Foo; +} + +#[cgp_getter { + name: BarGetterAtComponent, + provider: BarGetterAt, +}] +pub trait HasBarAt: HasBarType { + fn foo(&self, _tag: PhantomData<(I, J)>) -> &Self::Bar; +} + +#[derive(HasField)] +pub struct Context { + pub dummy: (), +} + +// Incidental wiring so that `Context` can use the components being checked below; +// the `delegate_components!` expansion is snapshotted in `basic_delegation`, so we +// invoke it plainly here. +delegate_components! { + Context { + [ + FooTypeProviderComponent, + BarTypeProviderComponent, + ]: + UseType<()>, + [ + FooGetterAtComponent, + BarGetterAtComponent, + ]: + UseField, + } +} + +snapshot_check_components! { + check_components! { + <'a, I> Context + where + I: Clone, + { + FooGetterAtComponent: &'a I, + BarGetterAtComponent: (I, &'a Index<0>), + } + } + + expand_check_context(output) { + insta::assert_snapshot!(output, @" + trait __CheckContext< + __Component__, + __Params__: ?Sized, + >: CanUseComponent<__Component__, __Params__> {} + impl<'a, I> __CheckContext for Context + where + I: Clone, + {} + impl<'a, I> __CheckContext, (I, &'a Index<0>)> for Context + where + I: Clone, + {} + ") + } +} diff --git a/crates/tests/cgp-tests/tests/checking/check_providers.rs b/crates/tests/cgp-tests/tests/checking/check_providers.rs new file mode 100644 index 00000000..eea2ddc0 --- /dev/null +++ b/crates/tests/cgp-tests/tests/checking/check_providers.rs @@ -0,0 +1,135 @@ +//! `check_components!` with `#[check_providers(...)]`: instead of checking that the +//! context can use each component through its wiring, this form checks that each +//! *listed provider* is a valid provider of each component for the context. The +//! generated check trait therefore supertraits `IsProviderFor<_, Context, _>` and +//! is implemented `for` each provider (here two `UseField` variants), rather than +//! `for` the context. This concept owns the macro's expansion snapshot. +//! +//! See docs/reference/macros/check_components.md and +//! docs/reference/traits/can_use_component.md. + +use core::marker::PhantomData; + +use cgp::prelude::*; +use cgp_macro_test_util::snapshot_check_components; + +#[cgp_type] +pub trait HasFooType { + type Foo; +} + +#[cgp_type] +pub trait HasBarType { + type Bar; +} + +#[cgp_getter { + provider: FooGetterAt, +}] +pub trait HasFooAt: HasFooType { + fn foo(&self, _tag: PhantomData) -> &Self::Foo; +} + +#[cgp_getter { + provider: BarGetterAt, +}] +pub trait HasBarAt: HasBarType { + fn foo(&self, _tag: PhantomData<(I, J)>) -> &Self::Bar; +} + +#[derive(HasField)] +pub struct Context { + pub dummy: (), + pub extra_dummy: (), +} + +// Incidental wiring: the getter providers depend on `Context: HasFooType` / +// `HasBarType`, so the abstract types must be wired before the providers can be +// checked. The `delegate_components!` expansion is snapshotted in +// `basic_delegation`, so we invoke it plainly here. +delegate_components! { + Context { + [ + FooTypeProviderComponent, + BarTypeProviderComponent, + ]: + UseType<()>, + } +} + +snapshot_check_components! { + check_components! { + #[check_trait(CanUseDummyField)] + #[check_providers( + UseField, + UseField, + )] + Context { + FooGetterAtComponent: [ + Index<0>, + Index<1>, + ], + FooGetterAtComponent: + Index<3>, + BarGetterAtComponent: [ + (Index<0>, Index<1>), + (Index<1>, Index<0>), + ], + BarGetterAtComponent: + (Index<3>, Index<4>), + [ + FooGetterAtComponent, + BarGetterAtComponent, + ]: [ + (Index<5>, Index<6>), + (Index<7>, Index<8>), + ] + } + } + + expand_check_context(output) { + insta::assert_snapshot!(output, @r#" + trait CanUseDummyField< + __Component__, + __Params__: ?Sized, + >: IsProviderFor<__Component__, Context, __Params__> {} + impl CanUseDummyField> for UseField {} + impl CanUseDummyField> + for UseField {} + impl CanUseDummyField> for UseField {} + impl CanUseDummyField> + for UseField {} + impl CanUseDummyField> for UseField {} + impl CanUseDummyField> + for UseField {} + impl CanUseDummyField, Index<1>)> + for UseField {} + impl CanUseDummyField, Index<1>)> + for UseField {} + impl CanUseDummyField, Index<0>)> + for UseField {} + impl CanUseDummyField, Index<0>)> + for UseField {} + impl CanUseDummyField, Index<4>)> + for UseField {} + impl CanUseDummyField, Index<4>)> + for UseField {} + impl CanUseDummyField, Index<6>)> + for UseField {} + impl CanUseDummyField, Index<6>)> + for UseField {} + impl CanUseDummyField, Index<8>)> + for UseField {} + impl CanUseDummyField, Index<8>)> + for UseField {} + impl CanUseDummyField, Index<6>)> + for UseField {} + impl CanUseDummyField, Index<6>)> + for UseField {} + impl CanUseDummyField, Index<8>)> + for UseField {} + impl CanUseDummyField, Index<8>)> + for UseField {} + "#) + } +} diff --git a/crates/tests/cgp-tests/tests/checking/check_trait.rs b/crates/tests/cgp-tests/tests/checking/check_trait.rs new file mode 100644 index 00000000..5108c877 --- /dev/null +++ b/crates/tests/cgp-tests/tests/checking/check_trait.rs @@ -0,0 +1,121 @@ +//! Standalone `check_components!`: a compile-time assertion that a context can use +//! a set of components, independent of the wiring. Exercises multiple check blocks +//! in one invocation, each renamed with `#[check_trait(...)]` so they do not clash; +//! per-entry parameter lists (`Component: [P, P]` and `Component: P`) for +//! generic-parameter components; and an array key checked against a parameter list. +//! The wiring itself is set up separately with a plain `delegate_components!`. +//! This concept owns the macro's expansion snapshot. +//! +//! See docs/reference/macros/check_components.md and +//! docs/reference/traits/can_use_component.md. + +use core::marker::PhantomData; + +use cgp::prelude::*; +use cgp_macro_test_util::snapshot_check_components; + +#[cgp_type] +pub trait HasFooType { + type Foo; +} + +#[cgp_type] +pub trait HasBarType { + type Bar; +} + +#[cgp_getter { + provider: FooGetterAt, +}] +pub trait HasFooAt: HasFooType { + fn foo(&self, _tag: PhantomData) -> &Self::Foo; +} + +#[cgp_getter { + provider: BarGetterAt, +}] +pub trait HasBarAt: HasBarType { + fn foo(&self, _tag: PhantomData<(I, J)>) -> &Self::Bar; +} + +#[derive(HasField)] +pub struct Context { + pub dummy: (), + pub extra_dummy: (), +} + +// Incidental wiring so that `Context` can actually use the components being +// checked below; the `delegate_components!` expansion is snapshotted in +// `basic_delegation`, so we invoke it plainly here. +delegate_components! { + Context { + [ + FooTypeProviderComponent, + BarTypeProviderComponent, + ]: + UseType<()>, + [ + FooGetterAtComponent, + BarGetterAtComponent, + ]: + UseField, + } +} + +snapshot_check_components! { + check_components! { + #[check_trait(CanUseContext)] + Context { + FooTypeProviderComponent, + BarTypeProviderComponent, + FooGetterAtComponent: [ + Index<0>, + Index<1>, + ], + FooGetterAtComponent: + Index<3>, + } + + #[check_trait(CanUseContext2)] + Context { + BarGetterAtComponent: [ + (Index<0>, Index<1>), + (Index<1>, Index<0>), + ], + BarGetterAtComponent: + (Index<3>, Index<4>), + [ + FooGetterAtComponent, + BarGetterAtComponent, + ]: [ + (Index<5>, Index<6>), + (Index<7>, Index<8>), + ] + } + } + + expand_check_context(output) { + insta::assert_snapshot!(output, @r#" + trait CanUseContext< + __Component__, + __Params__: ?Sized, + >: CanUseComponent<__Component__, __Params__> {} + impl CanUseContext for Context {} + impl CanUseContext for Context {} + impl CanUseContext> for Context {} + impl CanUseContext> for Context {} + impl CanUseContext> for Context {} + trait CanUseContext2< + __Component__, + __Params__: ?Sized, + >: CanUseComponent<__Component__, __Params__> {} + impl CanUseContext2, Index<1>)> for Context {} + impl CanUseContext2, Index<0>)> for Context {} + impl CanUseContext2, Index<4>)> for Context {} + impl CanUseContext2, Index<6>)> for Context {} + impl CanUseContext2, Index<8>)> for Context {} + impl CanUseContext2, Index<6>)> for Context {} + impl CanUseContext2, Index<8>)> for Context {} + "#) + } +} diff --git a/crates/tests/cgp-tests/tests/checking/delegate_and_check_basic.rs b/crates/tests/cgp-tests/tests/checking/delegate_and_check_basic.rs new file mode 100644 index 00000000..94904bed --- /dev/null +++ b/crates/tests/cgp-tests/tests/checking/delegate_and_check_basic.rs @@ -0,0 +1,66 @@ +//! `delegate_and_check_components!` in its basic form: it wires a context to +//! providers *and* asserts the wiring is usable in one step, generating a +//! `CanUseComponent`-supertraited check trait (here renamed with +//! `#[check_trait(...)]`). This concept owns the macro's expansion snapshot. +//! +//! See docs/reference/macros/delegate_and_check_components.md and +//! docs/reference/traits/can_use_component.md. + +use cgp::prelude::*; +use cgp_macro_test_util::snapshot_delegate_and_check_components; + +#[cgp_type] +pub trait HasNameType { + type Name; +} + +#[cgp_getter] +pub trait HasName: HasNameType { + fn name(&self) -> &Self::Name; +} + +#[derive(HasField)] +pub struct MyContext { + pub name: String, +} + +snapshot_delegate_and_check_components! { + delegate_and_check_components! { + #[check_trait(CheckMyContext)] + MyContext { + NameTypeProviderComponent: UseType, + NameGetterComponent: UseField, + } + } + + expand_my_context(output) { + insta::assert_snapshot!(output, @r#" + impl DelegateComponent for MyContext { + type Delegate = UseType; + } + impl< + __Context__, + __Params__, + > IsProviderFor for MyContext + where + UseType: IsProviderFor, + {} + impl DelegateComponent for MyContext { + type Delegate = UseField; + } + impl<__Context__, __Params__> IsProviderFor + for MyContext + where + UseField< + Symbol!("name"), + >: IsProviderFor, + {} + trait CheckMyContext< + __Component__, + __Params__: ?Sized, + >: CanUseComponent<__Component__, __Params__> {} + impl CheckMyContext for MyContext {} + impl CheckMyContext for MyContext {} + "#) + } +} diff --git a/crates/tests/cgp-tests/tests/checking/delegate_and_check_generic.rs b/crates/tests/cgp-tests/tests/checking/delegate_and_check_generic.rs new file mode 100644 index 00000000..2e549915 --- /dev/null +++ b/crates/tests/cgp-tests/tests/checking/delegate_and_check_generic.rs @@ -0,0 +1,70 @@ +//! `delegate_and_check_components!` on a generic context: the leading generic +//! list (` MyContext`) wires and checks a whole context family at once, and +//! the check trait defaults to `__CanUse{Context}`. This concept owns the macro's +//! expansion snapshot. +//! +//! See docs/reference/macros/delegate_and_check_components.md and +//! docs/reference/traits/can_use_component.md. + +use cgp::prelude::*; +use cgp_macro_test_util::snapshot_delegate_and_check_components; + +#[cgp_type] +pub trait HasNameType { + type Name; +} + +#[cgp_getter] +pub trait HasName: HasNameType { + fn name(&self) -> &Self::Name; +} + +#[derive(HasField)] +pub struct MyContext { + pub name: T, +} + +snapshot_delegate_and_check_components! { + delegate_and_check_components! { + + MyContext { + NameTypeProviderComponent: UseType, + NameGetterComponent: UseField, + } + } + + expand_my_context(output) { + insta::assert_snapshot!(output, @r#" + impl DelegateComponent for MyContext { + type Delegate = UseType; + } + impl< + T, + __Context__, + __Params__, + > IsProviderFor for MyContext + where + UseType: IsProviderFor, + {} + impl DelegateComponent for MyContext { + type Delegate = UseField; + } + impl< + T, + __Context__, + __Params__, + > IsProviderFor for MyContext + where + UseField< + Symbol!("name"), + >: IsProviderFor, + {} + trait __CanUseMyContext< + __Component__, + __Params__: ?Sized, + >: CanUseComponent<__Component__, __Params__> {} + impl __CanUseMyContext for MyContext {} + impl __CanUseMyContext for MyContext {} + "#) + } +} diff --git a/crates/tests/cgp-tests/tests/checking/delegate_and_check_params.rs b/crates/tests/cgp-tests/tests/checking/delegate_and_check_params.rs new file mode 100644 index 00000000..9a4db6d7 --- /dev/null +++ b/crates/tests/cgp-tests/tests/checking/delegate_and_check_params.rs @@ -0,0 +1,137 @@ +//! `delegate_and_check_components!` on components that carry generic parameters: +//! `#[check_params(...)]` supplies the parameter tuples to check each entry with, +//! an array key wires several components to one provider, and a block-level +//! `#[check_params(...)]` (on the array) is checked in addition to each entry's own +//! `#[check_params(...)]`. This concept owns the macro's expansion snapshot. +//! +//! See docs/reference/macros/delegate_and_check_components.md and +//! docs/reference/traits/can_use_component.md. + +use core::marker::PhantomData; + +use cgp::prelude::*; +use cgp_macro_test_util::snapshot_delegate_and_check_components; + +#[cgp_type] +pub trait HasFooType { + type Foo; +} + +#[cgp_type] +pub trait HasBarType { + type Bar; +} + +#[cgp_getter { + provider: FooGetterAt, +}] +pub trait HasFooAt: HasFooType { + fn foo(&self, _tag: PhantomData) -> &Self::Foo; +} + +#[cgp_getter { + provider: BarGetterAt, +}] +pub trait HasBarAt: HasBarType { + fn foo(&self, _tag: PhantomData<(I, J)>) -> &Self::Bar; +} + +#[derive(HasField)] +pub struct Context { + pub dummy: (), + pub extra_dummy: (), +} + +snapshot_delegate_and_check_components! { + delegate_and_check_components! { + Context { + [ + FooTypeProviderComponent, + BarTypeProviderComponent, + ]: + UseType<()>, + + #[check_params( + (Index<5>, Index<6>), + (Index<7>, Index<8>), + )] + [ + #[check_params( + Index<0>, + Index<1>, + )] + FooGetterAtComponent, + + #[check_params( + (Index<0>, Index<1>), + (Index<1>, Index<0>), + )] + BarGetterAtComponent, + ]: + UseField, + } + } + + expand_context(output) { + insta::assert_snapshot!(output, @r#" + impl DelegateComponent for Context { + type Delegate = UseType<()>; + } + impl< + __Context__, + __Params__, + > IsProviderFor for Context + where + UseType<()>: IsProviderFor, + {} + impl DelegateComponent for Context { + type Delegate = UseType<()>; + } + impl< + __Context__, + __Params__, + > IsProviderFor for Context + where + UseType<()>: IsProviderFor, + {} + impl DelegateComponent for Context { + type Delegate = UseField; + } + impl< + __Context__, + __Params__, + > IsProviderFor for Context + where + UseField< + Symbol!("dummy"), + >: IsProviderFor, + {} + impl DelegateComponent for Context { + type Delegate = UseField; + } + impl< + __Context__, + __Params__, + > IsProviderFor for Context + where + UseField< + Symbol!("dummy"), + >: IsProviderFor, + {} + trait __CanUseContext< + __Component__, + __Params__: ?Sized, + >: CanUseComponent<__Component__, __Params__> {} + impl __CanUseContext for Context {} + impl __CanUseContext for Context {} + impl __CanUseContext, Index<6>)> for Context {} + impl __CanUseContext, Index<8>)> for Context {} + impl __CanUseContext> for Context {} + impl __CanUseContext> for Context {} + impl __CanUseContext, Index<6>)> for Context {} + impl __CanUseContext, Index<8>)> for Context {} + impl __CanUseContext, Index<1>)> for Context {} + impl __CanUseContext, Index<0>)> for Context {} + "#) + } +} diff --git a/crates/tests/cgp-tests/tests/checking/mod.rs b/crates/tests/cgp-tests/tests/checking/mod.rs new file mode 100644 index 00000000..c2dc0762 --- /dev/null +++ b/crates/tests/cgp-tests/tests/checking/mod.rs @@ -0,0 +1,17 @@ +//! One unit test per file. Each file is self-contained: it defines its own +//! components, getters, and context types at module scope so that the type-level +//! wiring of one test never leaks into another. + +// `delegate_and_check_components!` snapshots (this concept owns the macro's +// expansion): the basic wire-and-check step, its generic-context form, and the +// generic-parameter `#[check_params]` / array-key form. +pub mod delegate_and_check_basic; +pub mod delegate_and_check_generic; +pub mod delegate_and_check_params; + +// `check_components!` snapshots (this concept owns the macro's expansion): the +// standalone check with `#[check_trait(...)]` overrides and per-entry parameter +// lists, the `#[check_providers(...)]` form, and the generic-context/lifetime form. +pub mod check_generic; +pub mod check_providers; +pub mod check_trait; diff --git a/crates/tests/cgp-tests/tests/checking_tests.rs b/crates/tests/cgp-tests/tests/checking_tests.rs new file mode 100644 index 00000000..5d6f95d0 --- /dev/null +++ b/crates/tests/cgp-tests/tests/checking_tests.rs @@ -0,0 +1,17 @@ +//! Entrypoint for the `checking` concept. +//! +//! Covers CGP's compile-time wiring verification: `check_components!` (a +//! standalone assertion that a context can use a set of components, generating a +//! `CanUseComponent`-supertraited check trait) and `delegate_and_check_components!` +//! (which wires a context and checks the same wiring in one step). This concept +//! owns the canonical macro-expansion snapshots for both, exercising the +//! generic-parameter forms (`#[check_params]`, per-entry parameter lists, array +//! keys), the `#[check_trait(...)]` name override, the `#[check_providers(...)]` +//! form that checks providers directly, and the generic-context/lifetime forms. +//! +//! See docs/reference/macros/check_components.md, +//! docs/reference/macros/delegate_and_check_components.md, +//! docs/reference/traits/can_use_component.md, and docs/concepts/check-traits.md. +#![allow(dead_code)] + +pub mod checking; diff --git a/crates/tests/cgp-tests/tests/component.rs b/crates/tests/cgp-tests/tests/component.rs deleted file mode 100644 index 17dfc53d..00000000 --- a/crates/tests/cgp-tests/tests/component.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod component_tests; diff --git a/crates/tests/cgp-tests/tests/component_tests/abstract_types/basic.rs b/crates/tests/cgp-tests/tests/component_tests/abstract_types/basic.rs deleted file mode 100644 index 240c0c73..00000000 --- a/crates/tests/cgp-tests/tests/component_tests/abstract_types/basic.rs +++ /dev/null @@ -1,182 +0,0 @@ -use std::convert::Infallible; -use std::ops::Mul; - -use cgp::core::error::ErrorTypeProviderComponent; -use cgp::prelude::*; -use cgp_macro_test_util::{snapshot_cgp_type, snapshot_delegate_and_check_components}; - -snapshot_cgp_type! { - #[cgp_type] - pub trait HasScalarType { - type Scalar; - } - - expand_has_scalar_type(output) { - insta::assert_snapshot!(output, @" - pub trait HasScalarType { - type Scalar; - } - impl<__Context__> HasScalarType for __Context__ - where - __Context__: ScalarTypeProvider<__Context__>, - { - type Scalar = <__Context__ as ScalarTypeProvider<__Context__>>::Scalar; - } - pub trait ScalarTypeProvider< - __Context__, - >: IsProviderFor { - type Scalar; - } - impl<__Provider__, __Context__> ScalarTypeProvider<__Context__> for __Provider__ - where - __Provider__: DelegateComponent - + IsProviderFor, - <__Provider__ as DelegateComponent< - ScalarTypeProviderComponent, - >>::Delegate: ScalarTypeProvider<__Context__>, - { - type Scalar = <<__Provider__ as DelegateComponent< - ScalarTypeProviderComponent, - >>::Delegate as ScalarTypeProvider<__Context__>>::Scalar; - } - pub struct ScalarTypeProviderComponent; - impl<__Context__> ScalarTypeProvider<__Context__> for UseContext - where - __Context__: HasScalarType, - { - type Scalar = <__Context__ as HasScalarType>::Scalar; - } - impl<__Context__> IsProviderFor - for UseContext - where - __Context__: HasScalarType, - {} - impl<__Context__, __Components__, __Path__> ScalarTypeProvider<__Context__> - for RedirectLookup<__Components__, __Path__> - where - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent< - __Path__, - >>::Delegate: ScalarTypeProvider<__Context__>, - { - type Scalar = <<__Components__ as DelegateComponent< - __Path__, - >>::Delegate as ScalarTypeProvider<__Context__>>::Scalar; - } - impl< - __Context__, - __Components__, - __Path__, - > IsProviderFor - for RedirectLookup<__Components__, __Path__> - where - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent< - __Path__, - >>::Delegate: IsProviderFor - + ScalarTypeProvider<__Context__>, - {} - impl ScalarTypeProvider<__Context__> for UseType { - type Scalar = Scalar; - } - impl IsProviderFor - for UseType {} - impl<__Provider__, Scalar, __Context__> ScalarTypeProvider<__Context__> - for WithProvider<__Provider__> - where - __Provider__: TypeProvider<__Context__, ScalarTypeProviderComponent, Type = Scalar>, - { - type Scalar = Scalar; - } - impl< - __Provider__, - Scalar, - __Context__, - > IsProviderFor - for WithProvider<__Provider__> - where - __Provider__: TypeProvider<__Context__, ScalarTypeProviderComponent, Type = Scalar>, - {} - ") - } -} - -#[cgp_component(AreaCalculator)] -#[use_type(HasScalarType::Scalar, HasErrorType::Error)] -pub trait CanCalculateArea { - fn area(&self) -> Result; -} - -#[cgp_impl(new RectangleArea)] -#[use_type(HasScalarType::Scalar, HasErrorType::Error)] -impl AreaCalculator -where - Scalar: Mul + Copy, -{ - fn area(&self, #[implicit] width: Scalar, #[implicit] height: Scalar) -> Result { - Ok(width * height) - } -} - -#[derive(HasField)] -pub struct Rectangle { - pub width: f64, - pub height: f64, -} - -snapshot_delegate_and_check_components! { - delegate_and_check_components! { - Rectangle { - ErrorTypeProviderComponent: - UseType, - ScalarTypeProviderComponent: - UseType, - AreaCalculatorComponent: - RectangleArea, - } - } - - expand_rectangle(output) { - insta::assert_snapshot!(output, @" - impl DelegateComponent for Rectangle { - type Delegate = UseType; - } - impl< - __Context__, - __Params__, - > IsProviderFor for Rectangle - where - UseType< - Infallible, - >: IsProviderFor, - {} - impl DelegateComponent for Rectangle { - type Delegate = UseType; - } - impl< - __Context__, - __Params__, - > IsProviderFor for Rectangle - where - UseType: IsProviderFor, - {} - impl DelegateComponent for Rectangle { - type Delegate = RectangleArea; - } - impl< - __Context__, - __Params__, - > IsProviderFor for Rectangle - where - RectangleArea: IsProviderFor, - {} - trait __CanUseRectangle< - __Component__, - __Params__: ?Sized, - >: CanUseComponent<__Component__, __Params__> {} - impl __CanUseRectangle for Rectangle {} - impl __CanUseRectangle for Rectangle {} - impl __CanUseRectangle for Rectangle {} - ") - } -} diff --git a/crates/tests/cgp-tests/tests/component_tests/abstract_types/extend.rs b/crates/tests/cgp-tests/tests/component_tests/abstract_types/extend.rs deleted file mode 100644 index d3a65396..00000000 --- a/crates/tests/cgp-tests/tests/component_tests/abstract_types/extend.rs +++ /dev/null @@ -1,186 +0,0 @@ -use std::convert::Infallible; -use std::ops::Mul; - -use cgp::core::error::ErrorTypeProviderComponent; -use cgp::prelude::*; -use cgp_macro_test_util::{snapshot_cgp_type, snapshot_delegate_and_check_components}; - -snapshot_cgp_type! { - #[cgp_type] - pub trait HasScalarType { - type Scalar; - } - - expand_has_scalar_type(output) { - insta::assert_snapshot!(output, @" - pub trait HasScalarType { - type Scalar; - } - impl<__Context__> HasScalarType for __Context__ - where - __Context__: ScalarTypeProvider<__Context__>, - { - type Scalar = <__Context__ as ScalarTypeProvider<__Context__>>::Scalar; - } - pub trait ScalarTypeProvider< - __Context__, - >: IsProviderFor { - type Scalar; - } - impl<__Provider__, __Context__> ScalarTypeProvider<__Context__> for __Provider__ - where - __Provider__: DelegateComponent - + IsProviderFor, - <__Provider__ as DelegateComponent< - ScalarTypeProviderComponent, - >>::Delegate: ScalarTypeProvider<__Context__>, - { - type Scalar = <<__Provider__ as DelegateComponent< - ScalarTypeProviderComponent, - >>::Delegate as ScalarTypeProvider<__Context__>>::Scalar; - } - pub struct ScalarTypeProviderComponent; - impl<__Context__> ScalarTypeProvider<__Context__> for UseContext - where - __Context__: HasScalarType, - { - type Scalar = <__Context__ as HasScalarType>::Scalar; - } - impl<__Context__> IsProviderFor - for UseContext - where - __Context__: HasScalarType, - {} - impl<__Context__, __Components__, __Path__> ScalarTypeProvider<__Context__> - for RedirectLookup<__Components__, __Path__> - where - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent< - __Path__, - >>::Delegate: ScalarTypeProvider<__Context__>, - { - type Scalar = <<__Components__ as DelegateComponent< - __Path__, - >>::Delegate as ScalarTypeProvider<__Context__>>::Scalar; - } - impl< - __Context__, - __Components__, - __Path__, - > IsProviderFor - for RedirectLookup<__Components__, __Path__> - where - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent< - __Path__, - >>::Delegate: IsProviderFor - + ScalarTypeProvider<__Context__>, - {} - impl ScalarTypeProvider<__Context__> for UseType { - type Scalar = Scalar; - } - impl IsProviderFor - for UseType {} - impl<__Provider__, Scalar, __Context__> ScalarTypeProvider<__Context__> - for WithProvider<__Provider__> - where - __Provider__: TypeProvider<__Context__, ScalarTypeProviderComponent, Type = Scalar>, - { - type Scalar = Scalar; - } - impl< - __Provider__, - Scalar, - __Context__, - > IsProviderFor - for WithProvider<__Provider__> - where - __Provider__: TypeProvider<__Context__, ScalarTypeProviderComponent, Type = Scalar>, - {} - ") - } -} - -#[cgp_component(AreaCalculator)] -#[extend(HasScalarType, HasErrorType)] -pub trait CanCalculateArea { - fn area(&self) -> Result; -} - -#[cgp_impl(new RectangleArea)] -#[uses(HasScalarType, HasErrorType)] -impl AreaCalculator -where - Self::Scalar: Mul + Copy, -{ - fn area( - &self, - #[implicit] width: Self::Scalar, - #[implicit] height: Self::Scalar, - ) -> Result { - Ok(width * height) - } -} - -#[derive(HasField)] -pub struct Rectangle { - pub width: f64, - pub height: f64, -} - -snapshot_delegate_and_check_components! { - delegate_and_check_components! { - Rectangle { - ErrorTypeProviderComponent: - UseType, - ScalarTypeProviderComponent: - UseType, - AreaCalculatorComponent: - RectangleArea, - } - } - - expand_rectangle(output) { - insta::assert_snapshot!(output, @" - impl DelegateComponent for Rectangle { - type Delegate = UseType; - } - impl< - __Context__, - __Params__, - > IsProviderFor for Rectangle - where - UseType< - Infallible, - >: IsProviderFor, - {} - impl DelegateComponent for Rectangle { - type Delegate = UseType; - } - impl< - __Context__, - __Params__, - > IsProviderFor for Rectangle - where - UseType: IsProviderFor, - {} - impl DelegateComponent for Rectangle { - type Delegate = RectangleArea; - } - impl< - __Context__, - __Params__, - > IsProviderFor for Rectangle - where - RectangleArea: IsProviderFor, - {} - trait __CanUseRectangle< - __Component__, - __Params__: ?Sized, - >: CanUseComponent<__Component__, __Params__> {} - impl __CanUseRectangle for Rectangle {} - impl __CanUseRectangle for Rectangle {} - impl __CanUseRectangle for Rectangle {} - ") - } -} diff --git a/crates/tests/cgp-tests/tests/component_tests/abstract_types/generics.rs b/crates/tests/cgp-tests/tests/component_tests/abstract_types/generics.rs deleted file mode 100644 index 26cb258f..00000000 --- a/crates/tests/cgp-tests/tests/component_tests/abstract_types/generics.rs +++ /dev/null @@ -1,14 +0,0 @@ -use cgp::prelude::*; - -#[cgp_component(FooProvider)] -pub trait Foo { - fn foo(&self, value: &T); -} - -// Test that the `Error` parameter in `FooProvider` -// is desugared correctly into `Context::Error` and not `Self::Error` -#[cgp_impl(new FooError)] -#[use_type(HasErrorType::Error)] -impl FooProvider { - fn foo(&self, _value: &Error) {} -} diff --git a/crates/tests/cgp-tests/tests/component_tests/abstract_types/mod.rs b/crates/tests/cgp-tests/tests/component_tests/abstract_types/mod.rs deleted file mode 100644 index 01fd4853..00000000 --- a/crates/tests/cgp-tests/tests/component_tests/abstract_types/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -pub mod basic; -pub mod extend; -pub mod foreign; -pub mod generics; -pub mod self_referential; diff --git a/crates/tests/cgp-tests/tests/component_tests/cgp_component/constant.rs b/crates/tests/cgp-tests/tests/component_tests/cgp_component/constant.rs deleted file mode 100644 index 8e5a3319..00000000 --- a/crates/tests/cgp-tests/tests/component_tests/cgp_component/constant.rs +++ /dev/null @@ -1,233 +0,0 @@ -mod basic_const { - use cgp::prelude::*; - use cgp_macro_test_util::{snapshot_cgp_provider, snapshot_delegate_and_check_components}; - - #[cgp_component(ConstantGetter)] - pub trait HasConstant { - const CONSTANT: u64; - } - - pub struct UseConstant; - - snapshot_cgp_provider! { - #[cgp_provider] - impl ConstantGetter for UseConstant { - const CONSTANT: u64 = CONSTANT; - } - - expand_use_constant(output) { - insta::assert_snapshot!(output, @" - impl ConstantGetter for UseConstant { - const CONSTANT: u64 = CONSTANT; - } - impl IsProviderFor - for UseConstant {} - ") - } - } - - pub struct MyContext; - - snapshot_delegate_and_check_components! { - delegate_and_check_components! { - MyContext { - ConstantGetterComponent: UseConstant<42>, - } - } - - expand_my_context(output) { - insta::assert_snapshot!(output, @" - impl DelegateComponent for MyContext { - type Delegate = UseConstant<42>; - } - impl< - __Context__, - __Params__, - > IsProviderFor for MyContext - where - UseConstant<42>: IsProviderFor, - {} - trait __CanUseMyContext< - __Component__, - __Params__: ?Sized, - >: CanUseComponent<__Component__, __Params__> {} - impl __CanUseMyContext for MyContext {} - ") - } - } -} - -pub fn test_component_with_const() { - use basic_const::{HasConstant, MyContext}; - - assert_eq!(::CONSTANT, 42); -} - -mod generic_const { - use cgp::prelude::*; - use cgp_macro_test_util::{ - snapshot_cgp_provider, snapshot_cgp_type, snapshot_check_components, - }; - - snapshot_cgp_type! { - #[cgp_type] - pub trait HasUnitType { - type Unit; - } - - expand_has_unit_type(output) { - insta::assert_snapshot!(output, @" - pub trait HasUnitType { - type Unit; - } - impl<__Context__> HasUnitType for __Context__ - where - __Context__: UnitTypeProvider<__Context__>, - { - type Unit = <__Context__ as UnitTypeProvider<__Context__>>::Unit; - } - pub trait UnitTypeProvider< - __Context__, - >: IsProviderFor { - type Unit; - } - impl<__Provider__, __Context__> UnitTypeProvider<__Context__> for __Provider__ - where - __Provider__: DelegateComponent - + IsProviderFor, - <__Provider__ as DelegateComponent< - UnitTypeProviderComponent, - >>::Delegate: UnitTypeProvider<__Context__>, - { - type Unit = <<__Provider__ as DelegateComponent< - UnitTypeProviderComponent, - >>::Delegate as UnitTypeProvider<__Context__>>::Unit; - } - pub struct UnitTypeProviderComponent; - impl<__Context__> UnitTypeProvider<__Context__> for UseContext - where - __Context__: HasUnitType, - { - type Unit = <__Context__ as HasUnitType>::Unit; - } - impl<__Context__> IsProviderFor - for UseContext - where - __Context__: HasUnitType, - {} - impl<__Context__, __Components__, __Path__> UnitTypeProvider<__Context__> - for RedirectLookup<__Components__, __Path__> - where - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent< - __Path__, - >>::Delegate: UnitTypeProvider<__Context__>, - { - type Unit = <<__Components__ as DelegateComponent< - __Path__, - >>::Delegate as UnitTypeProvider<__Context__>>::Unit; - } - impl< - __Context__, - __Components__, - __Path__, - > IsProviderFor - for RedirectLookup<__Components__, __Path__> - where - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent< - __Path__, - >>::Delegate: IsProviderFor - + UnitTypeProvider<__Context__>, - {} - impl UnitTypeProvider<__Context__> for UseType { - type Unit = Unit; - } - impl IsProviderFor - for UseType {} - impl<__Provider__, Unit, __Context__> UnitTypeProvider<__Context__> - for WithProvider<__Provider__> - where - __Provider__: TypeProvider<__Context__, UnitTypeProviderComponent, Type = Unit>, - { - type Unit = Unit; - } - impl< - __Provider__, - Unit, - __Context__, - > IsProviderFor - for WithProvider<__Provider__> - where - __Provider__: TypeProvider<__Context__, UnitTypeProviderComponent, Type = Unit>, - {} - ") - } - } - - #[cgp_component(ConstantGetter)] - pub trait HasConstant: HasUnitType { - const CONSTANT: Self::Unit; - } - - pub struct UseConstant; - - snapshot_cgp_provider! { - #[cgp_provider] - impl ConstantGetter for UseConstant - where - Context: HasUnitType, - { - const CONSTANT: u64 = CONSTANT; - } - - expand_use_constant(output) { - insta::assert_snapshot!(output, @" - impl ConstantGetter for UseConstant - where - Context: HasUnitType, - { - const CONSTANT: u64 = CONSTANT; - } - impl IsProviderFor - for UseConstant - where - Context: HasUnitType, - {} - ") - } - } - - pub struct MyContext; - - delegate_components! { - MyContext { - UnitTypeProviderComponent: UseType, - ConstantGetterComponent: UseConstant<42>, - } - } - - snapshot_check_components! { - check_components! { - MyContext { - ConstantGetterComponent, - } - } - - expand_check_my_context(output) { - insta::assert_snapshot!(output, @" - trait __CheckMyContext< - __Component__, - __Params__: ?Sized, - >: CanUseComponent<__Component__, __Params__> {} - impl __CheckMyContext for MyContext {} - ") - } - } -} - -pub fn test_component_with_generic_const() { - use generic_const::{HasConstant, MyContext}; - - assert_eq!(::CONSTANT, 42); -} diff --git a/crates/tests/cgp-tests/tests/component_tests/cgp_component/mod.rs b/crates/tests/cgp-tests/tests/component_tests/cgp_component/mod.rs deleted file mode 100644 index a41c9ffb..00000000 --- a/crates/tests/cgp-tests/tests/component_tests/cgp_component/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub mod constant; -pub mod default_impl; -pub mod lifetime; -pub mod sized; diff --git a/crates/tests/cgp-tests/tests/component_tests/cgp_impl/impl_self.rs b/crates/tests/cgp-tests/tests/component_tests/cgp_impl/impl_self.rs deleted file mode 100644 index 214742f4..00000000 --- a/crates/tests/cgp-tests/tests/component_tests/cgp_impl/impl_self.rs +++ /dev/null @@ -1,27 +0,0 @@ -use cgp::prelude::*; - -#[cgp_component(AreaCalculator)] -pub trait CanCalculateArea { - fn area(&self) -> f64; -} - -#[cgp_impl(new RectangleArea)] -impl AreaCalculator { - fn area(&self, #[implicit] width: f64, #[implicit] height: f64) -> f64 { - width * height - } -} - -#[derive(HasField)] -pub struct Rectangle { - pub width: f64, - pub height: f64, -} - -#[cgp_impl(Self)] -#[use_provider(RectangleArea: AreaCalculator)] -impl CanCalculateArea for Rectangle { - fn area(&self) -> f64 { - RectangleArea::area(self) - } -} diff --git a/crates/tests/cgp-tests/tests/component_tests/cgp_impl/implicit_args/basic.rs b/crates/tests/cgp-tests/tests/component_tests/cgp_impl/implicit_args/basic.rs deleted file mode 100644 index 88594614..00000000 --- a/crates/tests/cgp-tests/tests/component_tests/cgp_impl/implicit_args/basic.rs +++ /dev/null @@ -1,49 +0,0 @@ -use cgp::prelude::*; -use cgp_macro_test_util::snapshot_delegate_and_check_components; - -#[cgp_component(AreaCalculator)] -pub trait CanCalculateArea { - fn area(&self) -> f64; -} - -#[cgp_impl(new RectangleArea)] -impl AreaCalculator { - fn area(&self, #[implicit] width: f64, #[implicit] height: f64) -> f64 { - width * height - } -} - -#[derive(HasField)] -pub struct Rectangle { - pub width: f64, - pub height: f64, -} - -snapshot_delegate_and_check_components! { - delegate_and_check_components! { - Rectangle { - AreaCalculatorComponent: - RectangleArea, - } - } - - expand_rectangle(output) { - insta::assert_snapshot!(output, @" - impl DelegateComponent for Rectangle { - type Delegate = RectangleArea; - } - impl< - __Context__, - __Params__, - > IsProviderFor for Rectangle - where - RectangleArea: IsProviderFor, - {} - trait __CanUseRectangle< - __Component__, - __Params__: ?Sized, - >: CanUseComponent<__Component__, __Params__> {} - impl __CanUseRectangle for Rectangle {} - ") - } -} diff --git a/crates/tests/cgp-tests/tests/component_tests/cgp_impl/implicit_args/generics.rs b/crates/tests/cgp-tests/tests/component_tests/cgp_impl/implicit_args/generics.rs deleted file mode 100644 index 5e4a2d5b..00000000 --- a/crates/tests/cgp-tests/tests/component_tests/cgp_impl/implicit_args/generics.rs +++ /dev/null @@ -1,55 +0,0 @@ -use core::ops::Mul; - -use cgp::prelude::*; -use cgp_macro_test_util::snapshot_delegate_and_check_components; - -#[cgp_component(AreaCalculator)] -pub trait CanCalculateArea { - fn area(&self) -> Scalar; -} - -#[cgp_impl(new RectangleArea)] -impl AreaCalculator -where - Scalar: Mul + Copy, -{ - fn area(&self, #[implicit] width: Scalar, #[implicit] height: Scalar) -> Scalar { - width * height - } -} - -#[derive(HasField)] -pub struct Rectangle { - pub width: f64, - pub height: f64, -} - -snapshot_delegate_and_check_components! { - delegate_and_check_components! { - Rectangle { - #[check_params(f64)] - AreaCalculatorComponent: - RectangleArea, - } - } - - expand_rectangle(output) { - insta::assert_snapshot!(output, @" - impl DelegateComponent for Rectangle { - type Delegate = RectangleArea; - } - impl< - __Context__, - __Params__, - > IsProviderFor for Rectangle - where - RectangleArea: IsProviderFor, - {} - trait __CanUseRectangle< - __Component__, - __Params__: ?Sized, - >: CanUseComponent<__Component__, __Params__> {} - impl __CanUseRectangle for Rectangle {} - ") - } -} diff --git a/crates/tests/cgp-tests/tests/component_tests/cgp_impl/implicit_args/mod.rs b/crates/tests/cgp-tests/tests/component_tests/cgp_impl/implicit_args/mod.rs deleted file mode 100644 index e9cc3a18..00000000 --- a/crates/tests/cgp-tests/tests/component_tests/cgp_impl/implicit_args/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod basic; -pub mod generics; -pub mod import; diff --git a/crates/tests/cgp-tests/tests/component_tests/cgp_impl/mod.rs b/crates/tests/cgp-tests/tests/component_tests/cgp_impl/mod.rs deleted file mode 100644 index f6afef05..00000000 --- a/crates/tests/cgp-tests/tests/component_tests/cgp_impl/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -pub mod basic; -pub mod impl_self; -pub mod implicit_args; -pub mod implicit_context; -pub mod shape; -pub mod use_provider; diff --git a/crates/tests/cgp-tests/tests/component_tests/cgp_impl/shape.rs b/crates/tests/cgp-tests/tests/component_tests/cgp_impl/shape.rs deleted file mode 100644 index 2a9fc0cd..00000000 --- a/crates/tests/cgp-tests/tests/component_tests/cgp_impl/shape.rs +++ /dev/null @@ -1,402 +0,0 @@ -use core::f64::consts::PI; - -use cgp::prelude::*; -use cgp_macro_test_util::{ - snapshot_cgp_fn, snapshot_check_components, snapshot_delegate_and_check_components, -}; - -snapshot_cgp_fn! { - #[cgp_fn] - pub fn rectangle_area(&self, #[implicit] width: f64, #[implicit] height: f64) -> f64 { - width * height - } - - expand_rectangle_area(output) { - insta::assert_snapshot!(output, @" - pub trait RectangleArea { - fn rectangle_area(&self) -> f64; - } - impl<__Context__> RectangleArea for __Context__ - where - Self: HasField< - Symbol<5, Chars<'w', Chars<'i', Chars<'d', Chars<'t', Chars<'h', Nil>>>>>>, - Value = f64, - > - + HasField< - Symbol< - 6, - Chars< - 'h', - Chars<'e', Chars<'i', Chars<'g', Chars<'h', Chars<'t', Nil>>>>>, - >, - >, - Value = f64, - >, - { - fn rectangle_area(&self) -> f64 { - let width: f64 = self - .get_field( - ::core::marker::PhantomData::< - Symbol< - 5, - Chars<'w', Chars<'i', Chars<'d', Chars<'t', Chars<'h', Nil>>>>>, - >, - >, - ) - .clone(); - let height: f64 = self - .get_field( - ::core::marker::PhantomData::< - Symbol< - 6, - Chars< - 'h', - Chars< - 'e', - Chars<'i', Chars<'g', Chars<'h', Chars<'t', Nil>>>>, - >, - >, - >, - >, - ) - .clone(); - width * height - } - } - ") - } -} - -snapshot_cgp_fn! { - #[cgp_fn] - pub fn circle_area(&self, #[implicit] radius: f64) -> f64 { - PI * radius * radius - } - - expand_circle_area(output) { - insta::assert_snapshot!(output, @" - pub trait CircleArea { - fn circle_area(&self) -> f64; - } - impl<__Context__> CircleArea for __Context__ - where - Self: HasField< - Symbol< - 6, - Chars<'r', Chars<'a', Chars<'d', Chars<'i', Chars<'u', Chars<'s', Nil>>>>>>, - >, - Value = f64, - >, - { - fn circle_area(&self) -> f64 { - let radius: f64 = self - .get_field( - ::core::marker::PhantomData::< - Symbol< - 6, - Chars< - 'r', - Chars< - 'a', - Chars<'d', Chars<'i', Chars<'u', Chars<'s', Nil>>>>, - >, - >, - >, - >, - ) - .clone(); - PI * radius * radius - } - } - ") - } -} - -#[cgp_component(AreaCalculator)] -pub trait CanCalculateArea { - fn area(&self) -> f64; -} - -#[cgp_impl(new RectangleAreaCalculator)] -impl AreaCalculator { - fn area(&self, #[implicit] width: f64, #[implicit] height: f64) -> f64 { - width * height - } -} - -#[cgp_impl(new CircleAreaCalculator)] -impl AreaCalculator { - fn area(&self, #[implicit] radius: f64) -> f64 { - PI * radius * radius - } -} - -#[cgp_impl(new ScaledRectangleAreaCalculator)] -#[use_provider(RectangleAreaCalculator: AreaCalculator)] -impl AreaCalculator { - fn area(&self, #[implicit] scale_factor: f64) -> f64 { - RectangleAreaCalculator::area(self) * scale_factor * scale_factor - } -} - -#[cgp_impl(new ScaledCircleAreaCalculator)] -#[use_provider(CircleAreaCalculator: AreaCalculator)] -impl AreaCalculator { - fn area(&self, #[implicit] scale_factor: f64) -> f64 { - CircleAreaCalculator::area(self) * scale_factor * scale_factor - } -} - -#[cgp_impl(new ScaledAreaCalculator)] -#[use_provider(InnerCalculator: AreaCalculator)] -impl AreaCalculator { - fn area(&self, #[implicit] scale_factor: f64) -> f64 { - let base_area = InnerCalculator::area(self); - - base_area * scale_factor * scale_factor - } -} - -#[derive(HasField)] -pub struct IsThisRectangleOrCircle { - pub width: f64, - pub height: f64, - pub radius: f64, -} - -impl CanCalculateArea for IsThisRectangleOrCircle { - fn area(&self) -> f64 { - CircleAreaCalculator::area(self) - } -} - -#[test] -fn test_rectangle_or_circle() { - let rectangle_or_circle = IsThisRectangleOrCircle { - width: 2.0, - height: 3.0, - radius: 4.0, - }; - - let area = rectangle_or_circle.area(); - assert_eq!(area, 16.0 * PI); - - let rectangle_area = RectangleAreaCalculator::area(&rectangle_or_circle); - assert_eq!(rectangle_area, 6.0); - - let circle_area = CircleAreaCalculator::area(&rectangle_or_circle); - assert_eq!(circle_area, 16.0 * PI); - - let rectangle_area = rectangle_or_circle.rectangle_area(); - assert_eq!(rectangle_area, 6.0); - - let circle_area = rectangle_or_circle.circle_area(); - assert_eq!(circle_area, 16.0 * PI); -} - -#[derive(HasField)] -pub struct PlainRectangle { - pub width: f64, - pub height: f64, -} - -snapshot_delegate_and_check_components! { - delegate_and_check_components! { - PlainRectangle { - AreaCalculatorComponent: - RectangleAreaCalculator, - } - } - - expand_plain_rectangle(output) { - insta::assert_snapshot!(output, @" - impl DelegateComponent for PlainRectangle { - type Delegate = RectangleAreaCalculator; - } - impl< - __Context__, - __Params__, - > IsProviderFor for PlainRectangle - where - RectangleAreaCalculator: IsProviderFor< - AreaCalculatorComponent, - __Context__, - __Params__, - >, - {} - trait __CanUsePlainRectangle< - __Component__, - __Params__: ?Sized, - >: CanUseComponent<__Component__, __Params__> {} - impl __CanUsePlainRectangle for PlainRectangle {} - ") - } -} - -#[derive(HasField)] -pub struct ScaledRectangle { - pub scale_factor: f64, - pub width: f64, - pub height: f64, -} - -snapshot_delegate_and_check_components! { - delegate_and_check_components! { - ScaledRectangle { - AreaCalculatorComponent: - ScaledAreaCalculator, - } - } - - expand_scaled_rectangle(output) { - insta::assert_snapshot!(output, @" - impl DelegateComponent for ScaledRectangle { - type Delegate = ScaledAreaCalculator; - } - impl< - __Context__, - __Params__, - > IsProviderFor for ScaledRectangle - where - ScaledAreaCalculator< - RectangleAreaCalculator, - >: IsProviderFor, - {} - trait __CanUseScaledRectangle< - __Component__, - __Params__: ?Sized, - >: CanUseComponent<__Component__, __Params__> {} - impl __CanUseScaledRectangle for ScaledRectangle {} - ") - } -} - -#[derive(HasField)] -pub struct PlainCircle { - pub radius: f64, -} - -snapshot_delegate_and_check_components! { - delegate_and_check_components! { - PlainCircle { - AreaCalculatorComponent: - CircleAreaCalculator, - } - } - - expand_plain_circle(output) { - insta::assert_snapshot!(output, @" - impl DelegateComponent for PlainCircle { - type Delegate = CircleAreaCalculator; - } - impl< - __Context__, - __Params__, - > IsProviderFor for PlainCircle - where - CircleAreaCalculator: IsProviderFor< - AreaCalculatorComponent, - __Context__, - __Params__, - >, - {} - trait __CanUsePlainCircle< - __Component__, - __Params__: ?Sized, - >: CanUseComponent<__Component__, __Params__> {} - impl __CanUsePlainCircle for PlainCircle {} - ") - } -} - -#[derive(HasField)] -pub struct ScaledCircle { - pub scale_factor: f64, - pub radius: f64, -} - -snapshot_delegate_and_check_components! { - delegate_and_check_components! { - ScaledCircle { - AreaCalculatorComponent: - ScaledAreaCalculator, - } - } - - expand_scaled_circle(output) { - insta::assert_snapshot!(output, @" - impl DelegateComponent for ScaledCircle { - type Delegate = ScaledAreaCalculator; - } - impl< - __Context__, - __Params__, - > IsProviderFor for ScaledCircle - where - ScaledAreaCalculator< - CircleAreaCalculator, - >: IsProviderFor, - {} - trait __CanUseScaledCircle< - __Component__, - __Params__: ?Sized, - >: CanUseComponent<__Component__, __Params__> {} - impl __CanUseScaledCircle for ScaledCircle {} - ") - } -} - -snapshot_check_components! { - check_components! { - #[check_trait(CheckScaledRectangleProviders)] - #[check_providers( - RectangleAreaCalculator, - ScaledAreaCalculator, - )] - ScaledRectangle { - AreaCalculatorComponent, - } - } - - expand_check_scaled_rectangle_providers(output) { - insta::assert_snapshot!(output, @" - trait CheckScaledRectangleProviders< - __Component__, - __Params__: ?Sized, - >: IsProviderFor<__Component__, ScaledRectangle, __Params__> {} - impl CheckScaledRectangleProviders - for RectangleAreaCalculator {} - impl CheckScaledRectangleProviders - for ScaledAreaCalculator {} - ") - } -} - -#[test] -fn test_scaled_area() { - let rectangle = PlainRectangle { - width: 3.0, - height: 4.0, - }; - - assert_eq!(rectangle.area(), 12.0); - - let scaled_rectangle = ScaledRectangle { - scale_factor: 2.0, - width: 3.0, - height: 4.0, - }; - - let circle = PlainCircle { radius: 3.0 }; - - assert_eq!(circle.area(), 9.0 * PI); - - assert_eq!(scaled_rectangle.area(), 48.0); - - let scaled_circle = ScaledCircle { - scale_factor: 2.0, - radius: 3.0, - }; - - assert_eq!(scaled_circle.area(), 36.0 * PI); -} diff --git a/crates/tests/cgp-tests/tests/component_tests/cgp_impl/use_provider.rs b/crates/tests/cgp-tests/tests/component_tests/cgp_impl/use_provider.rs deleted file mode 100644 index a8fa174d..00000000 --- a/crates/tests/cgp-tests/tests/component_tests/cgp_impl/use_provider.rs +++ /dev/null @@ -1,33 +0,0 @@ -use cgp::prelude::*; - -#[cgp_component(AreaCalculator)] -pub trait CanCalculateArea { - fn area(&self) -> f64; -} - -#[cgp_impl(new RectangleArea)] -impl AreaCalculator { - fn area(&self, #[implicit] width: f64, #[implicit] height: f64) -> f64 { - width * height - } -} - -#[cgp_impl(new ScaledArea)] -#[use_provider(Inner: AreaCalculator)] -impl AreaCalculator { - fn area(&self, #[implicit] scale_factor: f64) -> f64 { - Inner::area(self) * scale_factor * scale_factor - } -} - -#[derive(HasField)] -pub struct Rectangle { - pub width: f64, - pub height: f64, -} - -impl CanCalculateArea for Rectangle { - fn area(&self) -> f64 { - RectangleArea::area(self) - } -} diff --git a/crates/tests/cgp-tests/tests/component_tests/consumer_delegate/mod.rs b/crates/tests/cgp-tests/tests/component_tests/consumer_delegate/mod.rs deleted file mode 100644 index bfe3d89b..00000000 --- a/crates/tests/cgp-tests/tests/component_tests/consumer_delegate/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod basic; -pub mod generics; diff --git a/crates/tests/cgp-tests/tests/component_tests/delegate_components/general.rs b/crates/tests/cgp-tests/tests/component_tests/delegate_components/general.rs deleted file mode 100644 index 76cbb9b5..00000000 --- a/crates/tests/cgp-tests/tests/component_tests/delegate_components/general.rs +++ /dev/null @@ -1,146 +0,0 @@ -#![allow(unused)] - -mod test_basic_delegate_components { - use cgp::prelude::DelegateComponent; - use cgp_macro_test_util::snapshot_delegate_components; - use insta::assert_snapshot; - - pub struct FooKey; - pub struct FooValue; - pub struct BarKey; - pub struct BarValue; - pub struct BazKey; - - pub struct Components; - - snapshot_delegate_components! { - delegate_components! { - Components { - FooKey: FooValue, - [ - BarKey, - BazKey, - ]: - BarValue, - } - } - - expand_components(output) { - insta::assert_snapshot!(output, @" - impl DelegateComponent for Components { - type Delegate = FooValue; - } - impl<__Context__, __Params__> IsProviderFor - for Components - where - FooValue: IsProviderFor, - {} - impl DelegateComponent for Components { - type Delegate = BarValue; - } - impl<__Context__, __Params__> IsProviderFor - for Components - where - BarValue: IsProviderFor, - {} - impl DelegateComponent for Components { - type Delegate = BarValue; - } - impl<__Context__, __Params__> IsProviderFor - for Components - where - BarValue: IsProviderFor, - {} - ") - } - } - - pub trait CheckDelegates: - DelegateComponent - + DelegateComponent - + DelegateComponent - { - } - - impl CheckDelegates for Components {} -} - -mod test_generic_delegate_components { - use core::marker::PhantomData; - - use cgp::prelude::DelegateComponent; - use cgp_macro_test_util::snapshot_delegate_components; - use insta::assert_snapshot; - - pub struct FooKey(pub PhantomData); - pub struct FooValue; - pub struct BarKey<'a, T>(pub PhantomData<(&'a (), T)>); - pub struct BarValue(pub PhantomData); - pub struct BazKey(pub PhantomData<(T1, T2)>); - - pub struct Components; - - snapshot_delegate_components! { - delegate_components! { - <'a, T1: Clone> - Components { - FooKey: FooValue, - [ - BarKey<'a, T1>, - BazKey, - ]: - BarValue, - } - } - expand_components(output) { - insta::assert_snapshot!(output, @" - impl<'a, T1: Clone> DelegateComponent> for Components { - type Delegate = FooValue; - } - impl< - 'a, - T1: Clone, - __Context__, - __Params__, - > IsProviderFor, __Context__, __Params__> for Components - where - FooValue: IsProviderFor, __Context__, __Params__>, - {} - impl<'a, T1: Clone> DelegateComponent> for Components { - type Delegate = BarValue; - } - impl< - 'a, - T1: Clone, - __Context__, - __Params__, - > IsProviderFor, __Context__, __Params__> for Components - where - BarValue: IsProviderFor, __Context__, __Params__>, - {} - impl<'a, T1: Clone, T2> DelegateComponent> for Components { - type Delegate = BarValue; - } - impl< - 'a, - T1: Clone, - T2, - __Context__, - __Params__, - > IsProviderFor, __Context__, __Params__> for Components - where - BarValue: IsProviderFor, __Context__, __Params__>, - {} - ") - } - } - - pub trait CheckDelegates<'a, T1, T2>: - DelegateComponent, Delegate = FooValue> - + DelegateComponent, Delegate = BarValue> - + DelegateComponent, Delegate = BarValue> - { - } - - impl CheckDelegates<'_, T1, T2> for Components where T1: Clone {} -} diff --git a/crates/tests/cgp-tests/tests/component_tests/delegate_components/mod.rs b/crates/tests/cgp-tests/tests/component_tests/delegate_components/mod.rs deleted file mode 100644 index d52b5d5b..00000000 --- a/crates/tests/cgp-tests/tests/component_tests/delegate_components/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod direct; -pub mod general; -pub mod new_struct; diff --git a/crates/tests/cgp-tests/tests/component_tests/delegate_components/new_struct.rs b/crates/tests/cgp-tests/tests/component_tests/delegate_components/new_struct.rs deleted file mode 100644 index 5d38c106..00000000 --- a/crates/tests/cgp-tests/tests/component_tests/delegate_components/new_struct.rs +++ /dev/null @@ -1,129 +0,0 @@ -#![allow(unused)] - -use core::marker::PhantomData; - -use cgp::core::component::UseDelegate; -use cgp::prelude::*; - -pub fn test_delegate_components_with_new_struct() { - struct FooKey; - struct FooValue; - struct BarKey; - struct BarValue; - - delegate_components! { - new MyComponents { - FooKey: FooValue, - BarKey: BarValue, - } - } - - trait CheckDelegates: - DelegateComponent - + DelegateComponent - { - } - - impl CheckDelegates for MyComponents {} -} - -pub fn test_delegate_components_with_new_generic_struct() { - struct FooKey(PhantomData); - struct FooValue; - struct BarKey; - struct BarValue(PhantomData); - - delegate_components! { - - new MyComponents { - FooKey: FooValue, - BarKey: BarValue, - } - } - - trait CheckDelegates: - DelegateComponent, Delegate = FooValue> - + DelegateComponent> - { - } - - impl CheckDelegates for MyComponents {} -} - -pub fn test_delegate_components_with_new_value() { - struct FooKey; - struct FooValue; - struct BarKey; - struct BazKey; - struct BazValue; - - delegate_components! { - new MyComponents { - FooKey: FooValue, - BarKey: UseDelegate, - } - } - - trait CheckDelegates: - DelegateComponent - + DelegateComponent> - { - } - - impl CheckDelegates for MyComponents {} - - trait CheckInnerDelegates: DelegateComponent {} - - impl CheckInnerDelegates for BarValue {} -} - -pub fn test_delegate_components_with_generic_new_value() { - struct FooKey; - struct FooValue; - struct BarKey(pub PhantomData); - struct BazKey; - struct BazValue(pub PhantomData); - - delegate_components! { - new MyComponents { - FooKey: FooValue, - BarKey: UseDelegate { - BazKey: BazValue, - }>, - } - } - - trait CheckDelegates: - DelegateComponent - + DelegateComponent, Delegate = UseDelegate>> - { - } - - impl CheckDelegates for MyComponents {} - - trait CheckInnerDelegates: DelegateComponent> {} - - impl CheckInnerDelegates for BarValue {} -} - -pub fn test_delegate_new_with_array_key() { - pub struct FooKey; - pub struct BarKey; - pub struct BazKey; - - delegate_components! { - new MyComponents { - [ - FooKey, - BarKey, - BazKey, - ]: - UseDelegate, - } - } -} diff --git a/crates/tests/cgp-tests/tests/component_tests/mod.rs b/crates/tests/cgp-tests/tests/component_tests/mod.rs deleted file mode 100644 index 9afc10e1..00000000 --- a/crates/tests/cgp-tests/tests/component_tests/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -pub mod abstract_types; -pub mod cgp_component; -pub mod cgp_impl; -pub mod consumer_delegate; -pub mod delegate_components; diff --git a/crates/tests/cgp-tests/tests/dispatcher_macro.rs b/crates/tests/cgp-tests/tests/dispatcher_macro.rs deleted file mode 100644 index 23a88a66..00000000 --- a/crates/tests/cgp-tests/tests/dispatcher_macro.rs +++ /dev/null @@ -1,3 +0,0 @@ -#![allow(clippy::needless_lifetimes)] - -pub mod dispatcher_macro_tests; diff --git a/crates/tests/cgp-tests/tests/dispatcher_macro_tests/mod.rs b/crates/tests/cgp-tests/tests/dispatcher_macro_tests/mod.rs deleted file mode 100644 index ff3c7293..00000000 --- a/crates/tests/cgp-tests/tests/dispatcher_macro_tests/mod.rs +++ /dev/null @@ -1,19 +0,0 @@ -pub mod async_generics; -pub mod async_multi_args; -pub mod async_multi_args_owned_self; -pub mod async_multi_args_ref; -pub mod async_self_mut_only; -pub mod async_self_only; -pub mod async_self_ref_only; -pub mod generics; -pub mod multi_args; -pub mod multi_args_owned_self; -pub mod multi_args_ref; -pub mod multi_methods; -pub mod self_mut_only; -pub mod self_only; -pub mod self_ref_only; -pub mod self_ref_return_explicit_ref; -pub mod self_ref_return_implicit_ref; -pub mod shape; -pub mod types; diff --git a/crates/tests/cgp-tests/tests/dispatcher_macro_tests/self_ref_return_explicit_ref.rs b/crates/tests/cgp-tests/tests/dispatcher_macro_tests/self_ref_return_explicit_ref.rs deleted file mode 100644 index 8ca95bf3..00000000 --- a/crates/tests/cgp-tests/tests/dispatcher_macro_tests/self_ref_return_explicit_ref.rs +++ /dev/null @@ -1,28 +0,0 @@ -use cgp::prelude::*; - -use crate::dispatcher_macro_tests::types::{Bar, Foo, FooBar}; - -#[cgp_auto_dispatch] -pub trait CanCall { - fn call<'a>(&'a self) -> &'a str; -} - -impl CanCall for Foo { - fn call(&self) -> &str { - "foo" - } -} - -impl CanCall for Bar { - fn call(&self) -> &str { - "bar" - } -} - -pub trait CheckCanCallFooBar: CanCall {} -impl CheckCanCallFooBar for FooBar {} - -#[test] -fn test_call_self_ref_only() { - assert_eq!(FooBar::Foo(Foo).call(), "foo"); -} diff --git a/crates/tests/cgp-tests/tests/dispatcher_macro_tests/self_ref_return_implicit_ref.rs b/crates/tests/cgp-tests/tests/dispatcher_macro_tests/self_ref_return_implicit_ref.rs deleted file mode 100644 index ccb6afe7..00000000 --- a/crates/tests/cgp-tests/tests/dispatcher_macro_tests/self_ref_return_implicit_ref.rs +++ /dev/null @@ -1,28 +0,0 @@ -use cgp::prelude::*; - -use crate::dispatcher_macro_tests::types::{Bar, Foo, FooBar}; - -#[cgp_auto_dispatch] -pub trait CanCall { - fn call(&self) -> &str; -} - -impl CanCall for Foo { - fn call(&self) -> &str { - "foo" - } -} - -impl CanCall for Bar { - fn call(&self) -> &str { - "bar" - } -} - -pub trait CheckCanCallFooBar: CanCall {} -impl CheckCanCallFooBar for FooBar {} - -#[test] -fn test_call_self_ref_only() { - assert_eq!(FooBar::Foo(Foo).call(), "foo"); -} diff --git a/crates/tests/cgp-tests/tests/dispatcher_macro_tests/types.rs b/crates/tests/cgp-tests/tests/dispatcher_macro_tests/types.rs deleted file mode 100644 index 2af30adc..00000000 --- a/crates/tests/cgp-tests/tests/dispatcher_macro_tests/types.rs +++ /dev/null @@ -1,10 +0,0 @@ -use cgp::prelude::*; - -pub struct Foo; -pub struct Bar; - -#[derive(CgpVariant)] -pub enum FooBar { - Foo(Foo), - Bar(Bar), -} diff --git a/crates/tests/cgp-tests/tests/dispatcher_macro_tests/async_generics.rs b/crates/tests/cgp-tests/tests/dispatching/auto_dispatch_async_generics.rs similarity index 70% rename from crates/tests/cgp-tests/tests/dispatcher_macro_tests/async_generics.rs rename to crates/tests/cgp-tests/tests/dispatching/auto_dispatch_async_generics.rs index 15de14d7..646394ff 100644 --- a/crates/tests/cgp-tests/tests/dispatcher_macro_tests/async_generics.rs +++ b/crates/tests/cgp-tests/tests/dispatching/auto_dispatch_async_generics.rs @@ -1,9 +1,15 @@ +//! `#[cgp_auto_dispatch]` combined with `#[async_trait]` on a generic trait +//! (`CanCall`) mixing an async and a sync method, with a per-variant +//! `T: Display` bound on `Foo`. Dispatched over `FooBar`. +//! +//! See docs/reference/macros/cgp_auto_dispatch.md. + use core::fmt::Display; use cgp::prelude::*; use futures::executor::block_on; -use crate::dispatcher_macro_tests::types::{Bar, Foo, FooBar}; +use super::types::{Bar, Foo, FooBar}; #[cgp_auto_dispatch] #[async_trait] @@ -37,6 +43,6 @@ pub trait CheckCanCallFooBar: CanCall {} impl CheckCanCallFooBar for FooBar {} #[test] -fn test_call_self_only() { +fn test_call_async_generics() { assert_eq!(block_on(FooBar::Foo(Foo).call_a(42, &"extra")), "foo-extra"); } diff --git a/crates/tests/cgp-tests/tests/dispatcher_macro_tests/async_multi_args.rs b/crates/tests/cgp-tests/tests/dispatching/auto_dispatch_async_multi_args.rs similarity index 66% rename from crates/tests/cgp-tests/tests/dispatcher_macro_tests/async_multi_args.rs rename to crates/tests/cgp-tests/tests/dispatching/auto_dispatch_async_multi_args.rs index 89e2d29e..edcd5cbf 100644 --- a/crates/tests/cgp-tests/tests/dispatcher_macro_tests/async_multi_args.rs +++ b/crates/tests/cgp-tests/tests/dispatching/auto_dispatch_async_multi_args.rs @@ -1,7 +1,12 @@ +//! `#[cgp_auto_dispatch]` combined with `#[async_trait]`: a `&self` async method +//! with extra by-value arguments, dispatched over `FooBar`. +//! +//! See docs/reference/macros/cgp_auto_dispatch.md. + use cgp::prelude::*; use futures::executor::block_on; -use crate::dispatcher_macro_tests::types::{Bar, Foo, FooBar}; +use super::types::{Bar, Foo, FooBar}; #[cgp_auto_dispatch] #[async_trait] @@ -25,6 +30,6 @@ pub trait CheckCanCallFooBar: CanCall {} impl CheckCanCallFooBar for FooBar {} #[test] -fn test_call_self_only() { +fn test_call_async_multi_args() { assert_eq!(block_on(FooBar::Foo(Foo).call(42, true)), "foo"); } diff --git a/crates/tests/cgp-tests/tests/dispatcher_macro_tests/async_multi_args_owned_self.rs b/crates/tests/cgp-tests/tests/dispatching/auto_dispatch_async_multi_args_owned_self.rs similarity index 62% rename from crates/tests/cgp-tests/tests/dispatcher_macro_tests/async_multi_args_owned_self.rs rename to crates/tests/cgp-tests/tests/dispatching/auto_dispatch_async_multi_args_owned_self.rs index f14259ee..a79a2f42 100644 --- a/crates/tests/cgp-tests/tests/dispatcher_macro_tests/async_multi_args_owned_self.rs +++ b/crates/tests/cgp-tests/tests/dispatching/auto_dispatch_async_multi_args_owned_self.rs @@ -1,7 +1,13 @@ +//! `#[cgp_auto_dispatch]` combined with `#[async_trait]`: a by-value `self` async +//! method taking a `&mut` argument and returning a borrow tied to it, dispatched +//! over `FooBar`. +//! +//! See docs/reference/macros/cgp_auto_dispatch.md. + use cgp::prelude::*; use futures::executor::block_on; -use crate::dispatcher_macro_tests::types::{Bar, Foo, FooBar}; +use super::types::{Bar, Foo, FooBar}; #[cgp_auto_dispatch] #[async_trait] @@ -25,6 +31,6 @@ pub trait CheckCanCallFooBar: CanCall {} impl CheckCanCallFooBar for FooBar {} #[test] -fn test_call_self_only() { +fn test_call_async_multi_args_owned_self() { assert_eq!(block_on(FooBar::Foo(Foo).call(&mut 42, true)), "foo"); } diff --git a/crates/tests/cgp-tests/tests/dispatcher_macro_tests/async_multi_args_ref.rs b/crates/tests/cgp-tests/tests/dispatching/auto_dispatch_async_multi_args_ref.rs similarity index 64% rename from crates/tests/cgp-tests/tests/dispatcher_macro_tests/async_multi_args_ref.rs rename to crates/tests/cgp-tests/tests/dispatching/auto_dispatch_async_multi_args_ref.rs index 36fb1d6a..f0d127bf 100644 --- a/crates/tests/cgp-tests/tests/dispatcher_macro_tests/async_multi_args_ref.rs +++ b/crates/tests/cgp-tests/tests/dispatching/auto_dispatch_async_multi_args_ref.rs @@ -1,7 +1,13 @@ +//! `#[cgp_auto_dispatch]` combined with `#[async_trait]`: a `&mut self` async +//! method with borrowed arguments and a borrowed return, dispatched over +//! `FooBar`. +//! +//! See docs/reference/macros/cgp_auto_dispatch.md. + use cgp::prelude::*; use futures::executor::block_on; -use crate::dispatcher_macro_tests::types::{Bar, Foo, FooBar}; +use super::types::{Bar, Foo, FooBar}; #[cgp_auto_dispatch] #[async_trait] @@ -25,6 +31,6 @@ pub trait CheckCanCallFooBar: CanCall {} impl CheckCanCallFooBar for FooBar {} #[test] -fn test_call_self_only() { +fn test_call_async_multi_args_ref() { assert_eq!(block_on(FooBar::Foo(Foo).call(&42, true)), "foo"); } diff --git a/crates/tests/cgp-tests/tests/dispatcher_macro_tests/async_self_mut_only.rs b/crates/tests/cgp-tests/tests/dispatching/auto_dispatch_async_self_mut_only.rs similarity index 66% rename from crates/tests/cgp-tests/tests/dispatcher_macro_tests/async_self_mut_only.rs rename to crates/tests/cgp-tests/tests/dispatching/auto_dispatch_async_self_mut_only.rs index 4c726246..c4e41a60 100644 --- a/crates/tests/cgp-tests/tests/dispatcher_macro_tests/async_self_mut_only.rs +++ b/crates/tests/cgp-tests/tests/dispatching/auto_dispatch_async_self_mut_only.rs @@ -1,7 +1,12 @@ +//! `#[cgp_auto_dispatch]` combined with `#[async_trait]`: a `&mut self` async +//! method, dispatched over `FooBar`. +//! +//! See docs/reference/macros/cgp_auto_dispatch.md. + use cgp::prelude::*; use futures::executor::block_on; -use crate::dispatcher_macro_tests::types::{Bar, Foo, FooBar}; +use super::types::{Bar, Foo, FooBar}; #[cgp_auto_dispatch] #[async_trait] @@ -25,6 +30,6 @@ pub trait CheckCanCallFooBar: CanCall {} impl CheckCanCallFooBar for FooBar {} #[test] -fn test_call_self_only() { +fn test_call_async_self_mut_only() { assert_eq!(block_on(FooBar::Foo(Foo).call()), "foo"); } diff --git a/crates/tests/cgp-tests/tests/dispatcher_macro_tests/async_self_only.rs b/crates/tests/cgp-tests/tests/dispatching/auto_dispatch_async_self_only.rs similarity index 65% rename from crates/tests/cgp-tests/tests/dispatcher_macro_tests/async_self_only.rs rename to crates/tests/cgp-tests/tests/dispatching/auto_dispatch_async_self_only.rs index 163ed265..fa6b7c34 100644 --- a/crates/tests/cgp-tests/tests/dispatcher_macro_tests/async_self_only.rs +++ b/crates/tests/cgp-tests/tests/dispatching/auto_dispatch_async_self_only.rs @@ -1,7 +1,12 @@ +//! `#[cgp_auto_dispatch]` combined with `#[async_trait]`: a by-value `self` +//! async method, dispatched over `FooBar`. +//! +//! See docs/reference/macros/cgp_auto_dispatch.md. + use cgp::prelude::*; use futures::executor::block_on; -use crate::dispatcher_macro_tests::types::{Bar, Foo, FooBar}; +use super::types::{Bar, Foo, FooBar}; #[cgp_auto_dispatch] #[async_trait] @@ -25,6 +30,6 @@ pub trait CheckCanCallFooBar: CanCall {} impl CheckCanCallFooBar for FooBar {} #[test] -fn test_call_self_only() { +fn test_call_async_self_only() { assert_eq!(block_on(FooBar::Foo(Foo).call()), "foo"); } diff --git a/crates/tests/cgp-tests/tests/dispatcher_macro_tests/async_self_ref_only.rs b/crates/tests/cgp-tests/tests/dispatching/auto_dispatch_async_self_ref_only.rs similarity index 66% rename from crates/tests/cgp-tests/tests/dispatcher_macro_tests/async_self_ref_only.rs rename to crates/tests/cgp-tests/tests/dispatching/auto_dispatch_async_self_ref_only.rs index 64f6530c..9174d2e9 100644 --- a/crates/tests/cgp-tests/tests/dispatcher_macro_tests/async_self_ref_only.rs +++ b/crates/tests/cgp-tests/tests/dispatching/auto_dispatch_async_self_ref_only.rs @@ -1,7 +1,12 @@ +//! `#[cgp_auto_dispatch]` combined with `#[async_trait]`: a `&self` async +//! method, dispatched over `FooBar`. +//! +//! See docs/reference/macros/cgp_auto_dispatch.md. + use cgp::prelude::*; use futures::executor::block_on; -use crate::dispatcher_macro_tests::types::{Bar, Foo, FooBar}; +use super::types::{Bar, Foo, FooBar}; #[cgp_auto_dispatch] #[async_trait] @@ -25,6 +30,6 @@ pub trait CheckCanCallFooBar: CanCall {} impl CheckCanCallFooBar for FooBar {} #[test] -fn test_call_self_only() { +fn test_call_async_self_ref_only() { assert_eq!(block_on(FooBar::Foo(Foo).call()), "foo"); } diff --git a/crates/tests/cgp-tests/tests/dispatcher_macro_tests/generics.rs b/crates/tests/cgp-tests/tests/dispatching/auto_dispatch_generics.rs similarity index 72% rename from crates/tests/cgp-tests/tests/dispatcher_macro_tests/generics.rs rename to crates/tests/cgp-tests/tests/dispatching/auto_dispatch_generics.rs index b89d4ba8..14fdae07 100644 --- a/crates/tests/cgp-tests/tests/dispatcher_macro_tests/generics.rs +++ b/crates/tests/cgp-tests/tests/dispatching/auto_dispatch_generics.rs @@ -1,8 +1,13 @@ +//! `#[cgp_auto_dispatch]` on a generic trait (`CanCall`), where the per-variant +//! impls may add their own bounds on `T` (here `Foo` requires `T: Display`). +//! +//! See docs/reference/macros/cgp_auto_dispatch.md. + use core::fmt::Display; use cgp::prelude::*; -use crate::dispatcher_macro_tests::types::{Bar, Foo, FooBar}; +use super::types::{Bar, Foo, FooBar}; #[cgp_auto_dispatch] pub trait CanCall { @@ -35,6 +40,6 @@ pub trait CheckCanCallFooBar: CanCall {} impl CheckCanCallFooBar for FooBar {} #[test] -fn test_call_self_only() { +fn test_call_generics() { assert_eq!(FooBar::Foo(Foo).call_a(42, &"extra"), "foo-extra"); } diff --git a/crates/tests/cgp-tests/tests/dispatcher_macro_tests/multi_args.rs b/crates/tests/cgp-tests/tests/dispatching/auto_dispatch_multi_args.rs similarity index 62% rename from crates/tests/cgp-tests/tests/dispatcher_macro_tests/multi_args.rs rename to crates/tests/cgp-tests/tests/dispatching/auto_dispatch_multi_args.rs index 677cf88c..4db67ce9 100644 --- a/crates/tests/cgp-tests/tests/dispatcher_macro_tests/multi_args.rs +++ b/crates/tests/cgp-tests/tests/dispatching/auto_dispatch_multi_args.rs @@ -1,6 +1,11 @@ +//! `#[cgp_auto_dispatch]` on a `&self` method that also takes extra by-value +//! arguments, which are forwarded unchanged to the dispatched variant impl. +//! +//! See docs/reference/macros/cgp_auto_dispatch.md. + use cgp::prelude::*; -use crate::dispatcher_macro_tests::types::{Bar, Foo, FooBar}; +use super::types::{Bar, Foo, FooBar}; #[cgp_auto_dispatch] pub trait CanCall { @@ -23,6 +28,6 @@ pub trait CheckCanCallFooBar: CanCall {} impl CheckCanCallFooBar for FooBar {} #[test] -fn test_call_self_only() { +fn test_call_multi_args() { assert_eq!(FooBar::Foo(Foo).call(42, true), "foo"); } diff --git a/crates/tests/cgp-tests/tests/dispatcher_macro_tests/multi_args_owned_self.rs b/crates/tests/cgp-tests/tests/dispatching/auto_dispatch_multi_args_owned_self.rs similarity index 63% rename from crates/tests/cgp-tests/tests/dispatcher_macro_tests/multi_args_owned_self.rs rename to crates/tests/cgp-tests/tests/dispatching/auto_dispatch_multi_args_owned_self.rs index 34c6ee64..08f32446 100644 --- a/crates/tests/cgp-tests/tests/dispatcher_macro_tests/multi_args_owned_self.rs +++ b/crates/tests/cgp-tests/tests/dispatching/auto_dispatch_multi_args_owned_self.rs @@ -1,6 +1,11 @@ +//! `#[cgp_auto_dispatch]` on a by-value `self` method that takes a `&mut` +//! argument and returns a borrow tied to that argument. +//! +//! See docs/reference/macros/cgp_auto_dispatch.md. + use cgp::prelude::*; -use crate::dispatcher_macro_tests::types::{Bar, Foo, FooBar}; +use super::types::{Bar, Foo, FooBar}; #[cgp_auto_dispatch] pub trait CanCall { @@ -23,6 +28,6 @@ pub trait CheckCanCallFooBar: CanCall {} impl CheckCanCallFooBar for FooBar {} #[test] -fn test_call_self_only() { +fn test_call_multi_args_owned_self() { assert_eq!(FooBar::Foo(Foo).call(&mut 42, true), "foo"); } diff --git a/crates/tests/cgp-tests/tests/dispatcher_macro_tests/multi_args_ref.rs b/crates/tests/cgp-tests/tests/dispatching/auto_dispatch_multi_args_ref.rs similarity index 62% rename from crates/tests/cgp-tests/tests/dispatcher_macro_tests/multi_args_ref.rs rename to crates/tests/cgp-tests/tests/dispatching/auto_dispatch_multi_args_ref.rs index 92d0262e..fa514275 100644 --- a/crates/tests/cgp-tests/tests/dispatcher_macro_tests/multi_args_ref.rs +++ b/crates/tests/cgp-tests/tests/dispatching/auto_dispatch_multi_args_ref.rs @@ -1,6 +1,11 @@ +//! `#[cgp_auto_dispatch]` on a `&mut self` method with borrowed arguments and a +//! borrowed return, exercising the lifetime handling in the generated handler. +//! +//! See docs/reference/macros/cgp_auto_dispatch.md. + use cgp::prelude::*; -use crate::dispatcher_macro_tests::types::{Bar, Foo, FooBar}; +use super::types::{Bar, Foo, FooBar}; #[cgp_auto_dispatch] pub trait CanCall { @@ -23,6 +28,6 @@ pub trait CheckCanCallFooBar: CanCall {} impl CheckCanCallFooBar for FooBar {} #[test] -fn test_call_self_only() { +fn test_call_multi_args_ref() { assert_eq!(FooBar::Foo(Foo).call(&42, true), "foo"); } diff --git a/crates/tests/cgp-tests/tests/dispatcher_macro_tests/multi_methods.rs b/crates/tests/cgp-tests/tests/dispatching/auto_dispatch_multi_methods.rs similarity index 73% rename from crates/tests/cgp-tests/tests/dispatcher_macro_tests/multi_methods.rs rename to crates/tests/cgp-tests/tests/dispatching/auto_dispatch_multi_methods.rs index 97f9af6b..6ee53604 100644 --- a/crates/tests/cgp-tests/tests/dispatcher_macro_tests/multi_methods.rs +++ b/crates/tests/cgp-tests/tests/dispatching/auto_dispatch_multi_methods.rs @@ -1,6 +1,12 @@ +//! `#[cgp_auto_dispatch]` on a trait with multiple methods of differing +//! receiver shapes (`&self`, `&mut self`, `self`), all dispatched over the same +//! `FooBar` enum. +//! +//! See docs/reference/macros/cgp_auto_dispatch.md. + use cgp::prelude::*; -use crate::dispatcher_macro_tests::types::{Bar, Foo, FooBar}; +use super::types::{Bar, Foo, FooBar}; #[cgp_auto_dispatch] pub trait CanCall { @@ -43,6 +49,6 @@ pub trait CheckCanCallFooBar: CanCall {} impl CheckCanCallFooBar for FooBar {} #[test] -fn test_call_self_only() { +fn test_call_multi_methods() { assert_eq!(FooBar::Foo(Foo).call_a(42, true), "foo"); } diff --git a/crates/tests/cgp-tests/tests/dispatcher_macro_tests/self_mut_only.rs b/crates/tests/cgp-tests/tests/dispatching/auto_dispatch_self_mut_only.rs similarity index 57% rename from crates/tests/cgp-tests/tests/dispatcher_macro_tests/self_mut_only.rs rename to crates/tests/cgp-tests/tests/dispatching/auto_dispatch_self_mut_only.rs index 42794902..6d585059 100644 --- a/crates/tests/cgp-tests/tests/dispatcher_macro_tests/self_mut_only.rs +++ b/crates/tests/cgp-tests/tests/dispatching/auto_dispatch_self_mut_only.rs @@ -1,6 +1,13 @@ +//! `#[cgp_auto_dispatch]` on a `&mut self` method with no extra arguments. +//! +//! The generated handler routes a `&mut FooBar` reference to the matching +//! variant impl of `CanCall`. +//! +//! See docs/reference/macros/cgp_auto_dispatch.md. + use cgp::prelude::*; -use crate::dispatcher_macro_tests::types::{Bar, Foo, FooBar}; +use super::types::{Bar, Foo, FooBar}; #[cgp_auto_dispatch] pub trait CanCall { @@ -23,6 +30,6 @@ pub trait CheckCanCallFooBar: CanCall {} impl CheckCanCallFooBar for FooBar {} #[test] -fn test_call_self_ref_only() { +fn test_call_self_mut_only() { assert_eq!(FooBar::Foo(Foo).call(), "foo"); } diff --git a/crates/tests/cgp-tests/tests/dispatcher_macro_tests/self_only.rs b/crates/tests/cgp-tests/tests/dispatching/auto_dispatch_self_only.rs similarity index 59% rename from crates/tests/cgp-tests/tests/dispatcher_macro_tests/self_only.rs rename to crates/tests/cgp-tests/tests/dispatching/auto_dispatch_self_only.rs index a7ec87a3..2fbbcf6b 100644 --- a/crates/tests/cgp-tests/tests/dispatcher_macro_tests/self_only.rs +++ b/crates/tests/cgp-tests/tests/dispatching/auto_dispatch_self_only.rs @@ -1,6 +1,13 @@ +//! `#[cgp_auto_dispatch]` on a by-value `self` method with no extra arguments. +//! +//! The generated handler routes a `FooBar` value to the `Foo`/`Bar` variant +//! impl of `CanCall`, consuming `self`. +//! +//! See docs/reference/macros/cgp_auto_dispatch.md. + use cgp::prelude::*; -use crate::dispatcher_macro_tests::types::{Bar, Foo, FooBar}; +use super::types::{Bar, Foo, FooBar}; #[cgp_auto_dispatch] pub trait CanCall { diff --git a/crates/tests/cgp-tests/tests/dispatcher_macro_tests/self_ref_only.rs b/crates/tests/cgp-tests/tests/dispatching/auto_dispatch_self_ref_only.rs similarity index 61% rename from crates/tests/cgp-tests/tests/dispatcher_macro_tests/self_ref_only.rs rename to crates/tests/cgp-tests/tests/dispatching/auto_dispatch_self_ref_only.rs index d26a3486..8fb8c27b 100644 --- a/crates/tests/cgp-tests/tests/dispatcher_macro_tests/self_ref_only.rs +++ b/crates/tests/cgp-tests/tests/dispatching/auto_dispatch_self_ref_only.rs @@ -1,6 +1,13 @@ +//! `#[cgp_auto_dispatch]` on a `&self` method with no extra arguments. +//! +//! The generated handler routes a `&FooBar` reference to the matching variant +//! impl of `CanCall`. +//! +//! See docs/reference/macros/cgp_auto_dispatch.md. + use cgp::prelude::*; -use crate::dispatcher_macro_tests::types::{Bar, Foo, FooBar}; +use super::types::{Bar, Foo, FooBar}; #[cgp_auto_dispatch] pub trait CanCall { diff --git a/crates/tests/cgp-tests/tests/dispatching/auto_dispatch_self_ref_return_explicit_ref.rs b/crates/tests/cgp-tests/tests/dispatching/auto_dispatch_self_ref_return_explicit_ref.rs new file mode 100644 index 00000000..74768087 --- /dev/null +++ b/crates/tests/cgp-tests/tests/dispatching/auto_dispatch_self_ref_return_explicit_ref.rs @@ -0,0 +1,36 @@ +//! `#[cgp_auto_dispatch]` on a `&self` method whose return borrow is written +//! with an *explicit* lifetime (`fn call<'a>(&'a self) -> &'a str`). +//! +//! Companion to `auto_dispatch_self_ref_return_implicit_ref`, which writes the +//! same shape with an elided lifetime. +//! +//! See docs/reference/macros/cgp_auto_dispatch.md. + +use cgp::prelude::*; + +use super::types::{Bar, Foo, FooBar}; + +#[cgp_auto_dispatch] +pub trait CanCall { + fn call<'a>(&'a self) -> &'a str; +} + +impl CanCall for Foo { + fn call(&self) -> &str { + "foo" + } +} + +impl CanCall for Bar { + fn call(&self) -> &str { + "bar" + } +} + +pub trait CheckCanCallFooBar: CanCall {} +impl CheckCanCallFooBar for FooBar {} + +#[test] +fn test_call_self_ref_return_explicit_ref() { + assert_eq!(FooBar::Foo(Foo).call(), "foo"); +} diff --git a/crates/tests/cgp-tests/tests/dispatching/auto_dispatch_self_ref_return_implicit_ref.rs b/crates/tests/cgp-tests/tests/dispatching/auto_dispatch_self_ref_return_implicit_ref.rs new file mode 100644 index 00000000..9e2c6801 --- /dev/null +++ b/crates/tests/cgp-tests/tests/dispatching/auto_dispatch_self_ref_return_implicit_ref.rs @@ -0,0 +1,36 @@ +//! `#[cgp_auto_dispatch]` on a `&self` method whose return borrow uses an +//! *elided* (implicit) lifetime (`fn call(&self) -> &str`). +//! +//! Companion to `auto_dispatch_self_ref_return_explicit_ref`, which writes the +//! same shape with an explicit lifetime. +//! +//! See docs/reference/macros/cgp_auto_dispatch.md. + +use cgp::prelude::*; + +use super::types::{Bar, Foo, FooBar}; + +#[cgp_auto_dispatch] +pub trait CanCall { + fn call(&self) -> &str; +} + +impl CanCall for Foo { + fn call(&self) -> &str { + "foo" + } +} + +impl CanCall for Bar { + fn call(&self) -> &str { + "bar" + } +} + +pub trait CheckCanCallFooBar: CanCall {} +impl CheckCanCallFooBar for FooBar {} + +#[test] +fn test_call_self_ref_return_implicit_ref() { + assert_eq!(FooBar::Foo(Foo).call(), "foo"); +} diff --git a/crates/tests/cgp-tests/tests/dispatcher_macro_tests/shape.rs b/crates/tests/cgp-tests/tests/dispatching/auto_dispatch_shape.rs similarity index 74% rename from crates/tests/cgp-tests/tests/dispatcher_macro_tests/shape.rs rename to crates/tests/cgp-tests/tests/dispatching/auto_dispatch_shape.rs index 11cee3be..43a9f7ba 100644 --- a/crates/tests/cgp-tests/tests/dispatcher_macro_tests/shape.rs +++ b/crates/tests/cgp-tests/tests/dispatching/auto_dispatch_shape.rs @@ -1,3 +1,11 @@ +//! A realistic `#[cgp_auto_dispatch]` example: dispatching over a `Shape` enum of +//! `Circle`/`Rectangle`, with one immutable method (`area`, `&self`) and one +//! mutating method (`scale`, `&mut self`). Exercises that a single enum can carry +//! several auto-dispatched capabilities at once. +//! +//! See docs/reference/macros/cgp_auto_dispatch.md and +//! docs/concepts/dispatching.md. + use core::f64::consts::PI; use cgp::prelude::*; diff --git a/crates/tests/cgp-tests/tests/dispatching/compose.rs b/crates/tests/cgp-tests/tests/dispatching/compose.rs new file mode 100644 index 00000000..f598ec0a --- /dev/null +++ b/crates/tests/cgp-tests/tests/dispatching/compose.rs @@ -0,0 +1,91 @@ +//! Composing `ComputerRef` providers into a combined provider. +//! +//! `FirstNameToString` and `LastNameToString` each read one field of the context; +//! `ConcatOutputs` is a higher-order `ComputerRef` provider +//! that runs two inner providers and concatenates their outputs. `FullNameToString` +//! is the composition of the two field readers. The plain-function versions +//! (`first_name_to_string`, …, `concate_outputs`) show the same composition +//! written with closures rather than CGP providers. +//! +//! `#[cgp_new_provider]` is incidental scaffolding here (its expansion is owned by +//! `basic_delegation`), so it is written plainly rather than snapshotted. +//! +//! See docs/reference/providers/dispatch_combinators.md and +//! docs/reference/components/computer.md. + +use core::fmt::Display; + +use cgp::extra::handler::ComputerRef; +use cgp::prelude::*; + +pub fn first_name_to_string(context: &Context) -> String +where + Context: HasField, +{ + context.get_field(PhantomData).to_string() +} + +pub fn last_name_to_string(context: &Context) -> String +where + Context: HasField, +{ + context.get_field(PhantomData).to_string() +} + +pub fn full_name_to_string(context: &Context) -> String +where + Context: HasField + + HasField, +{ + let composed = concate_outputs(first_name_to_string, last_name_to_string); + composed(context) +} + +pub fn concate_outputs( + fn_a: impl Fn(&Context) -> String, + fn_b: impl Fn(&Context) -> String, +) -> impl Fn(&Context) -> String { + move |context| format!("{} {}", fn_a(context), fn_b(context)) +} + +#[cgp_new_provider] +impl ComputerRef for FirstNameToString +where + Context: HasField, +{ + type Output = String; + + fn compute_ref(context: &Context, _code: PhantomData, _input: &Input) -> String { + context.get_field(PhantomData).to_string() + } +} + +#[cgp_new_provider] +impl ComputerRef for LastNameToString +where + Context: HasField, +{ + type Output = String; + + fn compute_ref(context: &Context, _code: PhantomData, _input: &Input) -> String { + context.get_field(PhantomData).to_string() + } +} + +#[cgp_new_provider] +impl ComputerRef + for ConcatOutputs +where + ProviderA: ComputerRef, + ProviderB: ComputerRef, +{ + type Output = String; + + fn compute_ref(context: &Context, code: PhantomData, input: &Input) -> String { + let output_a = ProviderA::compute_ref(context, code, input); + let output_b = ProviderB::compute_ref(context, code, input); + format!("{output_a} {output_b}") + } +} + +pub type FullNameToString = ConcatOutputs; diff --git a/crates/tests/cgp-tests/tests/dispatching/mod.rs b/crates/tests/cgp-tests/tests/dispatching/mod.rs new file mode 100644 index 00000000..f5d8072b --- /dev/null +++ b/crates/tests/cgp-tests/tests/dispatching/mod.rs @@ -0,0 +1,40 @@ +//! One unit test per file. Each file is self-contained: it defines its own +//! traits, providers, and context types at module scope so that the type-level +//! wiring of one test never leaks into another. +//! +//! The exception is `types`, a small shared fixture (the `Foo`/`Bar`/`FooBar` +//! enum) that the `#[cgp_auto_dispatch]` shape tests dispatch over. It is +//! declared here and referenced by siblings via `super::types`. + +// Shared fixture: the `Foo`/`Bar`/`FooBar` enum the auto-dispatch tests route over. +pub mod types; + +// `#[cgp_auto_dispatch]` shape coverage: one method-shape per file. Each defines +// per-variant impls and dispatches them over an extensible-data enum. +pub mod auto_dispatch_generics; +pub mod auto_dispatch_multi_args; +pub mod auto_dispatch_multi_args_owned_self; +pub mod auto_dispatch_multi_args_ref; +pub mod auto_dispatch_multi_methods; +pub mod auto_dispatch_self_mut_only; +pub mod auto_dispatch_self_only; +pub mod auto_dispatch_self_ref_only; +pub mod auto_dispatch_self_ref_return_explicit_ref; +pub mod auto_dispatch_self_ref_return_implicit_ref; +pub mod auto_dispatch_shape; + +// `#[cgp_auto_dispatch]` combined with `#[async_trait]` — the async shapes. +pub mod auto_dispatch_async_generics; +pub mod auto_dispatch_async_multi_args; +pub mod auto_dispatch_async_multi_args_owned_self; +pub mod auto_dispatch_async_multi_args_ref; +pub mod auto_dispatch_async_self_mut_only; +pub mod auto_dispatch_async_self_only; +pub mod auto_dispatch_async_self_ref_only; + +// The `UseDelegate` dispatch provider and the `UseDelegate`-table form of +// `delegate_components!` (this concept owns those snapshots). +pub mod use_delegate_getter; + +// Composing handler/computer providers. +pub mod compose; diff --git a/crates/tests/cgp-tests/tests/dispatching/types.rs b/crates/tests/cgp-tests/tests/dispatching/types.rs new file mode 100644 index 00000000..013d827c --- /dev/null +++ b/crates/tests/cgp-tests/tests/dispatching/types.rs @@ -0,0 +1,20 @@ +//! Shared fixture for the `#[cgp_auto_dispatch]` shape tests. +//! +//! `FooBar` is an extensible-data enum (`#[derive(CgpVariant)]`) over the two +//! unit structs `Foo` and `Bar`. The sibling shape tests each define a +//! `CanCall` trait with a different method shape, implement it for `Foo` and +//! `Bar`, and rely on `#[cgp_auto_dispatch]` to route a `FooBar` value to the +//! matching variant impl. Referenced by siblings via `super::types`. +//! +//! See docs/reference/macros/cgp_auto_dispatch.md. + +use cgp::prelude::*; + +pub struct Foo; +pub struct Bar; + +#[derive(CgpVariant)] +pub enum FooBar { + Foo(Foo), + Bar(Bar), +} diff --git a/crates/tests/cgp-tests/src/tests/use_delegate/getter.rs b/crates/tests/cgp-tests/tests/dispatching/use_delegate_getter.rs similarity index 93% rename from crates/tests/cgp-tests/src/tests/use_delegate/getter.rs rename to crates/tests/cgp-tests/tests/dispatching/use_delegate_getter.rs index 22568798..03cbdcb7 100644 --- a/crates/tests/cgp-tests/src/tests/use_delegate/getter.rs +++ b/crates/tests/cgp-tests/tests/dispatching/use_delegate_getter.rs @@ -1,3 +1,19 @@ +//! The `UseDelegate` dispatch provider and the `UseDelegate`-table form of +//! `delegate_components!` / `delegate_and_check_components!` — this concept owns +//! those snapshots. +//! +//! A multi-parameter getter component (`HasFooAt`) is dispatched per +//! `(I, J)` value by wiring the component to a `UseDelegate` (single-key `I`) or +//! a custom `UseDelegate2` (tuple key `(I, J)`) table. `#[derive_delegate(...)]` +//! generates the dispatch-provider impls for each table type; the retained +//! `snapshot_cgp_type!` / `snapshot_cgp_getter!` expansions pin those `UseDelegate` +//! / `UseDelegate2` impls, and the retained `snapshot_delegate_*` expansions pin +//! the `UseDelegate`-table wiring. The `check_components!` scaffolding uses the +//! plain macro (checking is owned by another target). +//! +//! See docs/reference/providers/use_delegate.md and +//! docs/reference/providers/dispatch_combinators.md. + use core::marker::PhantomData; use cgp::prelude::*; @@ -441,7 +457,7 @@ mod derive_delegate { use core::marker::PhantomData; use cgp::prelude::*; - use cgp_macro_test_util::{snapshot_check_components, snapshot_delegate_and_check_components}; + use cgp_macro_test_util::snapshot_delegate_and_check_components; use super::*; @@ -550,26 +566,14 @@ mod derive_delegate { } } - snapshot_check_components! { - check_components! { - #[check_trait(CanUseMyContext)] - MyContext { - FooGetterAtComponent: [ - (Index<1>, Index<0>), - (Index<0>, Index<1>), - ] - } - } - - expand_check_my_context(output) { - insta::assert_snapshot!(output, @" - trait CanUseMyContext< - __Component__, - __Params__: ?Sized, - >: CanUseComponent<__Component__, __Params__> {} - impl CanUseMyContext, Index<0>)> for MyContext {} - impl CanUseMyContext, Index<1>)> for MyContext {} - ") + // Checking is owned by another concept target, so this uses the plain macro. + check_components! { + #[check_trait(CanUseMyContext)] + MyContext { + FooGetterAtComponent: [ + (Index<1>, Index<0>), + (Index<0>, Index<1>), + ] } } @@ -589,7 +593,7 @@ mod derive_delegate2 { use core::marker::PhantomData; use cgp::prelude::*; - use cgp_macro_test_util::{snapshot_check_components, snapshot_delegate_components}; + use cgp_macro_test_util::snapshot_delegate_components; use super::*; @@ -693,25 +697,13 @@ mod derive_delegate2 { } } - snapshot_check_components! { - check_components! { - MyContext { - FooGetterAtComponent: [ - (Index<1>, Index<0>), - (Index<0>, Index<1>), - ] - } - } - - expand_check_my_context(output) { - insta::assert_snapshot!(output, @" - trait __CheckMyContext< - __Component__, - __Params__: ?Sized, - >: CanUseComponent<__Component__, __Params__> {} - impl __CheckMyContext, Index<0>)> for MyContext {} - impl __CheckMyContext, Index<1>)> for MyContext {} - ") + // Checking is owned by another concept target, so this uses the plain macro. + check_components! { + MyContext { + FooGetterAtComponent: [ + (Index<1>, Index<0>), + (Index<0>, Index<1>), + ] } } diff --git a/crates/tests/cgp-tests/tests/dispatching_tests.rs b/crates/tests/cgp-tests/tests/dispatching_tests.rs new file mode 100644 index 00000000..65c8d54f --- /dev/null +++ b/crates/tests/cgp-tests/tests/dispatching_tests.rs @@ -0,0 +1,21 @@ +//! Entrypoint for the `dispatching` concept. +//! +//! Covers CGP's dispatch machinery: the `#[cgp_auto_dispatch]` macro (which +//! turns a per-variant trait into a handler that routes an extensible-data enum +//! to the matching variant impl), the `UseDelegate` dispatch provider and its +//! `UseDelegate`-table form of `delegate_components!`, and composing +//! handler/computer providers. +//! +//! This concept owns the `UseDelegate`-table snapshots of `delegate_components!` +//! and `delegate_and_check_components!`; other macros used here as incidental +//! scaffolding are written in their plain form (their expansions are pinned in +//! their own owning targets). +//! +//! See docs/reference/macros/cgp_auto_dispatch.md, +//! docs/reference/providers/use_delegate.md, +//! docs/reference/providers/dispatch_combinators.md, and +//! docs/concepts/dispatching.md. +#![allow(dead_code)] +#![allow(clippy::needless_lifetimes)] + +pub mod dispatching; diff --git a/crates/tests/cgp-tests/tests/extensible_data.rs b/crates/tests/cgp-tests/tests/extensible_data.rs deleted file mode 100644 index 6e77957c..00000000 --- a/crates/tests/cgp-tests/tests/extensible_data.rs +++ /dev/null @@ -1,3 +0,0 @@ -#![allow(clippy::disallowed_names)] - -pub mod extensible_data_tests; diff --git a/crates/tests/cgp-tests/tests/extensible_data_tests/has_field/chain.rs b/crates/tests/cgp-tests/tests/extensible_data_tests/has_field/chain.rs deleted file mode 100644 index ad150600..00000000 --- a/crates/tests/cgp-tests/tests/extensible_data_tests/has_field/chain.rs +++ /dev/null @@ -1,634 +0,0 @@ -pub mod chained_getter { - use core::marker::PhantomData; - - use cgp::core::field::impls::ChainGetters; - use cgp::prelude::*; - use cgp_macro_test_util::snapshot_derive_has_field; - - snapshot_derive_has_field! { - #[derive(HasField)] - pub struct Inner { - pub name: String, - } - - expand_inner(output) { - insta::assert_snapshot!(output, @" - impl HasField>>>>> for Inner { - type Value = String; - fn get_field( - &self, - key: ::core::marker::PhantomData< - Symbol<4, Chars<'n', Chars<'a', Chars<'m', Chars<'e', Nil>>>>>, - >, - ) -> &Self::Value { - &self.name - } - } - impl HasFieldMut>>>>> - for Inner { - fn get_field_mut( - &mut self, - key: ::core::marker::PhantomData< - Symbol<4, Chars<'n', Chars<'a', Chars<'m', Chars<'e', Nil>>>>>, - >, - ) -> &mut Self::Value { - &mut self.name - } - } - ") - } - } - - snapshot_derive_has_field! { - #[derive(HasField)] - pub struct Outer { - pub inner: Inner, - } - - expand_outer(output) { - insta::assert_snapshot!(output, @" - impl HasField>>>>>> - for Outer { - type Value = Inner; - fn get_field( - &self, - key: ::core::marker::PhantomData< - Symbol<5, Chars<'i', Chars<'n', Chars<'n', Chars<'e', Chars<'r', Nil>>>>>>, - >, - ) -> &Self::Value { - &self.inner - } - } - impl HasFieldMut< - Symbol<5, Chars<'i', Chars<'n', Chars<'n', Chars<'e', Chars<'r', Nil>>>>>>, - > for Outer { - fn get_field_mut( - &mut self, - key: ::core::marker::PhantomData< - Symbol<5, Chars<'i', Chars<'n', Chars<'n', Chars<'e', Chars<'r', Nil>>>>>>, - >, - ) -> &mut Self::Value { - &mut self.inner - } - } - ") - } - } - - #[test] - fn test_chained_getter() { - let context = Outer { - inner: Inner { - name: "test".to_owned(), - }, - }; - - let name: &String = , UseField], - >>::get_field(&context, PhantomData::<()>); - assert_eq!(name, "test"); - } -} - -mod chained_getter_with_outer_life { - use core::marker::PhantomData; - - use cgp::core::field::impls::ChainGetters; - use cgp::prelude::*; - use cgp_macro_test_util::snapshot_derive_has_field; - - snapshot_derive_has_field! { - #[derive(HasField)] - pub struct Outer<'a> { - pub inner: &'a Inner, - } - - expand_outer(output) { - insta::assert_snapshot!(output, @" - impl< - 'a, - > HasField>>>>>> - for Outer<'a> { - type Value = &'a Inner; - fn get_field( - &self, - key: ::core::marker::PhantomData< - Symbol<5, Chars<'i', Chars<'n', Chars<'n', Chars<'e', Chars<'r', Nil>>>>>>, - >, - ) -> &Self::Value { - &self.inner - } - } - impl< - 'a, - > HasFieldMut>>>>>> - for Outer<'a> { - fn get_field_mut( - &mut self, - key: ::core::marker::PhantomData< - Symbol<5, Chars<'i', Chars<'n', Chars<'n', Chars<'e', Chars<'r', Nil>>>>>>, - >, - ) -> &mut Self::Value { - &mut self.inner - } - } - ") - } - } - - snapshot_derive_has_field! { - #[derive(HasField)] - pub struct Inner { - pub name: String, - } - - expand_inner(output) { - insta::assert_snapshot!(output, @" - impl HasField>>>>> for Inner { - type Value = String; - fn get_field( - &self, - key: ::core::marker::PhantomData< - Symbol<4, Chars<'n', Chars<'a', Chars<'m', Chars<'e', Nil>>>>>, - >, - ) -> &Self::Value { - &self.name - } - } - impl HasFieldMut>>>>> - for Inner { - fn get_field_mut( - &mut self, - key: ::core::marker::PhantomData< - Symbol<4, Chars<'n', Chars<'a', Chars<'m', Chars<'e', Nil>>>>>, - >, - ) -> &mut Self::Value { - &mut self.name - } - } - ") - } - } - - #[test] - fn test_chained_getter_with_outer_life() { - let context = Outer { - inner: &Inner { - name: "test".to_owned(), - }, - }; - - let name: &String = , UseField], - >>::get_field(&context, PhantomData::<()>); - assert_eq!(name, "test"); - } -} - -mod chained_getter_with_inner_life { - use core::marker::PhantomData; - - use cgp::core::field::impls::ChainGetters; - use cgp::prelude::*; - use cgp_macro_test_util::snapshot_derive_has_field; - - snapshot_derive_has_field! { - #[derive(HasField)] - pub struct Outer<'a> { - pub inner: Inner<'a>, - } - - expand_outer(output) { - insta::assert_snapshot!(output, @" - impl< - 'a, - > HasField>>>>>> - for Outer<'a> { - type Value = Inner<'a>; - fn get_field( - &self, - key: ::core::marker::PhantomData< - Symbol<5, Chars<'i', Chars<'n', Chars<'n', Chars<'e', Chars<'r', Nil>>>>>>, - >, - ) -> &Self::Value { - &self.inner - } - } - impl< - 'a, - > HasFieldMut>>>>>> - for Outer<'a> { - fn get_field_mut( - &mut self, - key: ::core::marker::PhantomData< - Symbol<5, Chars<'i', Chars<'n', Chars<'n', Chars<'e', Chars<'r', Nil>>>>>>, - >, - ) -> &mut Self::Value { - &mut self.inner - } - } - ") - } - } - - snapshot_derive_has_field! { - #[derive(HasField)] - pub struct Inner<'a> { - pub name: &'a String, - } - - expand_inner(output) { - insta::assert_snapshot!(output, @" - impl<'a> HasField>>>>> - for Inner<'a> { - type Value = &'a String; - fn get_field( - &self, - key: ::core::marker::PhantomData< - Symbol<4, Chars<'n', Chars<'a', Chars<'m', Chars<'e', Nil>>>>>, - >, - ) -> &Self::Value { - &self.name - } - } - impl<'a> HasFieldMut>>>>> - for Inner<'a> { - fn get_field_mut( - &mut self, - key: ::core::marker::PhantomData< - Symbol<4, Chars<'n', Chars<'a', Chars<'m', Chars<'e', Nil>>>>>, - >, - ) -> &mut Self::Value { - &mut self.name - } - } - ") - } - } - - #[test] - fn test_chained_getter_with_inner_life() { - let context = Outer { - inner: Inner { - name: &"test".to_owned(), - }, - }; - - let name: &String = , UseField], - >>::get_field(&context, PhantomData::<()>); - - assert_eq!(name, "test"); - } -} - -mod deeply_nested_getter { - use cgp::core::field::impls::ChainGetters; - use cgp::prelude::*; - use cgp_macro_test_util::{ - snapshot_cgp_getter, snapshot_delegate_and_check_components, snapshot_derive_has_field, - }; - - snapshot_derive_has_field! { - #[derive(HasField)] - pub struct A { - pub b: B, - } - - expand_a(output) { - insta::assert_snapshot!(output, @" - impl HasField>> for A { - type Value = B; - fn get_field( - &self, - key: ::core::marker::PhantomData>>, - ) -> &Self::Value { - &self.b - } - } - impl HasFieldMut>> for A { - fn get_field_mut( - &mut self, - key: ::core::marker::PhantomData>>, - ) -> &mut Self::Value { - &mut self.b - } - } - ") - } - } - - snapshot_derive_has_field! { - #[derive(HasField)] - pub struct B { - pub c: C, - } - - expand_b(output) { - insta::assert_snapshot!(output, @" - impl HasField>> for B { - type Value = C; - fn get_field( - &self, - key: ::core::marker::PhantomData>>, - ) -> &Self::Value { - &self.c - } - } - impl HasFieldMut>> for B { - fn get_field_mut( - &mut self, - key: ::core::marker::PhantomData>>, - ) -> &mut Self::Value { - &mut self.c - } - } - ") - } - } - - snapshot_derive_has_field! { - #[derive(HasField)] - pub struct C { - pub d: D, - } - - expand_c(output) { - insta::assert_snapshot!(output, @" - impl HasField>> for C { - type Value = D; - fn get_field( - &self, - key: ::core::marker::PhantomData>>, - ) -> &Self::Value { - &self.d - } - } - impl HasFieldMut>> for C { - fn get_field_mut( - &mut self, - key: ::core::marker::PhantomData>>, - ) -> &mut Self::Value { - &mut self.d - } - } - ") - } - } - - snapshot_derive_has_field! { - #[derive(HasField)] - pub struct D { - pub name: String, - } - - expand_d(output) { - insta::assert_snapshot!(output, @" - impl HasField>>>>> for D { - type Value = String; - fn get_field( - &self, - key: ::core::marker::PhantomData< - Symbol<4, Chars<'n', Chars<'a', Chars<'m', Chars<'e', Nil>>>>>, - >, - ) -> &Self::Value { - &self.name - } - } - impl HasFieldMut>>>>> for D { - fn get_field_mut( - &mut self, - key: ::core::marker::PhantomData< - Symbol<4, Chars<'n', Chars<'a', Chars<'m', Chars<'e', Nil>>>>>, - >, - ) -> &mut Self::Value { - &mut self.name - } - } - ") - } - } - - snapshot_derive_has_field! { - #[derive(HasField)] - pub struct MyContext { - pub a: A, - } - - expand_my_context_struct(output) { - insta::assert_snapshot!(output, @" - impl HasField>> for MyContext { - type Value = A; - fn get_field( - &self, - key: ::core::marker::PhantomData>>, - ) -> &Self::Value { - &self.a - } - } - impl HasFieldMut>> for MyContext { - fn get_field_mut( - &mut self, - key: ::core::marker::PhantomData>>, - ) -> &mut Self::Value { - &mut self.a - } - } - ") - } - } - - snapshot_cgp_getter! { - #[cgp_getter] - pub trait HasName { - fn name(&self) -> &str; - } - - expand_has_name(output) { - insta::assert_snapshot!(output, @" - pub trait HasName { - fn name(&self) -> &str; - } - impl<__Context__> HasName for __Context__ - where - __Context__: NameGetter<__Context__>, - { - fn name(&self) -> &str { - __Context__::name(self) - } - } - pub trait NameGetter<__Context__>: IsProviderFor { - fn name(__context__: &__Context__) -> &str; - } - impl<__Provider__, __Context__> NameGetter<__Context__> for __Provider__ - where - __Provider__: DelegateComponent - + IsProviderFor, - <__Provider__ as DelegateComponent< - NameGetterComponent, - >>::Delegate: NameGetter<__Context__>, - { - fn name(__context__: &__Context__) -> &str { - <__Provider__ as DelegateComponent< - NameGetterComponent, - >>::Delegate::name(__context__) - } - } - pub struct NameGetterComponent; - impl<__Context__> NameGetter<__Context__> for UseContext - where - __Context__: HasName, - { - fn name(__context__: &__Context__) -> &str { - __Context__::name(__context__) - } - } - impl<__Context__> IsProviderFor for UseContext - where - __Context__: HasName, - {} - impl<__Context__, __Components__, __Path__> NameGetter<__Context__> - for RedirectLookup<__Components__, __Path__> - where - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent<__Path__>>::Delegate: NameGetter<__Context__>, - { - fn name(__context__: &__Context__) -> &str { - <__Components__ as DelegateComponent<__Path__>>::Delegate::name(__context__) - } - } - impl< - __Context__, - __Components__, - __Path__, - > IsProviderFor - for RedirectLookup<__Components__, __Path__> - where - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent< - __Path__, - >>::Delegate: IsProviderFor - + NameGetter<__Context__>, - {} - impl<__Context__> NameGetter<__Context__> for UseFields - where - __Context__: HasField< - Symbol<4, Chars<'n', Chars<'a', Chars<'m', Chars<'e', Nil>>>>>, - Value = String, - >, - { - fn name(__context__: &__Context__) -> &str { - __context__ - .get_field( - ::core::marker::PhantomData::< - Symbol<4, Chars<'n', Chars<'a', Chars<'m', Chars<'e', Nil>>>>>, - >, - ) - .as_str() - } - } - impl<__Context__> IsProviderFor for UseFields - where - __Context__: HasField< - Symbol<4, Chars<'n', Chars<'a', Chars<'m', Chars<'e', Nil>>>>>, - Value = String, - >, - {} - impl<__Context__, __Tag__> NameGetter<__Context__> for UseField<__Tag__> - where - __Context__: HasField<__Tag__, Value = String>, - { - fn name(__context__: &__Context__) -> &str { - __context__.get_field(::core::marker::PhantomData::<__Tag__>).as_str() - } - } - impl<__Context__, __Tag__> IsProviderFor - for UseField<__Tag__> - where - __Context__: HasField<__Tag__, Value = String>, - {} - impl<__Context__, __Provider__> NameGetter<__Context__> for WithProvider<__Provider__> - where - __Provider__: FieldGetter<__Context__, NameGetterComponent, Value = String>, - { - fn name(__context__: &__Context__) -> &str { - __Provider__::get_field( - __context__, - ::core::marker::PhantomData::, - ) - .as_str() - } - } - impl<__Context__, __Provider__> IsProviderFor - for WithProvider<__Provider__> - where - __Provider__: FieldGetter<__Context__, NameGetterComponent, Value = String>, - {} - ") - } - } - - snapshot_delegate_and_check_components! { - delegate_and_check_components! { - MyContext { - NameGetterComponent: WithProvider< - ChainGetters, - UseField, - UseField, - UseField, - UseField - ]>> - } - } - - expand_my_context(output) { - insta::assert_snapshot!(output, @r#" - impl DelegateComponent for MyContext { - type Delegate = WithProvider< - ChainGetters< - Product![ - UseField < Symbol!("a") >, UseField < Symbol!("b") >, UseField < - Symbol!("c") >, UseField < Symbol!("d") >, UseField < Symbol!("name") > - ], - >, - >; - } - impl<__Context__, __Params__> IsProviderFor - for MyContext - where - WithProvider< - ChainGetters< - Product![ - UseField < Symbol!("a") >, UseField < Symbol!("b") >, UseField < - Symbol!("c") >, UseField < Symbol!("d") >, UseField < Symbol!("name") > - ], - >, - >: IsProviderFor, - {} - trait __CanUseMyContext< - __Component__, - __Params__: ?Sized, - >: CanUseComponent<__Component__, __Params__> {} - impl __CanUseMyContext for MyContext {} - "#) - } - } - - #[test] - fn test_deeply_nested_getter() { - let context = MyContext { - a: A { - b: B { - c: C { - d: D { - name: "test".to_owned(), - }, - }, - }, - }, - }; - - assert_eq!(context.name(), "test"); - } -} diff --git a/crates/tests/cgp-tests/tests/extensible_data_tests/has_field/life.rs b/crates/tests/cgp-tests/tests/extensible_data_tests/has_field/life.rs deleted file mode 100644 index 3dbf25f4..00000000 --- a/crates/tests/cgp-tests/tests/extensible_data_tests/has_field/life.rs +++ /dev/null @@ -1,48 +0,0 @@ -mod context_with_lifetime_field { - use core::marker::PhantomData; - - use cgp::prelude::*; - use cgp_macro_test_util::snapshot_derive_has_field; - - snapshot_derive_has_field! { - #[derive(HasField)] - pub struct Context<'a> { - pub name: &'a str, - } - - expand_context(output) { - insta::assert_snapshot!(output, @" - impl<'a> HasField>>>>> - for Context<'a> { - type Value = &'a str; - fn get_field( - &self, - key: ::core::marker::PhantomData< - Symbol<4, Chars<'n', Chars<'a', Chars<'m', Chars<'e', Nil>>>>>, - >, - ) -> &Self::Value { - &self.name - } - } - impl<'a> HasFieldMut>>>>> - for Context<'a> { - fn get_field_mut( - &mut self, - key: ::core::marker::PhantomData< - Symbol<4, Chars<'n', Chars<'a', Chars<'m', Chars<'e', Nil>>>>>, - >, - ) -> &mut Self::Value { - &mut self.name - } - } - ") - } - } - - #[test] - fn test_context_with_lifetime_field() { - let context = Context { name: "test" }; - - assert_eq!(context.get_field(PhantomData), &"test"); - } -} diff --git a/crates/tests/cgp-tests/tests/extensible_data_tests/has_field/mod.rs b/crates/tests/cgp-tests/tests/extensible_data_tests/has_field/mod.rs deleted file mode 100644 index 54d09ba3..00000000 --- a/crates/tests/cgp-tests/tests/extensible_data_tests/has_field/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod chain; -pub mod index; -pub mod life; diff --git a/crates/tests/cgp-tests/tests/extensible_data_tests/has_fields/enum_fields.rs b/crates/tests/cgp-tests/tests/extensible_data_tests/has_fields/enum_fields.rs deleted file mode 100644 index 074818b1..00000000 --- a/crates/tests/cgp-tests/tests/extensible_data_tests/has_fields/enum_fields.rs +++ /dev/null @@ -1,323 +0,0 @@ -pub mod simple_enum { - use cgp::prelude::*; - use cgp_macro_test_util::snapshot_derive_has_fields; - - snapshot_derive_has_fields! { - #[derive(HasFields)] - #[derive(Clone, Debug, Eq, PartialEq)] - pub enum Person { - Anonymous(u32), - Named(String), - } - - expand_person(output) { - insta::assert_snapshot!(output, @" - impl HasFields for Person { - type Fields = Either< - Field< - Symbol< - 9, - Chars< - 'A', - Chars< - 'n', - Chars< - 'o', - Chars< - 'n', - Chars< - 'y', - Chars<'m', Chars<'o', Chars<'u', Chars<'s', Nil>>>>, - >, - >, - >, - >, - >, - >, - u32, - >, - Either< - Field< - Symbol< - 5, - Chars<'N', Chars<'a', Chars<'m', Chars<'e', Chars<'d', Nil>>>>>, - >, - String, - >, - Void, - >, - >; - } - impl HasFieldsRef for Person { - type FieldsRef<'__a> = Either< - Field< - Symbol< - 9, - Chars< - 'A', - Chars< - 'n', - Chars< - 'o', - Chars< - 'n', - Chars< - 'y', - Chars<'m', Chars<'o', Chars<'u', Chars<'s', Nil>>>>, - >, - >, - >, - >, - >, - >, - &'__a u32, - >, - Either< - Field< - Symbol< - 5, - Chars<'N', Chars<'a', Chars<'m', Chars<'e', Chars<'d', Nil>>>>>, - >, - &'__a String, - >, - Void, - >, - > - where - Self: '__a; - } - impl FromFields for Person { - fn from_fields(rest: Self::Fields) -> Self { - match rest { - Either::Left(field) => { - let field = field.value; - Self::Anonymous(field) - } - Either::Right(rest) => { - match rest { - Either::Left(field) => { - let field = field.value; - Self::Named(field) - } - Either::Right(rest) => match rest {} - } - } - } - } - } - impl ToFields for Person { - fn to_fields(self) -> Self::Fields { - match self { - Self::Anonymous(field) => Either::Left(field.into()), - Self::Named(field) => Either::Right(Either::Left(field.into())), - } - } - } - impl ToFieldsRef for Person { - fn to_fields_ref<'__a>(&'__a self) -> Self::FieldsRef<'__a> - where - Self: '__a, - { - match self { - Self::Anonymous(field) => Either::Left(field.into()), - Self::Named(field) => Either::Right(Either::Left(field.into())), - } - } - } - ") - } - } - - #[test] - fn test_simple_enum() { - { - let person_a1 = Person::Anonymous(42); - - let person_a2 = person_a1.clone().to_fields(); - assert_eq!(person_a2, Either::Left(42.into())); - - let person_a3 = Person::from_fields(person_a2); - assert_eq!(person_a3, person_a1); - - let person_a4 = person_a1.to_fields_ref(); - assert_eq!(person_a4, Either::Left((&42).into())); - } - - { - let name = "Alice".to_owned(); - - let person_b1 = Person::Named(name.clone()); - - let person_b2 = person_b1.clone().to_fields(); - assert_eq!(person_b2, Either::Right(Either::Left(name.clone().into()))); - - let person_b3 = Person::from_fields(person_b2); - assert_eq!(person_b3, person_b1); - - let person_b4 = person_b1.to_fields_ref(); - assert_eq!(person_b4, Either::Right(Either::Left((&name).into()))); - } - } -} - -pub mod generic_enum { - use cgp::prelude::*; - use cgp_macro_test_util::snapshot_derive_has_fields; - - snapshot_derive_has_fields! { - #[derive(HasFields)] - #[derive(Clone, Debug, Eq, PartialEq)] - pub enum Person<'a, Name> { - Anonymous(u32), - Named(&'a Name), - } - - expand_person(output) { - insta::assert_snapshot!(output, @" - impl<'a, Name> HasFields for Person<'a, Name> { - type Fields = Either< - Field< - Symbol< - 9, - Chars< - 'A', - Chars< - 'n', - Chars< - 'o', - Chars< - 'n', - Chars< - 'y', - Chars<'m', Chars<'o', Chars<'u', Chars<'s', Nil>>>>, - >, - >, - >, - >, - >, - >, - u32, - >, - Either< - Field< - Symbol< - 5, - Chars<'N', Chars<'a', Chars<'m', Chars<'e', Chars<'d', Nil>>>>>, - >, - &'a Name, - >, - Void, - >, - >; - } - impl<'a, Name> HasFieldsRef for Person<'a, Name> { - type FieldsRef<'__a> = Either< - Field< - Symbol< - 9, - Chars< - 'A', - Chars< - 'n', - Chars< - 'o', - Chars< - 'n', - Chars< - 'y', - Chars<'m', Chars<'o', Chars<'u', Chars<'s', Nil>>>>, - >, - >, - >, - >, - >, - >, - &'__a u32, - >, - Either< - Field< - Symbol< - 5, - Chars<'N', Chars<'a', Chars<'m', Chars<'e', Chars<'d', Nil>>>>>, - >, - &'__a &'a Name, - >, - Void, - >, - > - where - Self: '__a; - } - impl<'a, Name> FromFields for Person<'a, Name> { - fn from_fields(rest: Self::Fields) -> Self { - match rest { - Either::Left(field) => { - let field = field.value; - Self::Anonymous(field) - } - Either::Right(rest) => { - match rest { - Either::Left(field) => { - let field = field.value; - Self::Named(field) - } - Either::Right(rest) => match rest {} - } - } - } - } - } - impl<'a, Name> ToFields for Person<'a, Name> { - fn to_fields(self) -> Self::Fields { - match self { - Self::Anonymous(field) => Either::Left(field.into()), - Self::Named(field) => Either::Right(Either::Left(field.into())), - } - } - } - impl<'a, Name> ToFieldsRef for Person<'a, Name> { - fn to_fields_ref<'__a>(&'__a self) -> Self::FieldsRef<'__a> - where - Self: '__a, - { - match self { - Self::Anonymous(field) => Either::Left(field.into()), - Self::Named(field) => Either::Right(Either::Left(field.into())), - } - } - } - ") - } - } - - #[test] - fn test_generic_enum() { - { - let person_a1: Person = Person::Anonymous(42); - - let person_a2 = person_a1.clone().to_fields(); - assert_eq!(person_a2, Either::Left(42.into())); - - let person_a3 = Person::from_fields(person_a2); - assert_eq!(person_a3, person_a1); - - let person_a4 = person_a1.to_fields_ref(); - assert_eq!(person_a4, Either::Left((&42).into())); - } - - { - let name = "Alice".to_owned(); - - let person_b1 = Person::Named(&name); - - let person_b2 = person_b1.clone().to_fields(); - assert_eq!(person_b2, Either::Right(Either::Left((&name).into()))); - - let person_b3 = Person::from_fields(person_b2); - assert_eq!(person_b3, person_b1); - - let person_b4 = person_b1.to_fields_ref(); - assert_eq!(person_b4, Either::Right(Either::Left((&&name).into()))); - } - } -} diff --git a/crates/tests/cgp-tests/tests/extensible_data_tests/has_fields/mod.rs b/crates/tests/cgp-tests/tests/extensible_data_tests/has_fields/mod.rs deleted file mode 100644 index eaa970f0..00000000 --- a/crates/tests/cgp-tests/tests/extensible_data_tests/has_fields/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -mod enum_fields; -mod struct_fields; diff --git a/crates/tests/cgp-tests/tests/extensible_data_tests/has_fields/struct_fields.rs b/crates/tests/cgp-tests/tests/extensible_data_tests/has_fields/struct_fields.rs deleted file mode 100644 index adcb2dc1..00000000 --- a/crates/tests/cgp-tests/tests/extensible_data_tests/has_fields/struct_fields.rs +++ /dev/null @@ -1,462 +0,0 @@ -pub mod single_name_field { - use cgp::prelude::*; - use cgp_macro_test_util::snapshot_derive_has_fields; - - snapshot_derive_has_fields! { - #[derive(HasFields)] - #[derive(Clone, Debug, Eq, PartialEq)] - pub struct Person { - pub name: String, - } - - expand_person(output) { - insta::assert_snapshot!(output, @" - impl HasFields for Person { - type Fields = Cons< - Field>>>>, String>, - Nil, - >; - } - impl HasFieldsRef for Person { - type FieldsRef<'__a> = Cons< - Field< - Symbol<4, Chars<'n', Chars<'a', Chars<'m', Chars<'e', Nil>>>>>, - &'__a String, - >, - Nil, - > - where - Self: '__a; - } - impl FromFields for Person { - fn from_fields(Cons(name, Nil): Self::Fields) -> Self { - Self { name: name.value } - } - } - impl ToFields for Person { - fn to_fields(self) -> Self::Fields { - Cons(self.name.into(), Nil) - } - } - impl ToFieldsRef for Person { - fn to_fields_ref<'__a>(&'__a self) -> Self::FieldsRef<'__a> - where - Self: '__a, - { - Cons((&self.name).into(), Nil) - } - } - ") - } - } - - #[test] - fn test_single_named_field() { - let name = "Alice".to_owned(); - - let person1 = Person { name: name.clone() }; - - let product = person1.clone().to_fields(); - assert_eq!(product, Cons(name.clone().into(), Nil)); - - let product_ref = person1.to_fields_ref(); - assert_eq!(product_ref, Cons((&name).into(), Nil)); - - let person2 = Person::from_fields(product); - - assert_eq!(person1, person2); - } -} - -pub mod two_named_field { - use cgp::prelude::*; - use cgp_macro_test_util::snapshot_derive_has_fields; - - snapshot_derive_has_fields! { - #[derive(HasFields)] - #[derive(Clone, Debug, Eq, PartialEq)] - pub struct Person { - pub name: String, - pub age: u8, - } - - expand_person(output) { - insta::assert_snapshot!(output, @" - impl HasFields for Person { - type Fields = Cons< - Field>>>>, String>, - Cons>>>, u8>, Nil>, - >; - } - impl HasFieldsRef for Person { - type FieldsRef<'__a> = Cons< - Field< - Symbol<4, Chars<'n', Chars<'a', Chars<'m', Chars<'e', Nil>>>>>, - &'__a String, - >, - Cons>>>, &'__a u8>, Nil>, - > - where - Self: '__a; - } - impl FromFields for Person { - fn from_fields(Cons(name, Cons(age, Nil)): Self::Fields) -> Self { - Self { - name: name.value, - age: age.value, - } - } - } - impl ToFields for Person { - fn to_fields(self) -> Self::Fields { - Cons(self.name.into(), Cons(self.age.into(), Nil)) - } - } - impl ToFieldsRef for Person { - fn to_fields_ref<'__a>(&'__a self) -> Self::FieldsRef<'__a> - where - Self: '__a, - { - Cons((&self.name).into(), Cons((&self.age).into(), Nil)) - } - } - ") - } - } - - #[test] - fn test_two_named_field() { - let name = "Alice".to_owned(); - - let person1 = Person { - name: name.clone(), - age: 32, - }; - - let product = person1.clone().to_fields(); - assert_eq!(product, Cons(name.clone().into(), Cons(32.into(), Nil))); - - let product_ref = person1.to_fields_ref(); - assert_eq!(product_ref, Cons((&name).into(), Cons((&32).into(), Nil))); - - let person2 = Person::from_fields(product); - - assert_eq!(person1, person2); - } -} - -pub mod generic_struct { - use core::fmt::Display; - - use cgp::prelude::*; - use cgp_macro_test_util::snapshot_derive_has_fields; - - snapshot_derive_has_fields! { - #[derive(HasFields)] - #[derive(Clone, Debug, Eq, PartialEq)] - pub struct Person - where - Name: Display, - { - pub name: Name, - pub age: u8, - } - - expand_person(output) { - insta::assert_snapshot!(output, @" - impl HasFields for Person - where - Name: Display, - { - type Fields = Cons< - Field>>>>, Name>, - Cons>>>, u8>, Nil>, - >; - } - impl HasFieldsRef for Person - where - Name: Display, - { - type FieldsRef<'__a> = Cons< - Field< - Symbol<4, Chars<'n', Chars<'a', Chars<'m', Chars<'e', Nil>>>>>, - &'__a Name, - >, - Cons>>>, &'__a u8>, Nil>, - > - where - Self: '__a; - } - impl FromFields for Person - where - Name: Display, - { - fn from_fields(Cons(name, Cons(age, Nil)): Self::Fields) -> Self { - Self { - name: name.value, - age: age.value, - } - } - } - impl ToFields for Person - where - Name: Display, - { - fn to_fields(self) -> Self::Fields { - Cons(self.name.into(), Cons(self.age.into(), Nil)) - } - } - impl ToFieldsRef for Person - where - Name: Display, - { - fn to_fields_ref<'__a>(&'__a self) -> Self::FieldsRef<'__a> - where - Self: '__a, - { - Cons((&self.name).into(), Cons((&self.age).into(), Nil)) - } - } - ") - } - } - - #[test] - fn test_generic_struct() { - let name = "Alice".to_owned(); - - let person1 = Person { - name: name.clone(), - age: 32, - }; - - let product = person1.clone().to_fields(); - assert_eq!(product, Cons(name.clone().into(), Cons(32.into(), Nil))); - - let product_ref = person1.to_fields_ref(); - assert_eq!(product_ref, Cons((&name).into(), Cons((&32).into(), Nil))); - - let person2 = Person::from_fields(product); - - assert_eq!(person1, person2); - } -} - -pub mod generic_lifetime_struct { - use core::fmt::Display; - - use cgp::prelude::*; - use cgp_macro_test_util::snapshot_derive_has_fields; - - snapshot_derive_has_fields! { - #[derive(HasFields)] - #[derive(Clone, Debug, Eq, PartialEq)] - pub struct Person<'a, Name> - where - Name: Display, - { - pub name: &'a Name, - pub age: &'a u8, - } - - expand_person(output) { - insta::assert_snapshot!(output, @" - impl<'a, Name> HasFields for Person<'a, Name> - where - Name: Display, - { - type Fields = Cons< - Field>>>>, &'a Name>, - Cons>>>, &'a u8>, Nil>, - >; - } - impl<'a, Name> HasFieldsRef for Person<'a, Name> - where - Name: Display, - { - type FieldsRef<'__a> = Cons< - Field< - Symbol<4, Chars<'n', Chars<'a', Chars<'m', Chars<'e', Nil>>>>>, - &'__a &'a Name, - >, - Cons< - Field>>>, &'__a &'a u8>, - Nil, - >, - > - where - Self: '__a; - } - impl<'a, Name> FromFields for Person<'a, Name> - where - Name: Display, - { - fn from_fields(Cons(name, Cons(age, Nil)): Self::Fields) -> Self { - Self { - name: name.value, - age: age.value, - } - } - } - impl<'a, Name> ToFields for Person<'a, Name> - where - Name: Display, - { - fn to_fields(self) -> Self::Fields { - Cons(self.name.into(), Cons(self.age.into(), Nil)) - } - } - impl<'a, Name> ToFieldsRef for Person<'a, Name> - where - Name: Display, - { - fn to_fields_ref<'__a>(&'__a self) -> Self::FieldsRef<'__a> - where - Self: '__a, - { - Cons((&self.name).into(), Cons((&self.age).into(), Nil)) - } - } - ") - } - } - - #[test] - fn test_generic_lifetime_struct() { - let name = "Alice".to_owned(); - - let person1 = Person { - name: &name, - age: &32, - }; - - let product = person1.clone().to_fields(); - assert_eq!(product, Cons((&name).into(), Cons((&32).into(), Nil))); - - let product_ref = person1.to_fields_ref(); - assert_eq!(product_ref, Cons((&&name).into(), Cons((&&32).into(), Nil))); - - let person2 = Person::from_fields(product); - - assert_eq!(person1, person2); - } -} - -pub mod single_unnamed_field { - use cgp::prelude::*; - use cgp_macro_test_util::snapshot_derive_has_fields; - - snapshot_derive_has_fields! { - #[derive(HasFields)] - #[derive(Clone, Debug, Eq, PartialEq)] - pub struct Person(String); - - expand_person(output) { - insta::assert_snapshot!(output, @" - impl HasFields for Person { - type Fields = String; - } - impl HasFieldsRef for Person { - type FieldsRef<'__a> = &'__a String where Self: '__a; - } - impl FromFields for Person { - fn from_fields(field: Self::Fields) -> Self { - Self(field) - } - } - impl ToFields for Person { - fn to_fields(self) -> Self::Fields { - self.0 - } - } - impl ToFieldsRef for Person { - fn to_fields_ref<'__a>(&'__a self) -> Self::FieldsRef<'__a> - where - Self: '__a, - { - &self.0 - } - } - ") - } - } - - #[test] - fn test_single_unnamed_field() { - let name = "Alice".to_owned(); - - let person1 = Person(name.clone()); - - let product = person1.clone().to_fields(); - assert_eq!(product, name.clone()); - - let product_ref = person1.to_fields_ref(); - assert_eq!(product_ref, &name); - - let person2 = Person::from_fields(product); - - assert_eq!(person1, person2); - } -} - -pub mod single_unnamed_multi_field { - use cgp::prelude::*; - use cgp_macro_test_util::snapshot_derive_has_fields; - - snapshot_derive_has_fields! { - #[derive(HasFields)] - #[derive(Clone, Debug, Eq, PartialEq)] - pub struct Person(String, u8); - - expand_person(output) { - insta::assert_snapshot!(output, @" - impl HasFields for Person { - type Fields = Cons, String>, Cons, u8>, Nil>>; - } - impl HasFieldsRef for Person { - type FieldsRef<'__a> = Cons< - Field, &'__a String>, - Cons, &'__a u8>, Nil>, - > - where - Self: '__a; - } - impl FromFields for Person { - fn from_fields(Cons(field_1, Cons(field_0, Nil)): Self::Fields) -> Self { - Self(field_1.value, field_0.value) - } - } - impl ToFields for Person { - fn to_fields(self) -> Self::Fields { - Cons(self.0.into(), Cons(self.1.into(), Nil)) - } - } - impl ToFieldsRef for Person { - fn to_fields_ref<'__a>(&'__a self) -> Self::FieldsRef<'__a> - where - Self: '__a, - { - Cons((&self.0).into(), Cons((&self.1).into(), Nil)) - } - } - ") - } - } - - #[test] - fn test_single_unnamed_multi_field() { - let name = "Alice".to_owned(); - - let person1 = Person(name.clone(), 32); - - let product = person1.clone().to_fields(); - assert_eq!(product, Cons(name.clone().into(), Cons(32.into(), Nil))); - - let product_ref = person1.to_fields_ref(); - assert_eq!(product_ref, Cons((&name).into(), Cons((&32).into(), Nil))); - - let person2 = Person::from_fields(product); - - assert_eq!(person1, person2); - } -} diff --git a/crates/tests/cgp-tests/tests/extensible_data_tests/mod.rs b/crates/tests/cgp-tests/tests/extensible_data_tests/mod.rs deleted file mode 100644 index f955c1f3..00000000 --- a/crates/tests/cgp-tests/tests/extensible_data_tests/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub mod has_field; -pub mod has_fields; -pub mod records; -pub mod variants; diff --git a/crates/tests/cgp-tests/tests/extensible_data_tests/records/mod.rs b/crates/tests/cgp-tests/tests/extensible_data_tests/records/mod.rs deleted file mode 100644 index 72fef815..00000000 --- a/crates/tests/cgp-tests/tests/extensible_data_tests/records/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -pub mod basic; -pub mod generics; -pub mod index; -pub mod optional; -pub mod person; -pub mod point; diff --git a/crates/tests/cgp-tests/tests/extensible_data_tests/variants/mod.rs b/crates/tests/cgp-tests/tests/extensible_data_tests/variants/mod.rs deleted file mode 100644 index 4bd634a1..00000000 --- a/crates/tests/cgp-tests/tests/extensible_data_tests/variants/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub mod basic; -pub mod generic; -pub mod shape; -pub mod shape_ref; diff --git a/crates/tests/cgp-tests/tests/extensible_data_tests/records/generics.rs b/crates/tests/cgp-tests/tests/extensible_records/generic_record.rs similarity index 96% rename from crates/tests/cgp-tests/tests/extensible_data_tests/records/generics.rs rename to crates/tests/cgp-tests/tests/extensible_records/generic_record.rs index 8c08ff53..ff3c0b42 100644 --- a/crates/tests/cgp-tests/tests/extensible_data_tests/records/generics.rs +++ b/crates/tests/cgp-tests/tests/extensible_records/generic_record.rs @@ -1,3 +1,12 @@ +//! `#[derive(CgpData)]` on a *generic* record with a `where` clause. +//! +//! Every generated impl (field access, field lists, the `__Partial…` builder, +//! and its `UpdateField`/`HasField` impls) carries the struct's generic +//! parameters and forwards its `where Foo: Clone` bound, so the derive works on +//! parameterized records just as on concrete ones. +//! +//! See docs/reference/derives/derive_cgp_data.md. + use cgp_macro_test_util::snapshot_derive_cgp_data; snapshot_derive_cgp_data! { diff --git a/crates/tests/cgp-tests/tests/extensible_records/mod.rs b/crates/tests/cgp-tests/tests/extensible_records/mod.rs new file mode 100644 index 00000000..efd4b191 --- /dev/null +++ b/crates/tests/cgp-tests/tests/extensible_records/mod.rs @@ -0,0 +1,27 @@ +//! One unit test per file. Each file is self-contained: it defines its own +//! record types, builders, and context wiring at module scope so that the +//! type-level machinery of one test never leaks into another. + +// `#[derive(CgpData)]` on structs (this concept owns the derive's expansion): +// the full record spine — field access, field lists, and the extensible builder. +pub mod generic_record; +pub mod optional_builder; +pub mod person_record; +pub mod point_cast; +pub mod record_derive; +pub mod tuple_record; + +// Behavioral record building: assembling a record from other records and from +// handler pipelines. These reuse `#[derive(CgpData)]` as plain scaffolding — +// the derive expansion is already pinned by `record_derive`. +pub mod record_build_from; +pub mod record_build_with_handlers; + +// `#[derive(HasFields)]` on structs (this concept owns the derive's expansion): +// deriving only the field list, across named/tuple/generic/lifetime shapes. +pub mod struct_generic; +pub mod struct_generic_lifetime; +pub mod struct_single_named_field; +pub mod struct_single_unnamed_field; +pub mod struct_tuple_fields; +pub mod struct_two_named_fields; diff --git a/crates/tests/cgp-tests/tests/extensible_data_tests/records/optional.rs b/crates/tests/cgp-tests/tests/extensible_records/optional_builder.rs similarity index 91% rename from crates/tests/cgp-tests/tests/extensible_data_tests/records/optional.rs rename to crates/tests/cgp-tests/tests/extensible_records/optional_builder.rs index 4c329a81..539a6e7a 100644 --- a/crates/tests/cgp-tests/tests/extensible_data_tests/records/optional.rs +++ b/crates/tests/cgp-tests/tests/extensible_records/optional_builder.rs @@ -1,3 +1,18 @@ +//! The *optional* builder variant of the extensible builder pattern. +//! +//! Besides the required-field builder pinned by `record_derive`, a `CgpData` +//! record also supports an `optional_builder()` where fields start absent and are +//! filled with `set`. `set` returns any previously-set value, `finalize_optional` +//! succeeds only once every field is present, and `finalize_with_default` fills +//! any still-absent field with its `Default`. +//! +//! The snapshot in this file is the `#[derive(CgpData)]` expansion for the +//! two-field record it exercises; the derive expansion itself is owned by this +//! concept. +//! +//! See docs/reference/traits/has_builder.md and +//! docs/concepts/extensible-records.md. + use cgp::extra::field::impls::{ CanFinalizeWithDefault, FinalizeOptional, HasOptionalBuilder, SetOptional, }; diff --git a/crates/tests/cgp-tests/tests/extensible_data_tests/records/person.rs b/crates/tests/cgp-tests/tests/extensible_records/person_record.rs similarity index 96% rename from crates/tests/cgp-tests/tests/extensible_data_tests/records/person.rs rename to crates/tests/cgp-tests/tests/extensible_records/person_record.rs index e032058d..0919eb96 100644 --- a/crates/tests/cgp-tests/tests/extensible_data_tests/records/person.rs +++ b/crates/tests/cgp-tests/tests/extensible_records/person_record.rs @@ -1,5 +1,24 @@ +//! `#[derive(CgpData)]` with multi-character field names, and building a larger +//! record from a smaller one and from a handler pipeline. +//! +//! The snapshot pins how a field name longer than a few characters +//! (`first_name`, `last_name`) expands into its `Symbol>` spine — +//! the leading `N` is the length. The runtime tests then show the extensible +//! builder absorbing a `Person` into an `Employee` builder (via `build_from`), +//! filling the remaining field either explicitly (`build_field`) or from another +//! record, and driving the same build through a `BuildWithHandlers` pipeline. +//! +//! `Person` owns the snapshot here; `Employee`/`EmployeeId` are plain `CgpData` +//! fixtures whose expansion is already pinned by `record_derive`. +//! +//! See docs/reference/derives/derive_cgp_data.md and +//! docs/reference/traits/has_builder.md. + +use core::marker::PhantomData; + use cgp::core::field::impls::CanBuildFrom; use cgp::extra::dispatch::{BuildAndMerge, BuildAndSetField, BuildWithHandlers}; +use cgp::extra::handler::Computer; use cgp::prelude::*; use cgp_macro_test_util::snapshot_derive_cgp_data; diff --git a/crates/tests/cgp-tests/tests/extensible_data_tests/records/point.rs b/crates/tests/cgp-tests/tests/extensible_records/point_cast.rs similarity index 92% rename from crates/tests/cgp-tests/tests/extensible_data_tests/records/point.rs rename to crates/tests/cgp-tests/tests/extensible_records/point_cast.rs index 26db2aee..660df89e 100644 --- a/crates/tests/cgp-tests/tests/extensible_data_tests/records/point.rs +++ b/crates/tests/cgp-tests/tests/extensible_records/point_cast.rs @@ -1,3 +1,14 @@ +//! Structural record casts with `build_with_default`. +//! +//! `build_with_default` builds a larger record from a smaller one, filling every +//! field the source lacks with its `Default` — so a `Point2d` casts up into a +//! `Point3d`/`Point4d` with the extra coordinates zeroed. The snapshot pins the +//! `#[derive(CgpData)]` expansion for the private `Point2d` (this concept owns +//! the derive's expansion); `Point3d`/`Point4d` are plain `CgpData` fixtures. +//! +//! See docs/concepts/extensible-records.md and +//! docs/reference/derives/derive_cgp_data.md. + use cgp::extra::field::impls::CanBuildWithDefault; use cgp::prelude::*; use cgp_macro_test_util::snapshot_derive_cgp_data; diff --git a/crates/tests/cgp-tests/tests/extensible_records/record_build_from.rs b/crates/tests/cgp-tests/tests/extensible_records/record_build_from.rs new file mode 100644 index 00000000..e9568a7a --- /dev/null +++ b/crates/tests/cgp-tests/tests/extensible_records/record_build_from.rs @@ -0,0 +1,53 @@ +//! Building a record from other records with `build_from`. +//! +//! The extensible builder can absorb a whole smaller record into the partial +//! build in one step: `build_from(foo_bar)` merges every field of `FooBar` into +//! the `FooBarBaz` builder, and `build_from(baz)` merges the remaining field, so +//! once all fields are present `finalize_build` yields the target. This relies on +//! `CanBuildFrom`, which rebuilds a record from a superset of its fields. +//! +//! `#[derive(CgpData)]` here is plain scaffolding; its full expansion is pinned +//! by `record_derive`. +//! +//! See docs/reference/traits/has_builder.md and +//! docs/concepts/extensible-records.md. + +use cgp::core::field::impls::CanBuildFrom; +use cgp::prelude::*; + +#[derive(Debug, Eq, PartialEq, CgpData)] +pub struct FooBarBaz { + pub foo: u64, + pub bar: String, + pub baz: bool, +} + +#[derive(Debug, Eq, PartialEq, CgpData)] +pub struct FooBar { + pub foo: u64, + pub bar: String, +} + +#[derive(Debug, Eq, PartialEq, CgpData)] +pub struct Baz { + pub baz: bool, +} + +#[test] +fn test_build_from() { + let foo_bar = FooBar { + foo: 1, + bar: "bar".to_owned(), + }; + + let baz = Baz { baz: true }; + + let foo_bar_baz: FooBarBaz = FooBarBaz::builder() + .build_from(foo_bar) + .build_from(baz) + .finalize_build(); + + assert_eq!(foo_bar_baz.foo, 1); + assert_eq!(foo_bar_baz.bar, "bar"); + assert!(foo_bar_baz.baz); +} diff --git a/crates/tests/cgp-tests/tests/extensible_records/record_build_with_handlers.rs b/crates/tests/cgp-tests/tests/extensible_records/record_build_with_handlers.rs new file mode 100644 index 00000000..6ecef03d --- /dev/null +++ b/crates/tests/cgp-tests/tests/extensible_records/record_build_with_handlers.rs @@ -0,0 +1,124 @@ +//! Building a record from a pipeline of handlers with `BuildWithHandlers`. +//! +//! Each field (or a merged sub-record) is produced by its own `#[cgp_producer]` +//! and slotted into the target with `BuildAndSetField` / `BuildAndMerge`; a +//! `Product![...]` of these handlers drives the whole record build, run through +//! either `compute` or `try_compute`. The order of the handlers in the product +//! is independent of field order. +//! +//! `#[derive(CgpData)]` here is plain scaffolding; its full expansion is pinned +//! by `record_derive`. The `delegate_components!` wiring is likewise incidental +//! (it only fixes the context's error type); its expansion is owned by +//! `basic_delegation`. +//! +//! See docs/concepts/extensible-records.md and +//! docs/reference/traits/has_builder.md. + +use core::convert::Infallible; +use std::marker::PhantomData; + +use cgp::core::error::ErrorTypeProviderComponent; +use cgp::extra::dispatch::{BuildAndMerge, BuildAndSetField, BuildWithHandlers}; +use cgp::extra::handler::Computer; +use cgp::prelude::*; + +#[derive(Debug, Eq, PartialEq, CgpData)] +pub struct FooBarBaz { + pub foo: u64, + pub bar: String, + pub baz: bool, +} + +#[derive(Debug, Eq, PartialEq, CgpData)] +pub struct FooBar { + pub foo: u64, + pub bar: String, +} + +#[cgp_producer] +fn build_foo_bar() -> FooBar { + FooBar { + foo: 1, + bar: "bar".to_owned(), + } +} + +#[cgp_producer] +pub fn build_foo() -> u64 { + 1 +} + +#[cgp_producer] +pub fn build_bar() -> String { + "bar".to_owned() +} + +#[cgp_producer(BuildBaz)] +pub fn build_baz() -> bool { + true +} + +pub struct App; + +delegate_components! { + App { + ErrorTypeProviderComponent: UseType, + } +} + +#[test] +fn test_build_with_handlers() { + let context = App; + let code = PhantomData::<()>; + + pub type Handlers = + Product![BuildAndMerge, BuildAndSetField]; + + assert_eq!( + BuildWithHandlers::::compute(&context, code, ()), + FooBarBaz { + foo: 1, + bar: "bar".to_owned(), + baz: true, + } + ); + + assert_eq!( + BuildWithHandlers::::try_compute(&context, code, ()), + Ok(FooBarBaz { + foo: 1, + bar: "bar".to_owned(), + baz: true, + }) + ); +} + +#[test] +fn test_build_with_fields() { + let context = App; + let code = PhantomData::<()>; + + pub type Handlers = Product![ + BuildAndSetField, + BuildAndSetField, + BuildAndSetField, + ]; + + assert_eq!( + BuildWithHandlers::::compute(&context, code, ()), + FooBarBaz { + foo: 1, + bar: "bar".to_owned(), + baz: true, + } + ); + + assert_eq!( + BuildWithHandlers::::try_compute(&context, code, ()), + Ok(FooBarBaz { + foo: 1, + bar: "bar".to_owned(), + baz: true, + }) + ); +} diff --git a/crates/tests/cgp-tests/tests/extensible_data_tests/records/basic.rs b/crates/tests/cgp-tests/tests/extensible_records/record_derive.rs similarity index 77% rename from crates/tests/cgp-tests/tests/extensible_data_tests/records/basic.rs rename to crates/tests/cgp-tests/tests/extensible_records/record_derive.rs index 01693cd6..35252c09 100644 --- a/crates/tests/cgp-tests/tests/extensible_data_tests/records/basic.rs +++ b/crates/tests/cgp-tests/tests/extensible_records/record_derive.rs @@ -1,12 +1,23 @@ -use core::convert::Infallible; -use std::marker::PhantomData; +//! Canonical expansion of `#[derive(CgpData)]` for a record (named-field struct). +//! +//! `#[derive(CgpData)]` derives the whole extensible-record spine at once: a +//! `HasField`/`HasFieldMut` impl per field, `HasFields`/`HasFieldsRef` exposing +//! the type-level field list, the `FromFields`/`ToFields`/`ToFieldsRef` +//! conversions, and a generated `__Partial…` builder type that powers the +//! extensible builder pattern (`HasBuilder`/`IntoBuilder`, `FinalizeBuild`, +//! `UpdateField`). This is the reference snapshot for that expansion; other +//! record tests reuse `#[derive(CgpData)]` without re-snapshotting it. +//! +//! The runtime test exercises the builder: start from `builder()`, set each +//! field with `build_field`, then `finalize_build` once every field is present. +//! +//! See docs/reference/derives/derive_cgp_data.md and +//! docs/reference/traits/has_builder.md. + +use core::marker::PhantomData; -use cgp::core::error::ErrorTypeProviderComponent; -use cgp::core::field::impls::CanBuildFrom; -use cgp::extra::dispatch::{BuildAndMerge, BuildAndSetField, BuildWithHandlers}; -use cgp::extra::handler::{Computer, Producer, ProducerComponent}; use cgp::prelude::*; -use cgp_macro_test_util::{snapshot_delegate_components, snapshot_derive_cgp_data}; +use cgp_macro_test_util::snapshot_derive_cgp_data; snapshot_derive_cgp_data! { #[derive(CgpData)] @@ -295,17 +306,6 @@ snapshot_derive_cgp_data! { } } -#[derive(Debug, Eq, PartialEq, CgpData)] -pub struct FooBar { - pub foo: u64, - pub bar: String, -} - -#[derive(Debug, Eq, PartialEq, CgpData)] -pub struct Baz { - pub baz: bool, -} - #[test] fn test_basic_builder() { let context: FooBarBaz = FooBarBaz::builder() @@ -318,129 +318,3 @@ fn test_basic_builder() { assert_eq!(context.bar, "bar"); assert!(context.baz); } - -#[test] -fn test_build_from() { - let foo_bar = FooBar { - foo: 1, - bar: "bar".to_owned(), - }; - - let baz = Baz { baz: true }; - - let foo_bar_baz: FooBarBaz = FooBarBaz::builder() - .build_from(foo_bar) - .build_from(baz) - .finalize_build(); - - assert_eq!(foo_bar_baz.foo, 1); - assert_eq!(foo_bar_baz.bar, "bar"); - assert!(foo_bar_baz.baz); -} - -#[cgp_producer] -fn build_foo_bar() -> FooBar { - FooBar { - foo: 1, - bar: "bar".to_owned(), - } -} - -#[cgp_producer] -pub fn build_foo() -> u64 { - 1 -} - -#[cgp_producer] -pub fn build_bar() -> String { - "bar".to_owned() -} - -#[cgp_producer(BuildBaz)] -pub fn build_baz() -> bool { - true -} - -pub struct App; - -snapshot_delegate_components! { - delegate_components! { - App { - ErrorTypeProviderComponent: UseType, - } - } - - expand_app(output) { - insta::assert_snapshot!(output, @" - impl DelegateComponent for App { - type Delegate = UseType; - } - impl< - __Context__, - __Params__, - > IsProviderFor for App - where - UseType< - Infallible, - >: IsProviderFor, - {} - ") - } -} - -#[test] -fn test_build_with_handlers() { - let context = App; - let code = PhantomData::<()>; - - pub type Handlers = - Product![BuildAndMerge, BuildAndSetField]; - - assert_eq!( - BuildWithHandlers::::compute(&context, code, ()), - FooBarBaz { - foo: 1, - bar: "bar".to_owned(), - baz: true, - } - ); - - assert_eq!( - BuildWithHandlers::::try_compute(&context, code, ()), - Ok(FooBarBaz { - foo: 1, - bar: "bar".to_owned(), - baz: true, - }) - ); -} - -#[test] -fn test_build_with_fields() { - let context = App; - let code = PhantomData::<()>; - - pub type Handlers = Product![ - BuildAndSetField, - BuildAndSetField, - BuildAndSetField, - ]; - - assert_eq!( - BuildWithHandlers::::compute(&context, code, ()), - FooBarBaz { - foo: 1, - bar: "bar".to_owned(), - baz: true, - } - ); - - assert_eq!( - BuildWithHandlers::::try_compute(&context, code, ()), - Ok(FooBarBaz { - foo: 1, - bar: "bar".to_owned(), - baz: true, - }) - ); -} diff --git a/crates/tests/cgp-tests/tests/extensible_records/struct_generic.rs b/crates/tests/cgp-tests/tests/extensible_records/struct_generic.rs new file mode 100644 index 00000000..8decff42 --- /dev/null +++ b/crates/tests/cgp-tests/tests/extensible_records/struct_generic.rs @@ -0,0 +1,102 @@ +//! `#[derive(HasFields)]` on a *generic* struct with a `where` clause. +//! +//! Each generated impl carries the struct's `Name` parameter and forwards its +//! `where Name: Display` bound, so the field-list derive works on parameterized +//! structs. The derive expansion is owned by this concept. +//! +//! See docs/reference/derives/derive_has_fields.md. + +use core::fmt::Display; + +use cgp::prelude::*; +use cgp_macro_test_util::snapshot_derive_has_fields; + +snapshot_derive_has_fields! { + #[derive(HasFields)] + #[derive(Clone, Debug, Eq, PartialEq)] + pub struct Person + where + Name: Display, + { + pub name: Name, + pub age: u8, + } + + expand_person(output) { + insta::assert_snapshot!(output, @" + impl HasFields for Person + where + Name: Display, + { + type Fields = Cons< + Field>>>>, Name>, + Cons>>>, u8>, Nil>, + >; + } + impl HasFieldsRef for Person + where + Name: Display, + { + type FieldsRef<'__a> = Cons< + Field< + Symbol<4, Chars<'n', Chars<'a', Chars<'m', Chars<'e', Nil>>>>>, + &'__a Name, + >, + Cons>>>, &'__a u8>, Nil>, + > + where + Self: '__a; + } + impl FromFields for Person + where + Name: Display, + { + fn from_fields(Cons(name, Cons(age, Nil)): Self::Fields) -> Self { + Self { + name: name.value, + age: age.value, + } + } + } + impl ToFields for Person + where + Name: Display, + { + fn to_fields(self) -> Self::Fields { + Cons(self.name.into(), Cons(self.age.into(), Nil)) + } + } + impl ToFieldsRef for Person + where + Name: Display, + { + fn to_fields_ref<'__a>(&'__a self) -> Self::FieldsRef<'__a> + where + Self: '__a, + { + Cons((&self.name).into(), Cons((&self.age).into(), Nil)) + } + } + ") + } +} + +#[test] +fn test_generic_struct() { + let name = "Alice".to_owned(); + + let person1 = Person { + name: name.clone(), + age: 32, + }; + + let product = person1.clone().to_fields(); + assert_eq!(product, Cons(name.clone().into(), Cons(32.into(), Nil))); + + let product_ref = person1.to_fields_ref(); + assert_eq!(product_ref, Cons((&name).into(), Cons((&32).into(), Nil))); + + let person2 = Person::from_fields(product); + + assert_eq!(person1, person2); +} diff --git a/crates/tests/cgp-tests/tests/extensible_records/struct_generic_lifetime.rs b/crates/tests/cgp-tests/tests/extensible_records/struct_generic_lifetime.rs new file mode 100644 index 00000000..93dc0219 --- /dev/null +++ b/crates/tests/cgp-tests/tests/extensible_records/struct_generic_lifetime.rs @@ -0,0 +1,106 @@ +//! `#[derive(HasFields)]` on a struct with both a lifetime and a type parameter. +//! +//! Fields that borrow (`&'a Name`, `&'a u8`) show how the derive threads the +//! struct's own lifetime through the field list, while `FieldsRef` adds its +//! separate `'__a` borrow on top (`&'__a &'a Name`). The derive expansion is +//! owned by this concept. +//! +//! See docs/reference/derives/derive_has_fields.md. + +use core::fmt::Display; + +use cgp::prelude::*; +use cgp_macro_test_util::snapshot_derive_has_fields; + +snapshot_derive_has_fields! { + #[derive(HasFields)] + #[derive(Clone, Debug, Eq, PartialEq)] + pub struct Person<'a, Name> + where + Name: Display, + { + pub name: &'a Name, + pub age: &'a u8, + } + + expand_person(output) { + insta::assert_snapshot!(output, @" + impl<'a, Name> HasFields for Person<'a, Name> + where + Name: Display, + { + type Fields = Cons< + Field>>>>, &'a Name>, + Cons>>>, &'a u8>, Nil>, + >; + } + impl<'a, Name> HasFieldsRef for Person<'a, Name> + where + Name: Display, + { + type FieldsRef<'__a> = Cons< + Field< + Symbol<4, Chars<'n', Chars<'a', Chars<'m', Chars<'e', Nil>>>>>, + &'__a &'a Name, + >, + Cons< + Field>>>, &'__a &'a u8>, + Nil, + >, + > + where + Self: '__a; + } + impl<'a, Name> FromFields for Person<'a, Name> + where + Name: Display, + { + fn from_fields(Cons(name, Cons(age, Nil)): Self::Fields) -> Self { + Self { + name: name.value, + age: age.value, + } + } + } + impl<'a, Name> ToFields for Person<'a, Name> + where + Name: Display, + { + fn to_fields(self) -> Self::Fields { + Cons(self.name.into(), Cons(self.age.into(), Nil)) + } + } + impl<'a, Name> ToFieldsRef for Person<'a, Name> + where + Name: Display, + { + fn to_fields_ref<'__a>(&'__a self) -> Self::FieldsRef<'__a> + where + Self: '__a, + { + Cons((&self.name).into(), Cons((&self.age).into(), Nil)) + } + } + ") + } +} + +#[test] +fn test_generic_lifetime_struct() { + let name = "Alice".to_owned(); + + let person1 = Person { + name: &name, + age: &32, + }; + + let product = person1.clone().to_fields(); + assert_eq!(product, Cons((&name).into(), Cons((&32).into(), Nil))); + + let product_ref = person1.to_fields_ref(); + assert_eq!(product_ref, Cons((&&name).into(), Cons((&&32).into(), Nil))); + + let person2 = Person::from_fields(product); + + assert_eq!(person1, person2); +} diff --git a/crates/tests/cgp-tests/tests/extensible_records/struct_single_named_field.rs b/crates/tests/cgp-tests/tests/extensible_records/struct_single_named_field.rs new file mode 100644 index 00000000..4b8c8ba1 --- /dev/null +++ b/crates/tests/cgp-tests/tests/extensible_records/struct_single_named_field.rs @@ -0,0 +1,79 @@ +//! Canonical expansion of `#[derive(HasFields)]` for a struct with one named +//! field. +//! +//! Unlike `#[derive(CgpData)]`, `#[derive(HasFields)]` derives *only* the field +//! list: `HasFields`/`HasFieldsRef` (the type-level `Cons`/`Nil` spine) plus the +//! `FromFields`/`ToFields`/`ToFieldsRef` round-trip — no per-field `HasField` +//! access and no builder. This is the reference snapshot for that derive on a +//! struct; the other `struct_*` files reuse it for the remaining field shapes. +//! +//! See docs/reference/derives/derive_has_fields.md and +//! docs/reference/traits/has_fields.md. + +use cgp::prelude::*; +use cgp_macro_test_util::snapshot_derive_has_fields; + +snapshot_derive_has_fields! { + #[derive(HasFields)] + #[derive(Clone, Debug, Eq, PartialEq)] + pub struct Person { + pub name: String, + } + + expand_person(output) { + insta::assert_snapshot!(output, @" + impl HasFields for Person { + type Fields = Cons< + Field>>>>, String>, + Nil, + >; + } + impl HasFieldsRef for Person { + type FieldsRef<'__a> = Cons< + Field< + Symbol<4, Chars<'n', Chars<'a', Chars<'m', Chars<'e', Nil>>>>>, + &'__a String, + >, + Nil, + > + where + Self: '__a; + } + impl FromFields for Person { + fn from_fields(Cons(name, Nil): Self::Fields) -> Self { + Self { name: name.value } + } + } + impl ToFields for Person { + fn to_fields(self) -> Self::Fields { + Cons(self.name.into(), Nil) + } + } + impl ToFieldsRef for Person { + fn to_fields_ref<'__a>(&'__a self) -> Self::FieldsRef<'__a> + where + Self: '__a, + { + Cons((&self.name).into(), Nil) + } + } + ") + } +} + +#[test] +fn test_single_named_field() { + let name = "Alice".to_owned(); + + let person1 = Person { name: name.clone() }; + + let product = person1.clone().to_fields(); + assert_eq!(product, Cons(name.clone().into(), Nil)); + + let product_ref = person1.to_fields_ref(); + assert_eq!(product_ref, Cons((&name).into(), Nil)); + + let person2 = Person::from_fields(product); + + assert_eq!(person1, person2); +} diff --git a/crates/tests/cgp-tests/tests/extensible_records/struct_single_unnamed_field.rs b/crates/tests/cgp-tests/tests/extensible_records/struct_single_unnamed_field.rs new file mode 100644 index 00000000..d811544b --- /dev/null +++ b/crates/tests/cgp-tests/tests/extensible_records/struct_single_unnamed_field.rs @@ -0,0 +1,62 @@ +//! `#[derive(HasFields)]` on a newtype (single unnamed field). +//! +//! A one-element tuple struct is a special case: its `Fields` is the inner type +//! directly (not a `Cons`/`Nil` list), and `from_fields`/`to_fields` pass the +//! single value straight through. The derive expansion is owned by this concept. +//! +//! See docs/reference/derives/derive_has_fields.md. + +use cgp::prelude::*; +use cgp_macro_test_util::snapshot_derive_has_fields; + +snapshot_derive_has_fields! { + #[derive(HasFields)] + #[derive(Clone, Debug, Eq, PartialEq)] + pub struct Person(String); + + expand_person(output) { + insta::assert_snapshot!(output, @" + impl HasFields for Person { + type Fields = String; + } + impl HasFieldsRef for Person { + type FieldsRef<'__a> = &'__a String where Self: '__a; + } + impl FromFields for Person { + fn from_fields(field: Self::Fields) -> Self { + Self(field) + } + } + impl ToFields for Person { + fn to_fields(self) -> Self::Fields { + self.0 + } + } + impl ToFieldsRef for Person { + fn to_fields_ref<'__a>(&'__a self) -> Self::FieldsRef<'__a> + where + Self: '__a, + { + &self.0 + } + } + ") + } +} + +#[test] +fn test_single_unnamed_field() { + let name = "Alice".to_owned(); + + let person1 = Person(name.clone()); + + let product = person1.clone().to_fields(); + assert_eq!(product, name.clone()); + + let product_ref = person1.to_fields_ref(); + assert_eq!(product_ref, &name); + + let person2 = Person::from_fields(product); + + assert_eq!(person1, person2); +} diff --git a/crates/tests/cgp-tests/tests/extensible_records/struct_tuple_fields.rs b/crates/tests/cgp-tests/tests/extensible_records/struct_tuple_fields.rs new file mode 100644 index 00000000..d1e5e9b3 --- /dev/null +++ b/crates/tests/cgp-tests/tests/extensible_records/struct_tuple_fields.rs @@ -0,0 +1,67 @@ +//! `#[derive(HasFields)]` on a multi-field tuple struct. +//! +//! With more than one unnamed field, the derive keys the field list by `Index` +//! rather than treating it as a bare newtype, producing the usual `Cons`/`Nil` +//! spine. The derive expansion is owned by this concept. +//! +//! See docs/reference/derives/derive_has_fields.md. + +use cgp::prelude::*; +use cgp_macro_test_util::snapshot_derive_has_fields; + +snapshot_derive_has_fields! { + #[derive(HasFields)] + #[derive(Clone, Debug, Eq, PartialEq)] + pub struct Person(String, u8); + + expand_person(output) { + insta::assert_snapshot!(output, @" + impl HasFields for Person { + type Fields = Cons, String>, Cons, u8>, Nil>>; + } + impl HasFieldsRef for Person { + type FieldsRef<'__a> = Cons< + Field, &'__a String>, + Cons, &'__a u8>, Nil>, + > + where + Self: '__a; + } + impl FromFields for Person { + fn from_fields(Cons(field_1, Cons(field_0, Nil)): Self::Fields) -> Self { + Self(field_1.value, field_0.value) + } + } + impl ToFields for Person { + fn to_fields(self) -> Self::Fields { + Cons(self.0.into(), Cons(self.1.into(), Nil)) + } + } + impl ToFieldsRef for Person { + fn to_fields_ref<'__a>(&'__a self) -> Self::FieldsRef<'__a> + where + Self: '__a, + { + Cons((&self.0).into(), Cons((&self.1).into(), Nil)) + } + } + ") + } +} + +#[test] +fn test_single_unnamed_multi_field() { + let name = "Alice".to_owned(); + + let person1 = Person(name.clone(), 32); + + let product = person1.clone().to_fields(); + assert_eq!(product, Cons(name.clone().into(), Cons(32.into(), Nil))); + + let product_ref = person1.to_fields_ref(); + assert_eq!(product_ref, Cons((&name).into(), Cons((&32).into(), Nil))); + + let person2 = Person::from_fields(product); + + assert_eq!(person1, person2); +} diff --git a/crates/tests/cgp-tests/tests/extensible_records/struct_two_named_fields.rs b/crates/tests/cgp-tests/tests/extensible_records/struct_two_named_fields.rs new file mode 100644 index 00000000..b683753e --- /dev/null +++ b/crates/tests/cgp-tests/tests/extensible_records/struct_two_named_fields.rs @@ -0,0 +1,82 @@ +//! `#[derive(HasFields)]` for a struct with two named fields. +//! +//! Shows the two-field `Cons<_, Cons<_, Nil>>` field list and the matching +//! `from_fields`/`to_fields` round-trip. The derive expansion is owned by this +//! concept; `struct_single_named_field` pins the canonical reference. +//! +//! See docs/reference/derives/derive_has_fields.md. + +use cgp::prelude::*; +use cgp_macro_test_util::snapshot_derive_has_fields; + +snapshot_derive_has_fields! { + #[derive(HasFields)] + #[derive(Clone, Debug, Eq, PartialEq)] + pub struct Person { + pub name: String, + pub age: u8, + } + + expand_person(output) { + insta::assert_snapshot!(output, @" + impl HasFields for Person { + type Fields = Cons< + Field>>>>, String>, + Cons>>>, u8>, Nil>, + >; + } + impl HasFieldsRef for Person { + type FieldsRef<'__a> = Cons< + Field< + Symbol<4, Chars<'n', Chars<'a', Chars<'m', Chars<'e', Nil>>>>>, + &'__a String, + >, + Cons>>>, &'__a u8>, Nil>, + > + where + Self: '__a; + } + impl FromFields for Person { + fn from_fields(Cons(name, Cons(age, Nil)): Self::Fields) -> Self { + Self { + name: name.value, + age: age.value, + } + } + } + impl ToFields for Person { + fn to_fields(self) -> Self::Fields { + Cons(self.name.into(), Cons(self.age.into(), Nil)) + } + } + impl ToFieldsRef for Person { + fn to_fields_ref<'__a>(&'__a self) -> Self::FieldsRef<'__a> + where + Self: '__a, + { + Cons((&self.name).into(), Cons((&self.age).into(), Nil)) + } + } + ") + } +} + +#[test] +fn test_two_named_field() { + let name = "Alice".to_owned(); + + let person1 = Person { + name: name.clone(), + age: 32, + }; + + let product = person1.clone().to_fields(); + assert_eq!(product, Cons(name.clone().into(), Cons(32.into(), Nil))); + + let product_ref = person1.to_fields_ref(); + assert_eq!(product_ref, Cons((&name).into(), Cons((&32).into(), Nil))); + + let person2 = Person::from_fields(product); + + assert_eq!(person1, person2); +} diff --git a/crates/tests/cgp-tests/tests/extensible_data_tests/records/index.rs b/crates/tests/cgp-tests/tests/extensible_records/tuple_record.rs similarity index 95% rename from crates/tests/cgp-tests/tests/extensible_data_tests/records/index.rs rename to crates/tests/cgp-tests/tests/extensible_records/tuple_record.rs index dd82fc18..fd29fa22 100644 --- a/crates/tests/cgp-tests/tests/extensible_data_tests/records/index.rs +++ b/crates/tests/cgp-tests/tests/extensible_records/tuple_record.rs @@ -1,3 +1,12 @@ +//! `#[derive(CgpData)]` on a *tuple* (unnamed-field) struct. +//! +//! Positional fields are tagged with `Index` instead of `Symbol!`, so the +//! whole record spine — field access, the `Cons` field list, the `__Partial…` +//! builder — is keyed by index. Otherwise the expansion mirrors the named-field +//! case pinned by `record_derive`. +//! +//! See docs/reference/derives/derive_cgp_data.md. + use cgp_macro_test_util::snapshot_derive_cgp_data; snapshot_derive_cgp_data! { diff --git a/crates/tests/cgp-tests/tests/extensible_records_tests.rs b/crates/tests/cgp-tests/tests/extensible_records_tests.rs new file mode 100644 index 00000000..d0fe75b9 --- /dev/null +++ b/crates/tests/cgp-tests/tests/extensible_records_tests.rs @@ -0,0 +1,19 @@ +//! Entrypoint for the `extensible_records` concept. +//! +//! Covers CGP's extensible-record (struct) data machinery: `#[derive(CgpData)]`, +//! which derives the full record spine — `HasField`/`HasFieldMut`, `HasFields`, +//! the `From`/`To` field-list conversions, and the extensible *builder* pattern +//! (`HasBuilder`, `BuildField`, `build_from`, `finalize_build`) via a generated +//! `__Partial…` type — and `#[derive(HasFields)]`, which derives only the field +//! list. This concept owns the canonical macro-expansion snapshots for both +//! derives on structs, and exercises named, tuple, generic, optional, and cast +//! variations. +//! +//! See docs/reference/derives/derive_cgp_data.md, +//! docs/reference/derives/derive_has_fields.md, +//! docs/reference/traits/has_builder.md, and +//! docs/concepts/extensible-records.md. +#![allow(dead_code)] +#![allow(clippy::disallowed_names)] + +pub mod extensible_records; diff --git a/crates/tests/cgp-tests/tests/extensible_data_tests/variants/basic.rs b/crates/tests/cgp-tests/tests/extensible_variants/derive_cgp_data.rs similarity index 71% rename from crates/tests/cgp-tests/tests/extensible_data_tests/variants/basic.rs rename to crates/tests/cgp-tests/tests/extensible_variants/derive_cgp_data.rs index 932e734e..59a6469c 100644 --- a/crates/tests/cgp-tests/tests/extensible_data_tests/variants/basic.rs +++ b/crates/tests/cgp-tests/tests/extensible_variants/derive_cgp_data.rs @@ -1,21 +1,24 @@ -use core::convert::Infallible; -use core::fmt::{Debug, Display}; +//! `#[derive(CgpData)]` on a concrete enum: the full variant machinery. Beyond +//! the `HasFields` field list, the derive emits the `FromVariant` constructors, +//! the `__Partial*` extractor enums, and the `HasExtractor`/`ExtractField`/ +//! `FinalizeExtract` impls that deconstruct a value one variant at a time, +//! narrowing the remainder's type as each present variant is removed. +//! +//! This concept owns the variant expansion of `#[derive(CgpData)]`; this file +//! is the canonical snapshot for a concrete (non-generic) enum. It also +//! exercises the extractor at runtime and the structural casts +//! (`CanUpcast`/`CanDowncast`) between enum shapes; the auxiliary enums are +//! plain derives, since the primary snapshot already pins the expansion. +//! +//! See docs/reference/derives/derive_cgp_data.md, +//! docs/reference/derives/derive_from_variant.md, and +//! docs/reference/derives/derive_extract_field.md. + use core::marker::PhantomData; -use cgp::core::error::ErrorTypeProviderComponent; use cgp::core::field::impls::{CanDowncast, CanDowncastFields, CanUpcast}; -use cgp::extra::dispatch::{ - DowncastAndHandle, ExtractFieldAndHandle, HandleFieldValue, MatchWithFieldHandlers, - MatchWithHandlers, MatchWithValueHandlersRef, -}; -use cgp::extra::handler::{ - Computer, ComputerComponent, ComputerRef, ComputerRefComponent, PromoteAsync, -}; use cgp::prelude::*; -use cgp_macro_test_util::{ - snapshot_cgp_new_provider, snapshot_delegate_components, snapshot_derive_cgp_data, -}; -use futures::executor::block_on; +use cgp_macro_test_util::snapshot_derive_cgp_data; snapshot_derive_cgp_data! { #[derive(CgpData)] @@ -355,6 +358,8 @@ snapshot_derive_cgp_data! { } } +// Auxiliary enums, used as cast targets. These are plain derives: the primary +// snapshot above already pins the `#[derive(CgpData)]` expansion. #[derive(Debug, Eq, PartialEq, CgpData)] pub enum FooBar { Foo(u64), @@ -373,6 +378,9 @@ pub enum BazBarFoo { Foo(u64), } +// Walk the extractor by hand: each `extract_field` pulls out one variant or +// hands back a remainder whose type no longer contains it, until the exhausted +// remainder is closed off with `finalize_extract`. fn context_to_string(context: FooBarBaz) -> String { match context .extractor_ref() @@ -458,279 +466,3 @@ fn test_downcast() { Some(BazBarFoo::Baz(true)) ); } - -pub struct App; - -snapshot_delegate_components! { - delegate_components! { - App { - ErrorTypeProviderComponent: UseType, - } - } - - expand_app(output) { - insta::assert_snapshot!(output, @" - impl DelegateComponent for App { - type Delegate = UseType; - } - impl< - __Context__, - __Params__, - > IsProviderFor for App - where - UseType< - Infallible, - >: IsProviderFor, - {} - ") - } -} - -#[cgp_computer] -pub fn field_to_string(Field { value, .. }: Field) -> String -where - Value: Display, -{ - value.to_string() -} - -#[cgp_computer] -pub fn value_to_string_ref(value: &Value) -> String -where - Value: Display, -{ - value.to_string() -} - -#[test] -fn test_dispatch_fields() { - let context = App; - let code = PhantomData::<()>; - - assert_eq!( - MatchWithFieldHandlers::::compute(&context, code, FooBarBaz::Foo(1)), - "1" - ); - - assert_eq!( - MatchWithFieldHandlers::::compute( - &context, - code, - FooBarBaz::Bar("hello".to_owned()) - ), - "hello" - ); - - assert_eq!( - MatchWithFieldHandlers::::compute(&context, code, FooBarBaz::Baz(true)), - "true" - ); -} - -#[test] -fn test_dispatch_values_ref() { - let context = App; - let code = PhantomData::<()>; - - assert_eq!( - MatchWithValueHandlersRef::::compute(&context, code, &FooBarBaz::Foo(1)), - "1" - ); - - assert_eq!( - MatchWithValueHandlersRef::::compute_ref( - &context, - code, - &FooBarBaz::Foo(1) - ), - "1" - ); - - assert_eq!( - MatchWithValueHandlersRef::::compute( - &context, - code, - &FooBarBaz::Bar("hello".to_owned()) - ), - "hello" - ); - - assert_eq!( - MatchWithValueHandlersRef::::compute( - &context, - code, - &FooBarBaz::Baz(true) - ), - "true" - ); -} - -snapshot_cgp_new_provider! { - #[cgp_new_provider] - impl Computer for ValueToString - where - Value: Display, - { - type Output = String; - - fn compute(_context: &Context, _code: PhantomData, input: &Value) -> Self::Output { - input.to_string() - } - } - - expand_value_to_string(output) { - insta::assert_snapshot!(output, @" - impl Computer for ValueToString - where - Value: Display, - { - type Output = String; - fn compute( - _context: &Context, - _code: PhantomData, - input: &Value, - ) -> Self::Output { - input.to_string() - } - } - impl IsProviderFor - for ValueToString - where - Value: Display, - {} - pub struct ValueToString; - ") - } -} - -#[test] -fn test_dispatch_fields_ref() { - let context = App; - let code = PhantomData::<()>; - - assert_eq!( - MatchWithValueHandlersRef::::compute(&context, code, &FooBarBaz::Foo(1)), - "1" - ); - - assert_eq!( - MatchWithValueHandlersRef::::compute( - &context, - code, - &FooBarBaz::Bar("hello".to_owned()) - ), - "hello" - ); - - assert_eq!( - MatchWithValueHandlersRef::::compute(&context, code, &FooBarBaz::Baz(true)), - "true" - ); -} - -#[test] -fn test_async_dispatch_fields() { - let context = App; - let code = PhantomData::<()>; - - assert_eq!( - block_on(MatchWithFieldHandlers::::compute_async( - &context, - code, - FooBarBaz::Foo(1) - )), - "1" - ); - - assert_eq!( - block_on(MatchWithFieldHandlers::::compute_async( - &context, - code, - FooBarBaz::Bar("hello".to_owned()) - )), - "hello" - ); - - assert_eq!( - block_on(MatchWithFieldHandlers::::compute_async( - &context, - code, - FooBarBaz::Baz(true) - )), - "true" - ); -} - -#[cgp_computer] -pub fn show_foo_bar(input: FooBar) -> String { - format!("FooBar::{input:?}") -} - -#[cgp_computer] -pub fn show_baz(input: bool) -> String { - format!("Baz({input:?})") -} - -type Computers = Product![ - ExtractFieldAndHandle>, - DowncastAndHandle, -]; - -type Handlers = Product![ - PromoteAsync>>, - PromoteAsync>, -]; - -#[test] -fn test_dispatch_computers() { - let context = App; - let code = PhantomData::<()>; - - assert_eq!( - MatchWithHandlers::::compute(&context, code, FooBarBaz::Foo(1)), - "FooBar::Foo(1)" - ); - - assert_eq!( - MatchWithHandlers::::compute(&context, code, FooBarBaz::Bar("hello".to_owned())), - "FooBar::Bar(\"hello\")" - ); - - assert_eq!( - MatchWithHandlers::::compute(&context, code, FooBarBaz::Baz(true)), - "Baz(true)" - ); -} - -#[test] -fn test_dispatch_handlers() { - let context = App; - let code = PhantomData::<()>; - - assert_eq!( - block_on(MatchWithHandlers::::compute_async( - &context, - code, - FooBarBaz::Foo(1) - )), - "FooBar::Foo(1)" - ); - - assert_eq!( - block_on(MatchWithHandlers::::compute_async( - &context, - code, - FooBarBaz::Bar("hello".to_owned()) - )), - "FooBar::Bar(\"hello\")" - ); - - assert_eq!( - block_on(MatchWithHandlers::::compute_async( - &context, - code, - FooBarBaz::Baz(true) - )), - "Baz(true)" - ); -} diff --git a/crates/tests/cgp-tests/tests/extensible_data_tests/variants/generic.rs b/crates/tests/cgp-tests/tests/extensible_variants/derive_cgp_data_generic.rs similarity index 96% rename from crates/tests/cgp-tests/tests/extensible_data_tests/variants/generic.rs rename to crates/tests/cgp-tests/tests/extensible_variants/derive_cgp_data_generic.rs index c0ab9c7d..56edec17 100644 --- a/crates/tests/cgp-tests/tests/extensible_data_tests/variants/generic.rs +++ b/crates/tests/cgp-tests/tests/extensible_variants/derive_cgp_data_generic.rs @@ -1,3 +1,16 @@ +//! `#[derive(CgpData)]` on a *generic* enum: the derive lifts the enum's type +//! parameters onto every generated impl and onto the `__Partial*` extractor +//! enums, so a variant's payload type is the corresponding type parameter rather +//! than a concrete type. Structural casts still line variants up by name, so a +//! two-variant instantiation upcasts into a three-variant one and downcasts back. +//! +//! This concept owns the variant expansion of `#[derive(CgpData)]`; this file is +//! the generic-enum snapshot. +//! +//! See docs/reference/derives/derive_cgp_data.md. + +use core::marker::PhantomData; + use cgp::core::field::impls::{CanDowncast, CanUpcast}; use cgp::prelude::*; use cgp_macro_test_util::snapshot_derive_cgp_data; @@ -467,6 +480,7 @@ snapshot_derive_cgp_data! { } } +// A two-variant instantiation, used as the cast counterpart. Plain derive. #[derive(Debug, Eq, PartialEq, CgpData)] pub enum FooBarGeneric { Foo(Foo), diff --git a/crates/tests/cgp-tests/tests/extensible_data_tests/variants/shape.rs b/crates/tests/cgp-tests/tests/extensible_variants/derive_cgp_data_shape.rs similarity index 76% rename from crates/tests/cgp-tests/tests/extensible_data_tests/variants/shape.rs rename to crates/tests/cgp-tests/tests/extensible_variants/derive_cgp_data_shape.rs index 7a98bf4a..41e98c48 100644 --- a/crates/tests/cgp-tests/tests/extensible_data_tests/variants/shape.rs +++ b/crates/tests/cgp-tests/tests/extensible_variants/derive_cgp_data_shape.rs @@ -1,16 +1,24 @@ +//! `#[derive(CgpData)]` on an enum whose variants carry *struct* payloads +//! (`Shape::Circle(Circle)`, `Shape::Rectangle(Rectangle)`). The variant machinery +//! is identical to a scalar-payload enum — the payload type is simply the +//! variant's `Value` — and the multi-character variant names (`Circle`, +//! `Rectangle`) show the full `Symbol!` character chains in the expansion. +//! +//! This concept owns the variant expansion of `#[derive(CgpData)]`; this file is +//! the struct-payload snapshot. It also exercises the extractor at runtime plus +//! the widening/narrowing casts against enums of different arity +//! (`ShapePlus`/`TriangleOnly`), which are plain derives. +//! +//! See docs/reference/derives/derive_cgp_data.md and +//! docs/reference/derives/derive_extract_field.md. + +use core::marker::PhantomData; use std::f64::consts::PI; use cgp::core::field::impls::{CanDowncast, CanDowncastFields, CanUpcast}; use cgp::core::field::traits::FinalizeExtractResult; -use cgp::extra::dispatch::{ - ExtractFieldAndHandle, ExtractFirstFieldAndHandle, HandleFieldValue, HandleFirstFieldValue, - MatchFirstWithHandlers, MatchFirstWithValueHandlers, MatchWithHandlers, MatchWithValueHandlers, -}; -use cgp::extra::handler::{NoCode, UseInputDelegate}; use cgp::prelude::*; -use cgp_macro_test_util::{ - snapshot_check_components, snapshot_delegate_components, snapshot_derive_cgp_data, -}; +use cgp_macro_test_util::snapshot_derive_cgp_data; snapshot_derive_cgp_data! { #[derive(CgpData)] @@ -441,6 +449,8 @@ snapshot_derive_cgp_data! { } } +// Cast targets of different arity. Plain derives: the snapshot above pins the +// `#[derive(CgpData)]` expansion. #[derive(Debug, PartialEq, CgpData)] pub enum TriangleOnly { Triangle(Triangle), @@ -524,188 +534,3 @@ fn test_shape_downcast() { } }; } - -#[cgp_auto_dispatch] -pub trait HasArea { - fn area(self) -> f64; -} - -impl HasArea for Circle { - fn area(self) -> f64 { - PI * self.radius * self.radius - } -} - -impl HasArea for Rectangle { - fn area(self) -> f64 { - self.width * self.height - } -} - -impl HasArea for Triangle { - fn area(self) -> f64 { - self.base * self.height / 2.0 - } -} - -#[test] -fn test_match_with_handlers() { - let circle = Shape::Circle(Circle { radius: 5.0 }); - - let _area = MatchWithHandlers::< - Product![ - ExtractFieldAndHandle>, - ExtractFieldAndHandle>, - ], - >::compute(&(), PhantomData::<()>, circle); -} - -pub trait Container { - fn contains(self, x: f64, y: f64) -> bool; -} - -impl Container for Circle { - fn contains(self, _x: f64, _y: f64) -> bool { - true // stub - } -} - -impl Container for Rectangle { - fn contains(self, _x: f64, _y: f64) -> bool { - true // stub - } -} - -impl Container for Triangle { - fn contains(self, _x: f64, _y: f64) -> bool { - true // stub - } -} - -impl Container for Shape { - fn contains(self, x: f64, y: f64) -> bool { - MatchFirstWithValueHandlers::::compute(&(), NoCode, (self, (x, y))) - } -} - -impl Container for ShapePlus { - fn contains(self, x: f64, y: f64) -> bool { - MatchFirstWithValueHandlers::::compute(&(), NoCode, (self, (x, y))) - } -} - -#[cgp_computer] -fn contains(shape: T, (x, y): (f64, f64)) -> bool { - shape.contains(x, y) -} - -#[test] -fn test_dispatch_contains() { - let circle = Shape::Circle(Circle { radius: 5.0 }); - - let _is_contained = MatchFirstWithHandlers::< - Product![ - ExtractFirstFieldAndHandle>, - ExtractFirstFieldAndHandle>, - ], - >::compute(&(), PhantomData::<()>, (circle, (1.0, 2.0))); -} - -pub struct App; - -snapshot_delegate_components! { - delegate_components! { - App { - ComputerComponent: UseInputDelegate - } - } - - expand_app(output) { - insta::assert_snapshot!(output, @" - pub struct AreaComputers; - impl DelegateComponent for App { - type Delegate = UseInputDelegate; - } - impl<__Context__, __Params__> IsProviderFor - for App - where - UseInputDelegate< - AreaComputers, - >: IsProviderFor, - {} - impl DelegateComponent for AreaComputers { - type Delegate = ComputeArea; - } - impl<__Context__, __Params__> IsProviderFor - for AreaComputers - where - ComputeArea: IsProviderFor, - {} - impl DelegateComponent for AreaComputers { - type Delegate = ComputeArea; - } - impl<__Context__, __Params__> IsProviderFor - for AreaComputers - where - ComputeArea: IsProviderFor, - {} - impl DelegateComponent for AreaComputers { - type Delegate = ComputeArea; - } - impl<__Context__, __Params__> IsProviderFor - for AreaComputers - where - ComputeArea: IsProviderFor, - {} - impl DelegateComponent for AreaComputers { - type Delegate = MatchWithValueHandlers; - } - impl<__Context__, __Params__> IsProviderFor - for AreaComputers - where - MatchWithValueHandlers: IsProviderFor, - {} - impl DelegateComponent for AreaComputers { - type Delegate = MatchWithValueHandlers; - } - impl<__Context__, __Params__> IsProviderFor - for AreaComputers - where - MatchWithValueHandlers: IsProviderFor, - {} - ") - } -} - -snapshot_check_components! { - check_components! { - App { - ComputerComponent: [ - ((), Shape), - ((), ShapePlus), - ], - } - } - - expand_check_app(output) { - insta::assert_snapshot!(output, @" - trait __CheckApp< - __Component__, - __Params__: ?Sized, - >: CanUseComponent<__Component__, __Params__> {} - impl __CheckApp for App {} - impl __CheckApp for App {} - ") - } -} diff --git a/crates/tests/cgp-tests/tests/extensible_variants/has_fields_enum.rs b/crates/tests/cgp-tests/tests/extensible_variants/has_fields_enum.rs new file mode 100644 index 00000000..235ec88d --- /dev/null +++ b/crates/tests/cgp-tests/tests/extensible_variants/has_fields_enum.rs @@ -0,0 +1,170 @@ +//! `#[derive(HasFields)]` on a plain enum: the derive exposes the variants as a +//! `Sum!` of `Field` entries and generates the +//! `HasFields`/`HasFieldsRef`/`FromFields`/`ToFields`/`ToFieldsRef` impls that +//! convert an enum value to and from that structural sum. Unlike +//! `#[derive(CgpData)]`, `HasFields` stops at the field list and does not derive +//! the extractor/`FromVariant` machinery. +//! +//! This concept owns the enum expansion of the `HasFields` derive. +//! +//! See docs/reference/derives/derive_has_fields.md and docs/reference/macros/sum.md. + +use cgp::prelude::*; +use cgp_macro_test_util::snapshot_derive_has_fields; + +snapshot_derive_has_fields! { + #[derive(HasFields)] + #[derive(Clone, Debug, Eq, PartialEq)] + pub enum Person { + Anonymous(u32), + Named(String), + } + + expand_person(output) { + insta::assert_snapshot!(output, @" + impl HasFields for Person { + type Fields = Either< + Field< + Symbol< + 9, + Chars< + 'A', + Chars< + 'n', + Chars< + 'o', + Chars< + 'n', + Chars< + 'y', + Chars<'m', Chars<'o', Chars<'u', Chars<'s', Nil>>>>, + >, + >, + >, + >, + >, + >, + u32, + >, + Either< + Field< + Symbol< + 5, + Chars<'N', Chars<'a', Chars<'m', Chars<'e', Chars<'d', Nil>>>>>, + >, + String, + >, + Void, + >, + >; + } + impl HasFieldsRef for Person { + type FieldsRef<'__a> = Either< + Field< + Symbol< + 9, + Chars< + 'A', + Chars< + 'n', + Chars< + 'o', + Chars< + 'n', + Chars< + 'y', + Chars<'m', Chars<'o', Chars<'u', Chars<'s', Nil>>>>, + >, + >, + >, + >, + >, + >, + &'__a u32, + >, + Either< + Field< + Symbol< + 5, + Chars<'N', Chars<'a', Chars<'m', Chars<'e', Chars<'d', Nil>>>>>, + >, + &'__a String, + >, + Void, + >, + > + where + Self: '__a; + } + impl FromFields for Person { + fn from_fields(rest: Self::Fields) -> Self { + match rest { + Either::Left(field) => { + let field = field.value; + Self::Anonymous(field) + } + Either::Right(rest) => { + match rest { + Either::Left(field) => { + let field = field.value; + Self::Named(field) + } + Either::Right(rest) => match rest {} + } + } + } + } + } + impl ToFields for Person { + fn to_fields(self) -> Self::Fields { + match self { + Self::Anonymous(field) => Either::Left(field.into()), + Self::Named(field) => Either::Right(Either::Left(field.into())), + } + } + } + impl ToFieldsRef for Person { + fn to_fields_ref<'__a>(&'__a self) -> Self::FieldsRef<'__a> + where + Self: '__a, + { + match self { + Self::Anonymous(field) => Either::Left(field.into()), + Self::Named(field) => Either::Right(Either::Left(field.into())), + } + } + } + ") + } +} + +#[test] +fn test_simple_enum() { + { + let person_a1 = Person::Anonymous(42); + + let person_a2 = person_a1.clone().to_fields(); + assert_eq!(person_a2, Either::Left(42.into())); + + let person_a3 = Person::from_fields(person_a2); + assert_eq!(person_a3, person_a1); + + let person_a4 = person_a1.to_fields_ref(); + assert_eq!(person_a4, Either::Left((&42).into())); + } + + { + let name = "Alice".to_owned(); + + let person_b1 = Person::Named(name.clone()); + + let person_b2 = person_b1.clone().to_fields(); + assert_eq!(person_b2, Either::Right(Either::Left(name.clone().into()))); + + let person_b3 = Person::from_fields(person_b2); + assert_eq!(person_b3, person_b1); + + let person_b4 = person_b1.to_fields_ref(); + assert_eq!(person_b4, Either::Right(Either::Left((&name).into()))); + } +} diff --git a/crates/tests/cgp-tests/tests/extensible_variants/has_fields_enum_generic.rs b/crates/tests/cgp-tests/tests/extensible_variants/has_fields_enum_generic.rs new file mode 100644 index 00000000..2758bbc1 --- /dev/null +++ b/crates/tests/cgp-tests/tests/extensible_variants/has_fields_enum_generic.rs @@ -0,0 +1,169 @@ +//! `#[derive(HasFields)]` on a generic enum with a lifetime and a type +//! parameter: the derive lifts the enum's generics onto every generated impl, +//! and a variant payload that is itself a reference (`&'a Name`) appears +//! verbatim in the field list (and gains a second borrow in `FieldsRef`). +//! +//! This concept owns the enum expansion of the `HasFields` derive; this file +//! pins the generic-enum variant of that expansion. +//! +//! See docs/reference/derives/derive_has_fields.md. + +use cgp::prelude::*; +use cgp_macro_test_util::snapshot_derive_has_fields; + +snapshot_derive_has_fields! { + #[derive(HasFields)] + #[derive(Clone, Debug, Eq, PartialEq)] + pub enum Person<'a, Name> { + Anonymous(u32), + Named(&'a Name), + } + + expand_person(output) { + insta::assert_snapshot!(output, @" + impl<'a, Name> HasFields for Person<'a, Name> { + type Fields = Either< + Field< + Symbol< + 9, + Chars< + 'A', + Chars< + 'n', + Chars< + 'o', + Chars< + 'n', + Chars< + 'y', + Chars<'m', Chars<'o', Chars<'u', Chars<'s', Nil>>>>, + >, + >, + >, + >, + >, + >, + u32, + >, + Either< + Field< + Symbol< + 5, + Chars<'N', Chars<'a', Chars<'m', Chars<'e', Chars<'d', Nil>>>>>, + >, + &'a Name, + >, + Void, + >, + >; + } + impl<'a, Name> HasFieldsRef for Person<'a, Name> { + type FieldsRef<'__a> = Either< + Field< + Symbol< + 9, + Chars< + 'A', + Chars< + 'n', + Chars< + 'o', + Chars< + 'n', + Chars< + 'y', + Chars<'m', Chars<'o', Chars<'u', Chars<'s', Nil>>>>, + >, + >, + >, + >, + >, + >, + &'__a u32, + >, + Either< + Field< + Symbol< + 5, + Chars<'N', Chars<'a', Chars<'m', Chars<'e', Chars<'d', Nil>>>>>, + >, + &'__a &'a Name, + >, + Void, + >, + > + where + Self: '__a; + } + impl<'a, Name> FromFields for Person<'a, Name> { + fn from_fields(rest: Self::Fields) -> Self { + match rest { + Either::Left(field) => { + let field = field.value; + Self::Anonymous(field) + } + Either::Right(rest) => { + match rest { + Either::Left(field) => { + let field = field.value; + Self::Named(field) + } + Either::Right(rest) => match rest {} + } + } + } + } + } + impl<'a, Name> ToFields for Person<'a, Name> { + fn to_fields(self) -> Self::Fields { + match self { + Self::Anonymous(field) => Either::Left(field.into()), + Self::Named(field) => Either::Right(Either::Left(field.into())), + } + } + } + impl<'a, Name> ToFieldsRef for Person<'a, Name> { + fn to_fields_ref<'__a>(&'__a self) -> Self::FieldsRef<'__a> + where + Self: '__a, + { + match self { + Self::Anonymous(field) => Either::Left(field.into()), + Self::Named(field) => Either::Right(Either::Left(field.into())), + } + } + } + ") + } +} + +#[test] +fn test_generic_enum() { + { + let person_a1: Person = Person::Anonymous(42); + + let person_a2 = person_a1.clone().to_fields(); + assert_eq!(person_a2, Either::Left(42.into())); + + let person_a3 = Person::from_fields(person_a2); + assert_eq!(person_a3, person_a1); + + let person_a4 = person_a1.to_fields_ref(); + assert_eq!(person_a4, Either::Left((&42).into())); + } + + { + let name = "Alice".to_owned(); + + let person_b1 = Person::Named(&name); + + let person_b2 = person_b1.clone().to_fields(); + assert_eq!(person_b2, Either::Right(Either::Left((&name).into()))); + + let person_b3 = Person::from_fields(person_b2); + assert_eq!(person_b3, person_b1); + + let person_b4 = person_b1.to_fields_ref(); + assert_eq!(person_b4, Either::Right(Either::Left((&&name).into()))); + } +} diff --git a/crates/tests/cgp-tests/tests/extensible_variants/mod.rs b/crates/tests/cgp-tests/tests/extensible_variants/mod.rs new file mode 100644 index 00000000..95915851 --- /dev/null +++ b/crates/tests/cgp-tests/tests/extensible_variants/mod.rs @@ -0,0 +1,26 @@ +//! One unit test per file. Each file is self-contained: it defines its own +//! enums, providers, and context types at module scope so that the type-level +//! wiring of one test never leaks into another. + +// The `Sum!` type-level sum list that represents an enum's variants. +pub mod sum_macro; + +// `#[derive(HasFields)]` snapshots for enums (this concept owns the enum +// expansion of the derive): the plain field list of an enum, and the generic +// variant. +pub mod has_fields_enum; +pub mod has_fields_enum_generic; + +// `#[derive(CgpData)]` snapshots for enums (this concept owns the variant +// expansion of the derive): the full extractor/extractor-ref machinery for a +// concrete enum, a generic enum, and an enum whose variants carry struct +// payloads. +pub mod derive_cgp_data; +pub mod derive_cgp_data_generic; +pub mod derive_cgp_data_shape; + +// Dispatching an extensible-variant input to per-variant handlers (the derives +// here are plain scaffolding — the dispatch combinators are owned elsewhere). +pub mod shape_dispatch; +pub mod shape_dispatch_ref; +pub mod variant_dispatch; diff --git a/crates/tests/cgp-tests/tests/extensible_variants/shape_dispatch.rs b/crates/tests/cgp-tests/tests/extensible_variants/shape_dispatch.rs new file mode 100644 index 00000000..522b8e5a --- /dev/null +++ b/crates/tests/cgp-tests/tests/extensible_variants/shape_dispatch.rs @@ -0,0 +1,167 @@ +//! Dispatching an owned extensible-variant value to per-variant handlers, over +//! an enum whose variants carry struct payloads. A `#[cgp_auto_dispatch]` trait +//! (`HasArea`) yields a `ComputeArea` provider that each variant's payload +//! implements; the dispatch combinators (`MatchWithHandlers`/ +//! `MatchWithValueHandlers` with `ExtractFieldAndHandle`/`HandleFieldValue`, and +//! their `First` variants for handlers that take extra arguments) route each +//! variant to it, and `UseInputDelegate` selects a per-input-type provider. +//! +//! The dispatch combinators, `delegate_components!`, and `check_components!` are +//! owned by other concepts, so those macros appear plainly here; the derives are +//! plain scaffolding. This file exercises the variant side of owned dispatch. +//! +//! See docs/concepts/extensible-variants.md. + +use core::marker::PhantomData; +use std::f64::consts::PI; + +use cgp::extra::dispatch::{ + ExtractFieldAndHandle, ExtractFirstFieldAndHandle, HandleFieldValue, HandleFirstFieldValue, + MatchFirstWithHandlers, MatchFirstWithValueHandlers, MatchWithHandlers, MatchWithValueHandlers, +}; +use cgp::extra::handler::{NoCode, UseInputDelegate}; +use cgp::prelude::*; + +#[derive(Debug, PartialEq, CgpData)] +pub enum Shape { + Circle(Circle), + Rectangle(Rectangle), +} + +#[derive(Debug, PartialEq, CgpData)] +pub enum ShapePlus { + Triangle(Triangle), + Rectangle(Rectangle), + Circle(Circle), +} + +#[derive(Debug, PartialEq)] +pub struct Circle { + pub radius: f64, +} + +#[derive(Debug, PartialEq)] +pub struct Rectangle { + pub width: f64, + pub height: f64, +} + +#[derive(Debug, PartialEq)] +pub struct Triangle { + pub base: f64, + pub height: f64, +} + +#[cgp_auto_dispatch] +pub trait HasArea { + fn area(self) -> f64; +} + +impl HasArea for Circle { + fn area(self) -> f64 { + PI * self.radius * self.radius + } +} + +impl HasArea for Rectangle { + fn area(self) -> f64 { + self.width * self.height + } +} + +impl HasArea for Triangle { + fn area(self) -> f64 { + self.base * self.height / 2.0 + } +} + +#[test] +fn test_match_with_handlers() { + let circle = Shape::Circle(Circle { radius: 5.0 }); + + let _area = MatchWithHandlers::< + Product![ + ExtractFieldAndHandle>, + ExtractFieldAndHandle>, + ], + >::compute(&(), PhantomData::<()>, circle); +} + +pub trait Container { + fn contains(self, x: f64, y: f64) -> bool; +} + +impl Container for Circle { + fn contains(self, _x: f64, _y: f64) -> bool { + true // stub + } +} + +impl Container for Rectangle { + fn contains(self, _x: f64, _y: f64) -> bool { + true // stub + } +} + +impl Container for Triangle { + fn contains(self, _x: f64, _y: f64) -> bool { + true // stub + } +} + +impl Container for Shape { + fn contains(self, x: f64, y: f64) -> bool { + MatchFirstWithValueHandlers::::compute(&(), NoCode, (self, (x, y))) + } +} + +impl Container for ShapePlus { + fn contains(self, x: f64, y: f64) -> bool { + MatchFirstWithValueHandlers::::compute(&(), NoCode, (self, (x, y))) + } +} + +#[cgp_computer] +fn contains(shape: T, (x, y): (f64, f64)) -> bool { + shape.contains(x, y) +} + +#[test] +fn test_dispatch_contains() { + let circle = Shape::Circle(Circle { radius: 5.0 }); + + let _is_contained = MatchFirstWithHandlers::< + Product![ + ExtractFirstFieldAndHandle>, + ExtractFirstFieldAndHandle>, + ], + >::compute(&(), PhantomData::<()>, (circle, (1.0, 2.0))); +} + +pub struct App; + +delegate_components! { + App { + ComputerComponent: UseInputDelegate + } +} + +check_components! { + App { + ComputerComponent: [ + ((), Shape), + ((), ShapePlus), + ], + } +} diff --git a/crates/tests/cgp-tests/tests/extensible_data_tests/variants/shape_ref.rs b/crates/tests/cgp-tests/tests/extensible_variants/shape_dispatch_ref.rs similarity index 63% rename from crates/tests/cgp-tests/tests/extensible_data_tests/variants/shape_ref.rs rename to crates/tests/cgp-tests/tests/extensible_variants/shape_dispatch_ref.rs index 3df7b7fc..5304ea07 100644 --- a/crates/tests/cgp-tests/tests/extensible_data_tests/variants/shape_ref.rs +++ b/crates/tests/cgp-tests/tests/extensible_variants/shape_dispatch_ref.rs @@ -1,3 +1,16 @@ +//! Dispatching an extensible-variant value by *reference* and by *mutable +//! reference*. `HasExtractorMut`/`HasExtractorRef` expose the variants for +//! borrowing dispatch, and the `Ref`/`Mut` dispatch combinators +//! (`MatchFirstWithValueHandlersRef`/`Mut`, `MatchWithValueHandlersRef`) route +//! each borrowed variant to a per-variant handler — including a `&mut` handler +//! (`Scale`) that mutates the payload in place. A blanket `CanScale` impl over +//! any `HasExtractorMut` context shows dispatch driving a trait method. +//! +//! The dispatch combinators are owned elsewhere; the derives here are plain +//! scaffolding. This file exercises the by-reference / by-mut variant side. +//! +//! See docs/concepts/extensible-variants.md. + use std::f64::consts::PI; use cgp::extra::dispatch::{ @@ -6,7 +19,35 @@ use cgp::extra::dispatch::{ use cgp::extra::handler::NoCode; use cgp::prelude::*; -use super::shape::{Circle, Rectangle, Shape, ShapePlus, Triangle}; +#[derive(Debug, PartialEq, CgpData)] +pub enum Shape { + Circle(Circle), + Rectangle(Rectangle), +} + +#[derive(Debug, PartialEq, CgpData)] +pub enum ShapePlus { + Triangle(Triangle), + Rectangle(Rectangle), + Circle(Circle), +} + +#[derive(Debug, PartialEq)] +pub struct Circle { + pub radius: f64, +} + +#[derive(Debug, PartialEq)] +pub struct Rectangle { + pub width: f64, + pub height: f64, +} + +#[derive(Debug, PartialEq)] +pub struct Triangle { + pub base: f64, + pub height: f64, +} #[cgp_auto_dispatch] pub trait HasAreaRef { diff --git a/crates/tests/cgp-tests/tests/extensible_variants/sum_macro.rs b/crates/tests/cgp-tests/tests/extensible_variants/sum_macro.rs new file mode 100644 index 00000000..d07d8a6a --- /dev/null +++ b/crates/tests/cgp-tests/tests/extensible_variants/sum_macro.rs @@ -0,0 +1,64 @@ +//! The `Sum!` type-level sum list: the variant-level analogue of `Product!`. +//! +//! `Sum![A, B, C]` expands to a right-nested chain of `Either`, terminated by +//! the uninhabited `Void` (`Either>>`). A value of +//! the sum holds exactly one of the listed types: `Left` selects the head, +//! `Right` defers to the rest of the chain, and `Void` — being unconstructible — +//! closes it off. This is the structure an enum's variants desugar to via +//! `#[derive(HasFields)]`, and the spine every extractor/cast in this concept +//! walks. +//! +//! See docs/reference/macros/sum.md and docs/reference/types/either.md. + +use cgp::prelude::*; + +// A standalone three-element sum, written with the `Sum!` sugar. +type Token = Sum![u32, String, bool]; + +// The same type spelled out in terms of the `Either`/`Void` spine. Asserting +// the two are the *same type* pins the expansion of `Sum!`. +type TokenExpanded = Either>>; + +fn _assert_same_type(token: Token) -> TokenExpanded { + // Compiles only if `Sum![u32, String, bool]` and the hand-written chain are + // the identical type. + token +} + +// A value of a `Sum!` is built by nesting `Left`/`Right` to select a branch. +fn make_u32(value: u32) -> Token { + Either::Left(value) +} + +fn make_string(value: String) -> Token { + Either::Right(Either::Left(value)) +} + +fn make_bool(value: bool) -> Token { + Either::Right(Either::Right(Either::Left(value))) +} + +// Matching a `Sum!` walks the chain; the final `Void` arm is a `match {}`, +// since `Void` can never be constructed and so needs no value. +fn describe(token: Token) -> String { + match token { + Either::Left(value) => format!("u32: {value}"), + Either::Right(Either::Left(value)) => format!("string: {value}"), + Either::Right(Either::Right(Either::Left(value))) => format!("bool: {value}"), + Either::Right(Either::Right(Either::Right(void))) => match void {}, + } +} + +#[test] +fn test_sum_variants() { + assert_eq!(describe(make_u32(42)), "u32: 42"); + assert_eq!(describe(make_string("hello".to_owned())), "string: hello"); + assert_eq!(describe(make_bool(true)), "bool: true"); +} + +// The empty sum `Sum![]` is just `Void`, a type with no values. +type EmptySum = Sum![]; + +fn _empty_is_void(value: EmptySum) -> Void { + value +} diff --git a/crates/tests/cgp-tests/tests/extensible_variants/variant_dispatch.rs b/crates/tests/cgp-tests/tests/extensible_variants/variant_dispatch.rs new file mode 100644 index 00000000..c91eee19 --- /dev/null +++ b/crates/tests/cgp-tests/tests/extensible_variants/variant_dispatch.rs @@ -0,0 +1,269 @@ +//! Dispatching an extensible-variant input to per-variant handlers. The +//! `#[derive(CgpData)]` enum exposes its variants structurally, and the dispatch +//! combinators (`MatchWithFieldHandlers`, `MatchWithValueHandlersRef`, +//! `MatchWithHandlers` with `ExtractFieldAndHandle`/`DowncastAndHandle`) route +//! each variant to a sub-handler, proving exhaustiveness without a wildcard. +//! +//! The dispatch combinators are owned by the `dispatching`/`handlers` concepts; +//! here the derives are plain scaffolding and the wiring snapshots are trimmed +//! to plain macros. This file exercises the *variant* side end-to-end: matching +//! by field, by value (ref), and via casts. +//! +//! See docs/concepts/extensible-variants.md. + +use core::convert::Infallible; +use core::fmt::Display; +use core::marker::PhantomData; + +use cgp::core::error::ErrorTypeProviderComponent; +use cgp::extra::dispatch::{ + DowncastAndHandle, ExtractFieldAndHandle, HandleFieldValue, MatchWithFieldHandlers, + MatchWithHandlers, MatchWithValueHandlersRef, +}; +use cgp::extra::handler::{Computer, ComputerComponent, ComputerRef, PromoteAsync}; +use cgp::prelude::*; +use futures::executor::block_on; + +#[derive(Debug, Eq, PartialEq, CgpData)] +pub enum FooBarBaz { + Foo(u64), + Bar(String), + Baz(bool), +} + +#[derive(Debug, Eq, PartialEq, CgpData)] +pub enum FooBar { + Foo(u64), + Bar(String), +} + +pub struct App; + +delegate_components! { + App { + ErrorTypeProviderComponent: UseType, + } +} + +#[cgp_computer] +pub fn field_to_string(Field { value, .. }: Field) -> String +where + Value: Display, +{ + value.to_string() +} + +#[cgp_computer] +pub fn value_to_string_ref(value: &Value) -> String +where + Value: Display, +{ + value.to_string() +} + +#[test] +fn test_dispatch_fields() { + let context = App; + let code = PhantomData::<()>; + + assert_eq!( + MatchWithFieldHandlers::::compute(&context, code, FooBarBaz::Foo(1)), + "1" + ); + + assert_eq!( + MatchWithFieldHandlers::::compute( + &context, + code, + FooBarBaz::Bar("hello".to_owned()) + ), + "hello" + ); + + assert_eq!( + MatchWithFieldHandlers::::compute(&context, code, FooBarBaz::Baz(true)), + "true" + ); +} + +#[test] +fn test_dispatch_values_ref() { + let context = App; + let code = PhantomData::<()>; + + assert_eq!( + MatchWithValueHandlersRef::::compute(&context, code, &FooBarBaz::Foo(1)), + "1" + ); + + assert_eq!( + MatchWithValueHandlersRef::::compute_ref( + &context, + code, + &FooBarBaz::Foo(1) + ), + "1" + ); + + assert_eq!( + MatchWithValueHandlersRef::::compute( + &context, + code, + &FooBarBaz::Bar("hello".to_owned()) + ), + "hello" + ); + + assert_eq!( + MatchWithValueHandlersRef::::compute( + &context, + code, + &FooBarBaz::Baz(true) + ), + "true" + ); +} + +#[cgp_new_provider] +impl Computer for ValueToString +where + Value: Display, +{ + type Output = String; + + fn compute(_context: &Context, _code: PhantomData, input: &Value) -> Self::Output { + input.to_string() + } +} + +#[test] +fn test_dispatch_fields_ref() { + let context = App; + let code = PhantomData::<()>; + + assert_eq!( + MatchWithValueHandlersRef::::compute(&context, code, &FooBarBaz::Foo(1)), + "1" + ); + + assert_eq!( + MatchWithValueHandlersRef::::compute( + &context, + code, + &FooBarBaz::Bar("hello".to_owned()) + ), + "hello" + ); + + assert_eq!( + MatchWithValueHandlersRef::::compute(&context, code, &FooBarBaz::Baz(true)), + "true" + ); +} + +#[test] +fn test_async_dispatch_fields() { + let context = App; + let code = PhantomData::<()>; + + assert_eq!( + block_on(MatchWithFieldHandlers::::compute_async( + &context, + code, + FooBarBaz::Foo(1) + )), + "1" + ); + + assert_eq!( + block_on(MatchWithFieldHandlers::::compute_async( + &context, + code, + FooBarBaz::Bar("hello".to_owned()) + )), + "hello" + ); + + assert_eq!( + block_on(MatchWithFieldHandlers::::compute_async( + &context, + code, + FooBarBaz::Baz(true) + )), + "true" + ); +} + +#[cgp_computer] +pub fn show_foo_bar(input: FooBar) -> String { + format!("FooBar::{input:?}") +} + +#[cgp_computer] +pub fn show_baz(input: bool) -> String { + format!("Baz({input:?})") +} + +type Computers = Product![ + ExtractFieldAndHandle>, + DowncastAndHandle, +]; + +type Handlers = Product![ + PromoteAsync>>, + PromoteAsync>, +]; + +#[test] +fn test_dispatch_computers() { + let context = App; + let code = PhantomData::<()>; + + assert_eq!( + MatchWithHandlers::::compute(&context, code, FooBarBaz::Foo(1)), + "FooBar::Foo(1)" + ); + + assert_eq!( + MatchWithHandlers::::compute(&context, code, FooBarBaz::Bar("hello".to_owned())), + "FooBar::Bar(\"hello\")" + ); + + assert_eq!( + MatchWithHandlers::::compute(&context, code, FooBarBaz::Baz(true)), + "Baz(true)" + ); +} + +#[test] +fn test_dispatch_handlers() { + let context = App; + let code = PhantomData::<()>; + + assert_eq!( + block_on(MatchWithHandlers::::compute_async( + &context, + code, + FooBarBaz::Foo(1) + )), + "FooBar::Foo(1)" + ); + + assert_eq!( + block_on(MatchWithHandlers::::compute_async( + &context, + code, + FooBarBaz::Bar("hello".to_owned()) + )), + "FooBar::Bar(\"hello\")" + ); + + assert_eq!( + block_on(MatchWithHandlers::::compute_async( + &context, + code, + FooBarBaz::Baz(true) + )), + "Baz(true)" + ); +} diff --git a/crates/tests/cgp-tests/tests/extensible_variants_tests.rs b/crates/tests/cgp-tests/tests/extensible_variants_tests.rs new file mode 100644 index 00000000..e1f35a3c --- /dev/null +++ b/crates/tests/cgp-tests/tests/extensible_variants_tests.rs @@ -0,0 +1,19 @@ +//! Entrypoint for the `extensible_variants` concept. +//! +//! Covers CGP's extensible *variant* (enum) data: `#[derive(CgpData)]` and +//! `#[derive(HasFields)]` applied to enums, the extractor family that +//! deconstructs a value variant-by-variant (`HasExtractor`/`ExtractField`/ +//! `FinalizeExtract`), the `FromVariant` constructor, the structural casts +//! (`CanUpcast`/`CanDowncast`) between enum shapes, dispatching an +//! extensible-variant input to per-variant handlers, and the `Sum!` type-level +//! sum list that underlies all of it. +//! +//! The dual concept for structs is `extensible_records`; `field_access` owns +//! the `#[derive(HasField)]` struct derive. +//! +//! See docs/concepts/extensible-variants.md, docs/reference/derives/derive_cgp_data.md, +//! docs/reference/derives/derive_from_variant.md, docs/reference/derives/derive_extract_field.md, +//! and docs/reference/macros/sum.md. +#![allow(dead_code)] + +pub mod extensible_variants; diff --git a/crates/tests/cgp-tests/tests/field_access/chain.rs b/crates/tests/cgp-tests/tests/field_access/chain.rs new file mode 100644 index 00000000..731548c6 --- /dev/null +++ b/crates/tests/cgp-tests/tests/field_access/chain.rs @@ -0,0 +1,98 @@ +//! Chained field access over two owned structs: each struct derives +//! `HasField`, and `ChainGetters, UseField<..>]>` composes +//! the two per-field impls to read `outer.inner.name` through a single call. The +//! `#[derive(HasField)]` snapshots are the point of the test; the `ChainGetters` +//! wiring is behavioral scaffolding. +//! +//! See docs/reference/derives/derive_has_field.md and +//! docs/reference/traits/has_field.md. + +use core::marker::PhantomData; + +use cgp::core::field::impls::ChainGetters; +use cgp::prelude::*; +use cgp_macro_test_util::snapshot_derive_has_field; + +snapshot_derive_has_field! { + #[derive(HasField)] + pub struct Inner { + pub name: String, + } + + expand_inner(output) { + insta::assert_snapshot!(output, @" + impl HasField>>>>> for Inner { + type Value = String; + fn get_field( + &self, + key: ::core::marker::PhantomData< + Symbol<4, Chars<'n', Chars<'a', Chars<'m', Chars<'e', Nil>>>>>, + >, + ) -> &Self::Value { + &self.name + } + } + impl HasFieldMut>>>>> + for Inner { + fn get_field_mut( + &mut self, + key: ::core::marker::PhantomData< + Symbol<4, Chars<'n', Chars<'a', Chars<'m', Chars<'e', Nil>>>>>, + >, + ) -> &mut Self::Value { + &mut self.name + } + } + ") + } +} + +snapshot_derive_has_field! { + #[derive(HasField)] + pub struct Outer { + pub inner: Inner, + } + + expand_outer(output) { + insta::assert_snapshot!(output, @" + impl HasField>>>>>> + for Outer { + type Value = Inner; + fn get_field( + &self, + key: ::core::marker::PhantomData< + Symbol<5, Chars<'i', Chars<'n', Chars<'n', Chars<'e', Chars<'r', Nil>>>>>>, + >, + ) -> &Self::Value { + &self.inner + } + } + impl HasFieldMut< + Symbol<5, Chars<'i', Chars<'n', Chars<'n', Chars<'e', Chars<'r', Nil>>>>>>, + > for Outer { + fn get_field_mut( + &mut self, + key: ::core::marker::PhantomData< + Symbol<5, Chars<'i', Chars<'n', Chars<'n', Chars<'e', Chars<'r', Nil>>>>>>, + >, + ) -> &mut Self::Value { + &mut self.inner + } + } + ") + } +} + +#[test] +fn test_chained_getter() { + let context = Outer { + inner: Inner { + name: "test".to_owned(), + }, + }; + + let name: &String = , UseField], + >>::get_field(&context, PhantomData::<()>); + assert_eq!(name, "test"); +} diff --git a/crates/tests/cgp-tests/tests/field_access/chain_deeply_nested.rs b/crates/tests/cgp-tests/tests/field_access/chain_deeply_nested.rs new file mode 100644 index 00000000..9b4bfcc2 --- /dev/null +++ b/crates/tests/cgp-tests/tests/field_access/chain_deeply_nested.rs @@ -0,0 +1,204 @@ +//! Deeply nested field access: five owned structs (`MyContext.a.b.c.d.name`), +//! each deriving `HasField`, reached in one hop by wiring a `#[cgp_getter]` to +//! `WithProvider, ..]>>`. +//! +//! This concept owns the `#[derive(HasField)]` snapshots, so every derive here +//! keeps its snapshot. The `#[cgp_getter]` trait and the +//! `delegate_and_check_components!` wiring are incidental scaffolding — their +//! expansions are owned by the `getters` and `checking` targets — so they are +//! written as the plain macros. +//! +//! See docs/reference/derives/derive_has_field.md and +//! docs/reference/traits/has_field.md. + +use cgp::core::field::impls::ChainGetters; +use cgp::prelude::*; +use cgp_macro_test_util::snapshot_derive_has_field; + +snapshot_derive_has_field! { + #[derive(HasField)] + pub struct A { + pub b: B, + } + + expand_a(output) { + insta::assert_snapshot!(output, @" + impl HasField>> for A { + type Value = B; + fn get_field( + &self, + key: ::core::marker::PhantomData>>, + ) -> &Self::Value { + &self.b + } + } + impl HasFieldMut>> for A { + fn get_field_mut( + &mut self, + key: ::core::marker::PhantomData>>, + ) -> &mut Self::Value { + &mut self.b + } + } + ") + } +} + +snapshot_derive_has_field! { + #[derive(HasField)] + pub struct B { + pub c: C, + } + + expand_b(output) { + insta::assert_snapshot!(output, @" + impl HasField>> for B { + type Value = C; + fn get_field( + &self, + key: ::core::marker::PhantomData>>, + ) -> &Self::Value { + &self.c + } + } + impl HasFieldMut>> for B { + fn get_field_mut( + &mut self, + key: ::core::marker::PhantomData>>, + ) -> &mut Self::Value { + &mut self.c + } + } + ") + } +} + +snapshot_derive_has_field! { + #[derive(HasField)] + pub struct C { + pub d: D, + } + + expand_c(output) { + insta::assert_snapshot!(output, @" + impl HasField>> for C { + type Value = D; + fn get_field( + &self, + key: ::core::marker::PhantomData>>, + ) -> &Self::Value { + &self.d + } + } + impl HasFieldMut>> for C { + fn get_field_mut( + &mut self, + key: ::core::marker::PhantomData>>, + ) -> &mut Self::Value { + &mut self.d + } + } + ") + } +} + +snapshot_derive_has_field! { + #[derive(HasField)] + pub struct D { + pub name: String, + } + + expand_d(output) { + insta::assert_snapshot!(output, @" + impl HasField>>>>> for D { + type Value = String; + fn get_field( + &self, + key: ::core::marker::PhantomData< + Symbol<4, Chars<'n', Chars<'a', Chars<'m', Chars<'e', Nil>>>>>, + >, + ) -> &Self::Value { + &self.name + } + } + impl HasFieldMut>>>>> for D { + fn get_field_mut( + &mut self, + key: ::core::marker::PhantomData< + Symbol<4, Chars<'n', Chars<'a', Chars<'m', Chars<'e', Nil>>>>>, + >, + ) -> &mut Self::Value { + &mut self.name + } + } + ") + } +} + +snapshot_derive_has_field! { + #[derive(HasField)] + pub struct MyContext { + pub a: A, + } + + expand_my_context_struct(output) { + insta::assert_snapshot!(output, @" + impl HasField>> for MyContext { + type Value = A; + fn get_field( + &self, + key: ::core::marker::PhantomData>>, + ) -> &Self::Value { + &self.a + } + } + impl HasFieldMut>> for MyContext { + fn get_field_mut( + &mut self, + key: ::core::marker::PhantomData>>, + ) -> &mut Self::Value { + &mut self.a + } + } + ") + } +} + +// Incidental scaffolding: the `#[cgp_getter]` expansion is owned by the +// `getters` target, so it is written plainly here. +#[cgp_getter] +pub trait HasName { + fn name(&self) -> &str; +} + +// Incidental scaffolding: the `delegate_and_check_components!` expansion is owned +// by the `checking` target, so it is written plainly here. +delegate_and_check_components! { + MyContext { + NameGetterComponent: WithProvider< + ChainGetters, + UseField, + UseField, + UseField, + UseField + ]>> + } +} + +#[test] +fn test_deeply_nested_getter() { + let context = MyContext { + a: A { + b: B { + c: C { + d: D { + name: "test".to_owned(), + }, + }, + }, + }, + }; + + assert_eq!(context.name(), "test"); +} diff --git a/crates/tests/cgp-tests/tests/field_access/chain_inner_life.rs b/crates/tests/cgp-tests/tests/field_access/chain_inner_life.rs new file mode 100644 index 00000000..b0601cce --- /dev/null +++ b/crates/tests/cgp-tests/tests/field_access/chain_inner_life.rs @@ -0,0 +1,100 @@ +//! Chained field access where the inner struct itself carries a lifetime +//! (`Inner<'a>` holding `&'a String`): both derives lift the lifetime onto their +//! impls, and `ChainGetters` composes them to read the borrowed name. +//! +//! See docs/reference/derives/derive_has_field.md. + +use core::marker::PhantomData; + +use cgp::core::field::impls::ChainGetters; +use cgp::prelude::*; +use cgp_macro_test_util::snapshot_derive_has_field; + +snapshot_derive_has_field! { + #[derive(HasField)] + pub struct Outer<'a> { + pub inner: Inner<'a>, + } + + expand_outer(output) { + insta::assert_snapshot!(output, @" + impl< + 'a, + > HasField>>>>>> + for Outer<'a> { + type Value = Inner<'a>; + fn get_field( + &self, + key: ::core::marker::PhantomData< + Symbol<5, Chars<'i', Chars<'n', Chars<'n', Chars<'e', Chars<'r', Nil>>>>>>, + >, + ) -> &Self::Value { + &self.inner + } + } + impl< + 'a, + > HasFieldMut>>>>>> + for Outer<'a> { + fn get_field_mut( + &mut self, + key: ::core::marker::PhantomData< + Symbol<5, Chars<'i', Chars<'n', Chars<'n', Chars<'e', Chars<'r', Nil>>>>>>, + >, + ) -> &mut Self::Value { + &mut self.inner + } + } + ") + } +} + +snapshot_derive_has_field! { + #[derive(HasField)] + pub struct Inner<'a> { + pub name: &'a String, + } + + expand_inner(output) { + insta::assert_snapshot!(output, @" + impl<'a> HasField>>>>> + for Inner<'a> { + type Value = &'a String; + fn get_field( + &self, + key: ::core::marker::PhantomData< + Symbol<4, Chars<'n', Chars<'a', Chars<'m', Chars<'e', Nil>>>>>, + >, + ) -> &Self::Value { + &self.name + } + } + impl<'a> HasFieldMut>>>>> + for Inner<'a> { + fn get_field_mut( + &mut self, + key: ::core::marker::PhantomData< + Symbol<4, Chars<'n', Chars<'a', Chars<'m', Chars<'e', Nil>>>>>, + >, + ) -> &mut Self::Value { + &mut self.name + } + } + ") + } +} + +#[test] +fn test_chained_getter_with_inner_life() { + let context = Outer { + inner: Inner { + name: &"test".to_owned(), + }, + }; + + let name: &String = , UseField], + >>::get_field(&context, PhantomData::<()>); + + assert_eq!(name, "test"); +} diff --git a/crates/tests/cgp-tests/tests/field_access/chain_outer_life.rs b/crates/tests/cgp-tests/tests/field_access/chain_outer_life.rs new file mode 100644 index 00000000..be6ae6c4 --- /dev/null +++ b/crates/tests/cgp-tests/tests/field_access/chain_outer_life.rs @@ -0,0 +1,98 @@ +//! Chained field access where the outer struct borrows the inner one +//! (`inner: &'a Inner`): the derive lifts the outer lifetime onto its impls, +//! and `ChainGetters` still resolves `outer.inner.name` across the borrow. +//! +//! See docs/reference/derives/derive_has_field.md. + +use core::marker::PhantomData; + +use cgp::core::field::impls::ChainGetters; +use cgp::prelude::*; +use cgp_macro_test_util::snapshot_derive_has_field; + +snapshot_derive_has_field! { + #[derive(HasField)] + pub struct Outer<'a> { + pub inner: &'a Inner, + } + + expand_outer(output) { + insta::assert_snapshot!(output, @" + impl< + 'a, + > HasField>>>>>> + for Outer<'a> { + type Value = &'a Inner; + fn get_field( + &self, + key: ::core::marker::PhantomData< + Symbol<5, Chars<'i', Chars<'n', Chars<'n', Chars<'e', Chars<'r', Nil>>>>>>, + >, + ) -> &Self::Value { + &self.inner + } + } + impl< + 'a, + > HasFieldMut>>>>>> + for Outer<'a> { + fn get_field_mut( + &mut self, + key: ::core::marker::PhantomData< + Symbol<5, Chars<'i', Chars<'n', Chars<'n', Chars<'e', Chars<'r', Nil>>>>>>, + >, + ) -> &mut Self::Value { + &mut self.inner + } + } + ") + } +} + +snapshot_derive_has_field! { + #[derive(HasField)] + pub struct Inner { + pub name: String, + } + + expand_inner(output) { + insta::assert_snapshot!(output, @" + impl HasField>>>>> for Inner { + type Value = String; + fn get_field( + &self, + key: ::core::marker::PhantomData< + Symbol<4, Chars<'n', Chars<'a', Chars<'m', Chars<'e', Nil>>>>>, + >, + ) -> &Self::Value { + &self.name + } + } + impl HasFieldMut>>>>> + for Inner { + fn get_field_mut( + &mut self, + key: ::core::marker::PhantomData< + Symbol<4, Chars<'n', Chars<'a', Chars<'m', Chars<'e', Nil>>>>>, + >, + ) -> &mut Self::Value { + &mut self.name + } + } + ") + } +} + +#[test] +fn test_chained_getter_with_outer_life() { + let context = Outer { + inner: &Inner { + name: "test".to_owned(), + }, + }; + + let name: &String = , UseField], + >>::get_field(&context, PhantomData::<()>); + assert_eq!(name, "test"); +} diff --git a/crates/tests/cgp-tests/tests/extensible_data_tests/has_field/index.rs b/crates/tests/cgp-tests/tests/field_access/index.rs similarity index 83% rename from crates/tests/cgp-tests/tests/extensible_data_tests/has_field/index.rs rename to crates/tests/cgp-tests/tests/field_access/index.rs index 396dbf71..d14cb815 100644 --- a/crates/tests/cgp-tests/tests/extensible_data_tests/has_field/index.rs +++ b/crates/tests/cgp-tests/tests/field_access/index.rs @@ -1,3 +1,10 @@ +//! `#[derive(HasField)]` on a tuple struct: positional fields have no string +//! name, so the derive keys each one by the type-level number `Index` +//! (`Index<0>` for `.0`, `Index<1>` for `.1`) instead of a `Symbol!`. +//! +//! See docs/reference/derives/derive_has_field.md and +//! docs/reference/types/index.md. + use cgp::prelude::*; use cgp_macro_test_util::snapshot_derive_has_field; diff --git a/crates/tests/cgp-tests/tests/field_access/index_display.rs b/crates/tests/cgp-tests/tests/field_access/index_display.rs new file mode 100644 index 00000000..b78fdb57 --- /dev/null +++ b/crates/tests/cgp-tests/tests/field_access/index_display.rs @@ -0,0 +1,12 @@ +//! Runtime behavior of the `Index` type-level number tag: an `Index` +//! value `Display`s as its underlying number, with no Greek-letter alias. +//! +//! See docs/reference/types/index.md. + +use cgp::prelude::*; + +#[test] +pub fn test_index_display() { + let val: Index<123> = Default::default(); + assert_eq!(val.to_string(), "123"); +} diff --git a/crates/tests/cgp-tests/tests/field_access/lifetime_field.rs b/crates/tests/cgp-tests/tests/field_access/lifetime_field.rs new file mode 100644 index 00000000..8a4acadd --- /dev/null +++ b/crates/tests/cgp-tests/tests/field_access/lifetime_field.rs @@ -0,0 +1,52 @@ +//! `#[derive(HasField)]` on a struct with a lifetime-carrying field: the derive +//! lifts the struct's lifetime onto the generated impls and keeps the field's +//! borrowed type (`&'a str`) as the `HasField::Value`. +//! +//! See docs/reference/derives/derive_has_field.md. + +use core::marker::PhantomData; + +use cgp::prelude::*; +use cgp_macro_test_util::snapshot_derive_has_field; + +snapshot_derive_has_field! { + #[derive(HasField)] + pub struct Context<'a> { + pub name: &'a str, + } + + expand_context(output) { + insta::assert_snapshot!(output, @" + impl<'a> HasField>>>>> + for Context<'a> { + type Value = &'a str; + fn get_field( + &self, + key: ::core::marker::PhantomData< + Symbol<4, Chars<'n', Chars<'a', Chars<'m', Chars<'e', Nil>>>>>, + >, + ) -> &Self::Value { + &self.name + } + } + impl<'a> HasFieldMut>>>>> + for Context<'a> { + fn get_field_mut( + &mut self, + key: ::core::marker::PhantomData< + Symbol<4, Chars<'n', Chars<'a', Chars<'m', Chars<'e', Nil>>>>>, + >, + ) -> &mut Self::Value { + &mut self.name + } + } + ") + } +} + +#[test] +fn test_context_with_lifetime_field() { + let context = Context { name: "test" }; + + assert_eq!(context.get_field(PhantomData), &"test"); +} diff --git a/crates/tests/cgp-tests/tests/field_access/mod.rs b/crates/tests/cgp-tests/tests/field_access/mod.rs new file mode 100644 index 00000000..0bd36450 --- /dev/null +++ b/crates/tests/cgp-tests/tests/field_access/mod.rs @@ -0,0 +1,23 @@ +//! One unit test per file. Each file is self-contained: it defines its own +//! structs, tags, and context types at module scope so that the type-level +//! impls of one test never leak into another. + +// `#[derive(HasField)]` snapshots (this concept owns the derive's expansion): +// the distinct field shapes the derive supports — named fields, tuple +// (positional) fields, and lifetime-carrying fields. +pub mod index; +pub mod lifetime_field; + +// Nested/chained field access: composing per-field `HasField` impls with +// `ChainGetters` + `UseField` to reach a deeply nested value. The +// `#[derive(HasField)]` snapshots are kept; the incidental `#[cgp_getter]` and +// `delegate_and_check_components!` scaffolding is written plainly. +pub mod chain; +pub mod chain_deeply_nested; +pub mod chain_inner_life; +pub mod chain_outer_life; + +// Runtime behavior of the type-level tags themselves (no snapshot): `Symbol!` +// and `Index` `Display`/`StaticString`. +pub mod index_display; +pub mod symbol; diff --git a/crates/tests/cgp-tests/src/tests/symbol.rs b/crates/tests/cgp-tests/tests/field_access/symbol.rs similarity index 67% rename from crates/tests/cgp-tests/src/tests/symbol.rs rename to crates/tests/cgp-tests/tests/field_access/symbol.rs index 0568cb3a..2eccbd56 100644 --- a/crates/tests/cgp-tests/src/tests/symbol.rs +++ b/crates/tests/cgp-tests/tests/field_access/symbol.rs @@ -1,3 +1,9 @@ +//! Runtime behavior of the `Symbol!` type-level string tag: a `Symbol!` value +//! `Display`s as the string it encodes, and its `StaticString::VALUE` recovers +//! the original literal — including the empty string and multi-byte Unicode. +//! +//! See docs/reference/macros/symbol.md and docs/reference/traits/has_field.md. + use cgp::core::field::traits::StaticString; use cgp::prelude::*; @@ -7,12 +13,6 @@ pub fn test_symbol_display() { assert_eq!(val.to_string(), "hello"); } -#[test] -pub fn test_index_display() { - let val: Index<123> = Default::default(); - assert_eq!(val.to_string(), "123"); -} - #[test] fn test_static_chars() { assert_eq!(::VALUE, ""); diff --git a/crates/tests/cgp-tests/tests/field_access_tests.rs b/crates/tests/cgp-tests/tests/field_access_tests.rs new file mode 100644 index 00000000..fd791f66 --- /dev/null +++ b/crates/tests/cgp-tests/tests/field_access_tests.rs @@ -0,0 +1,17 @@ +//! Entrypoint for the `field_access` concept. +//! +//! Covers tag-keyed field access: the `HasField`/`HasFieldMut` traits and the +//! `#[derive(HasField)]` derive that generates their impls from a struct's +//! fields, together with the type-level tags those impls are keyed by — +//! `Symbol!("name")` for named fields and `Index` for tuple fields. This +//! concept owns the canonical `#[derive(HasField)]` macro-expansion snapshots, +//! exercising named fields, tuple (positional) fields, lifetime-carrying fields, +//! and nested/chained field access, plus the runtime behavior of the `Symbol!` +//! and `Index` tags themselves (`Display` and `StaticString`). +//! +//! See docs/reference/traits/has_field.md, +//! docs/reference/derives/derive_has_field.md, +//! docs/reference/macros/symbol.md and docs/reference/types/index.md. +#![allow(dead_code)] + +pub mod field_access; diff --git a/crates/tests/cgp-tests/tests/generic_components/component_const.rs b/crates/tests/cgp-tests/tests/generic_components/component_const.rs new file mode 100644 index 00000000..fbe8f78f --- /dev/null +++ b/crates/tests/cgp-tests/tests/generic_components/component_const.rs @@ -0,0 +1,51 @@ +//! A `#[cgp_component]` carrying a `const` item, provided by a const-generic +//! provider `UseConstant`. +//! +//! The const generic on the provider struct flows through the generated provider +//! trait impl and its `IsProviderFor` impl unchanged; wiring the context to +//! `UseConstant<42>` fixes the constant. This file owns the const-generic +//! provider snapshot; the `delegate_and_check_components!` wiring is written +//! plainly (its expansion is owned by the `basic_delegation` / `checking` +//! concepts). +//! +//! See docs/reference/macros/cgp_component.md. + +use cgp::prelude::*; +use cgp_macro_test_util::snapshot_cgp_provider; + +#[cgp_component(ConstantGetter)] +pub trait HasConstant { + const CONSTANT: u64; +} + +pub struct UseConstant; + +snapshot_cgp_provider! { + #[cgp_provider] + impl ConstantGetter for UseConstant { + const CONSTANT: u64 = CONSTANT; + } + + expand_use_constant(output) { + insta::assert_snapshot!(output, @" + impl ConstantGetter for UseConstant { + const CONSTANT: u64 = CONSTANT; + } + impl IsProviderFor + for UseConstant {} + ") + } +} + +pub struct MyContext; + +delegate_and_check_components! { + MyContext { + ConstantGetterComponent: UseConstant<42>, + } +} + +#[test] +fn test_component_with_const() { + assert_eq!(::CONSTANT, 42); +} diff --git a/crates/tests/cgp-tests/tests/generic_components/component_generic_const.rs b/crates/tests/cgp-tests/tests/generic_components/component_generic_const.rs new file mode 100644 index 00000000..4ff0cd3f --- /dev/null +++ b/crates/tests/cgp-tests/tests/generic_components/component_generic_const.rs @@ -0,0 +1,73 @@ +//! A `#[cgp_component]` whose `const` item has an abstract type +//! (`const CONSTANT: Self::Unit`), provided by a const-generic provider +//! constrained on that abstract type. +//! +//! This combines a const generic on the provider (`UseConstant`) +//! with an impl-side dependency (`Context: HasUnitType`) that ties the +//! const's type to the context's abstract `Unit`. The const-generic provider +//! snapshot is owned here; the `#[cgp_type]`, `delegate_components!`, and +//! `check_components!` wiring is written plainly (owned by the `abstract_types`, +//! `basic_delegation`, and `checking` concepts). +//! +//! See docs/reference/macros/cgp_component.md. + +use cgp::prelude::*; +use cgp_macro_test_util::snapshot_cgp_provider; + +#[cgp_type] +pub trait HasUnitType { + type Unit; +} + +#[cgp_component(ConstantGetter)] +pub trait HasConstant: HasUnitType { + const CONSTANT: Self::Unit; +} + +pub struct UseConstant; + +snapshot_cgp_provider! { + #[cgp_provider] + impl ConstantGetter for UseConstant + where + Context: HasUnitType, + { + const CONSTANT: u64 = CONSTANT; + } + + expand_use_constant(output) { + insta::assert_snapshot!(output, @" + impl ConstantGetter for UseConstant + where + Context: HasUnitType, + { + const CONSTANT: u64 = CONSTANT; + } + impl IsProviderFor + for UseConstant + where + Context: HasUnitType, + {} + ") + } +} + +pub struct MyContext; + +delegate_components! { + MyContext { + UnitTypeProviderComponent: UseType, + ConstantGetterComponent: UseConstant<42>, + } +} + +check_components! { + MyContext { + ConstantGetterComponent, + } +} + +#[test] +fn test_component_with_generic_const() { + assert_eq!(::CONSTANT, 42); +} diff --git a/crates/tests/cgp-tests/tests/component_tests/cgp_component/lifetime.rs b/crates/tests/cgp-tests/tests/generic_components/component_lifetime.rs similarity index 83% rename from crates/tests/cgp-tests/tests/component_tests/cgp_component/lifetime.rs rename to crates/tests/cgp-tests/tests/generic_components/component_lifetime.rs index 6806116e..ed58a455 100644 --- a/crates/tests/cgp-tests/tests/component_tests/cgp_component/lifetime.rs +++ b/crates/tests/cgp-tests/tests/generic_components/component_lifetime.rs @@ -1,7 +1,18 @@ +//! `#[cgp_component]` on a trait with a lifetime and a type parameter. +//! +//! When a component carries a lifetime, the generated provider trait keeps the +//! lifetime ahead of `__Context__` and the lifetime is lifted into `Life<'a>` in +//! the `IsProviderFor` params tuple (`(Life<'a>, T)`); the `RedirectLookup` impl +//! appends the type parameter to the lookup path via `ConcatPath`. This is the +//! reference snapshot for the lifetime/type-parameter variant of the macro, plus +//! the matching lifetime-carrying `UseField` provider. The `#[cgp_impl]`, +//! `delegate_components!`, and `check_components!` wiring below is written plainly +//! (its expansion is owned by the `basic_delegation` and `checking` concepts). +//! +//! See docs/reference/macros/cgp_component.md and docs/reference/types/life.md. + use cgp::prelude::*; -use cgp_macro_test_util::{ - snapshot_cgp_component, snapshot_cgp_provider, snapshot_check_components, -}; +use cgp_macro_test_util::{snapshot_cgp_component, snapshot_cgp_provider}; snapshot_cgp_component! { #[cgp_component(ReferenceGetter)] @@ -156,21 +167,9 @@ delegate_components! { } } -snapshot_check_components! { - check_components! { - <'a> App<'a> { - ReferenceGetterComponent: - (Life<'a>, str), - } - } - - expand_check_app(output) { - insta::assert_snapshot!(output, @" - trait __CheckApp< - __Component__, - __Params__: ?Sized, - >: CanUseComponent<__Component__, __Params__> {} - impl<'a> __CheckApp, str)> for App<'a> {} - ") +check_components! { + <'a> App<'a> { + ReferenceGetterComponent: + (Life<'a>, str), } } diff --git a/crates/tests/cgp-tests/tests/cgp_fn_tests/generics.rs b/crates/tests/cgp-tests/tests/generic_components/fn_generic_param.rs similarity index 85% rename from crates/tests/cgp-tests/tests/cgp_fn_tests/generics.rs rename to crates/tests/cgp-tests/tests/generic_components/fn_generic_param.rs index c9f1a0e9..4a5006fc 100644 --- a/crates/tests/cgp-tests/tests/cgp_fn_tests/generics.rs +++ b/crates/tests/cgp-tests/tests/generic_components/fn_generic_param.rs @@ -1,3 +1,12 @@ +//! `#[cgp_fn]` on a function generic over a type parameter `Scalar`. +//! +//! The generic parameter moves onto the generated trait (`RectangleArea`) +//! and impl, its `where` clause becomes an impl-side dependency, and the +//! `#[implicit]` arguments are pulled from the context's `width`/`height` fields. +//! This is the reference snapshot for a `#[cgp_fn]` carrying a type parameter. +//! +//! See docs/reference/macros/cgp_fn.md. + use std::ops::Mul; use cgp::prelude::*; diff --git a/crates/tests/cgp-tests/tests/cgp_fn_tests/impl_generics.rs b/crates/tests/cgp-tests/tests/generic_components/fn_impl_generics.rs similarity index 78% rename from crates/tests/cgp-tests/tests/cgp_fn_tests/impl_generics.rs rename to crates/tests/cgp-tests/tests/generic_components/fn_impl_generics.rs index af4bae12..1e00cb9b 100644 --- a/crates/tests/cgp-tests/tests/cgp_fn_tests/impl_generics.rs +++ b/crates/tests/cgp-tests/tests/generic_components/fn_impl_generics.rs @@ -1,3 +1,13 @@ +//! `#[cgp_fn]` with `#[impl_generics(...)]`: a generic parameter that appears +//! only on the impl, not on the generated trait. +//! +//! `greet` reads a `&Name` field implicitly, but `Name` is bound only where the +//! impl needs it, so the trait stays parameter-free (`Greet`) while the impl +//! carries `Name: Display`. A second `#[cgp_fn]` (`test_greet`) then imports the +//! `Greet` capability with `#[uses(...)]` and drives the runtime assertion. +//! +//! See docs/reference/macros/cgp_fn.md. + use core::fmt::Display; use cgp::prelude::*; diff --git a/crates/tests/cgp-tests/tests/generic_components/mod.rs b/crates/tests/cgp-tests/tests/generic_components/mod.rs new file mode 100644 index 00000000..6d5ac472 --- /dev/null +++ b/crates/tests/cgp-tests/tests/generic_components/mod.rs @@ -0,0 +1,16 @@ +//! One unit test per file. Each file is self-contained: it defines its own +//! components, providers, and context types at module scope so that the +//! type-level wiring of one test never leaks into another. + +// Generic-parameter variants of `#[cgp_fn]` (this concept owns their snapshots): +// a function generic over a type parameter, and one whose impl generics are +// declared with `#[impl_generics(...)]`. +pub mod fn_generic_param; +pub mod fn_impl_generics; + +// Generic-parameter variants of `#[cgp_component]` (this concept owns their +// snapshots): a component with a lifetime and type parameter, and components +// carrying const generics (a plain const and a const of an abstract type). +pub mod component_const; +pub mod component_generic_const; +pub mod component_lifetime; diff --git a/crates/tests/cgp-tests/tests/generic_components_tests.rs b/crates/tests/cgp-tests/tests/generic_components_tests.rs new file mode 100644 index 00000000..289cad2b --- /dev/null +++ b/crates/tests/cgp-tests/tests/generic_components_tests.rs @@ -0,0 +1,15 @@ +//! Entrypoint for the `generic_components` concept. +//! +//! Covers components and functions that carry generic parameters: type +//! parameters, lifetimes, and const generics. This concept owns the +//! generic-parameter variants of the `#[cgp_component]` and `#[cgp_fn]` +//! macro-expansion snapshots — how a leading `__Context__` parameter is threaded +//! alongside user generics, how lifetimes are lifted into `Life<'a>` in the +//! `IsProviderFor` params tuple, and how const generics flow onto the generated +//! provider trait and its providers. +//! +//! See docs/reference/macros/cgp_component.md, docs/reference/macros/cgp_fn.md, +//! docs/reference/types/life.md, and docs/concepts/higher-order-providers.md. +#![allow(dead_code)] + +pub mod generic_components; diff --git a/crates/tests/cgp-tests/tests/getter.rs b/crates/tests/cgp-tests/tests/getter.rs deleted file mode 100644 index 7bc4e98e..00000000 --- a/crates/tests/cgp-tests/tests/getter.rs +++ /dev/null @@ -1,3 +0,0 @@ -#![allow(clippy::disallowed_names)] - -pub mod getter_tests; diff --git a/crates/tests/cgp-tests/tests/getter_tests/abstract_type/import.rs b/crates/tests/cgp-tests/tests/getter_tests/abstract_type/import.rs deleted file mode 100644 index ef603bbb..00000000 --- a/crates/tests/cgp-tests/tests/getter_tests/abstract_type/import.rs +++ /dev/null @@ -1,339 +0,0 @@ -use cgp_macro_test_util::{snapshot_cgp_auto_getter, snapshot_cgp_getter, snapshot_cgp_type}; - -snapshot_cgp_type! { - #[cgp_type] - pub trait HasScalarType { - type Scalar: Copy; - } - - expand_has_scalar_type(output) { - insta::assert_snapshot!(output, @" - pub trait HasScalarType { - type Scalar: Copy; - } - impl<__Context__> HasScalarType for __Context__ - where - __Context__: ScalarTypeProvider<__Context__>, - { - type Scalar = <__Context__ as ScalarTypeProvider<__Context__>>::Scalar; - } - pub trait ScalarTypeProvider< - __Context__, - >: IsProviderFor { - type Scalar: Copy; - } - impl<__Provider__, __Context__> ScalarTypeProvider<__Context__> for __Provider__ - where - __Provider__: DelegateComponent - + IsProviderFor, - <__Provider__ as DelegateComponent< - ScalarTypeProviderComponent, - >>::Delegate: ScalarTypeProvider<__Context__>, - { - type Scalar = <<__Provider__ as DelegateComponent< - ScalarTypeProviderComponent, - >>::Delegate as ScalarTypeProvider<__Context__>>::Scalar; - } - pub struct ScalarTypeProviderComponent; - impl<__Context__> ScalarTypeProvider<__Context__> for UseContext - where - __Context__: HasScalarType, - { - type Scalar = <__Context__ as HasScalarType>::Scalar; - } - impl<__Context__> IsProviderFor - for UseContext - where - __Context__: HasScalarType, - {} - impl<__Context__, __Components__, __Path__> ScalarTypeProvider<__Context__> - for RedirectLookup<__Components__, __Path__> - where - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent< - __Path__, - >>::Delegate: ScalarTypeProvider<__Context__>, - { - type Scalar = <<__Components__ as DelegateComponent< - __Path__, - >>::Delegate as ScalarTypeProvider<__Context__>>::Scalar; - } - impl< - __Context__, - __Components__, - __Path__, - > IsProviderFor - for RedirectLookup<__Components__, __Path__> - where - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent< - __Path__, - >>::Delegate: IsProviderFor - + ScalarTypeProvider<__Context__>, - {} - impl ScalarTypeProvider<__Context__> for UseType - where - Scalar: Copy, - { - type Scalar = Scalar; - } - impl IsProviderFor - for UseType - where - Scalar: Copy, - {} - impl<__Provider__, Scalar, __Context__> ScalarTypeProvider<__Context__> - for WithProvider<__Provider__> - where - Scalar: Copy, - __Provider__: TypeProvider<__Context__, ScalarTypeProviderComponent, Type = Scalar>, - { - type Scalar = Scalar; - } - impl< - __Provider__, - Scalar, - __Context__, - > IsProviderFor - for WithProvider<__Provider__> - where - Scalar: Copy, - __Provider__: TypeProvider<__Context__, ScalarTypeProviderComponent, Type = Scalar>, - {} - ") - } -} - -snapshot_cgp_auto_getter! { - #[cgp_auto_getter] - #[use_type(HasScalarType::Scalar)] - pub trait AutoRectangleFields { - fn width(&self) -> Scalar; - - fn height(&self) -> Scalar; - } - - expand_auto_rectangle_fields(output) { - insta::assert_snapshot!(output, @" - pub trait AutoRectangleFields: HasScalarType { - fn width(&self) -> ::Scalar; - fn height(&self) -> ::Scalar; - } - impl<__Context__> AutoRectangleFields for __Context__ - where - __Context__: HasScalarType, - __Context__: HasField< - Symbol<5, Chars<'w', Chars<'i', Chars<'d', Chars<'t', Chars<'h', Nil>>>>>>, - Value = <__Context__ as HasScalarType>::Scalar, - >, - __Context__: HasField< - Symbol< - 6, - Chars<'h', Chars<'e', Chars<'i', Chars<'g', Chars<'h', Chars<'t', Nil>>>>>>, - >, - Value = <__Context__ as HasScalarType>::Scalar, - >, - { - fn width(&self) -> <__Context__ as HasScalarType>::Scalar { - self.get_field( - ::core::marker::PhantomData::< - Symbol< - 5, - Chars<'w', Chars<'i', Chars<'d', Chars<'t', Chars<'h', Nil>>>>>, - >, - >, - ) - .clone() - } - fn height(&self) -> <__Context__ as HasScalarType>::Scalar { - self.get_field( - ::core::marker::PhantomData::< - Symbol< - 6, - Chars< - 'h', - Chars< - 'e', - Chars<'i', Chars<'g', Chars<'h', Chars<'t', Nil>>>>, - >, - >, - >, - >, - ) - .clone() - } - } - ") - } -} - -snapshot_cgp_getter! { - #[cgp_getter(RectangleFieldsGetter)] - #[use_type(HasScalarType::Scalar)] - pub trait HasRectangleFields { - fn width(&self) -> Scalar; - - fn height(&self) -> Scalar; - } - - expand_has_rectangle_fields(output) { - insta::assert_snapshot!(output, @" - pub trait HasRectangleFields: HasScalarType { - fn width(&self) -> ::Scalar; - fn height(&self) -> ::Scalar; - } - impl<__Context__> HasRectangleFields for __Context__ - where - __Context__: HasScalarType, - __Context__: RectangleFieldsGetter<__Context__>, - { - fn width(&self) -> ::Scalar { - __Context__::width(self) - } - fn height(&self) -> ::Scalar { - __Context__::height(self) - } - } - pub trait RectangleFieldsGetter< - __Context__, - >: IsProviderFor - where - __Context__: HasScalarType, - { - fn width(__context__: &__Context__) -> <__Context__ as HasScalarType>::Scalar; - fn height(__context__: &__Context__) -> <__Context__ as HasScalarType>::Scalar; - } - impl<__Provider__, __Context__> RectangleFieldsGetter<__Context__> for __Provider__ - where - __Context__: HasScalarType, - __Provider__: DelegateComponent - + IsProviderFor, - <__Provider__ as DelegateComponent< - RectangleFieldsGetterComponent, - >>::Delegate: RectangleFieldsGetter<__Context__>, - { - fn width(__context__: &__Context__) -> <__Context__ as HasScalarType>::Scalar { - <__Provider__ as DelegateComponent< - RectangleFieldsGetterComponent, - >>::Delegate::width(__context__) - } - fn height(__context__: &__Context__) -> <__Context__ as HasScalarType>::Scalar { - <__Provider__ as DelegateComponent< - RectangleFieldsGetterComponent, - >>::Delegate::height(__context__) - } - } - pub struct RectangleFieldsGetterComponent; - impl<__Context__> RectangleFieldsGetter<__Context__> for UseContext - where - __Context__: HasScalarType, - __Context__: HasRectangleFields, - { - fn width(__context__: &__Context__) -> <__Context__ as HasScalarType>::Scalar { - __Context__::width(__context__) - } - fn height(__context__: &__Context__) -> <__Context__ as HasScalarType>::Scalar { - __Context__::height(__context__) - } - } - impl<__Context__> IsProviderFor - for UseContext - where - __Context__: HasScalarType, - __Context__: HasRectangleFields, - {} - impl<__Context__, __Components__, __Path__> RectangleFieldsGetter<__Context__> - for RedirectLookup<__Components__, __Path__> - where - __Context__: HasScalarType, - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent< - __Path__, - >>::Delegate: RectangleFieldsGetter<__Context__>, - { - fn width(__context__: &__Context__) -> <__Context__ as HasScalarType>::Scalar { - <__Components__ as DelegateComponent<__Path__>>::Delegate::width(__context__) - } - fn height(__context__: &__Context__) -> <__Context__ as HasScalarType>::Scalar { - <__Components__ as DelegateComponent<__Path__>>::Delegate::height(__context__) - } - } - impl< - __Context__, - __Components__, - __Path__, - > IsProviderFor - for RedirectLookup<__Components__, __Path__> - where - __Context__: HasScalarType, - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent< - __Path__, - >>::Delegate: IsProviderFor - + RectangleFieldsGetter<__Context__>, - {} - impl<__Context__> RectangleFieldsGetter<__Context__> for UseFields - where - __Context__: HasScalarType, - __Context__: HasField< - Symbol<5, Chars<'w', Chars<'i', Chars<'d', Chars<'t', Chars<'h', Nil>>>>>>, - Value = <__Context__ as HasScalarType>::Scalar, - >, - __Context__: HasField< - Symbol< - 6, - Chars<'h', Chars<'e', Chars<'i', Chars<'g', Chars<'h', Chars<'t', Nil>>>>>>, - >, - Value = <__Context__ as HasScalarType>::Scalar, - >, - { - fn width(__context__: &__Context__) -> <__Context__ as HasScalarType>::Scalar { - __context__ - .get_field( - ::core::marker::PhantomData::< - Symbol< - 5, - Chars<'w', Chars<'i', Chars<'d', Chars<'t', Chars<'h', Nil>>>>>, - >, - >, - ) - .clone() - } - fn height(__context__: &__Context__) -> <__Context__ as HasScalarType>::Scalar { - __context__ - .get_field( - ::core::marker::PhantomData::< - Symbol< - 6, - Chars< - 'h', - Chars< - 'e', - Chars<'i', Chars<'g', Chars<'h', Chars<'t', Nil>>>>, - >, - >, - >, - >, - ) - .clone() - } - } - impl<__Context__> IsProviderFor - for UseFields - where - __Context__: HasScalarType, - __Context__: HasField< - Symbol<5, Chars<'w', Chars<'i', Chars<'d', Chars<'t', Chars<'h', Nil>>>>>>, - Value = <__Context__ as HasScalarType>::Scalar, - >, - __Context__: HasField< - Symbol< - 6, - Chars<'h', Chars<'e', Chars<'i', Chars<'g', Chars<'h', Chars<'t', Nil>>>>>>, - >, - Value = <__Context__ as HasScalarType>::Scalar, - >, - {} - ") - } -} diff --git a/crates/tests/cgp-tests/tests/getter_tests/abstract_type/mod.rs b/crates/tests/cgp-tests/tests/getter_tests/abstract_type/mod.rs deleted file mode 100644 index f4ccf34b..00000000 --- a/crates/tests/cgp-tests/tests/getter_tests/abstract_type/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod explicit; -pub mod import; -pub mod use_type; diff --git a/crates/tests/cgp-tests/tests/getter_tests/assoc_type/mod.rs b/crates/tests/cgp-tests/tests/getter_tests/assoc_type/mod.rs deleted file mode 100644 index 5661cac9..00000000 --- a/crates/tests/cgp-tests/tests/getter_tests/assoc_type/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub mod auto_getter; -pub mod getter; -pub mod self_referential; -pub mod self_referential_auto; diff --git a/crates/tests/cgp-tests/tests/getter_tests/clone.rs b/crates/tests/cgp-tests/tests/getter_tests/clone.rs deleted file mode 100644 index 25599ccc..00000000 --- a/crates/tests/cgp-tests/tests/getter_tests/clone.rs +++ /dev/null @@ -1,475 +0,0 @@ -mod clone_getter { - use cgp::prelude::*; - use cgp_macro_test_util::{ - snapshot_cgp_getter, snapshot_cgp_type, snapshot_delegate_components, - }; - - snapshot_cgp_type! { - #[cgp_type] - pub trait HasNameType { - type Name; - } - - expand_has_name_type(output) { - insta::assert_snapshot!(output, @" - pub trait HasNameType { - type Name; - } - impl<__Context__> HasNameType for __Context__ - where - __Context__: NameTypeProvider<__Context__>, - { - type Name = <__Context__ as NameTypeProvider<__Context__>>::Name; - } - pub trait NameTypeProvider< - __Context__, - >: IsProviderFor { - type Name; - } - impl<__Provider__, __Context__> NameTypeProvider<__Context__> for __Provider__ - where - __Provider__: DelegateComponent - + IsProviderFor, - <__Provider__ as DelegateComponent< - NameTypeProviderComponent, - >>::Delegate: NameTypeProvider<__Context__>, - { - type Name = <<__Provider__ as DelegateComponent< - NameTypeProviderComponent, - >>::Delegate as NameTypeProvider<__Context__>>::Name; - } - pub struct NameTypeProviderComponent; - impl<__Context__> NameTypeProvider<__Context__> for UseContext - where - __Context__: HasNameType, - { - type Name = <__Context__ as HasNameType>::Name; - } - impl<__Context__> IsProviderFor - for UseContext - where - __Context__: HasNameType, - {} - impl<__Context__, __Components__, __Path__> NameTypeProvider<__Context__> - for RedirectLookup<__Components__, __Path__> - where - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent< - __Path__, - >>::Delegate: NameTypeProvider<__Context__>, - { - type Name = <<__Components__ as DelegateComponent< - __Path__, - >>::Delegate as NameTypeProvider<__Context__>>::Name; - } - impl< - __Context__, - __Components__, - __Path__, - > IsProviderFor - for RedirectLookup<__Components__, __Path__> - where - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent< - __Path__, - >>::Delegate: IsProviderFor - + NameTypeProvider<__Context__>, - {} - impl NameTypeProvider<__Context__> for UseType { - type Name = Name; - } - impl IsProviderFor - for UseType {} - impl<__Provider__, Name, __Context__> NameTypeProvider<__Context__> - for WithProvider<__Provider__> - where - __Provider__: TypeProvider<__Context__, NameTypeProviderComponent, Type = Name>, - { - type Name = Name; - } - impl< - __Provider__, - Name, - __Context__, - > IsProviderFor - for WithProvider<__Provider__> - where - __Provider__: TypeProvider<__Context__, NameTypeProviderComponent, Type = Name>, - {} - ") - } - } - - snapshot_cgp_getter! { - #[cgp_getter] - pub trait HasName: HasNameType { - fn name(&self) -> Self::Name; - } - - expand_has_name(output) { - insta::assert_snapshot!(output, @" - pub trait HasName: HasNameType { - fn name(&self) -> Self::Name; - } - impl<__Context__> HasName for __Context__ - where - __Context__: HasNameType, - __Context__: NameGetter<__Context__>, - { - fn name(&self) -> Self::Name { - __Context__::name(self) - } - } - pub trait NameGetter<__Context__>: IsProviderFor - where - __Context__: HasNameType, - { - fn name(__context__: &__Context__) -> __Context__::Name; - } - impl<__Provider__, __Context__> NameGetter<__Context__> for __Provider__ - where - __Context__: HasNameType, - __Provider__: DelegateComponent - + IsProviderFor, - <__Provider__ as DelegateComponent< - NameGetterComponent, - >>::Delegate: NameGetter<__Context__>, - { - fn name(__context__: &__Context__) -> __Context__::Name { - <__Provider__ as DelegateComponent< - NameGetterComponent, - >>::Delegate::name(__context__) - } - } - pub struct NameGetterComponent; - impl<__Context__> NameGetter<__Context__> for UseContext - where - __Context__: HasNameType, - __Context__: HasName, - { - fn name(__context__: &__Context__) -> __Context__::Name { - __Context__::name(__context__) - } - } - impl<__Context__> IsProviderFor for UseContext - where - __Context__: HasNameType, - __Context__: HasName, - {} - impl<__Context__, __Components__, __Path__> NameGetter<__Context__> - for RedirectLookup<__Components__, __Path__> - where - __Context__: HasNameType, - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent<__Path__>>::Delegate: NameGetter<__Context__>, - { - fn name(__context__: &__Context__) -> __Context__::Name { - <__Components__ as DelegateComponent<__Path__>>::Delegate::name(__context__) - } - } - impl< - __Context__, - __Components__, - __Path__, - > IsProviderFor - for RedirectLookup<__Components__, __Path__> - where - __Context__: HasNameType, - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent< - __Path__, - >>::Delegate: IsProviderFor - + NameGetter<__Context__>, - {} - impl<__Context__> NameGetter<__Context__> for UseFields - where - __Context__: HasNameType, - __Context__: HasField< - Symbol<4, Chars<'n', Chars<'a', Chars<'m', Chars<'e', Nil>>>>>, - Value = __Context__::Name, - >, - { - fn name(__context__: &__Context__) -> __Context__::Name { - __context__ - .get_field( - ::core::marker::PhantomData::< - Symbol<4, Chars<'n', Chars<'a', Chars<'m', Chars<'e', Nil>>>>>, - >, - ) - .clone() - } - } - impl<__Context__> IsProviderFor for UseFields - where - __Context__: HasNameType, - __Context__: HasField< - Symbol<4, Chars<'n', Chars<'a', Chars<'m', Chars<'e', Nil>>>>>, - Value = __Context__::Name, - >, - {} - impl<__Context__, __Tag__> NameGetter<__Context__> for UseField<__Tag__> - where - __Context__: HasNameType, - __Context__: HasField<__Tag__, Value = __Context__::Name>, - { - fn name(__context__: &__Context__) -> __Context__::Name { - __context__.get_field(::core::marker::PhantomData::<__Tag__>).clone() - } - } - impl<__Context__, __Tag__> IsProviderFor - for UseField<__Tag__> - where - __Context__: HasNameType, - __Context__: HasField<__Tag__, Value = __Context__::Name>, - {} - impl<__Context__, __Provider__> NameGetter<__Context__> for WithProvider<__Provider__> - where - __Context__: HasNameType, - __Provider__: FieldGetter< - __Context__, - NameGetterComponent, - Value = __Context__::Name, - >, - { - fn name(__context__: &__Context__) -> __Context__::Name { - __Provider__::get_field( - __context__, - ::core::marker::PhantomData::, - ) - .clone() - } - } - impl<__Context__, __Provider__> IsProviderFor - for WithProvider<__Provider__> - where - __Context__: HasNameType, - __Provider__: FieldGetter< - __Context__, - NameGetterComponent, - Value = __Context__::Name, - >, - {} - ") - } - } - - #[derive(HasField)] - pub struct App { - pub name: &'static str, - } - - snapshot_delegate_components! { - delegate_components! { - App { - NameTypeProviderComponent: UseType<&'static str>, - NameGetterComponent: UseField, - } - } - - expand_app(output) { - insta::assert_snapshot!(output, @r#" - impl DelegateComponent for App { - type Delegate = UseType<&'static str>; - } - impl< - __Context__, - __Params__, - > IsProviderFor for App - where - UseType< - &'static str, - >: IsProviderFor, - {} - impl DelegateComponent for App { - type Delegate = UseField; - } - impl<__Context__, __Params__> IsProviderFor - for App - where - UseField< - Symbol!("name"), - >: IsProviderFor, - {} - "#) - } - } - - #[test] - pub fn test_clone_getter() { - let context = App { name: "Alice" }; - - assert_eq!(context.name(), "Alice"); - } -} - -mod clone_auto_getter { - use cgp::prelude::*; - use cgp_macro_test_util::{ - snapshot_cgp_auto_getter, snapshot_cgp_type, snapshot_delegate_components, - }; - - snapshot_cgp_type! { - #[cgp_type] - pub trait HasNameType { - type Name; - } - - expand_has_name_type(output) { - insta::assert_snapshot!(output, @" - pub trait HasNameType { - type Name; - } - impl<__Context__> HasNameType for __Context__ - where - __Context__: NameTypeProvider<__Context__>, - { - type Name = <__Context__ as NameTypeProvider<__Context__>>::Name; - } - pub trait NameTypeProvider< - __Context__, - >: IsProviderFor { - type Name; - } - impl<__Provider__, __Context__> NameTypeProvider<__Context__> for __Provider__ - where - __Provider__: DelegateComponent - + IsProviderFor, - <__Provider__ as DelegateComponent< - NameTypeProviderComponent, - >>::Delegate: NameTypeProvider<__Context__>, - { - type Name = <<__Provider__ as DelegateComponent< - NameTypeProviderComponent, - >>::Delegate as NameTypeProvider<__Context__>>::Name; - } - pub struct NameTypeProviderComponent; - impl<__Context__> NameTypeProvider<__Context__> for UseContext - where - __Context__: HasNameType, - { - type Name = <__Context__ as HasNameType>::Name; - } - impl<__Context__> IsProviderFor - for UseContext - where - __Context__: HasNameType, - {} - impl<__Context__, __Components__, __Path__> NameTypeProvider<__Context__> - for RedirectLookup<__Components__, __Path__> - where - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent< - __Path__, - >>::Delegate: NameTypeProvider<__Context__>, - { - type Name = <<__Components__ as DelegateComponent< - __Path__, - >>::Delegate as NameTypeProvider<__Context__>>::Name; - } - impl< - __Context__, - __Components__, - __Path__, - > IsProviderFor - for RedirectLookup<__Components__, __Path__> - where - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent< - __Path__, - >>::Delegate: IsProviderFor - + NameTypeProvider<__Context__>, - {} - impl NameTypeProvider<__Context__> for UseType { - type Name = Name; - } - impl IsProviderFor - for UseType {} - impl<__Provider__, Name, __Context__> NameTypeProvider<__Context__> - for WithProvider<__Provider__> - where - __Provider__: TypeProvider<__Context__, NameTypeProviderComponent, Type = Name>, - { - type Name = Name; - } - impl< - __Provider__, - Name, - __Context__, - > IsProviderFor - for WithProvider<__Provider__> - where - __Provider__: TypeProvider<__Context__, NameTypeProviderComponent, Type = Name>, - {} - ") - } - } - - snapshot_cgp_auto_getter! { - #[cgp_auto_getter] - pub trait HasName: HasNameType { - fn name(&self) -> Self::Name; - } - - expand_has_name(output) { - insta::assert_snapshot!(output, @" - pub trait HasName: HasNameType { - fn name(&self) -> Self::Name; - } - impl<__Context__> HasName for __Context__ - where - __Context__: HasNameType, - __Context__: HasField< - Symbol<4, Chars<'n', Chars<'a', Chars<'m', Chars<'e', Nil>>>>>, - Value = __Context__::Name, - >, - { - fn name(&self) -> __Context__::Name { - self.get_field( - ::core::marker::PhantomData::< - Symbol<4, Chars<'n', Chars<'a', Chars<'m', Chars<'e', Nil>>>>>, - >, - ) - .clone() - } - } - ") - } - } - - #[derive(HasField)] - pub struct App { - pub name: &'static str, - } - - snapshot_delegate_components! { - delegate_components! { - App { - NameTypeProviderComponent: UseType<&'static str>, - } - } - - expand_app(output) { - insta::assert_snapshot!(output, @" - impl DelegateComponent for App { - type Delegate = UseType<&'static str>; - } - impl< - __Context__, - __Params__, - > IsProviderFor for App - where - UseType< - &'static str, - >: IsProviderFor, - {} - ") - } - } - - #[test] - pub fn test_clone_auto_getter() { - let context = App { name: "Alice" }; - - assert_eq!(context.name(), "Alice"); - } -} diff --git a/crates/tests/cgp-tests/tests/getter_tests/mod.rs b/crates/tests/cgp-tests/tests/getter_tests/mod.rs deleted file mode 100644 index 30fc034d..00000000 --- a/crates/tests/cgp-tests/tests/getter_tests/mod.rs +++ /dev/null @@ -1,10 +0,0 @@ -pub mod abstract_type; -pub mod assoc_type; -pub mod auto_generics; -pub mod clone; -pub mod mref; -pub mod non_self; -pub mod non_self_auto; -pub mod option; -pub mod slice; -pub mod string; diff --git a/crates/tests/cgp-tests/tests/getter_tests/mref.rs b/crates/tests/cgp-tests/tests/getter_tests/mref.rs deleted file mode 100644 index f703e842..00000000 --- a/crates/tests/cgp-tests/tests/getter_tests/mref.rs +++ /dev/null @@ -1,221 +0,0 @@ -mod mref_getter { - use cgp::core::field::types::MRef; - use cgp::prelude::*; - use cgp_macro_test_util::{snapshot_cgp_getter, snapshot_delegate_components}; - - snapshot_cgp_getter! { - #[cgp_getter] - pub trait HasFoo { - fn foo(&self) -> MRef<'_, String>; - } - - expand_has_foo(output) { - insta::assert_snapshot!(output, @" - pub trait HasFoo { - fn foo(&self) -> MRef<'_, String>; - } - impl<__Context__> HasFoo for __Context__ - where - __Context__: FooGetter<__Context__>, - { - fn foo(&self) -> MRef<'_, String> { - __Context__::foo(self) - } - } - pub trait FooGetter<__Context__>: IsProviderFor { - fn foo(__context__: &__Context__) -> MRef<'_, String>; - } - impl<__Provider__, __Context__> FooGetter<__Context__> for __Provider__ - where - __Provider__: DelegateComponent - + IsProviderFor, - <__Provider__ as DelegateComponent< - FooGetterComponent, - >>::Delegate: FooGetter<__Context__>, - { - fn foo(__context__: &__Context__) -> MRef<'_, String> { - <__Provider__ as DelegateComponent< - FooGetterComponent, - >>::Delegate::foo(__context__) - } - } - pub struct FooGetterComponent; - impl<__Context__> FooGetter<__Context__> for UseContext - where - __Context__: HasFoo, - { - fn foo(__context__: &__Context__) -> MRef<'_, String> { - __Context__::foo(__context__) - } - } - impl<__Context__> IsProviderFor for UseContext - where - __Context__: HasFoo, - {} - impl<__Context__, __Components__, __Path__> FooGetter<__Context__> - for RedirectLookup<__Components__, __Path__> - where - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent<__Path__>>::Delegate: FooGetter<__Context__>, - { - fn foo(__context__: &__Context__) -> MRef<'_, String> { - <__Components__ as DelegateComponent<__Path__>>::Delegate::foo(__context__) - } - } - impl< - __Context__, - __Components__, - __Path__, - > IsProviderFor - for RedirectLookup<__Components__, __Path__> - where - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent< - __Path__, - >>::Delegate: IsProviderFor - + FooGetter<__Context__>, - {} - impl<__Context__> FooGetter<__Context__> for UseFields - where - __Context__: HasField< - Symbol<3, Chars<'f', Chars<'o', Chars<'o', Nil>>>>, - Value = String, - >, - { - fn foo(__context__: &__Context__) -> MRef<'_, String> { - MRef::Ref( - __context__ - .get_field( - ::core::marker::PhantomData::< - Symbol<3, Chars<'f', Chars<'o', Chars<'o', Nil>>>>, - >, - ), - ) - } - } - impl<__Context__> IsProviderFor for UseFields - where - __Context__: HasField< - Symbol<3, Chars<'f', Chars<'o', Chars<'o', Nil>>>>, - Value = String, - >, - {} - impl<__Context__, __Tag__> FooGetter<__Context__> for UseField<__Tag__> - where - __Context__: HasField<__Tag__, Value = String>, - { - fn foo(__context__: &__Context__) -> MRef<'_, String> { - MRef::Ref(__context__.get_field(::core::marker::PhantomData::<__Tag__>)) - } - } - impl<__Context__, __Tag__> IsProviderFor - for UseField<__Tag__> - where - __Context__: HasField<__Tag__, Value = String>, - {} - impl<__Context__, __Provider__> FooGetter<__Context__> for WithProvider<__Provider__> - where - __Provider__: FieldGetter<__Context__, FooGetterComponent, Value = String>, - { - fn foo(__context__: &__Context__) -> MRef<'_, String> { - MRef::Ref( - __Provider__::get_field( - __context__, - ::core::marker::PhantomData::, - ), - ) - } - } - impl<__Context__, __Provider__> IsProviderFor - for WithProvider<__Provider__> - where - __Provider__: FieldGetter<__Context__, FooGetterComponent, Value = String>, - {} - ") - } - } - - #[derive(HasField)] - pub struct App { - pub bar: String, - } - - snapshot_delegate_components! { - delegate_components! { - App { - FooGetterComponent: UseField, - } - } - - expand_app(output) { - insta::assert_snapshot!(output, @r#" - impl DelegateComponent for App { - type Delegate = UseField; - } - impl<__Context__, __Params__> IsProviderFor - for App - where - UseField: IsProviderFor, - {} - "#) - } - } - - #[test] - pub fn test_mref_getter() { - let context = App { bar: "foo".into() }; - - assert_eq!(context.foo().as_ref(), "foo"); - } -} - -mod mref_auto_getter { - use cgp::core::field::types::MRef; - use cgp::prelude::*; - use cgp_macro_test_util::snapshot_cgp_auto_getter; - - snapshot_cgp_auto_getter! { - #[cgp_auto_getter] - pub trait HasFoo { - fn foo(&self) -> MRef<'_, String>; - } - - expand_has_foo(output) { - insta::assert_snapshot!(output, @" - pub trait HasFoo { - fn foo(&self) -> MRef<'_, String>; - } - impl<__Context__> HasFoo for __Context__ - where - __Context__: HasField< - Symbol<3, Chars<'f', Chars<'o', Chars<'o', Nil>>>>, - Value = String, - >, - { - fn foo(&self) -> MRef<'_, String> { - MRef::Ref( - self - .get_field( - ::core::marker::PhantomData::< - Symbol<3, Chars<'f', Chars<'o', Chars<'o', Nil>>>>, - >, - ), - ) - } - } - ") - } - } - - #[derive(HasField)] - pub struct App { - pub foo: String, - } - - #[test] - pub fn test_mref_auto_getter() { - let context = App { foo: "foo".into() }; - - assert_eq!(context.foo().as_ref(), "foo"); - } -} diff --git a/crates/tests/cgp-tests/tests/getter_tests/non_self.rs b/crates/tests/cgp-tests/tests/getter_tests/non_self.rs deleted file mode 100644 index 3aade009..00000000 --- a/crates/tests/cgp-tests/tests/getter_tests/non_self.rs +++ /dev/null @@ -1,438 +0,0 @@ -use cgp::prelude::*; -use cgp_macro_test_util::{snapshot_cgp_getter, snapshot_cgp_type, snapshot_delegate_components}; - -snapshot_cgp_type! { - #[cgp_type] - pub trait HasFooType { - type Foo; - } - - expand_has_foo_type(output) { - insta::assert_snapshot!(output, @" - pub trait HasFooType { - type Foo; - } - impl<__Context__> HasFooType for __Context__ - where - __Context__: FooTypeProvider<__Context__>, - { - type Foo = <__Context__ as FooTypeProvider<__Context__>>::Foo; - } - pub trait FooTypeProvider< - __Context__, - >: IsProviderFor { - type Foo; - } - impl<__Provider__, __Context__> FooTypeProvider<__Context__> for __Provider__ - where - __Provider__: DelegateComponent - + IsProviderFor, - <__Provider__ as DelegateComponent< - FooTypeProviderComponent, - >>::Delegate: FooTypeProvider<__Context__>, - { - type Foo = <<__Provider__ as DelegateComponent< - FooTypeProviderComponent, - >>::Delegate as FooTypeProvider<__Context__>>::Foo; - } - pub struct FooTypeProviderComponent; - impl<__Context__> FooTypeProvider<__Context__> for UseContext - where - __Context__: HasFooType, - { - type Foo = <__Context__ as HasFooType>::Foo; - } - impl<__Context__> IsProviderFor for UseContext - where - __Context__: HasFooType, - {} - impl<__Context__, __Components__, __Path__> FooTypeProvider<__Context__> - for RedirectLookup<__Components__, __Path__> - where - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent< - __Path__, - >>::Delegate: FooTypeProvider<__Context__>, - { - type Foo = <<__Components__ as DelegateComponent< - __Path__, - >>::Delegate as FooTypeProvider<__Context__>>::Foo; - } - impl< - __Context__, - __Components__, - __Path__, - > IsProviderFor - for RedirectLookup<__Components__, __Path__> - where - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent< - __Path__, - >>::Delegate: IsProviderFor - + FooTypeProvider<__Context__>, - {} - impl FooTypeProvider<__Context__> for UseType { - type Foo = Foo; - } - impl IsProviderFor - for UseType {} - impl<__Provider__, Foo, __Context__> FooTypeProvider<__Context__> - for WithProvider<__Provider__> - where - __Provider__: TypeProvider<__Context__, FooTypeProviderComponent, Type = Foo>, - { - type Foo = Foo; - } - impl< - __Provider__, - Foo, - __Context__, - > IsProviderFor for WithProvider<__Provider__> - where - __Provider__: TypeProvider<__Context__, FooTypeProviderComponent, Type = Foo>, - {} - ") - } -} - -snapshot_cgp_type! { - #[cgp_type] - pub trait HasBarType { - type Bar; - } - - expand_has_bar_type(output) { - insta::assert_snapshot!(output, @" - pub trait HasBarType { - type Bar; - } - impl<__Context__> HasBarType for __Context__ - where - __Context__: BarTypeProvider<__Context__>, - { - type Bar = <__Context__ as BarTypeProvider<__Context__>>::Bar; - } - pub trait BarTypeProvider< - __Context__, - >: IsProviderFor { - type Bar; - } - impl<__Provider__, __Context__> BarTypeProvider<__Context__> for __Provider__ - where - __Provider__: DelegateComponent - + IsProviderFor, - <__Provider__ as DelegateComponent< - BarTypeProviderComponent, - >>::Delegate: BarTypeProvider<__Context__>, - { - type Bar = <<__Provider__ as DelegateComponent< - BarTypeProviderComponent, - >>::Delegate as BarTypeProvider<__Context__>>::Bar; - } - pub struct BarTypeProviderComponent; - impl<__Context__> BarTypeProvider<__Context__> for UseContext - where - __Context__: HasBarType, - { - type Bar = <__Context__ as HasBarType>::Bar; - } - impl<__Context__> IsProviderFor for UseContext - where - __Context__: HasBarType, - {} - impl<__Context__, __Components__, __Path__> BarTypeProvider<__Context__> - for RedirectLookup<__Components__, __Path__> - where - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent< - __Path__, - >>::Delegate: BarTypeProvider<__Context__>, - { - type Bar = <<__Components__ as DelegateComponent< - __Path__, - >>::Delegate as BarTypeProvider<__Context__>>::Bar; - } - impl< - __Context__, - __Components__, - __Path__, - > IsProviderFor - for RedirectLookup<__Components__, __Path__> - where - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent< - __Path__, - >>::Delegate: IsProviderFor - + BarTypeProvider<__Context__>, - {} - impl BarTypeProvider<__Context__> for UseType { - type Bar = Bar; - } - impl IsProviderFor - for UseType {} - impl<__Provider__, Bar, __Context__> BarTypeProvider<__Context__> - for WithProvider<__Provider__> - where - __Provider__: TypeProvider<__Context__, BarTypeProviderComponent, Type = Bar>, - { - type Bar = Bar; - } - impl< - __Provider__, - Bar, - __Context__, - > IsProviderFor for WithProvider<__Provider__> - where - __Provider__: TypeProvider<__Context__, BarTypeProviderComponent, Type = Bar>, - {} - ") - } -} - -snapshot_cgp_getter! { - #[cgp_getter] - pub trait HasFooBar: HasFooType + HasBarType { - fn foo_bar(foo: &Self::Foo) -> &Self::Bar; - } - - expand_has_foo_bar(output) { - insta::assert_snapshot!(output, @" - pub trait HasFooBar: HasFooType + HasBarType { - fn foo_bar(foo: &Self::Foo) -> &Self::Bar; - } - impl<__Context__> HasFooBar for __Context__ - where - __Context__: HasFooType + HasBarType, - __Context__: FooBarGetter<__Context__>, - { - fn foo_bar(foo: &Self::Foo) -> &Self::Bar { - __Context__::foo_bar(foo) - } - } - pub trait FooBarGetter< - __Context__, - >: IsProviderFor - where - __Context__: HasFooType + HasBarType, - { - fn foo_bar(foo: &__Context__::Foo) -> &__Context__::Bar; - } - impl<__Provider__, __Context__> FooBarGetter<__Context__> for __Provider__ - where - __Context__: HasFooType + HasBarType, - __Provider__: DelegateComponent - + IsProviderFor, - <__Provider__ as DelegateComponent< - FooBarGetterComponent, - >>::Delegate: FooBarGetter<__Context__>, - { - fn foo_bar(foo: &__Context__::Foo) -> &__Context__::Bar { - <__Provider__ as DelegateComponent< - FooBarGetterComponent, - >>::Delegate::foo_bar(foo) - } - } - pub struct FooBarGetterComponent; - impl<__Context__> FooBarGetter<__Context__> for UseContext - where - __Context__: HasFooType + HasBarType, - __Context__: HasFooBar, - { - fn foo_bar(foo: &__Context__::Foo) -> &__Context__::Bar { - __Context__::foo_bar(foo) - } - } - impl<__Context__> IsProviderFor for UseContext - where - __Context__: HasFooType + HasBarType, - __Context__: HasFooBar, - {} - impl<__Context__, __Components__, __Path__> FooBarGetter<__Context__> - for RedirectLookup<__Components__, __Path__> - where - __Context__: HasFooType + HasBarType, - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent<__Path__>>::Delegate: FooBarGetter<__Context__>, - { - fn foo_bar(foo: &__Context__::Foo) -> &__Context__::Bar { - <__Components__ as DelegateComponent<__Path__>>::Delegate::foo_bar(foo) - } - } - impl< - __Context__, - __Components__, - __Path__, - > IsProviderFor - for RedirectLookup<__Components__, __Path__> - where - __Context__: HasFooType + HasBarType, - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent< - __Path__, - >>::Delegate: IsProviderFor - + FooBarGetter<__Context__>, - {} - impl<__Context__> FooBarGetter<__Context__> for UseFields - where - __Context__: HasFooType + HasBarType, - __Context__::Foo: HasField< - Symbol< - 7, - Chars< - 'f', - Chars< - 'o', - Chars<'o', Chars<'_', Chars<'b', Chars<'a', Chars<'r', Nil>>>>>, - >, - >, - >, - Value = __Context__::Bar, - >, - { - fn foo_bar(__context__: &__Context__::Foo) -> &__Context__::Bar { - __context__ - .get_field( - ::core::marker::PhantomData::< - Symbol< - 7, - Chars< - 'f', - Chars< - 'o', - Chars< - 'o', - Chars<'_', Chars<'b', Chars<'a', Chars<'r', Nil>>>>, - >, - >, - >, - >, - >, - ) - } - } - impl<__Context__> IsProviderFor for UseFields - where - __Context__: HasFooType + HasBarType, - __Context__::Foo: HasField< - Symbol< - 7, - Chars< - 'f', - Chars< - 'o', - Chars<'o', Chars<'_', Chars<'b', Chars<'a', Chars<'r', Nil>>>>>, - >, - >, - >, - Value = __Context__::Bar, - >, - {} - impl<__Context__, __Tag__> FooBarGetter<__Context__> for UseField<__Tag__> - where - __Context__: HasFooType + HasBarType, - __Context__::Foo: HasField<__Tag__, Value = __Context__::Bar>, - { - fn foo_bar(__context__: &__Context__::Foo) -> &__Context__::Bar { - __context__.get_field(::core::marker::PhantomData::<__Tag__>) - } - } - impl<__Context__, __Tag__> IsProviderFor - for UseField<__Tag__> - where - __Context__: HasFooType + HasBarType, - __Context__::Foo: HasField<__Tag__, Value = __Context__::Bar>, - {} - impl<__Context__, __Provider__> FooBarGetter<__Context__> for WithProvider<__Provider__> - where - __Context__: HasFooType + HasBarType, - __Provider__: FieldGetter< - __Context__::Foo, - FooBarGetterComponent, - Value = __Context__::Bar, - >, - { - fn foo_bar(__context__: &__Context__::Foo) -> &__Context__::Bar { - __Provider__::get_field( - __context__, - ::core::marker::PhantomData::, - ) - } - } - impl<__Context__, __Provider__> IsProviderFor - for WithProvider<__Provider__> - where - __Context__: HasFooType + HasBarType, - __Provider__: FieldGetter< - __Context__::Foo, - FooBarGetterComponent, - Value = __Context__::Bar, - >, - {} - ") - } -} - -pub struct App; - -#[derive(HasField)] -pub struct Foo { - pub bar: u32, -} - -snapshot_delegate_components! { - delegate_components! { - App { - FooTypeProviderComponent: - UseType, - BarTypeProviderComponent: - UseType, - FooBarGetterComponent: - UseField, - } - } - - expand_app(output) { - insta::assert_snapshot!(output, @r#" - impl DelegateComponent for App { - type Delegate = UseType; - } - impl< - __Context__, - __Params__, - > IsProviderFor for App - where - UseType: IsProviderFor, - {} - impl DelegateComponent for App { - type Delegate = UseType; - } - impl< - __Context__, - __Params__, - > IsProviderFor for App - where - UseType: IsProviderFor, - {} - impl DelegateComponent for App { - type Delegate = UseField; - } - impl< - __Context__, - __Params__, - > IsProviderFor for App - where - UseField< - Symbol!("bar"), - >: IsProviderFor, - {} - "#) - } -} - -#[test] -fn test_non_self_getter() { - let foo = Foo { bar: 42 }; - - let bar = ::foo_bar(&foo); - assert_eq!(bar, &42); -} diff --git a/crates/tests/cgp-tests/tests/getter_tests/non_self_auto.rs b/crates/tests/cgp-tests/tests/getter_tests/non_self_auto.rs deleted file mode 100644 index 0fc47e79..00000000 --- a/crates/tests/cgp-tests/tests/getter_tests/non_self_auto.rs +++ /dev/null @@ -1,296 +0,0 @@ -use cgp::prelude::*; -use cgp_macro_test_util::{ - snapshot_cgp_auto_getter, snapshot_cgp_type, snapshot_delegate_components, -}; - -snapshot_cgp_type! { - #[cgp_type] - pub trait HasFooType { - type Foo; - } - - expand_has_foo_type(output) { - insta::assert_snapshot!(output, @" - pub trait HasFooType { - type Foo; - } - impl<__Context__> HasFooType for __Context__ - where - __Context__: FooTypeProvider<__Context__>, - { - type Foo = <__Context__ as FooTypeProvider<__Context__>>::Foo; - } - pub trait FooTypeProvider< - __Context__, - >: IsProviderFor { - type Foo; - } - impl<__Provider__, __Context__> FooTypeProvider<__Context__> for __Provider__ - where - __Provider__: DelegateComponent - + IsProviderFor, - <__Provider__ as DelegateComponent< - FooTypeProviderComponent, - >>::Delegate: FooTypeProvider<__Context__>, - { - type Foo = <<__Provider__ as DelegateComponent< - FooTypeProviderComponent, - >>::Delegate as FooTypeProvider<__Context__>>::Foo; - } - pub struct FooTypeProviderComponent; - impl<__Context__> FooTypeProvider<__Context__> for UseContext - where - __Context__: HasFooType, - { - type Foo = <__Context__ as HasFooType>::Foo; - } - impl<__Context__> IsProviderFor for UseContext - where - __Context__: HasFooType, - {} - impl<__Context__, __Components__, __Path__> FooTypeProvider<__Context__> - for RedirectLookup<__Components__, __Path__> - where - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent< - __Path__, - >>::Delegate: FooTypeProvider<__Context__>, - { - type Foo = <<__Components__ as DelegateComponent< - __Path__, - >>::Delegate as FooTypeProvider<__Context__>>::Foo; - } - impl< - __Context__, - __Components__, - __Path__, - > IsProviderFor - for RedirectLookup<__Components__, __Path__> - where - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent< - __Path__, - >>::Delegate: IsProviderFor - + FooTypeProvider<__Context__>, - {} - impl FooTypeProvider<__Context__> for UseType { - type Foo = Foo; - } - impl IsProviderFor - for UseType {} - impl<__Provider__, Foo, __Context__> FooTypeProvider<__Context__> - for WithProvider<__Provider__> - where - __Provider__: TypeProvider<__Context__, FooTypeProviderComponent, Type = Foo>, - { - type Foo = Foo; - } - impl< - __Provider__, - Foo, - __Context__, - > IsProviderFor for WithProvider<__Provider__> - where - __Provider__: TypeProvider<__Context__, FooTypeProviderComponent, Type = Foo>, - {} - ") - } -} - -snapshot_cgp_type! { - #[cgp_type] - pub trait HasBarType { - type Bar; - } - - expand_has_bar_type(output) { - insta::assert_snapshot!(output, @" - pub trait HasBarType { - type Bar; - } - impl<__Context__> HasBarType for __Context__ - where - __Context__: BarTypeProvider<__Context__>, - { - type Bar = <__Context__ as BarTypeProvider<__Context__>>::Bar; - } - pub trait BarTypeProvider< - __Context__, - >: IsProviderFor { - type Bar; - } - impl<__Provider__, __Context__> BarTypeProvider<__Context__> for __Provider__ - where - __Provider__: DelegateComponent - + IsProviderFor, - <__Provider__ as DelegateComponent< - BarTypeProviderComponent, - >>::Delegate: BarTypeProvider<__Context__>, - { - type Bar = <<__Provider__ as DelegateComponent< - BarTypeProviderComponent, - >>::Delegate as BarTypeProvider<__Context__>>::Bar; - } - pub struct BarTypeProviderComponent; - impl<__Context__> BarTypeProvider<__Context__> for UseContext - where - __Context__: HasBarType, - { - type Bar = <__Context__ as HasBarType>::Bar; - } - impl<__Context__> IsProviderFor for UseContext - where - __Context__: HasBarType, - {} - impl<__Context__, __Components__, __Path__> BarTypeProvider<__Context__> - for RedirectLookup<__Components__, __Path__> - where - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent< - __Path__, - >>::Delegate: BarTypeProvider<__Context__>, - { - type Bar = <<__Components__ as DelegateComponent< - __Path__, - >>::Delegate as BarTypeProvider<__Context__>>::Bar; - } - impl< - __Context__, - __Components__, - __Path__, - > IsProviderFor - for RedirectLookup<__Components__, __Path__> - where - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent< - __Path__, - >>::Delegate: IsProviderFor - + BarTypeProvider<__Context__>, - {} - impl BarTypeProvider<__Context__> for UseType { - type Bar = Bar; - } - impl IsProviderFor - for UseType {} - impl<__Provider__, Bar, __Context__> BarTypeProvider<__Context__> - for WithProvider<__Provider__> - where - __Provider__: TypeProvider<__Context__, BarTypeProviderComponent, Type = Bar>, - { - type Bar = Bar; - } - impl< - __Provider__, - Bar, - __Context__, - > IsProviderFor for WithProvider<__Provider__> - where - __Provider__: TypeProvider<__Context__, BarTypeProviderComponent, Type = Bar>, - {} - ") - } -} - -snapshot_cgp_auto_getter! { - #[cgp_auto_getter] - pub trait HasFooBar: HasFooType + HasBarType { - fn foo_bar(foo: &Self::Foo) -> &Self::Bar; - } - - expand_has_foo_bar(output) { - insta::assert_snapshot!(output, @" - pub trait HasFooBar: HasFooType + HasBarType { - fn foo_bar(foo: &Self::Foo) -> &Self::Bar; - } - impl<__Context__> HasFooBar for __Context__ - where - __Context__: HasFooType + HasBarType, - __Context__::Foo: HasField< - Symbol< - 7, - Chars< - 'f', - Chars< - 'o', - Chars<'o', Chars<'_', Chars<'b', Chars<'a', Chars<'r', Nil>>>>>, - >, - >, - >, - Value = __Context__::Bar, - >, - { - fn foo_bar(__context__: &__Context__::Foo) -> &__Context__::Bar { - __context__ - .get_field( - ::core::marker::PhantomData::< - Symbol< - 7, - Chars< - 'f', - Chars< - 'o', - Chars< - 'o', - Chars<'_', Chars<'b', Chars<'a', Chars<'r', Nil>>>>, - >, - >, - >, - >, - >, - ) - } - } - ") - } -} - -pub struct App; - -#[derive(HasField)] -pub struct Foo { - pub foo_bar: u32, -} - -snapshot_delegate_components! { - delegate_components! { - App { - FooTypeProviderComponent: - UseType, - BarTypeProviderComponent: - UseType, - } - } - - expand_app(output) { - insta::assert_snapshot!(output, @" - impl DelegateComponent for App { - type Delegate = UseType; - } - impl< - __Context__, - __Params__, - > IsProviderFor for App - where - UseType: IsProviderFor, - {} - impl DelegateComponent for App { - type Delegate = UseType; - } - impl< - __Context__, - __Params__, - > IsProviderFor for App - where - UseType: IsProviderFor, - {} - ") - } -} - -#[test] -fn test_non_self_getter() { - let foo = Foo { foo_bar: 42 }; - - let bar = App::foo_bar(&foo); - assert_eq!(bar, &42); -} diff --git a/crates/tests/cgp-tests/tests/getter_tests/option.rs b/crates/tests/cgp-tests/tests/getter_tests/option.rs deleted file mode 100644 index b8f626b2..00000000 --- a/crates/tests/cgp-tests/tests/getter_tests/option.rs +++ /dev/null @@ -1,219 +0,0 @@ -mod option_getter { - use cgp::prelude::*; - use cgp_macro_test_util::{snapshot_cgp_getter, snapshot_delegate_components}; - - snapshot_cgp_getter! { - #[cgp_getter] - pub trait HasFoo { - fn foo(&self) -> Option<&String>; - } - - expand_has_foo(output) { - insta::assert_snapshot!(output, @" - pub trait HasFoo { - fn foo(&self) -> Option<&String>; - } - impl<__Context__> HasFoo for __Context__ - where - __Context__: FooGetter<__Context__>, - { - fn foo(&self) -> Option<&String> { - __Context__::foo(self) - } - } - pub trait FooGetter<__Context__>: IsProviderFor { - fn foo(__context__: &__Context__) -> Option<&String>; - } - impl<__Provider__, __Context__> FooGetter<__Context__> for __Provider__ - where - __Provider__: DelegateComponent - + IsProviderFor, - <__Provider__ as DelegateComponent< - FooGetterComponent, - >>::Delegate: FooGetter<__Context__>, - { - fn foo(__context__: &__Context__) -> Option<&String> { - <__Provider__ as DelegateComponent< - FooGetterComponent, - >>::Delegate::foo(__context__) - } - } - pub struct FooGetterComponent; - impl<__Context__> FooGetter<__Context__> for UseContext - where - __Context__: HasFoo, - { - fn foo(__context__: &__Context__) -> Option<&String> { - __Context__::foo(__context__) - } - } - impl<__Context__> IsProviderFor for UseContext - where - __Context__: HasFoo, - {} - impl<__Context__, __Components__, __Path__> FooGetter<__Context__> - for RedirectLookup<__Components__, __Path__> - where - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent<__Path__>>::Delegate: FooGetter<__Context__>, - { - fn foo(__context__: &__Context__) -> Option<&String> { - <__Components__ as DelegateComponent<__Path__>>::Delegate::foo(__context__) - } - } - impl< - __Context__, - __Components__, - __Path__, - > IsProviderFor - for RedirectLookup<__Components__, __Path__> - where - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent< - __Path__, - >>::Delegate: IsProviderFor - + FooGetter<__Context__>, - {} - impl<__Context__> FooGetter<__Context__> for UseFields - where - __Context__: HasField< - Symbol<3, Chars<'f', Chars<'o', Chars<'o', Nil>>>>, - Value = Option, - >, - { - fn foo(__context__: &__Context__) -> Option<&String> { - __context__ - .get_field( - ::core::marker::PhantomData::< - Symbol<3, Chars<'f', Chars<'o', Chars<'o', Nil>>>>, - >, - ) - .as_ref() - } - } - impl<__Context__> IsProviderFor for UseFields - where - __Context__: HasField< - Symbol<3, Chars<'f', Chars<'o', Chars<'o', Nil>>>>, - Value = Option, - >, - {} - impl<__Context__, __Tag__> FooGetter<__Context__> for UseField<__Tag__> - where - __Context__: HasField<__Tag__, Value = Option>, - { - fn foo(__context__: &__Context__) -> Option<&String> { - __context__.get_field(::core::marker::PhantomData::<__Tag__>).as_ref() - } - } - impl<__Context__, __Tag__> IsProviderFor - for UseField<__Tag__> - where - __Context__: HasField<__Tag__, Value = Option>, - {} - impl<__Context__, __Provider__> FooGetter<__Context__> for WithProvider<__Provider__> - where - __Provider__: FieldGetter<__Context__, FooGetterComponent, Value = Option>, - { - fn foo(__context__: &__Context__) -> Option<&String> { - __Provider__::get_field( - __context__, - ::core::marker::PhantomData::, - ) - .as_ref() - } - } - impl<__Context__, __Provider__> IsProviderFor - for WithProvider<__Provider__> - where - __Provider__: FieldGetter<__Context__, FooGetterComponent, Value = Option>, - {} - ") - } - } - - #[derive(HasField)] - pub struct App { - pub bar: Option, - } - - snapshot_delegate_components! { - delegate_components! { - App { - FooGetterComponent: UseField, - } - } - - expand_app(output) { - insta::assert_snapshot!(output, @r#" - impl DelegateComponent for App { - type Delegate = UseField; - } - impl<__Context__, __Params__> IsProviderFor - for App - where - UseField: IsProviderFor, - {} - "#) - } - } - - #[test] - pub fn test_option_getter() { - let context = App { - bar: Some("foo".to_owned()), - }; - - assert_eq!(context.foo(), Some(&"foo".to_owned())); - } -} - -mod option_auto_getter { - use cgp::prelude::*; - use cgp_macro_test_util::snapshot_cgp_auto_getter; - - snapshot_cgp_auto_getter! { - #[cgp_auto_getter] - pub trait HasFoo { - fn foo(&self) -> Option<&String>; - } - - expand_has_foo(output) { - insta::assert_snapshot!(output, @" - pub trait HasFoo { - fn foo(&self) -> Option<&String>; - } - impl<__Context__> HasFoo for __Context__ - where - __Context__: HasField< - Symbol<3, Chars<'f', Chars<'o', Chars<'o', Nil>>>>, - Value = Option, - >, - { - fn foo(&self) -> Option<&String> { - self.get_field( - ::core::marker::PhantomData::< - Symbol<3, Chars<'f', Chars<'o', Chars<'o', Nil>>>>, - >, - ) - .as_ref() - } - } - ") - } - } - - #[derive(HasField)] - pub struct App { - pub foo: Option, - } - - #[test] - pub fn test_option_auto_getter() { - let context = App { - foo: Some("foo".to_owned()), - }; - - assert_eq!(context.foo(), Some(&"foo".to_owned())); - } -} diff --git a/crates/tests/cgp-tests/tests/getter_tests/slice.rs b/crates/tests/cgp-tests/tests/getter_tests/slice.rs deleted file mode 100644 index b6c9f926..00000000 --- a/crates/tests/cgp-tests/tests/getter_tests/slice.rs +++ /dev/null @@ -1,223 +0,0 @@ -mod slice_getter { - use cgp::prelude::*; - use cgp_macro_test_util::{snapshot_cgp_getter, snapshot_delegate_components}; - - snapshot_cgp_getter! { - #[cgp_getter] - pub trait HasFoo { - fn foo(&self) -> &[u8]; - } - - expand_has_foo(output) { - insta::assert_snapshot!(output, @" - pub trait HasFoo { - fn foo(&self) -> &[u8]; - } - impl<__Context__> HasFoo for __Context__ - where - __Context__: FooGetter<__Context__>, - { - fn foo(&self) -> &[u8] { - __Context__::foo(self) - } - } - pub trait FooGetter<__Context__>: IsProviderFor { - fn foo(__context__: &__Context__) -> &[u8]; - } - impl<__Provider__, __Context__> FooGetter<__Context__> for __Provider__ - where - __Provider__: DelegateComponent - + IsProviderFor, - <__Provider__ as DelegateComponent< - FooGetterComponent, - >>::Delegate: FooGetter<__Context__>, - { - fn foo(__context__: &__Context__) -> &[u8] { - <__Provider__ as DelegateComponent< - FooGetterComponent, - >>::Delegate::foo(__context__) - } - } - pub struct FooGetterComponent; - impl<__Context__> FooGetter<__Context__> for UseContext - where - __Context__: HasFoo, - { - fn foo(__context__: &__Context__) -> &[u8] { - __Context__::foo(__context__) - } - } - impl<__Context__> IsProviderFor for UseContext - where - __Context__: HasFoo, - {} - impl<__Context__, __Components__, __Path__> FooGetter<__Context__> - for RedirectLookup<__Components__, __Path__> - where - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent<__Path__>>::Delegate: FooGetter<__Context__>, - { - fn foo(__context__: &__Context__) -> &[u8] { - <__Components__ as DelegateComponent<__Path__>>::Delegate::foo(__context__) - } - } - impl< - __Context__, - __Components__, - __Path__, - > IsProviderFor - for RedirectLookup<__Components__, __Path__> - where - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent< - __Path__, - >>::Delegate: IsProviderFor - + FooGetter<__Context__>, - {} - impl<__Context__> FooGetter<__Context__> for UseFields - where - __Context__: HasField< - Symbol<3, Chars<'f', Chars<'o', Chars<'o', Nil>>>>, - Value: AsRef<[u8]> + 'static, - >, - { - fn foo(__context__: &__Context__) -> &[u8] { - __context__ - .get_field( - ::core::marker::PhantomData::< - Symbol<3, Chars<'f', Chars<'o', Chars<'o', Nil>>>>, - >, - ) - .as_ref() - } - } - impl<__Context__> IsProviderFor for UseFields - where - __Context__: HasField< - Symbol<3, Chars<'f', Chars<'o', Chars<'o', Nil>>>>, - Value: AsRef<[u8]> + 'static, - >, - {} - impl<__Context__, __Tag__> FooGetter<__Context__> for UseField<__Tag__> - where - __Context__: HasField<__Tag__, Value: AsRef<[u8]> + 'static>, - { - fn foo(__context__: &__Context__) -> &[u8] { - __context__.get_field(::core::marker::PhantomData::<__Tag__>).as_ref() - } - } - impl<__Context__, __Tag__> IsProviderFor - for UseField<__Tag__> - where - __Context__: HasField<__Tag__, Value: AsRef<[u8]> + 'static>, - {} - impl<__Context__, __Provider__> FooGetter<__Context__> for WithProvider<__Provider__> - where - __Provider__: FieldGetter< - __Context__, - FooGetterComponent, - Value: AsRef<[u8]> + 'static, - >, - { - fn foo(__context__: &__Context__) -> &[u8] { - __Provider__::get_field( - __context__, - ::core::marker::PhantomData::, - ) - .as_ref() - } - } - impl<__Context__, __Provider__> IsProviderFor - for WithProvider<__Provider__> - where - __Provider__: FieldGetter< - __Context__, - FooGetterComponent, - Value: AsRef<[u8]> + 'static, - >, - {} - ") - } - } - - #[derive(HasField)] - pub struct App { - pub bar: Vec, - } - - snapshot_delegate_components! { - delegate_components! { - App { - FooGetterComponent: UseField, - } - } - - expand_app(output) { - insta::assert_snapshot!(output, @r#" - impl DelegateComponent for App { - type Delegate = UseField; - } - impl<__Context__, __Params__> IsProviderFor - for App - where - UseField: IsProviderFor, - {} - "#) - } - } - - #[test] - pub fn test_slice_getter() { - let context = App { bar: vec![1, 2, 3] }; - - assert_eq!(context.foo(), &[1, 2, 3]); - } -} - -mod slice_auto_getter { - use cgp::prelude::*; - use cgp_macro_test_util::snapshot_cgp_auto_getter; - - snapshot_cgp_auto_getter! { - #[cgp_auto_getter] - pub trait HasFoo { - fn foo(&self) -> &[u8]; - } - - expand_has_foo(output) { - insta::assert_snapshot!(output, @" - pub trait HasFoo { - fn foo(&self) -> &[u8]; - } - impl<__Context__> HasFoo for __Context__ - where - __Context__: HasField< - Symbol<3, Chars<'f', Chars<'o', Chars<'o', Nil>>>>, - Value: AsRef<[u8]> + 'static, - >, - { - fn foo(&self) -> &[u8] { - self.get_field( - ::core::marker::PhantomData::< - Symbol<3, Chars<'f', Chars<'o', Chars<'o', Nil>>>>, - >, - ) - .as_ref() - } - } - ") - } - } - - #[derive(HasField)] - pub struct App { - pub foo: Vec, - } - - #[test] - pub fn test_slice_auto_getter() { - let context = App { foo: vec![1, 2, 3] }; - - assert_eq!(context.foo(), &[1, 2, 3]); - } -} diff --git a/crates/tests/cgp-tests/tests/getter_tests/string.rs b/crates/tests/cgp-tests/tests/getter_tests/string.rs deleted file mode 100644 index bd94f9bd..00000000 --- a/crates/tests/cgp-tests/tests/getter_tests/string.rs +++ /dev/null @@ -1,553 +0,0 @@ -mod string_getter { - use cgp::prelude::*; - use cgp_macro_test_util::{snapshot_cgp_getter, snapshot_delegate_components}; - - snapshot_cgp_getter! { - #[cgp_getter] - pub trait HasFoo { - fn foo(&self) -> &str; - } - - expand_has_foo(output) { - insta::assert_snapshot!(output, @" - pub trait HasFoo { - fn foo(&self) -> &str; - } - impl<__Context__> HasFoo for __Context__ - where - __Context__: FooGetter<__Context__>, - { - fn foo(&self) -> &str { - __Context__::foo(self) - } - } - pub trait FooGetter<__Context__>: IsProviderFor { - fn foo(__context__: &__Context__) -> &str; - } - impl<__Provider__, __Context__> FooGetter<__Context__> for __Provider__ - where - __Provider__: DelegateComponent - + IsProviderFor, - <__Provider__ as DelegateComponent< - FooGetterComponent, - >>::Delegate: FooGetter<__Context__>, - { - fn foo(__context__: &__Context__) -> &str { - <__Provider__ as DelegateComponent< - FooGetterComponent, - >>::Delegate::foo(__context__) - } - } - pub struct FooGetterComponent; - impl<__Context__> FooGetter<__Context__> for UseContext - where - __Context__: HasFoo, - { - fn foo(__context__: &__Context__) -> &str { - __Context__::foo(__context__) - } - } - impl<__Context__> IsProviderFor for UseContext - where - __Context__: HasFoo, - {} - impl<__Context__, __Components__, __Path__> FooGetter<__Context__> - for RedirectLookup<__Components__, __Path__> - where - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent<__Path__>>::Delegate: FooGetter<__Context__>, - { - fn foo(__context__: &__Context__) -> &str { - <__Components__ as DelegateComponent<__Path__>>::Delegate::foo(__context__) - } - } - impl< - __Context__, - __Components__, - __Path__, - > IsProviderFor - for RedirectLookup<__Components__, __Path__> - where - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent< - __Path__, - >>::Delegate: IsProviderFor - + FooGetter<__Context__>, - {} - impl<__Context__> FooGetter<__Context__> for UseFields - where - __Context__: HasField< - Symbol<3, Chars<'f', Chars<'o', Chars<'o', Nil>>>>, - Value = String, - >, - { - fn foo(__context__: &__Context__) -> &str { - __context__ - .get_field( - ::core::marker::PhantomData::< - Symbol<3, Chars<'f', Chars<'o', Chars<'o', Nil>>>>, - >, - ) - .as_str() - } - } - impl<__Context__> IsProviderFor for UseFields - where - __Context__: HasField< - Symbol<3, Chars<'f', Chars<'o', Chars<'o', Nil>>>>, - Value = String, - >, - {} - impl<__Context__, __Tag__> FooGetter<__Context__> for UseField<__Tag__> - where - __Context__: HasField<__Tag__, Value = String>, - { - fn foo(__context__: &__Context__) -> &str { - __context__.get_field(::core::marker::PhantomData::<__Tag__>).as_str() - } - } - impl<__Context__, __Tag__> IsProviderFor - for UseField<__Tag__> - where - __Context__: HasField<__Tag__, Value = String>, - {} - impl<__Context__, __Provider__> FooGetter<__Context__> for WithProvider<__Provider__> - where - __Provider__: FieldGetter<__Context__, FooGetterComponent, Value = String>, - { - fn foo(__context__: &__Context__) -> &str { - __Provider__::get_field( - __context__, - ::core::marker::PhantomData::, - ) - .as_str() - } - } - impl<__Context__, __Provider__> IsProviderFor - for WithProvider<__Provider__> - where - __Provider__: FieldGetter<__Context__, FooGetterComponent, Value = String>, - {} - ") - } - } - - #[derive(HasField)] - pub struct App { - pub bar: String, - } - - snapshot_delegate_components! { - delegate_components! { - App { - FooGetterComponent: UseField, - } - } - - expand_app(output) { - insta::assert_snapshot!(output, @r#" - impl DelegateComponent for App { - type Delegate = UseField; - } - impl<__Context__, __Params__> IsProviderFor - for App - where - UseField: IsProviderFor, - {} - "#) - } - } - - #[test] - pub fn test_string_getter() { - let context = App { - bar: "abc".to_owned(), - }; - - assert_eq!(context.foo(), "abc"); - } -} - -mod string_getter_with_custom_name { - use cgp::prelude::*; - use cgp_macro_test_util::{snapshot_cgp_getter, snapshot_delegate_components}; - - snapshot_cgp_getter! { - #[cgp_getter(GetString)] - pub trait HasFoo { - fn foo(&self) -> &str; - } - - expand_has_foo(output) { - insta::assert_snapshot!(output, @" - pub trait HasFoo { - fn foo(&self) -> &str; - } - impl<__Context__> HasFoo for __Context__ - where - __Context__: GetString<__Context__>, - { - fn foo(&self) -> &str { - __Context__::foo(self) - } - } - pub trait GetString<__Context__>: IsProviderFor { - fn foo(__context__: &__Context__) -> &str; - } - impl<__Provider__, __Context__> GetString<__Context__> for __Provider__ - where - __Provider__: DelegateComponent - + IsProviderFor, - <__Provider__ as DelegateComponent< - GetStringComponent, - >>::Delegate: GetString<__Context__>, - { - fn foo(__context__: &__Context__) -> &str { - <__Provider__ as DelegateComponent< - GetStringComponent, - >>::Delegate::foo(__context__) - } - } - pub struct GetStringComponent; - impl<__Context__> GetString<__Context__> for UseContext - where - __Context__: HasFoo, - { - fn foo(__context__: &__Context__) -> &str { - __Context__::foo(__context__) - } - } - impl<__Context__> IsProviderFor for UseContext - where - __Context__: HasFoo, - {} - impl<__Context__, __Components__, __Path__> GetString<__Context__> - for RedirectLookup<__Components__, __Path__> - where - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent<__Path__>>::Delegate: GetString<__Context__>, - { - fn foo(__context__: &__Context__) -> &str { - <__Components__ as DelegateComponent<__Path__>>::Delegate::foo(__context__) - } - } - impl< - __Context__, - __Components__, - __Path__, - > IsProviderFor - for RedirectLookup<__Components__, __Path__> - where - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent< - __Path__, - >>::Delegate: IsProviderFor - + GetString<__Context__>, - {} - impl<__Context__> GetString<__Context__> for UseFields - where - __Context__: HasField< - Symbol<3, Chars<'f', Chars<'o', Chars<'o', Nil>>>>, - Value = String, - >, - { - fn foo(__context__: &__Context__) -> &str { - __context__ - .get_field( - ::core::marker::PhantomData::< - Symbol<3, Chars<'f', Chars<'o', Chars<'o', Nil>>>>, - >, - ) - .as_str() - } - } - impl<__Context__> IsProviderFor for UseFields - where - __Context__: HasField< - Symbol<3, Chars<'f', Chars<'o', Chars<'o', Nil>>>>, - Value = String, - >, - {} - impl<__Context__, __Tag__> GetString<__Context__> for UseField<__Tag__> - where - __Context__: HasField<__Tag__, Value = String>, - { - fn foo(__context__: &__Context__) -> &str { - __context__.get_field(::core::marker::PhantomData::<__Tag__>).as_str() - } - } - impl<__Context__, __Tag__> IsProviderFor - for UseField<__Tag__> - where - __Context__: HasField<__Tag__, Value = String>, - {} - impl<__Context__, __Provider__> GetString<__Context__> for WithProvider<__Provider__> - where - __Provider__: FieldGetter<__Context__, GetStringComponent, Value = String>, - { - fn foo(__context__: &__Context__) -> &str { - __Provider__::get_field( - __context__, - ::core::marker::PhantomData::, - ) - .as_str() - } - } - impl<__Context__, __Provider__> IsProviderFor - for WithProvider<__Provider__> - where - __Provider__: FieldGetter<__Context__, GetStringComponent, Value = String>, - {} - ") - } - } - - #[derive(HasField)] - pub struct App { - pub bar: String, - } - - snapshot_delegate_components! { - delegate_components! { - App { - GetStringComponent: UseField, - } - } - - expand_app(output) { - insta::assert_snapshot!(output, @r#" - impl DelegateComponent for App { - type Delegate = UseField; - } - impl<__Context__, __Params__> IsProviderFor - for App - where - UseField: IsProviderFor, - {} - "#) - } - } - - #[test] - pub fn test_string_getter_with_custom_name() { - let context = App { - bar: "abc".to_owned(), - }; - - assert_eq!(context.foo(), "abc"); - } -} - -mod string_getter_with_custom_spec { - use cgp::prelude::*; - use cgp_macro_test_util::{snapshot_cgp_getter, snapshot_delegate_components}; - - snapshot_cgp_getter! { - #[cgp_getter{ - provider: GetString, - name: GetStringComp, - }] - pub trait HasFoo { - fn foo(&self) -> &str; - } - - expand_has_foo(output) { - insta::assert_snapshot!(output, @" - pub trait HasFoo { - fn foo(&self) -> &str; - } - impl<__Context__> HasFoo for __Context__ - where - __Context__: GetString<__Context__>, - { - fn foo(&self) -> &str { - __Context__::foo(self) - } - } - pub trait GetString<__Context__>: IsProviderFor { - fn foo(__context__: &__Context__) -> &str; - } - impl<__Provider__, __Context__> GetString<__Context__> for __Provider__ - where - __Provider__: DelegateComponent - + IsProviderFor, - <__Provider__ as DelegateComponent>::Delegate: GetString<__Context__>, - { - fn foo(__context__: &__Context__) -> &str { - <__Provider__ as DelegateComponent>::Delegate::foo(__context__) - } - } - pub struct GetStringComp; - impl<__Context__> GetString<__Context__> for UseContext - where - __Context__: HasFoo, - { - fn foo(__context__: &__Context__) -> &str { - __Context__::foo(__context__) - } - } - impl<__Context__> IsProviderFor for UseContext - where - __Context__: HasFoo, - {} - impl<__Context__, __Components__, __Path__> GetString<__Context__> - for RedirectLookup<__Components__, __Path__> - where - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent<__Path__>>::Delegate: GetString<__Context__>, - { - fn foo(__context__: &__Context__) -> &str { - <__Components__ as DelegateComponent<__Path__>>::Delegate::foo(__context__) - } - } - impl<__Context__, __Components__, __Path__> IsProviderFor - for RedirectLookup<__Components__, __Path__> - where - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent< - __Path__, - >>::Delegate: IsProviderFor + GetString<__Context__>, - {} - impl<__Context__> GetString<__Context__> for UseFields - where - __Context__: HasField< - Symbol<3, Chars<'f', Chars<'o', Chars<'o', Nil>>>>, - Value = String, - >, - { - fn foo(__context__: &__Context__) -> &str { - __context__ - .get_field( - ::core::marker::PhantomData::< - Symbol<3, Chars<'f', Chars<'o', Chars<'o', Nil>>>>, - >, - ) - .as_str() - } - } - impl<__Context__> IsProviderFor for UseFields - where - __Context__: HasField< - Symbol<3, Chars<'f', Chars<'o', Chars<'o', Nil>>>>, - Value = String, - >, - {} - impl<__Context__, __Tag__> GetString<__Context__> for UseField<__Tag__> - where - __Context__: HasField<__Tag__, Value = String>, - { - fn foo(__context__: &__Context__) -> &str { - __context__.get_field(::core::marker::PhantomData::<__Tag__>).as_str() - } - } - impl<__Context__, __Tag__> IsProviderFor - for UseField<__Tag__> - where - __Context__: HasField<__Tag__, Value = String>, - {} - impl<__Context__, __Provider__> GetString<__Context__> for WithProvider<__Provider__> - where - __Provider__: FieldGetter<__Context__, GetStringComp, Value = String>, - { - fn foo(__context__: &__Context__) -> &str { - __Provider__::get_field( - __context__, - ::core::marker::PhantomData::, - ) - .as_str() - } - } - impl<__Context__, __Provider__> IsProviderFor - for WithProvider<__Provider__> - where - __Provider__: FieldGetter<__Context__, GetStringComp, Value = String>, - {} - ") - } - } - - #[derive(HasField)] - pub struct App { - pub bar: String, - } - - snapshot_delegate_components! { - delegate_components! { - App { - GetStringComp: UseField, - } - } - - expand_app(output) { - insta::assert_snapshot!(output, @r#" - impl DelegateComponent for App { - type Delegate = UseField; - } - impl<__Context__, __Params__> IsProviderFor - for App - where - UseField: IsProviderFor, - {} - "#) - } - } - - #[test] - pub fn test_string_getter_with_custom_spec() { - let context = App { - bar: "abc".to_owned(), - }; - - assert_eq!(context.foo(), "abc"); - } -} - -mod string_auto_getter { - use cgp::prelude::*; - use cgp_macro_test_util::snapshot_cgp_auto_getter; - - snapshot_cgp_auto_getter! { - #[cgp_auto_getter] - pub trait HasFoo { - fn foo(&self) -> &str; - } - - expand_has_foo(output) { - insta::assert_snapshot!(output, @" - pub trait HasFoo { - fn foo(&self) -> &str; - } - impl<__Context__> HasFoo for __Context__ - where - __Context__: HasField< - Symbol<3, Chars<'f', Chars<'o', Chars<'o', Nil>>>>, - Value = String, - >, - { - fn foo(&self) -> &str { - self.get_field( - ::core::marker::PhantomData::< - Symbol<3, Chars<'f', Chars<'o', Chars<'o', Nil>>>>, - >, - ) - .as_str() - } - } - ") - } - } - - #[derive(HasField)] - pub struct App { - pub foo: String, - } - - #[test] - pub fn test_string_auto_getter() { - let context = App { - foo: "abc".to_owned(), - }; - - assert_eq!(context.foo(), "abc"); - } -} diff --git a/crates/tests/cgp-tests/tests/getter_tests/abstract_type/explicit.rs b/crates/tests/cgp-tests/tests/getters/abstract_type_extend.rs similarity index 70% rename from crates/tests/cgp-tests/tests/getter_tests/abstract_type/explicit.rs rename to crates/tests/cgp-tests/tests/getters/abstract_type_extend.rs index ce61576e..435d9144 100644 --- a/crates/tests/cgp-tests/tests/getter_tests/abstract_type/explicit.rs +++ b/crates/tests/cgp-tests/tests/getters/abstract_type_extend.rs @@ -1,107 +1,18 @@ -use cgp_macro_test_util::{snapshot_cgp_auto_getter, snapshot_cgp_getter, snapshot_cgp_type}; +//! Getters whose return type is an abstract type imported from another component +//! via `#[extend(HasScalarType)]`, so the signatures name the type as +//! `Self::Scalar`. Both the auto getter and the full getter variant are pinned. +//! The `#[cgp_type]` scaffolding is written plainly here — its expansion is owned +//! by the `abstract_types` concept. +//! +//! See docs/reference/macros/cgp_getter.md, docs/reference/macros/cgp_auto_getter.md, +//! and docs/reference/providers/use_type.md. -snapshot_cgp_type! { - #[cgp_type] - pub trait HasScalarType { - type Scalar: Copy; - } +use cgp::prelude::*; +use cgp_macro_test_util::{snapshot_cgp_auto_getter, snapshot_cgp_getter}; - expand_has_scalar_type(output) { - insta::assert_snapshot!(output, @" - pub trait HasScalarType { - type Scalar: Copy; - } - impl<__Context__> HasScalarType for __Context__ - where - __Context__: ScalarTypeProvider<__Context__>, - { - type Scalar = <__Context__ as ScalarTypeProvider<__Context__>>::Scalar; - } - pub trait ScalarTypeProvider< - __Context__, - >: IsProviderFor { - type Scalar: Copy; - } - impl<__Provider__, __Context__> ScalarTypeProvider<__Context__> for __Provider__ - where - __Provider__: DelegateComponent - + IsProviderFor, - <__Provider__ as DelegateComponent< - ScalarTypeProviderComponent, - >>::Delegate: ScalarTypeProvider<__Context__>, - { - type Scalar = <<__Provider__ as DelegateComponent< - ScalarTypeProviderComponent, - >>::Delegate as ScalarTypeProvider<__Context__>>::Scalar; - } - pub struct ScalarTypeProviderComponent; - impl<__Context__> ScalarTypeProvider<__Context__> for UseContext - where - __Context__: HasScalarType, - { - type Scalar = <__Context__ as HasScalarType>::Scalar; - } - impl<__Context__> IsProviderFor - for UseContext - where - __Context__: HasScalarType, - {} - impl<__Context__, __Components__, __Path__> ScalarTypeProvider<__Context__> - for RedirectLookup<__Components__, __Path__> - where - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent< - __Path__, - >>::Delegate: ScalarTypeProvider<__Context__>, - { - type Scalar = <<__Components__ as DelegateComponent< - __Path__, - >>::Delegate as ScalarTypeProvider<__Context__>>::Scalar; - } - impl< - __Context__, - __Components__, - __Path__, - > IsProviderFor - for RedirectLookup<__Components__, __Path__> - where - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent< - __Path__, - >>::Delegate: IsProviderFor - + ScalarTypeProvider<__Context__>, - {} - impl ScalarTypeProvider<__Context__> for UseType - where - Scalar: Copy, - { - type Scalar = Scalar; - } - impl IsProviderFor - for UseType - where - Scalar: Copy, - {} - impl<__Provider__, Scalar, __Context__> ScalarTypeProvider<__Context__> - for WithProvider<__Provider__> - where - Scalar: Copy, - __Provider__: TypeProvider<__Context__, ScalarTypeProviderComponent, Type = Scalar>, - { - type Scalar = Scalar; - } - impl< - __Provider__, - Scalar, - __Context__, - > IsProviderFor - for WithProvider<__Provider__> - where - Scalar: Copy, - __Provider__: TypeProvider<__Context__, ScalarTypeProviderComponent, Type = Scalar>, - {} - ") - } +#[cgp_type] +pub trait HasScalarType { + type Scalar: Copy; } snapshot_cgp_auto_getter! { diff --git a/crates/tests/cgp-tests/tests/getter_tests/abstract_type/use_type.rs b/crates/tests/cgp-tests/tests/getters/abstract_type_use_type.rs similarity index 71% rename from crates/tests/cgp-tests/tests/getter_tests/abstract_type/use_type.rs rename to crates/tests/cgp-tests/tests/getters/abstract_type_use_type.rs index ef603bbb..c62c323d 100644 --- a/crates/tests/cgp-tests/tests/getter_tests/abstract_type/use_type.rs +++ b/crates/tests/cgp-tests/tests/getters/abstract_type_use_type.rs @@ -1,107 +1,19 @@ -use cgp_macro_test_util::{snapshot_cgp_auto_getter, snapshot_cgp_getter, snapshot_cgp_type}; +//! Getters whose return type is an abstract type imported from another component +//! via `#[use_type(HasScalarType::Scalar)]`, so the signatures name it as the +//! bare `Scalar` while the expansion rewrites it to +//! `::Scalar`. Both the auto getter and the full getter +//! variant are pinned. The `#[cgp_type]` scaffolding is written plainly here — +//! its expansion is owned by the `abstract_types` concept. +//! +//! See docs/reference/macros/cgp_getter.md, docs/reference/macros/cgp_auto_getter.md, +//! and docs/reference/providers/use_type.md. -snapshot_cgp_type! { - #[cgp_type] - pub trait HasScalarType { - type Scalar: Copy; - } +use cgp::prelude::*; +use cgp_macro_test_util::{snapshot_cgp_auto_getter, snapshot_cgp_getter}; - expand_has_scalar_type(output) { - insta::assert_snapshot!(output, @" - pub trait HasScalarType { - type Scalar: Copy; - } - impl<__Context__> HasScalarType for __Context__ - where - __Context__: ScalarTypeProvider<__Context__>, - { - type Scalar = <__Context__ as ScalarTypeProvider<__Context__>>::Scalar; - } - pub trait ScalarTypeProvider< - __Context__, - >: IsProviderFor { - type Scalar: Copy; - } - impl<__Provider__, __Context__> ScalarTypeProvider<__Context__> for __Provider__ - where - __Provider__: DelegateComponent - + IsProviderFor, - <__Provider__ as DelegateComponent< - ScalarTypeProviderComponent, - >>::Delegate: ScalarTypeProvider<__Context__>, - { - type Scalar = <<__Provider__ as DelegateComponent< - ScalarTypeProviderComponent, - >>::Delegate as ScalarTypeProvider<__Context__>>::Scalar; - } - pub struct ScalarTypeProviderComponent; - impl<__Context__> ScalarTypeProvider<__Context__> for UseContext - where - __Context__: HasScalarType, - { - type Scalar = <__Context__ as HasScalarType>::Scalar; - } - impl<__Context__> IsProviderFor - for UseContext - where - __Context__: HasScalarType, - {} - impl<__Context__, __Components__, __Path__> ScalarTypeProvider<__Context__> - for RedirectLookup<__Components__, __Path__> - where - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent< - __Path__, - >>::Delegate: ScalarTypeProvider<__Context__>, - { - type Scalar = <<__Components__ as DelegateComponent< - __Path__, - >>::Delegate as ScalarTypeProvider<__Context__>>::Scalar; - } - impl< - __Context__, - __Components__, - __Path__, - > IsProviderFor - for RedirectLookup<__Components__, __Path__> - where - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent< - __Path__, - >>::Delegate: IsProviderFor - + ScalarTypeProvider<__Context__>, - {} - impl ScalarTypeProvider<__Context__> for UseType - where - Scalar: Copy, - { - type Scalar = Scalar; - } - impl IsProviderFor - for UseType - where - Scalar: Copy, - {} - impl<__Provider__, Scalar, __Context__> ScalarTypeProvider<__Context__> - for WithProvider<__Provider__> - where - Scalar: Copy, - __Provider__: TypeProvider<__Context__, ScalarTypeProviderComponent, Type = Scalar>, - { - type Scalar = Scalar; - } - impl< - __Provider__, - Scalar, - __Context__, - > IsProviderFor - for WithProvider<__Provider__> - where - Scalar: Copy, - __Provider__: TypeProvider<__Context__, ScalarTypeProviderComponent, Type = Scalar>, - {} - ") - } +#[cgp_type] +pub trait HasScalarType { + type Scalar: Copy; } snapshot_cgp_auto_getter! { diff --git a/crates/tests/cgp-tests/tests/getter_tests/assoc_type/auto_getter.rs b/crates/tests/cgp-tests/tests/getters/assoc_type_auto_getter.rs similarity index 80% rename from crates/tests/cgp-tests/tests/getter_tests/assoc_type/auto_getter.rs rename to crates/tests/cgp-tests/tests/getters/assoc_type_auto_getter.rs index 28c20431..7d3d3262 100644 --- a/crates/tests/cgp-tests/tests/getter_tests/assoc_type/auto_getter.rs +++ b/crates/tests/cgp-tests/tests/getters/assoc_type_auto_getter.rs @@ -1,3 +1,9 @@ +//! `#[cgp_auto_getter]` on a single-getter trait that declares a local +//! associated type used as the return type: the type is inferred from the field +//! and the trait bound (`Display`) is carried onto the generated blanket impl. +//! +//! See docs/reference/macros/cgp_auto_getter.md. + use core::fmt::Display; use cgp::prelude::*; diff --git a/crates/tests/cgp-tests/tests/getter_tests/assoc_type/getter.rs b/crates/tests/cgp-tests/tests/getters/assoc_type_getter.rs similarity index 89% rename from crates/tests/cgp-tests/tests/getter_tests/assoc_type/getter.rs rename to crates/tests/cgp-tests/tests/getters/assoc_type_getter.rs index 6b5abdae..7456c4e2 100644 --- a/crates/tests/cgp-tests/tests/getter_tests/assoc_type/getter.rs +++ b/crates/tests/cgp-tests/tests/getters/assoc_type_getter.rs @@ -1,7 +1,15 @@ +//! `#[cgp_getter]` on a getter trait with a local associated return type +//! (`type Scalar`): the full getter component is generated, including the +//! `UseContext`/`RedirectLookup`/`UseFields`/`UseField`/`WithProvider` provider +//! impls that carry the associated type through. The context then binds the +//! source field by wiring the getter to `UseField`. +//! +//! See docs/reference/macros/cgp_getter.md and docs/reference/providers/use_field.md. + use core::ops::Mul; use cgp::prelude::*; -use cgp_macro_test_util::{snapshot_cgp_getter, snapshot_delegate_components}; +use cgp_macro_test_util::snapshot_cgp_getter; snapshot_cgp_getter! { #[cgp_getter] @@ -179,29 +187,10 @@ pub struct App { pub scalar: f64, } -snapshot_delegate_components! { - delegate_components! { - App { - ScalarGetterComponent: - UseField, - } - } - - expand_app(output) { - insta::assert_snapshot!(output, @r#" - impl DelegateComponent for App { - type Delegate = UseField; - } - impl< - __Context__, - __Params__, - > IsProviderFor for App - where - UseField< - Symbol!("scalar"), - >: IsProviderFor, - {} - "#) +delegate_components! { + App { + ScalarGetterComponent: + UseField, } } diff --git a/crates/tests/cgp-tests/tests/getter_tests/assoc_type/self_referential.rs b/crates/tests/cgp-tests/tests/getters/assoc_type_self_referential.rs similarity index 88% rename from crates/tests/cgp-tests/tests/getter_tests/assoc_type/self_referential.rs rename to crates/tests/cgp-tests/tests/getters/assoc_type_self_referential.rs index 40abfa1e..81af7166 100644 --- a/crates/tests/cgp-tests/tests/getter_tests/assoc_type/self_referential.rs +++ b/crates/tests/cgp-tests/tests/getters/assoc_type_self_referential.rs @@ -1,7 +1,14 @@ +//! `#[cgp_getter]` with a self-referential associated-type bound +//! (`Name: Display`) and the source field bound by wiring to a differently-named +//! field: `Person::name()` reads the `first_name` field via +//! `UseField`. +//! +//! See docs/reference/macros/cgp_getter.md and docs/reference/providers/use_field.md. + use core::fmt::Display; use cgp::prelude::*; -use cgp_macro_test_util::{snapshot_cgp_getter, snapshot_delegate_components}; +use cgp_macro_test_util::snapshot_cgp_getter; snapshot_cgp_getter! { #[cgp_getter] @@ -158,27 +165,10 @@ pub struct Person { pub first_name: String, } -snapshot_delegate_components! { - delegate_components! { - Person { - NameGetterComponent: - UseField, - } - } - - expand_person(output) { - insta::assert_snapshot!(output, @r#" - impl DelegateComponent for Person { - type Delegate = UseField; - } - impl<__Context__, __Params__> IsProviderFor - for Person - where - UseField< - Symbol!("first_name"), - >: IsProviderFor, - {} - "#) +delegate_components! { + Person { + NameGetterComponent: + UseField, } } diff --git a/crates/tests/cgp-tests/tests/getter_tests/assoc_type/self_referential_auto.rs b/crates/tests/cgp-tests/tests/getters/assoc_type_self_referential_auto.rs similarity index 84% rename from crates/tests/cgp-tests/tests/getter_tests/assoc_type/self_referential_auto.rs rename to crates/tests/cgp-tests/tests/getters/assoc_type_self_referential_auto.rs index 8ac420c6..53592b58 100644 --- a/crates/tests/cgp-tests/tests/getter_tests/assoc_type/self_referential_auto.rs +++ b/crates/tests/cgp-tests/tests/getters/assoc_type_self_referential_auto.rs @@ -1,3 +1,9 @@ +//! `#[cgp_auto_getter]` with a self-referential associated-type bound +//! (`Scalar: Mul`): the bound survives onto the generated +//! blanket impl with `Self::Scalar` rewritten to the inferred type parameter. +//! +//! See docs/reference/macros/cgp_auto_getter.md. + use core::ops::Mul; use cgp::prelude::*; diff --git a/crates/tests/cgp-tests/tests/getter_tests/auto_generics.rs b/crates/tests/cgp-tests/tests/getters/auto_getter_generic.rs similarity index 79% rename from crates/tests/cgp-tests/tests/getter_tests/auto_generics.rs rename to crates/tests/cgp-tests/tests/getters/auto_getter_generic.rs index 0e68c9c3..57c5e6e1 100644 --- a/crates/tests/cgp-tests/tests/getter_tests/auto_generics.rs +++ b/crates/tests/cgp-tests/tests/getters/auto_getter_generic.rs @@ -1,3 +1,9 @@ +//! `#[cgp_auto_getter]` on a trait with a generic parameter: the getter is keyed +//! by a `PhantomData` tag and reads the `foo` field, so the same getter +//! works for any field type inferred from the generic argument. +//! +//! See docs/reference/macros/cgp_auto_getter.md. + use cgp::prelude::*; use cgp_macro_test_util::snapshot_cgp_auto_getter; diff --git a/crates/tests/cgp-tests/tests/getters/clone.rs b/crates/tests/cgp-tests/tests/getters/clone.rs new file mode 100644 index 00000000..1a731578 --- /dev/null +++ b/crates/tests/cgp-tests/tests/getters/clone.rs @@ -0,0 +1,187 @@ +//! `#[cgp_getter]` returning an owned `Self::Name` where the associated type is +//! `Copy`: the getter reads the field and `.clone()`s it out by value. The +//! abstract `HasNameType` and the `delegate_components!` wiring are written +//! plainly here (their expansions are owned by the `abstract_types` and +//! `basic_delegation` concepts). +//! +//! See docs/reference/macros/cgp_getter.md and docs/reference/providers/use_field.md. + +use cgp::prelude::*; +use cgp_macro_test_util::snapshot_cgp_getter; + +#[cgp_type] +pub trait HasNameType { + type Name; +} + +snapshot_cgp_getter! { + #[cgp_getter] + pub trait HasName: HasNameType { + fn name(&self) -> Self::Name; + } + + expand_has_name(output) { + insta::assert_snapshot!(output, @" + pub trait HasName: HasNameType { + fn name(&self) -> Self::Name; + } + impl<__Context__> HasName for __Context__ + where + __Context__: HasNameType, + __Context__: NameGetter<__Context__>, + { + fn name(&self) -> Self::Name { + __Context__::name(self) + } + } + pub trait NameGetter<__Context__>: IsProviderFor + where + __Context__: HasNameType, + { + fn name(__context__: &__Context__) -> __Context__::Name; + } + impl<__Provider__, __Context__> NameGetter<__Context__> for __Provider__ + where + __Context__: HasNameType, + __Provider__: DelegateComponent + + IsProviderFor, + <__Provider__ as DelegateComponent< + NameGetterComponent, + >>::Delegate: NameGetter<__Context__>, + { + fn name(__context__: &__Context__) -> __Context__::Name { + <__Provider__ as DelegateComponent< + NameGetterComponent, + >>::Delegate::name(__context__) + } + } + pub struct NameGetterComponent; + impl<__Context__> NameGetter<__Context__> for UseContext + where + __Context__: HasNameType, + __Context__: HasName, + { + fn name(__context__: &__Context__) -> __Context__::Name { + __Context__::name(__context__) + } + } + impl<__Context__> IsProviderFor for UseContext + where + __Context__: HasNameType, + __Context__: HasName, + {} + impl<__Context__, __Components__, __Path__> NameGetter<__Context__> + for RedirectLookup<__Components__, __Path__> + where + __Context__: HasNameType, + __Components__: DelegateComponent<__Path__>, + <__Components__ as DelegateComponent<__Path__>>::Delegate: NameGetter<__Context__>, + { + fn name(__context__: &__Context__) -> __Context__::Name { + <__Components__ as DelegateComponent<__Path__>>::Delegate::name(__context__) + } + } + impl< + __Context__, + __Components__, + __Path__, + > IsProviderFor + for RedirectLookup<__Components__, __Path__> + where + __Context__: HasNameType, + __Components__: DelegateComponent<__Path__>, + <__Components__ as DelegateComponent< + __Path__, + >>::Delegate: IsProviderFor + + NameGetter<__Context__>, + {} + impl<__Context__> NameGetter<__Context__> for UseFields + where + __Context__: HasNameType, + __Context__: HasField< + Symbol<4, Chars<'n', Chars<'a', Chars<'m', Chars<'e', Nil>>>>>, + Value = __Context__::Name, + >, + { + fn name(__context__: &__Context__) -> __Context__::Name { + __context__ + .get_field( + ::core::marker::PhantomData::< + Symbol<4, Chars<'n', Chars<'a', Chars<'m', Chars<'e', Nil>>>>>, + >, + ) + .clone() + } + } + impl<__Context__> IsProviderFor for UseFields + where + __Context__: HasNameType, + __Context__: HasField< + Symbol<4, Chars<'n', Chars<'a', Chars<'m', Chars<'e', Nil>>>>>, + Value = __Context__::Name, + >, + {} + impl<__Context__, __Tag__> NameGetter<__Context__> for UseField<__Tag__> + where + __Context__: HasNameType, + __Context__: HasField<__Tag__, Value = __Context__::Name>, + { + fn name(__context__: &__Context__) -> __Context__::Name { + __context__.get_field(::core::marker::PhantomData::<__Tag__>).clone() + } + } + impl<__Context__, __Tag__> IsProviderFor + for UseField<__Tag__> + where + __Context__: HasNameType, + __Context__: HasField<__Tag__, Value = __Context__::Name>, + {} + impl<__Context__, __Provider__> NameGetter<__Context__> for WithProvider<__Provider__> + where + __Context__: HasNameType, + __Provider__: FieldGetter< + __Context__, + NameGetterComponent, + Value = __Context__::Name, + >, + { + fn name(__context__: &__Context__) -> __Context__::Name { + __Provider__::get_field( + __context__, + ::core::marker::PhantomData::, + ) + .clone() + } + } + impl<__Context__, __Provider__> IsProviderFor + for WithProvider<__Provider__> + where + __Context__: HasNameType, + __Provider__: FieldGetter< + __Context__, + NameGetterComponent, + Value = __Context__::Name, + >, + {} + ") + } +} + +#[derive(HasField)] +pub struct App { + pub name: &'static str, +} + +delegate_components! { + App { + NameTypeProviderComponent: UseType<&'static str>, + NameGetterComponent: UseField, + } +} + +#[test] +pub fn test_clone_getter() { + let context = App { name: "Alice" }; + + assert_eq!(context.name(), "Alice"); +} diff --git a/crates/tests/cgp-tests/tests/getters/clone_auto.rs b/crates/tests/cgp-tests/tests/getters/clone_auto.rs new file mode 100644 index 00000000..a69519a7 --- /dev/null +++ b/crates/tests/cgp-tests/tests/getters/clone_auto.rs @@ -0,0 +1,65 @@ +//! `#[cgp_auto_getter]` returning an owned `Self::Name` where the associated type +//! is `Copy`: the blanket impl reads the field named after the method and +//! `.clone()`s it out by value. The abstract `HasNameType` and the +//! `delegate_components!` wiring are written plainly here (their expansions are +//! owned by the `abstract_types` and `basic_delegation` concepts). +//! +//! See docs/reference/macros/cgp_auto_getter.md. + +use cgp::prelude::*; +use cgp_macro_test_util::snapshot_cgp_auto_getter; + +#[cgp_type] +pub trait HasNameType { + type Name; +} + +snapshot_cgp_auto_getter! { + #[cgp_auto_getter] + pub trait HasName: HasNameType { + fn name(&self) -> Self::Name; + } + + expand_has_name(output) { + insta::assert_snapshot!(output, @" + pub trait HasName: HasNameType { + fn name(&self) -> Self::Name; + } + impl<__Context__> HasName for __Context__ + where + __Context__: HasNameType, + __Context__: HasField< + Symbol<4, Chars<'n', Chars<'a', Chars<'m', Chars<'e', Nil>>>>>, + Value = __Context__::Name, + >, + { + fn name(&self) -> __Context__::Name { + self.get_field( + ::core::marker::PhantomData::< + Symbol<4, Chars<'n', Chars<'a', Chars<'m', Chars<'e', Nil>>>>>, + >, + ) + .clone() + } + } + ") + } +} + +#[derive(HasField)] +pub struct App { + pub name: &'static str, +} + +delegate_components! { + App { + NameTypeProviderComponent: UseType<&'static str>, + } +} + +#[test] +pub fn test_clone_auto_getter() { + let context = App { name: "Alice" }; + + assert_eq!(context.name(), "Alice"); +} diff --git a/crates/tests/cgp-tests/tests/getters/mod.rs b/crates/tests/cgp-tests/tests/getters/mod.rs new file mode 100644 index 00000000..782489b2 --- /dev/null +++ b/crates/tests/cgp-tests/tests/getters/mod.rs @@ -0,0 +1,33 @@ +//! One unit test per file. Each file is self-contained: it defines its own +//! getter traits, providers, and context types at module scope so that the +//! type-level wiring of one test never leaks into another. + +// `#[cgp_auto_getter]` snapshots (this concept owns the macro's expansion): the +// distinct return-type shapes and trait shapes the auto getter supports. +pub mod assoc_type_auto_getter; +pub mod assoc_type_self_referential_auto; +pub mod auto_getter_generic; +pub mod clone_auto; +pub mod mref_auto; +pub mod non_self_auto; +pub mod option_auto; +pub mod slice_auto; +pub mod string_auto; + +// `#[cgp_getter]` snapshots (this concept owns the macro's expansion): the full +// getter component, its return-type shapes, and its `provider`/`name` overrides. +pub mod assoc_type_getter; +pub mod assoc_type_self_referential; +pub mod clone; +pub mod mref; +pub mod non_self; +pub mod option; +pub mod slice; +pub mod string; +pub mod string_custom_name; +pub mod string_custom_spec; + +// Getters that name an abstract type imported from another component, via either +// `#[extend(...)]` + `Self::Scalar` or `#[use_type(...)]` + bare `Scalar`. +pub mod abstract_type_extend; +pub mod abstract_type_use_type; diff --git a/crates/tests/cgp-tests/tests/getters/mref.rs b/crates/tests/cgp-tests/tests/getters/mref.rs new file mode 100644 index 00000000..d77c8081 --- /dev/null +++ b/crates/tests/cgp-tests/tests/getters/mref.rs @@ -0,0 +1,159 @@ +//! `#[cgp_getter]` returning `MRef<'_, String>` (an owned-or-borrowed value): +//! the getter reads a `String` field and wraps the borrow in `MRef::Ref`. The +//! context binds the source field via `UseField`. +//! +//! See docs/reference/macros/cgp_getter.md and docs/reference/providers/use_field.md. + +use cgp::core::field::types::MRef; +use cgp::prelude::*; +use cgp_macro_test_util::snapshot_cgp_getter; + +snapshot_cgp_getter! { + #[cgp_getter] + pub trait HasFoo { + fn foo(&self) -> MRef<'_, String>; + } + + expand_has_foo(output) { + insta::assert_snapshot!(output, @" + pub trait HasFoo { + fn foo(&self) -> MRef<'_, String>; + } + impl<__Context__> HasFoo for __Context__ + where + __Context__: FooGetter<__Context__>, + { + fn foo(&self) -> MRef<'_, String> { + __Context__::foo(self) + } + } + pub trait FooGetter<__Context__>: IsProviderFor { + fn foo(__context__: &__Context__) -> MRef<'_, String>; + } + impl<__Provider__, __Context__> FooGetter<__Context__> for __Provider__ + where + __Provider__: DelegateComponent + + IsProviderFor, + <__Provider__ as DelegateComponent< + FooGetterComponent, + >>::Delegate: FooGetter<__Context__>, + { + fn foo(__context__: &__Context__) -> MRef<'_, String> { + <__Provider__ as DelegateComponent< + FooGetterComponent, + >>::Delegate::foo(__context__) + } + } + pub struct FooGetterComponent; + impl<__Context__> FooGetter<__Context__> for UseContext + where + __Context__: HasFoo, + { + fn foo(__context__: &__Context__) -> MRef<'_, String> { + __Context__::foo(__context__) + } + } + impl<__Context__> IsProviderFor for UseContext + where + __Context__: HasFoo, + {} + impl<__Context__, __Components__, __Path__> FooGetter<__Context__> + for RedirectLookup<__Components__, __Path__> + where + __Components__: DelegateComponent<__Path__>, + <__Components__ as DelegateComponent<__Path__>>::Delegate: FooGetter<__Context__>, + { + fn foo(__context__: &__Context__) -> MRef<'_, String> { + <__Components__ as DelegateComponent<__Path__>>::Delegate::foo(__context__) + } + } + impl< + __Context__, + __Components__, + __Path__, + > IsProviderFor + for RedirectLookup<__Components__, __Path__> + where + __Components__: DelegateComponent<__Path__>, + <__Components__ as DelegateComponent< + __Path__, + >>::Delegate: IsProviderFor + + FooGetter<__Context__>, + {} + impl<__Context__> FooGetter<__Context__> for UseFields + where + __Context__: HasField< + Symbol<3, Chars<'f', Chars<'o', Chars<'o', Nil>>>>, + Value = String, + >, + { + fn foo(__context__: &__Context__) -> MRef<'_, String> { + MRef::Ref( + __context__ + .get_field( + ::core::marker::PhantomData::< + Symbol<3, Chars<'f', Chars<'o', Chars<'o', Nil>>>>, + >, + ), + ) + } + } + impl<__Context__> IsProviderFor for UseFields + where + __Context__: HasField< + Symbol<3, Chars<'f', Chars<'o', Chars<'o', Nil>>>>, + Value = String, + >, + {} + impl<__Context__, __Tag__> FooGetter<__Context__> for UseField<__Tag__> + where + __Context__: HasField<__Tag__, Value = String>, + { + fn foo(__context__: &__Context__) -> MRef<'_, String> { + MRef::Ref(__context__.get_field(::core::marker::PhantomData::<__Tag__>)) + } + } + impl<__Context__, __Tag__> IsProviderFor + for UseField<__Tag__> + where + __Context__: HasField<__Tag__, Value = String>, + {} + impl<__Context__, __Provider__> FooGetter<__Context__> for WithProvider<__Provider__> + where + __Provider__: FieldGetter<__Context__, FooGetterComponent, Value = String>, + { + fn foo(__context__: &__Context__) -> MRef<'_, String> { + MRef::Ref( + __Provider__::get_field( + __context__, + ::core::marker::PhantomData::, + ), + ) + } + } + impl<__Context__, __Provider__> IsProviderFor + for WithProvider<__Provider__> + where + __Provider__: FieldGetter<__Context__, FooGetterComponent, Value = String>, + {} + ") + } +} + +#[derive(HasField)] +pub struct App { + pub bar: String, +} + +delegate_components! { + App { + FooGetterComponent: UseField, + } +} + +#[test] +pub fn test_mref_getter() { + let context = App { bar: "foo".into() }; + + assert_eq!(context.foo().as_ref(), "foo"); +} diff --git a/crates/tests/cgp-tests/tests/getters/mref_auto.rs b/crates/tests/cgp-tests/tests/getters/mref_auto.rs new file mode 100644 index 00000000..21b30472 --- /dev/null +++ b/crates/tests/cgp-tests/tests/getters/mref_auto.rs @@ -0,0 +1,54 @@ +//! `#[cgp_auto_getter]` returning `MRef<'_, String>` (an owned-or-borrowed +//! value): the blanket impl reads a `String` field named after the method and +//! wraps the borrow in `MRef::Ref` without wiring. +//! +//! See docs/reference/macros/cgp_auto_getter.md. + +use cgp::core::field::types::MRef; +use cgp::prelude::*; +use cgp_macro_test_util::snapshot_cgp_auto_getter; + +snapshot_cgp_auto_getter! { + #[cgp_auto_getter] + pub trait HasFoo { + fn foo(&self) -> MRef<'_, String>; + } + + expand_has_foo(output) { + insta::assert_snapshot!(output, @" + pub trait HasFoo { + fn foo(&self) -> MRef<'_, String>; + } + impl<__Context__> HasFoo for __Context__ + where + __Context__: HasField< + Symbol<3, Chars<'f', Chars<'o', Chars<'o', Nil>>>>, + Value = String, + >, + { + fn foo(&self) -> MRef<'_, String> { + MRef::Ref( + self + .get_field( + ::core::marker::PhantomData::< + Symbol<3, Chars<'f', Chars<'o', Chars<'o', Nil>>>>, + >, + ), + ) + } + } + ") + } +} + +#[derive(HasField)] +pub struct App { + pub foo: String, +} + +#[test] +pub fn test_mref_auto_getter() { + let context = App { foo: "foo".into() }; + + assert_eq!(context.foo().as_ref(), "foo"); +} diff --git a/crates/tests/cgp-tests/tests/getters/non_self.rs b/crates/tests/cgp-tests/tests/getters/non_self.rs new file mode 100644 index 00000000..95b10fe3 --- /dev/null +++ b/crates/tests/cgp-tests/tests/getters/non_self.rs @@ -0,0 +1,230 @@ +//! `#[cgp_getter]` on a non-`self` getter (`fn foo_bar(foo: &Self::Foo) -> &Self::Bar`): +//! the getter reads a field *of another type* (`Self::Foo`) rather than of the +//! context, so `App::foo_bar` fetches the `bar` field out of a `Foo` value. The +//! abstract types and the `delegate_components!` wiring are written plainly here +//! (their expansions are owned by the `abstract_types` and `basic_delegation` +//! concepts). +//! +//! See docs/reference/macros/cgp_getter.md and docs/reference/providers/use_field.md. + +use cgp::prelude::*; +use cgp_macro_test_util::snapshot_cgp_getter; + +#[cgp_type] +pub trait HasFooType { + type Foo; +} + +#[cgp_type] +pub trait HasBarType { + type Bar; +} + +snapshot_cgp_getter! { + #[cgp_getter] + pub trait HasFooBar: HasFooType + HasBarType { + fn foo_bar(foo: &Self::Foo) -> &Self::Bar; + } + + expand_has_foo_bar(output) { + insta::assert_snapshot!(output, @" + pub trait HasFooBar: HasFooType + HasBarType { + fn foo_bar(foo: &Self::Foo) -> &Self::Bar; + } + impl<__Context__> HasFooBar for __Context__ + where + __Context__: HasFooType + HasBarType, + __Context__: FooBarGetter<__Context__>, + { + fn foo_bar(foo: &Self::Foo) -> &Self::Bar { + __Context__::foo_bar(foo) + } + } + pub trait FooBarGetter< + __Context__, + >: IsProviderFor + where + __Context__: HasFooType + HasBarType, + { + fn foo_bar(foo: &__Context__::Foo) -> &__Context__::Bar; + } + impl<__Provider__, __Context__> FooBarGetter<__Context__> for __Provider__ + where + __Context__: HasFooType + HasBarType, + __Provider__: DelegateComponent + + IsProviderFor, + <__Provider__ as DelegateComponent< + FooBarGetterComponent, + >>::Delegate: FooBarGetter<__Context__>, + { + fn foo_bar(foo: &__Context__::Foo) -> &__Context__::Bar { + <__Provider__ as DelegateComponent< + FooBarGetterComponent, + >>::Delegate::foo_bar(foo) + } + } + pub struct FooBarGetterComponent; + impl<__Context__> FooBarGetter<__Context__> for UseContext + where + __Context__: HasFooType + HasBarType, + __Context__: HasFooBar, + { + fn foo_bar(foo: &__Context__::Foo) -> &__Context__::Bar { + __Context__::foo_bar(foo) + } + } + impl<__Context__> IsProviderFor for UseContext + where + __Context__: HasFooType + HasBarType, + __Context__: HasFooBar, + {} + impl<__Context__, __Components__, __Path__> FooBarGetter<__Context__> + for RedirectLookup<__Components__, __Path__> + where + __Context__: HasFooType + HasBarType, + __Components__: DelegateComponent<__Path__>, + <__Components__ as DelegateComponent<__Path__>>::Delegate: FooBarGetter<__Context__>, + { + fn foo_bar(foo: &__Context__::Foo) -> &__Context__::Bar { + <__Components__ as DelegateComponent<__Path__>>::Delegate::foo_bar(foo) + } + } + impl< + __Context__, + __Components__, + __Path__, + > IsProviderFor + for RedirectLookup<__Components__, __Path__> + where + __Context__: HasFooType + HasBarType, + __Components__: DelegateComponent<__Path__>, + <__Components__ as DelegateComponent< + __Path__, + >>::Delegate: IsProviderFor + + FooBarGetter<__Context__>, + {} + impl<__Context__> FooBarGetter<__Context__> for UseFields + where + __Context__: HasFooType + HasBarType, + __Context__::Foo: HasField< + Symbol< + 7, + Chars< + 'f', + Chars< + 'o', + Chars<'o', Chars<'_', Chars<'b', Chars<'a', Chars<'r', Nil>>>>>, + >, + >, + >, + Value = __Context__::Bar, + >, + { + fn foo_bar(__context__: &__Context__::Foo) -> &__Context__::Bar { + __context__ + .get_field( + ::core::marker::PhantomData::< + Symbol< + 7, + Chars< + 'f', + Chars< + 'o', + Chars< + 'o', + Chars<'_', Chars<'b', Chars<'a', Chars<'r', Nil>>>>, + >, + >, + >, + >, + >, + ) + } + } + impl<__Context__> IsProviderFor for UseFields + where + __Context__: HasFooType + HasBarType, + __Context__::Foo: HasField< + Symbol< + 7, + Chars< + 'f', + Chars< + 'o', + Chars<'o', Chars<'_', Chars<'b', Chars<'a', Chars<'r', Nil>>>>>, + >, + >, + >, + Value = __Context__::Bar, + >, + {} + impl<__Context__, __Tag__> FooBarGetter<__Context__> for UseField<__Tag__> + where + __Context__: HasFooType + HasBarType, + __Context__::Foo: HasField<__Tag__, Value = __Context__::Bar>, + { + fn foo_bar(__context__: &__Context__::Foo) -> &__Context__::Bar { + __context__.get_field(::core::marker::PhantomData::<__Tag__>) + } + } + impl<__Context__, __Tag__> IsProviderFor + for UseField<__Tag__> + where + __Context__: HasFooType + HasBarType, + __Context__::Foo: HasField<__Tag__, Value = __Context__::Bar>, + {} + impl<__Context__, __Provider__> FooBarGetter<__Context__> for WithProvider<__Provider__> + where + __Context__: HasFooType + HasBarType, + __Provider__: FieldGetter< + __Context__::Foo, + FooBarGetterComponent, + Value = __Context__::Bar, + >, + { + fn foo_bar(__context__: &__Context__::Foo) -> &__Context__::Bar { + __Provider__::get_field( + __context__, + ::core::marker::PhantomData::, + ) + } + } + impl<__Context__, __Provider__> IsProviderFor + for WithProvider<__Provider__> + where + __Context__: HasFooType + HasBarType, + __Provider__: FieldGetter< + __Context__::Foo, + FooBarGetterComponent, + Value = __Context__::Bar, + >, + {} + ") + } +} + +pub struct App; + +#[derive(HasField)] +pub struct Foo { + pub bar: u32, +} + +delegate_components! { + App { + FooTypeProviderComponent: + UseType, + BarTypeProviderComponent: + UseType, + FooBarGetterComponent: + UseField, + } +} + +#[test] +fn test_non_self_getter() { + let foo = Foo { bar: 42 }; + + let bar = ::foo_bar(&foo); + assert_eq!(bar, &42); +} diff --git a/crates/tests/cgp-tests/tests/getters/non_self_auto.rs b/crates/tests/cgp-tests/tests/getters/non_self_auto.rs new file mode 100644 index 00000000..7a427a22 --- /dev/null +++ b/crates/tests/cgp-tests/tests/getters/non_self_auto.rs @@ -0,0 +1,98 @@ +//! `#[cgp_auto_getter]` on a non-`self` getter +//! (`fn foo_bar(foo: &Self::Foo) -> &Self::Bar`): the blanket impl reads a field +//! *of another type* (`Self::Foo`) named after the method, so `App::foo_bar` +//! fetches the `foo_bar` field out of a `Foo` value. The abstract types and the +//! `delegate_components!` wiring are written plainly here (their expansions are +//! owned by the `abstract_types` and `basic_delegation` concepts). +//! +//! See docs/reference/macros/cgp_auto_getter.md. + +use cgp::prelude::*; +use cgp_macro_test_util::snapshot_cgp_auto_getter; + +#[cgp_type] +pub trait HasFooType { + type Foo; +} + +#[cgp_type] +pub trait HasBarType { + type Bar; +} + +snapshot_cgp_auto_getter! { + #[cgp_auto_getter] + pub trait HasFooBar: HasFooType + HasBarType { + fn foo_bar(foo: &Self::Foo) -> &Self::Bar; + } + + expand_has_foo_bar(output) { + insta::assert_snapshot!(output, @" + pub trait HasFooBar: HasFooType + HasBarType { + fn foo_bar(foo: &Self::Foo) -> &Self::Bar; + } + impl<__Context__> HasFooBar for __Context__ + where + __Context__: HasFooType + HasBarType, + __Context__::Foo: HasField< + Symbol< + 7, + Chars< + 'f', + Chars< + 'o', + Chars<'o', Chars<'_', Chars<'b', Chars<'a', Chars<'r', Nil>>>>>, + >, + >, + >, + Value = __Context__::Bar, + >, + { + fn foo_bar(__context__: &__Context__::Foo) -> &__Context__::Bar { + __context__ + .get_field( + ::core::marker::PhantomData::< + Symbol< + 7, + Chars< + 'f', + Chars< + 'o', + Chars< + 'o', + Chars<'_', Chars<'b', Chars<'a', Chars<'r', Nil>>>>, + >, + >, + >, + >, + >, + ) + } + } + ") + } +} + +pub struct App; + +#[derive(HasField)] +pub struct Foo { + pub foo_bar: u32, +} + +delegate_components! { + App { + FooTypeProviderComponent: + UseType, + BarTypeProviderComponent: + UseType, + } +} + +#[test] +fn test_non_self_getter() { + let foo = Foo { foo_bar: 42 }; + + let bar = App::foo_bar(&foo); + assert_eq!(bar, &42); +} diff --git a/crates/tests/cgp-tests/tests/getters/option.rs b/crates/tests/cgp-tests/tests/getters/option.rs new file mode 100644 index 00000000..8c45ea04 --- /dev/null +++ b/crates/tests/cgp-tests/tests/getters/option.rs @@ -0,0 +1,158 @@ +//! `#[cgp_getter]` returning `Option<&String>`: the getter reads an +//! `Option` field and calls `.as_ref()`, converting `&Option` +//! into `Option<&String>`. The context binds the source field via `UseField`. +//! +//! See docs/reference/macros/cgp_getter.md and docs/reference/providers/use_field.md. + +use cgp::prelude::*; +use cgp_macro_test_util::snapshot_cgp_getter; + +snapshot_cgp_getter! { + #[cgp_getter] + pub trait HasFoo { + fn foo(&self) -> Option<&String>; + } + + expand_has_foo(output) { + insta::assert_snapshot!(output, @" + pub trait HasFoo { + fn foo(&self) -> Option<&String>; + } + impl<__Context__> HasFoo for __Context__ + where + __Context__: FooGetter<__Context__>, + { + fn foo(&self) -> Option<&String> { + __Context__::foo(self) + } + } + pub trait FooGetter<__Context__>: IsProviderFor { + fn foo(__context__: &__Context__) -> Option<&String>; + } + impl<__Provider__, __Context__> FooGetter<__Context__> for __Provider__ + where + __Provider__: DelegateComponent + + IsProviderFor, + <__Provider__ as DelegateComponent< + FooGetterComponent, + >>::Delegate: FooGetter<__Context__>, + { + fn foo(__context__: &__Context__) -> Option<&String> { + <__Provider__ as DelegateComponent< + FooGetterComponent, + >>::Delegate::foo(__context__) + } + } + pub struct FooGetterComponent; + impl<__Context__> FooGetter<__Context__> for UseContext + where + __Context__: HasFoo, + { + fn foo(__context__: &__Context__) -> Option<&String> { + __Context__::foo(__context__) + } + } + impl<__Context__> IsProviderFor for UseContext + where + __Context__: HasFoo, + {} + impl<__Context__, __Components__, __Path__> FooGetter<__Context__> + for RedirectLookup<__Components__, __Path__> + where + __Components__: DelegateComponent<__Path__>, + <__Components__ as DelegateComponent<__Path__>>::Delegate: FooGetter<__Context__>, + { + fn foo(__context__: &__Context__) -> Option<&String> { + <__Components__ as DelegateComponent<__Path__>>::Delegate::foo(__context__) + } + } + impl< + __Context__, + __Components__, + __Path__, + > IsProviderFor + for RedirectLookup<__Components__, __Path__> + where + __Components__: DelegateComponent<__Path__>, + <__Components__ as DelegateComponent< + __Path__, + >>::Delegate: IsProviderFor + + FooGetter<__Context__>, + {} + impl<__Context__> FooGetter<__Context__> for UseFields + where + __Context__: HasField< + Symbol<3, Chars<'f', Chars<'o', Chars<'o', Nil>>>>, + Value = Option, + >, + { + fn foo(__context__: &__Context__) -> Option<&String> { + __context__ + .get_field( + ::core::marker::PhantomData::< + Symbol<3, Chars<'f', Chars<'o', Chars<'o', Nil>>>>, + >, + ) + .as_ref() + } + } + impl<__Context__> IsProviderFor for UseFields + where + __Context__: HasField< + Symbol<3, Chars<'f', Chars<'o', Chars<'o', Nil>>>>, + Value = Option, + >, + {} + impl<__Context__, __Tag__> FooGetter<__Context__> for UseField<__Tag__> + where + __Context__: HasField<__Tag__, Value = Option>, + { + fn foo(__context__: &__Context__) -> Option<&String> { + __context__.get_field(::core::marker::PhantomData::<__Tag__>).as_ref() + } + } + impl<__Context__, __Tag__> IsProviderFor + for UseField<__Tag__> + where + __Context__: HasField<__Tag__, Value = Option>, + {} + impl<__Context__, __Provider__> FooGetter<__Context__> for WithProvider<__Provider__> + where + __Provider__: FieldGetter<__Context__, FooGetterComponent, Value = Option>, + { + fn foo(__context__: &__Context__) -> Option<&String> { + __Provider__::get_field( + __context__, + ::core::marker::PhantomData::, + ) + .as_ref() + } + } + impl<__Context__, __Provider__> IsProviderFor + for WithProvider<__Provider__> + where + __Provider__: FieldGetter<__Context__, FooGetterComponent, Value = Option>, + {} + ") + } +} + +#[derive(HasField)] +pub struct App { + pub bar: Option, +} + +delegate_components! { + App { + FooGetterComponent: UseField, + } +} + +#[test] +pub fn test_option_getter() { + let context = App { + bar: Some("foo".to_owned()), + }; + + assert_eq!(context.foo(), Some(&"foo".to_owned())); +} diff --git a/crates/tests/cgp-tests/tests/getters/option_auto.rs b/crates/tests/cgp-tests/tests/getters/option_auto.rs new file mode 100644 index 00000000..9c002750 --- /dev/null +++ b/crates/tests/cgp-tests/tests/getters/option_auto.rs @@ -0,0 +1,53 @@ +//! `#[cgp_auto_getter]` returning `Option<&String>`: the blanket impl reads an +//! `Option` field named after the method and calls `.as_ref()`, +//! converting `&Option` into `Option<&String>` without wiring. +//! +//! See docs/reference/macros/cgp_auto_getter.md. + +use cgp::prelude::*; +use cgp_macro_test_util::snapshot_cgp_auto_getter; + +snapshot_cgp_auto_getter! { + #[cgp_auto_getter] + pub trait HasFoo { + fn foo(&self) -> Option<&String>; + } + + expand_has_foo(output) { + insta::assert_snapshot!(output, @" + pub trait HasFoo { + fn foo(&self) -> Option<&String>; + } + impl<__Context__> HasFoo for __Context__ + where + __Context__: HasField< + Symbol<3, Chars<'f', Chars<'o', Chars<'o', Nil>>>>, + Value = Option, + >, + { + fn foo(&self) -> Option<&String> { + self.get_field( + ::core::marker::PhantomData::< + Symbol<3, Chars<'f', Chars<'o', Chars<'o', Nil>>>>, + >, + ) + .as_ref() + } + } + ") + } +} + +#[derive(HasField)] +pub struct App { + pub foo: Option, +} + +#[test] +pub fn test_option_auto_getter() { + let context = App { + foo: Some("foo".to_owned()), + }; + + assert_eq!(context.foo(), Some(&"foo".to_owned())); +} diff --git a/crates/tests/cgp-tests/tests/getters/slice.rs b/crates/tests/cgp-tests/tests/getters/slice.rs new file mode 100644 index 00000000..2dfe8789 --- /dev/null +++ b/crates/tests/cgp-tests/tests/getters/slice.rs @@ -0,0 +1,165 @@ +//! `#[cgp_getter]` returning `&[u8]`: the getter reads any field whose type is +//! `AsRef<[u8]> + 'static` (e.g. `Vec`) and calls `.as_ref()`, so an owned +//! byte buffer is exposed as a slice. The context binds the source field via +//! `UseField`. +//! +//! See docs/reference/macros/cgp_getter.md and docs/reference/providers/use_field.md. + +use cgp::prelude::*; +use cgp_macro_test_util::snapshot_cgp_getter; + +snapshot_cgp_getter! { + #[cgp_getter] + pub trait HasFoo { + fn foo(&self) -> &[u8]; + } + + expand_has_foo(output) { + insta::assert_snapshot!(output, @" + pub trait HasFoo { + fn foo(&self) -> &[u8]; + } + impl<__Context__> HasFoo for __Context__ + where + __Context__: FooGetter<__Context__>, + { + fn foo(&self) -> &[u8] { + __Context__::foo(self) + } + } + pub trait FooGetter<__Context__>: IsProviderFor { + fn foo(__context__: &__Context__) -> &[u8]; + } + impl<__Provider__, __Context__> FooGetter<__Context__> for __Provider__ + where + __Provider__: DelegateComponent + + IsProviderFor, + <__Provider__ as DelegateComponent< + FooGetterComponent, + >>::Delegate: FooGetter<__Context__>, + { + fn foo(__context__: &__Context__) -> &[u8] { + <__Provider__ as DelegateComponent< + FooGetterComponent, + >>::Delegate::foo(__context__) + } + } + pub struct FooGetterComponent; + impl<__Context__> FooGetter<__Context__> for UseContext + where + __Context__: HasFoo, + { + fn foo(__context__: &__Context__) -> &[u8] { + __Context__::foo(__context__) + } + } + impl<__Context__> IsProviderFor for UseContext + where + __Context__: HasFoo, + {} + impl<__Context__, __Components__, __Path__> FooGetter<__Context__> + for RedirectLookup<__Components__, __Path__> + where + __Components__: DelegateComponent<__Path__>, + <__Components__ as DelegateComponent<__Path__>>::Delegate: FooGetter<__Context__>, + { + fn foo(__context__: &__Context__) -> &[u8] { + <__Components__ as DelegateComponent<__Path__>>::Delegate::foo(__context__) + } + } + impl< + __Context__, + __Components__, + __Path__, + > IsProviderFor + for RedirectLookup<__Components__, __Path__> + where + __Components__: DelegateComponent<__Path__>, + <__Components__ as DelegateComponent< + __Path__, + >>::Delegate: IsProviderFor + + FooGetter<__Context__>, + {} + impl<__Context__> FooGetter<__Context__> for UseFields + where + __Context__: HasField< + Symbol<3, Chars<'f', Chars<'o', Chars<'o', Nil>>>>, + Value: AsRef<[u8]> + 'static, + >, + { + fn foo(__context__: &__Context__) -> &[u8] { + __context__ + .get_field( + ::core::marker::PhantomData::< + Symbol<3, Chars<'f', Chars<'o', Chars<'o', Nil>>>>, + >, + ) + .as_ref() + } + } + impl<__Context__> IsProviderFor for UseFields + where + __Context__: HasField< + Symbol<3, Chars<'f', Chars<'o', Chars<'o', Nil>>>>, + Value: AsRef<[u8]> + 'static, + >, + {} + impl<__Context__, __Tag__> FooGetter<__Context__> for UseField<__Tag__> + where + __Context__: HasField<__Tag__, Value: AsRef<[u8]> + 'static>, + { + fn foo(__context__: &__Context__) -> &[u8] { + __context__.get_field(::core::marker::PhantomData::<__Tag__>).as_ref() + } + } + impl<__Context__, __Tag__> IsProviderFor + for UseField<__Tag__> + where + __Context__: HasField<__Tag__, Value: AsRef<[u8]> + 'static>, + {} + impl<__Context__, __Provider__> FooGetter<__Context__> for WithProvider<__Provider__> + where + __Provider__: FieldGetter< + __Context__, + FooGetterComponent, + Value: AsRef<[u8]> + 'static, + >, + { + fn foo(__context__: &__Context__) -> &[u8] { + __Provider__::get_field( + __context__, + ::core::marker::PhantomData::, + ) + .as_ref() + } + } + impl<__Context__, __Provider__> IsProviderFor + for WithProvider<__Provider__> + where + __Provider__: FieldGetter< + __Context__, + FooGetterComponent, + Value: AsRef<[u8]> + 'static, + >, + {} + ") + } +} + +#[derive(HasField)] +pub struct App { + pub bar: Vec, +} + +delegate_components! { + App { + FooGetterComponent: UseField, + } +} + +#[test] +pub fn test_slice_getter() { + let context = App { bar: vec![1, 2, 3] }; + + assert_eq!(context.foo(), &[1, 2, 3]); +} diff --git a/crates/tests/cgp-tests/tests/getters/slice_auto.rs b/crates/tests/cgp-tests/tests/getters/slice_auto.rs new file mode 100644 index 00000000..955ec7dc --- /dev/null +++ b/crates/tests/cgp-tests/tests/getters/slice_auto.rs @@ -0,0 +1,51 @@ +//! `#[cgp_auto_getter]` returning `&[u8]`: the blanket impl reads a field whose +//! type is `AsRef<[u8]> + 'static` (e.g. `Vec`) named after the method and +//! calls `.as_ref()`, exposing an owned byte buffer as a slice without wiring. +//! +//! See docs/reference/macros/cgp_auto_getter.md. + +use cgp::prelude::*; +use cgp_macro_test_util::snapshot_cgp_auto_getter; + +snapshot_cgp_auto_getter! { + #[cgp_auto_getter] + pub trait HasFoo { + fn foo(&self) -> &[u8]; + } + + expand_has_foo(output) { + insta::assert_snapshot!(output, @" + pub trait HasFoo { + fn foo(&self) -> &[u8]; + } + impl<__Context__> HasFoo for __Context__ + where + __Context__: HasField< + Symbol<3, Chars<'f', Chars<'o', Chars<'o', Nil>>>>, + Value: AsRef<[u8]> + 'static, + >, + { + fn foo(&self) -> &[u8] { + self.get_field( + ::core::marker::PhantomData::< + Symbol<3, Chars<'f', Chars<'o', Chars<'o', Nil>>>>, + >, + ) + .as_ref() + } + } + ") + } +} + +#[derive(HasField)] +pub struct App { + pub foo: Vec, +} + +#[test] +pub fn test_slice_auto_getter() { + let context = App { foo: vec![1, 2, 3] }; + + assert_eq!(context.foo(), &[1, 2, 3]); +} diff --git a/crates/tests/cgp-tests/tests/getters/string.rs b/crates/tests/cgp-tests/tests/getters/string.rs new file mode 100644 index 00000000..20eaaad6 --- /dev/null +++ b/crates/tests/cgp-tests/tests/getters/string.rs @@ -0,0 +1,158 @@ +//! `#[cgp_getter]` returning `&str`: the getter reads a `String` field and the +//! generated provider impls call `.as_str()`, so a `String` field can be exposed +//! as `&str`. The context binds the source field by wiring to `UseField`. +//! +//! See docs/reference/macros/cgp_getter.md and docs/reference/providers/use_field.md. + +use cgp::prelude::*; +use cgp_macro_test_util::snapshot_cgp_getter; + +snapshot_cgp_getter! { + #[cgp_getter] + pub trait HasFoo { + fn foo(&self) -> &str; + } + + expand_has_foo(output) { + insta::assert_snapshot!(output, @" + pub trait HasFoo { + fn foo(&self) -> &str; + } + impl<__Context__> HasFoo for __Context__ + where + __Context__: FooGetter<__Context__>, + { + fn foo(&self) -> &str { + __Context__::foo(self) + } + } + pub trait FooGetter<__Context__>: IsProviderFor { + fn foo(__context__: &__Context__) -> &str; + } + impl<__Provider__, __Context__> FooGetter<__Context__> for __Provider__ + where + __Provider__: DelegateComponent + + IsProviderFor, + <__Provider__ as DelegateComponent< + FooGetterComponent, + >>::Delegate: FooGetter<__Context__>, + { + fn foo(__context__: &__Context__) -> &str { + <__Provider__ as DelegateComponent< + FooGetterComponent, + >>::Delegate::foo(__context__) + } + } + pub struct FooGetterComponent; + impl<__Context__> FooGetter<__Context__> for UseContext + where + __Context__: HasFoo, + { + fn foo(__context__: &__Context__) -> &str { + __Context__::foo(__context__) + } + } + impl<__Context__> IsProviderFor for UseContext + where + __Context__: HasFoo, + {} + impl<__Context__, __Components__, __Path__> FooGetter<__Context__> + for RedirectLookup<__Components__, __Path__> + where + __Components__: DelegateComponent<__Path__>, + <__Components__ as DelegateComponent<__Path__>>::Delegate: FooGetter<__Context__>, + { + fn foo(__context__: &__Context__) -> &str { + <__Components__ as DelegateComponent<__Path__>>::Delegate::foo(__context__) + } + } + impl< + __Context__, + __Components__, + __Path__, + > IsProviderFor + for RedirectLookup<__Components__, __Path__> + where + __Components__: DelegateComponent<__Path__>, + <__Components__ as DelegateComponent< + __Path__, + >>::Delegate: IsProviderFor + + FooGetter<__Context__>, + {} + impl<__Context__> FooGetter<__Context__> for UseFields + where + __Context__: HasField< + Symbol<3, Chars<'f', Chars<'o', Chars<'o', Nil>>>>, + Value = String, + >, + { + fn foo(__context__: &__Context__) -> &str { + __context__ + .get_field( + ::core::marker::PhantomData::< + Symbol<3, Chars<'f', Chars<'o', Chars<'o', Nil>>>>, + >, + ) + .as_str() + } + } + impl<__Context__> IsProviderFor for UseFields + where + __Context__: HasField< + Symbol<3, Chars<'f', Chars<'o', Chars<'o', Nil>>>>, + Value = String, + >, + {} + impl<__Context__, __Tag__> FooGetter<__Context__> for UseField<__Tag__> + where + __Context__: HasField<__Tag__, Value = String>, + { + fn foo(__context__: &__Context__) -> &str { + __context__.get_field(::core::marker::PhantomData::<__Tag__>).as_str() + } + } + impl<__Context__, __Tag__> IsProviderFor + for UseField<__Tag__> + where + __Context__: HasField<__Tag__, Value = String>, + {} + impl<__Context__, __Provider__> FooGetter<__Context__> for WithProvider<__Provider__> + where + __Provider__: FieldGetter<__Context__, FooGetterComponent, Value = String>, + { + fn foo(__context__: &__Context__) -> &str { + __Provider__::get_field( + __context__, + ::core::marker::PhantomData::, + ) + .as_str() + } + } + impl<__Context__, __Provider__> IsProviderFor + for WithProvider<__Provider__> + where + __Provider__: FieldGetter<__Context__, FooGetterComponent, Value = String>, + {} + ") + } +} + +#[derive(HasField)] +pub struct App { + pub bar: String, +} + +delegate_components! { + App { + FooGetterComponent: UseField, + } +} + +#[test] +pub fn test_string_getter() { + let context = App { + bar: "abc".to_owned(), + }; + + assert_eq!(context.foo(), "abc"); +} diff --git a/crates/tests/cgp-tests/tests/getters/string_auto.rs b/crates/tests/cgp-tests/tests/getters/string_auto.rs new file mode 100644 index 00000000..d049c700 --- /dev/null +++ b/crates/tests/cgp-tests/tests/getters/string_auto.rs @@ -0,0 +1,53 @@ +//! `#[cgp_auto_getter]` returning `&str`: the blanket impl reads a `String` +//! field named after the method and calls `.as_str()`, exposing a `String` field +//! as `&str` without any wiring. +//! +//! See docs/reference/macros/cgp_auto_getter.md. + +use cgp::prelude::*; +use cgp_macro_test_util::snapshot_cgp_auto_getter; + +snapshot_cgp_auto_getter! { + #[cgp_auto_getter] + pub trait HasFoo { + fn foo(&self) -> &str; + } + + expand_has_foo(output) { + insta::assert_snapshot!(output, @" + pub trait HasFoo { + fn foo(&self) -> &str; + } + impl<__Context__> HasFoo for __Context__ + where + __Context__: HasField< + Symbol<3, Chars<'f', Chars<'o', Chars<'o', Nil>>>>, + Value = String, + >, + { + fn foo(&self) -> &str { + self.get_field( + ::core::marker::PhantomData::< + Symbol<3, Chars<'f', Chars<'o', Chars<'o', Nil>>>>, + >, + ) + .as_str() + } + } + ") + } +} + +#[derive(HasField)] +pub struct App { + pub foo: String, +} + +#[test] +pub fn test_string_auto_getter() { + let context = App { + foo: "abc".to_owned(), + }; + + assert_eq!(context.foo(), "abc"); +} diff --git a/crates/tests/cgp-tests/tests/getters/string_custom_name.rs b/crates/tests/cgp-tests/tests/getters/string_custom_name.rs new file mode 100644 index 00000000..7793198a --- /dev/null +++ b/crates/tests/cgp-tests/tests/getters/string_custom_name.rs @@ -0,0 +1,158 @@ +//! `#[cgp_getter(GetString)]` overrides the generated provider name to +//! `GetString` (and the component to `GetStringComponent`), while the consumer +//! trait `HasFoo` and its `&str` return shorthand are unchanged. +//! +//! See docs/reference/macros/cgp_getter.md and docs/reference/providers/use_field.md. + +use cgp::prelude::*; +use cgp_macro_test_util::snapshot_cgp_getter; + +snapshot_cgp_getter! { + #[cgp_getter(GetString)] + pub trait HasFoo { + fn foo(&self) -> &str; + } + + expand_has_foo(output) { + insta::assert_snapshot!(output, @" + pub trait HasFoo { + fn foo(&self) -> &str; + } + impl<__Context__> HasFoo for __Context__ + where + __Context__: GetString<__Context__>, + { + fn foo(&self) -> &str { + __Context__::foo(self) + } + } + pub trait GetString<__Context__>: IsProviderFor { + fn foo(__context__: &__Context__) -> &str; + } + impl<__Provider__, __Context__> GetString<__Context__> for __Provider__ + where + __Provider__: DelegateComponent + + IsProviderFor, + <__Provider__ as DelegateComponent< + GetStringComponent, + >>::Delegate: GetString<__Context__>, + { + fn foo(__context__: &__Context__) -> &str { + <__Provider__ as DelegateComponent< + GetStringComponent, + >>::Delegate::foo(__context__) + } + } + pub struct GetStringComponent; + impl<__Context__> GetString<__Context__> for UseContext + where + __Context__: HasFoo, + { + fn foo(__context__: &__Context__) -> &str { + __Context__::foo(__context__) + } + } + impl<__Context__> IsProviderFor for UseContext + where + __Context__: HasFoo, + {} + impl<__Context__, __Components__, __Path__> GetString<__Context__> + for RedirectLookup<__Components__, __Path__> + where + __Components__: DelegateComponent<__Path__>, + <__Components__ as DelegateComponent<__Path__>>::Delegate: GetString<__Context__>, + { + fn foo(__context__: &__Context__) -> &str { + <__Components__ as DelegateComponent<__Path__>>::Delegate::foo(__context__) + } + } + impl< + __Context__, + __Components__, + __Path__, + > IsProviderFor + for RedirectLookup<__Components__, __Path__> + where + __Components__: DelegateComponent<__Path__>, + <__Components__ as DelegateComponent< + __Path__, + >>::Delegate: IsProviderFor + + GetString<__Context__>, + {} + impl<__Context__> GetString<__Context__> for UseFields + where + __Context__: HasField< + Symbol<3, Chars<'f', Chars<'o', Chars<'o', Nil>>>>, + Value = String, + >, + { + fn foo(__context__: &__Context__) -> &str { + __context__ + .get_field( + ::core::marker::PhantomData::< + Symbol<3, Chars<'f', Chars<'o', Chars<'o', Nil>>>>, + >, + ) + .as_str() + } + } + impl<__Context__> IsProviderFor for UseFields + where + __Context__: HasField< + Symbol<3, Chars<'f', Chars<'o', Chars<'o', Nil>>>>, + Value = String, + >, + {} + impl<__Context__, __Tag__> GetString<__Context__> for UseField<__Tag__> + where + __Context__: HasField<__Tag__, Value = String>, + { + fn foo(__context__: &__Context__) -> &str { + __context__.get_field(::core::marker::PhantomData::<__Tag__>).as_str() + } + } + impl<__Context__, __Tag__> IsProviderFor + for UseField<__Tag__> + where + __Context__: HasField<__Tag__, Value = String>, + {} + impl<__Context__, __Provider__> GetString<__Context__> for WithProvider<__Provider__> + where + __Provider__: FieldGetter<__Context__, GetStringComponent, Value = String>, + { + fn foo(__context__: &__Context__) -> &str { + __Provider__::get_field( + __context__, + ::core::marker::PhantomData::, + ) + .as_str() + } + } + impl<__Context__, __Provider__> IsProviderFor + for WithProvider<__Provider__> + where + __Provider__: FieldGetter<__Context__, GetStringComponent, Value = String>, + {} + ") + } +} + +#[derive(HasField)] +pub struct App { + pub bar: String, +} + +delegate_components! { + App { + GetStringComponent: UseField, + } +} + +#[test] +pub fn test_string_getter_with_custom_name() { + let context = App { + bar: "abc".to_owned(), + }; + + assert_eq!(context.foo(), "abc"); +} diff --git a/crates/tests/cgp-tests/tests/getters/string_custom_spec.rs b/crates/tests/cgp-tests/tests/getters/string_custom_spec.rs new file mode 100644 index 00000000..af5bb203 --- /dev/null +++ b/crates/tests/cgp-tests/tests/getters/string_custom_spec.rs @@ -0,0 +1,152 @@ +//! `#[cgp_getter { provider: GetString, name: GetStringComp }]` overrides both +//! the provider name and the component marker name independently, via the brace +//! spec form. The consumer trait and its `&str` return shorthand are unchanged. +//! +//! See docs/reference/macros/cgp_getter.md and docs/reference/providers/use_field.md. + +use cgp::prelude::*; +use cgp_macro_test_util::snapshot_cgp_getter; + +snapshot_cgp_getter! { + #[cgp_getter{ + provider: GetString, + name: GetStringComp, + }] + pub trait HasFoo { + fn foo(&self) -> &str; + } + + expand_has_foo(output) { + insta::assert_snapshot!(output, @" + pub trait HasFoo { + fn foo(&self) -> &str; + } + impl<__Context__> HasFoo for __Context__ + where + __Context__: GetString<__Context__>, + { + fn foo(&self) -> &str { + __Context__::foo(self) + } + } + pub trait GetString<__Context__>: IsProviderFor { + fn foo(__context__: &__Context__) -> &str; + } + impl<__Provider__, __Context__> GetString<__Context__> for __Provider__ + where + __Provider__: DelegateComponent + + IsProviderFor, + <__Provider__ as DelegateComponent>::Delegate: GetString<__Context__>, + { + fn foo(__context__: &__Context__) -> &str { + <__Provider__ as DelegateComponent>::Delegate::foo(__context__) + } + } + pub struct GetStringComp; + impl<__Context__> GetString<__Context__> for UseContext + where + __Context__: HasFoo, + { + fn foo(__context__: &__Context__) -> &str { + __Context__::foo(__context__) + } + } + impl<__Context__> IsProviderFor for UseContext + where + __Context__: HasFoo, + {} + impl<__Context__, __Components__, __Path__> GetString<__Context__> + for RedirectLookup<__Components__, __Path__> + where + __Components__: DelegateComponent<__Path__>, + <__Components__ as DelegateComponent<__Path__>>::Delegate: GetString<__Context__>, + { + fn foo(__context__: &__Context__) -> &str { + <__Components__ as DelegateComponent<__Path__>>::Delegate::foo(__context__) + } + } + impl<__Context__, __Components__, __Path__> IsProviderFor + for RedirectLookup<__Components__, __Path__> + where + __Components__: DelegateComponent<__Path__>, + <__Components__ as DelegateComponent< + __Path__, + >>::Delegate: IsProviderFor + GetString<__Context__>, + {} + impl<__Context__> GetString<__Context__> for UseFields + where + __Context__: HasField< + Symbol<3, Chars<'f', Chars<'o', Chars<'o', Nil>>>>, + Value = String, + >, + { + fn foo(__context__: &__Context__) -> &str { + __context__ + .get_field( + ::core::marker::PhantomData::< + Symbol<3, Chars<'f', Chars<'o', Chars<'o', Nil>>>>, + >, + ) + .as_str() + } + } + impl<__Context__> IsProviderFor for UseFields + where + __Context__: HasField< + Symbol<3, Chars<'f', Chars<'o', Chars<'o', Nil>>>>, + Value = String, + >, + {} + impl<__Context__, __Tag__> GetString<__Context__> for UseField<__Tag__> + where + __Context__: HasField<__Tag__, Value = String>, + { + fn foo(__context__: &__Context__) -> &str { + __context__.get_field(::core::marker::PhantomData::<__Tag__>).as_str() + } + } + impl<__Context__, __Tag__> IsProviderFor + for UseField<__Tag__> + where + __Context__: HasField<__Tag__, Value = String>, + {} + impl<__Context__, __Provider__> GetString<__Context__> for WithProvider<__Provider__> + where + __Provider__: FieldGetter<__Context__, GetStringComp, Value = String>, + { + fn foo(__context__: &__Context__) -> &str { + __Provider__::get_field( + __context__, + ::core::marker::PhantomData::, + ) + .as_str() + } + } + impl<__Context__, __Provider__> IsProviderFor + for WithProvider<__Provider__> + where + __Provider__: FieldGetter<__Context__, GetStringComp, Value = String>, + {} + ") + } +} + +#[derive(HasField)] +pub struct App { + pub bar: String, +} + +delegate_components! { + App { + GetStringComp: UseField, + } +} + +#[test] +pub fn test_string_getter_with_custom_spec() { + let context = App { + bar: "abc".to_owned(), + }; + + assert_eq!(context.foo(), "abc"); +} diff --git a/crates/tests/cgp-tests/tests/getters_tests.rs b/crates/tests/cgp-tests/tests/getters_tests.rs new file mode 100644 index 00000000..a9de3a7a --- /dev/null +++ b/crates/tests/cgp-tests/tests/getters_tests.rs @@ -0,0 +1,16 @@ +//! Entrypoint for the `getters` concept. +//! +//! Covers CGP's getter macros: `#[cgp_auto_getter]` (a blanket getter impl over +//! `HasField`, with the field taken from the method name) and `#[cgp_getter]` +//! (a full component whose source field can be chosen by wiring through +//! `UseField`). This concept owns the canonical macro-expansion snapshots for +//! both, exercising the return-type shorthands (`&str`, `&[u8]`, `Option<&T>`, +//! `MRef`, clone-on-`Copy`), associated getter types, generic getters, non-`self` +//! getters, and abstract-type integration. +//! +//! See docs/reference/macros/cgp_getter.md and +//! docs/reference/macros/cgp_auto_getter.md. +#![allow(dead_code)] +#![allow(clippy::disallowed_names)] + +pub mod getters; diff --git a/crates/tests/cgp-tests/tests/handler.rs b/crates/tests/cgp-tests/tests/handler.rs deleted file mode 100644 index 8a61d337..00000000 --- a/crates/tests/cgp-tests/tests/handler.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod handler_tests; diff --git a/crates/tests/cgp-tests/tests/handler_tests/mod.rs b/crates/tests/cgp-tests/tests/handler_tests/mod.rs deleted file mode 100644 index 64e1dfb9..00000000 --- a/crates/tests/cgp-tests/tests/handler_tests/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub mod computer_macro; -pub mod handler_macro; -pub mod pipe; -pub mod producer_macro; diff --git a/crates/tests/cgp-tests/tests/handler_tests/pipe.rs b/crates/tests/cgp-tests/tests/handler_tests/pipe.rs deleted file mode 100644 index 23765c1a..00000000 --- a/crates/tests/cgp-tests/tests/handler_tests/pipe.rs +++ /dev/null @@ -1,335 +0,0 @@ -mod pipe_computers { - use core::marker::PhantomData; - - use cgp::extra::handler::{CanCompute, Computer, ComputerComponent, PipeHandlers}; - use cgp::prelude::*; - use cgp_macro_test_util::{ - snapshot_cgp_new_provider, snapshot_check_components, snapshot_delegate_components, - }; - - snapshot_cgp_new_provider! { - #[cgp_new_provider] - impl Computer for Multiply - where - Context: HasField, - { - type Output = u64; - - fn compute(context: &Context, _tag: PhantomData, input: u64) -> u64 { - input * context.get_field(PhantomData) - } - } - - expand_multiply(output) { - insta::assert_snapshot!(output, @" - impl Computer for Multiply - where - Context: HasField, - { - type Output = u64; - fn compute(context: &Context, _tag: PhantomData, input: u64) -> u64 { - input * context.get_field(PhantomData) - } - } - impl IsProviderFor - for Multiply - where - Context: HasField, - {} - pub struct Multiply(pub ::core::marker::PhantomData<(Field)>); - ") - } - } - - snapshot_cgp_new_provider! { - #[cgp_new_provider] - impl Computer for Add - where - Context: HasField, - { - type Output = u64; - - fn compute(context: &Context, _tag: PhantomData, input: u64) -> u64 { - input + context.get_field(PhantomData) - } - } - - expand_add(output) { - insta::assert_snapshot!(output, @" - impl Computer for Add - where - Context: HasField, - { - type Output = u64; - fn compute(context: &Context, _tag: PhantomData, input: u64) -> u64 { - input + context.get_field(PhantomData) - } - } - impl IsProviderFor - for Add - where - Context: HasField, - {} - pub struct Add(pub ::core::marker::PhantomData<(Field)>); - ") - } - } - - #[derive(HasField)] - pub struct MyContext { - pub foo: u64, - pub bar: u64, - pub baz: u64, - } - - snapshot_delegate_components! { - delegate_components! { - MyContext { - ComputerComponent: - PipeHandlers< - Product! [ - Multiply, - Add, - Multiply, - ] - >, - } - } - - expand_pipe_computers(output) { - insta::assert_snapshot!(output, @r#" - impl DelegateComponent for MyContext { - type Delegate = PipeHandlers< - Product![ - Multiply < Symbol!("foo") >, Add < Symbol!("bar") >, Multiply < - Symbol!("baz") >, - ], - >; - } - impl<__Context__, __Params__> IsProviderFor - for MyContext - where - PipeHandlers< - Product![ - Multiply < Symbol!("foo") >, Add < Symbol!("bar") >, Multiply < - Symbol!("baz") >, - ], - >: IsProviderFor, - {} - "#) - } - } - - snapshot_check_components! { - check_components! { - - MyContext { - ComputerComponent: (Tag, u64), - } - } - - expand_check_pipe_computers(output) { - insta::assert_snapshot!(output, @" - trait __CheckMyContext< - __Component__, - __Params__: ?Sized, - >: CanUseComponent<__Component__, __Params__> {} - impl __CheckMyContext for MyContext {} - ") - } - } - - #[test] - pub fn test_pipe_computers() { - let context = MyContext { - foo: 2, - bar: 3, - baz: 4, - }; - - let result = context.compute(PhantomData::<()>, 5); - - assert_eq!(result, ((5 * 2) + 3) * 4); - } -} - -mod pipe_handlers { - use core::convert::Infallible; - use core::marker::PhantomData; - - use cgp::core::error::ErrorTypeProviderComponent; - use cgp::extra::handler::{ - CanHandle, Computer, Handler, HandlerComponent, PipeHandlers, Promote, PromoteAsync, - }; - use cgp::prelude::*; - use cgp_macro_test_util::{ - snapshot_cgp_new_provider, snapshot_check_components, snapshot_delegate_components, - }; - use futures::executor::block_on; - - snapshot_cgp_new_provider! { - #[cgp_new_provider] - impl Handler for Multiply - where - Context: HasErrorType + HasField, - { - type Output = u64; - - async fn handle( - context: &Context, - _tag: PhantomData, - input: u64, - ) -> Result { - Ok(input * context.get_field(PhantomData)) - } - } - - expand_multiply(output) { - insta::assert_snapshot!(output, @" - impl Handler for Multiply - where - Context: HasErrorType + HasField, - { - type Output = u64; - async fn handle( - context: &Context, - _tag: PhantomData, - input: u64, - ) -> Result { - Ok(input * context.get_field(PhantomData)) - } - } - impl IsProviderFor - for Multiply - where - Context: HasErrorType + HasField, - {} - pub struct Multiply(pub ::core::marker::PhantomData<(Field)>); - ") - } - } - - snapshot_cgp_new_provider! { - #[cgp_new_provider] - impl Computer for Add - where - Context: HasField, - { - type Output = u64; - - fn compute(context: &Context, _tag: PhantomData, input: u64) -> u64 { - input + context.get_field(PhantomData) - } - } - - expand_add(output) { - insta::assert_snapshot!(output, @" - impl Computer for Add - where - Context: HasField, - { - type Output = u64; - fn compute(context: &Context, _tag: PhantomData, input: u64) -> u64 { - input + context.get_field(PhantomData) - } - } - impl IsProviderFor - for Add - where - Context: HasField, - {} - pub struct Add(pub ::core::marker::PhantomData<(Field)>); - ") - } - } - - #[derive(HasField)] - pub struct MyContext { - pub foo: u64, - pub bar: u64, - pub baz: u64, - } - - snapshot_delegate_components! { - delegate_components! { - MyContext { - ErrorTypeProviderComponent: UseType, - HandlerComponent: - PipeHandlers< - Product! [ - Multiply, - PromoteAsync>>, - Multiply, - ] - >, - } - } - - expand_pipe_handlers(output) { - insta::assert_snapshot!(output, @r#" - impl DelegateComponent for MyContext { - type Delegate = UseType; - } - impl< - __Context__, - __Params__, - > IsProviderFor for MyContext - where - UseType< - Infallible, - >: IsProviderFor, - {} - impl DelegateComponent for MyContext { - type Delegate = PipeHandlers< - Product![ - Multiply < Symbol!("foo") >, PromoteAsync < Promote < Add < Symbol!("bar") - >>>, Multiply < Symbol!("baz") >, - ], - >; - } - impl<__Context__, __Params__> IsProviderFor - for MyContext - where - PipeHandlers< - Product![ - Multiply < Symbol!("foo") >, PromoteAsync < Promote < Add < Symbol!("bar") - >>>, Multiply < Symbol!("baz") >, - ], - >: IsProviderFor, - {} - "#) - } - } - - snapshot_check_components! { - check_components! { - - MyContext { - HandlerComponent: (Tag, u64), - } - } - - expand_check_pipe_handlers(output) { - insta::assert_snapshot!(output, @" - trait __CheckMyContext< - __Component__, - __Params__: ?Sized, - >: CanUseComponent<__Component__, __Params__> {} - impl __CheckMyContext for MyContext {} - ") - } - } - - #[test] - pub fn test_pipe_handlers() { - let context = MyContext { - foo: 2, - bar: 3, - baz: 4, - }; - - let result = block_on(context.handle(PhantomData::<()>, 5)).unwrap(); - - assert_eq!(result, ((5 * 2) + 3) * 4); - } -} diff --git a/crates/tests/cgp-tests/tests/handler_tests/computer_macro.rs b/crates/tests/cgp-tests/tests/handlers/computer_macro.rs similarity index 69% rename from crates/tests/cgp-tests/tests/handler_tests/computer_macro.rs rename to crates/tests/cgp-tests/tests/handlers/computer_macro.rs index deb06236..92d33527 100644 --- a/crates/tests/cgp-tests/tests/handler_tests/computer_macro.rs +++ b/crates/tests/cgp-tests/tests/handlers/computer_macro.rs @@ -1,10 +1,27 @@ +//! `#[cgp_computer]`: turning a plain function into a `Computer` provider. +//! +//! A synchronous, infallible function annotated with `#[cgp_computer]` becomes a +//! provider usable across the whole computation family — the same definition is +//! callable as `compute`, `try_compute`, `compute_async`, and `handle` (and the +//! by-reference `compute_ref` / `try_compute_ref` / `compute_async_ref` / +//! `handle_ref` variants), because CGP automatically promotes a `Computer` into +//! the more capable `TryComputer`, `AsyncComputer`, and `Handler`. A fallible +//! `Result`-returning body promotes so that its error path surfaces through +//! `try_compute` / `handle`. A generic function parameter carries through to the +//! generated provider. +//! +//! The error wiring on `App` is incidental scaffolding needed so the promoted +//! `Handler` has an error type; it uses the plain `delegate_components!` (the +//! error and wiring macros are owned by other concept targets). +//! +//! See docs/reference/components/computer.md. + use core::fmt::Display; use cgp::core::error::{ErrorRaiserComponent, ErrorTypeProviderComponent}; use cgp::extra::error::RaiseFrom; use cgp::extra::handler::{ComputerRef, HandlerRef, TryComputerRef}; use cgp::prelude::*; -use cgp_macro_test_util::snapshot_delegate_components; use futures::executor::block_on; #[cgp_computer] @@ -19,39 +36,12 @@ fn add_with_error(a: u64, b: u64) -> Result { pub struct App; -snapshot_delegate_components! { - delegate_components! { - App { - ErrorTypeProviderComponent: - UseType, - ErrorRaiserComponent: - RaiseFrom, - } - } - - expand_computer_macro_app(output) { - insta::assert_snapshot!(output, @" - impl DelegateComponent for App { - type Delegate = UseType; - } - impl< - __Context__, - __Params__, - > IsProviderFor for App - where - UseType: IsProviderFor, - {} - impl DelegateComponent for App { - type Delegate = RaiseFrom; - } - impl< - __Context__, - __Params__, - > IsProviderFor for App - where - RaiseFrom: IsProviderFor, - {} - ") +delegate_components! { + App { + ErrorTypeProviderComponent: + UseType, + ErrorRaiserComponent: + RaiseFrom, } } diff --git a/crates/tests/cgp-tests/tests/handler_tests/handler_macro.rs b/crates/tests/cgp-tests/tests/handlers/handler_macro.rs similarity index 60% rename from crates/tests/cgp-tests/tests/handler_tests/handler_macro.rs rename to crates/tests/cgp-tests/tests/handlers/handler_macro.rs index b693cab6..b6156860 100644 --- a/crates/tests/cgp-tests/tests/handler_tests/handler_macro.rs +++ b/crates/tests/cgp-tests/tests/handlers/handler_macro.rs @@ -1,10 +1,24 @@ +//! `#[cgp_computer]` on an `async` function: producing a provider usable as a +//! `Handler`. +//! +//! An `async` function annotated with `#[cgp_computer]` becomes an +//! `AsyncComputer` that CGP promotes into the async, fallible `Handler`, so the +//! same definition is callable as `compute_async` and `handle` (plus the by-ref +//! `compute_async_ref` / `handle_ref`). A `Result`-returning async body promotes +//! so its error path surfaces through `handle`. +//! +//! The error wiring on `App` is incidental scaffolding, so it uses the plain +//! `delegate_components!`. +//! +//! See docs/reference/components/handler.md and +//! docs/reference/components/computer.md. + use core::fmt::Display; use cgp::core::error::{ErrorRaiserComponent, ErrorTypeProviderComponent}; use cgp::extra::error::RaiseFrom; use cgp::extra::handler::HandlerRef; use cgp::prelude::*; -use cgp_macro_test_util::snapshot_delegate_components; use futures::executor::block_on; #[cgp_computer] @@ -19,39 +33,12 @@ async fn add_with_error(a: u64, b: u64) -> Result { pub struct App; -snapshot_delegate_components! { - delegate_components! { - App { - ErrorTypeProviderComponent: - UseType, - ErrorRaiserComponent: - RaiseFrom, - } - } - - expand_handler_macro_app(output) { - insta::assert_snapshot!(output, @" - impl DelegateComponent for App { - type Delegate = UseType; - } - impl< - __Context__, - __Params__, - > IsProviderFor for App - where - UseType: IsProviderFor, - {} - impl DelegateComponent for App { - type Delegate = RaiseFrom; - } - impl< - __Context__, - __Params__, - > IsProviderFor for App - where - RaiseFrom: IsProviderFor, - {} - ") +delegate_components! { + App { + ErrorTypeProviderComponent: + UseType, + ErrorRaiserComponent: + RaiseFrom, } } diff --git a/crates/tests/cgp-tests/tests/handlers/mod.rs b/crates/tests/cgp-tests/tests/handlers/mod.rs new file mode 100644 index 00000000..98a6c161 --- /dev/null +++ b/crates/tests/cgp-tests/tests/handlers/mod.rs @@ -0,0 +1,16 @@ +//! One unit test per file. Each file is self-contained: it defines its own +//! providers, context types, and wiring at module scope so that the type-level +//! setup of one test never leaks into another. + +// The `#[cgp_computer]` / `#[cgp_producer]` macros and the `Handler` family: +// runtime tests that a single function definition yields a provider usable +// across the whole computation family (compute / try_compute / produce / handle +// and their async and by-ref variants). +pub mod computer_macro; +pub mod handler_macro; +pub mod producer_macro; + +// The `PipeHandlers` combinator: chaining computers, and chaining handlers with +// the `Promote*` adapters that lift a simpler handler into a more capable one. +pub mod pipe_computers; +pub mod pipe_handlers; diff --git a/crates/tests/cgp-tests/tests/handlers/pipe_computers.rs b/crates/tests/cgp-tests/tests/handlers/pipe_computers.rs new file mode 100644 index 00000000..b25903a0 --- /dev/null +++ b/crates/tests/cgp-tests/tests/handlers/pipe_computers.rs @@ -0,0 +1,82 @@ +//! `PipeHandlers` chaining a series of `Computer` providers left-to-right. +//! +//! Each stage is a `Computer` that reads one context field and folds it into the +//! running value; `PipeHandlers` runs them in order so the +//! output of one becomes the input of the next. Wiring `ComputerComponent` to +//! the pipe makes `context.compute(...)` evaluate the whole chain. +//! +//! The providers here are incidental scaffolding written with the plain +//! `#[cgp_new_provider]`, and the wiring / check use plain `delegate_components!` +//! / `check_components!` (those macros are owned by other concept targets). +//! +//! See docs/reference/providers/handler_combinators.md and +//! docs/reference/components/computer.md. + +use core::marker::PhantomData; + +use cgp::extra::handler::{CanCompute, Computer, ComputerComponent, PipeHandlers}; +use cgp::prelude::*; + +#[cgp_new_provider] +impl Computer for Multiply +where + Context: HasField, +{ + type Output = u64; + + fn compute(context: &Context, _tag: PhantomData, input: u64) -> u64 { + input * context.get_field(PhantomData) + } +} + +#[cgp_new_provider] +impl Computer for Add +where + Context: HasField, +{ + type Output = u64; + + fn compute(context: &Context, _tag: PhantomData, input: u64) -> u64 { + input + context.get_field(PhantomData) + } +} + +#[derive(HasField)] +pub struct MyContext { + pub foo: u64, + pub bar: u64, + pub baz: u64, +} + +delegate_components! { + MyContext { + ComputerComponent: + PipeHandlers< + Product! [ + Multiply, + Add, + Multiply, + ] + >, + } +} + +check_components! { + + MyContext { + ComputerComponent: (Tag, u64), + } +} + +#[test] +pub fn test_pipe_computers() { + let context = MyContext { + foo: 2, + bar: 3, + baz: 4, + }; + + let result = context.compute(PhantomData::<()>, 5); + + assert_eq!(result, ((5 * 2) + 3) * 4); +} diff --git a/crates/tests/cgp-tests/tests/handlers/pipe_handlers.rs b/crates/tests/cgp-tests/tests/handlers/pipe_handlers.rs new file mode 100644 index 00000000..fb52443a --- /dev/null +++ b/crates/tests/cgp-tests/tests/handlers/pipe_handlers.rs @@ -0,0 +1,95 @@ +//! `PipeHandlers` chaining `Handler` stages, including a `Computer` lifted into +//! a `Handler` with the `Promote*` adapters. +//! +//! A pipeline built at the `HandlerComponent` level runs async, fallible stages +//! in order. A middle stage here is a plain `Computer` (`Add`) that is lifted +//! into the pipeline with `PromoteAsync>`: `Promote` turns a +//! `Computer` into a `TryComputer`/`Handler`-shaped step and `PromoteAsync` +//! makes it async, so a simpler synchronous handler composes alongside async +//! ones. The context uses `Infallible` as its error type since no stage fails. +//! +//! The providers here are incidental scaffolding written with the plain +//! `#[cgp_new_provider]`, and the wiring / check use plain `delegate_components!` +//! / `check_components!` (those macros are owned by other concept targets). +//! +//! See docs/reference/providers/handler_combinators.md and +//! docs/reference/components/handler.md. + +use core::convert::Infallible; +use core::marker::PhantomData; + +use cgp::core::error::ErrorTypeProviderComponent; +use cgp::extra::handler::{ + CanHandle, Computer, Handler, HandlerComponent, PipeHandlers, Promote, PromoteAsync, +}; +use cgp::prelude::*; +use futures::executor::block_on; + +#[cgp_new_provider] +impl Handler for Multiply +where + Context: HasErrorType + HasField, +{ + type Output = u64; + + async fn handle( + context: &Context, + _tag: PhantomData, + input: u64, + ) -> Result { + Ok(input * context.get_field(PhantomData)) + } +} + +#[cgp_new_provider] +impl Computer for Add +where + Context: HasField, +{ + type Output = u64; + + fn compute(context: &Context, _tag: PhantomData, input: u64) -> u64 { + input + context.get_field(PhantomData) + } +} + +#[derive(HasField)] +pub struct MyContext { + pub foo: u64, + pub bar: u64, + pub baz: u64, +} + +delegate_components! { + MyContext { + ErrorTypeProviderComponent: UseType, + HandlerComponent: + PipeHandlers< + Product! [ + Multiply, + PromoteAsync>>, + Multiply, + ] + >, + } +} + +check_components! { + + MyContext { + HandlerComponent: (Tag, u64), + } +} + +#[test] +pub fn test_pipe_handlers() { + let context = MyContext { + foo: 2, + bar: 3, + baz: 4, + }; + + let result = block_on(context.handle(PhantomData::<()>, 5)).unwrap(); + + assert_eq!(result, ((5 * 2) + 3) * 4); +} diff --git a/crates/tests/cgp-tests/tests/handler_tests/producer_macro.rs b/crates/tests/cgp-tests/tests/handlers/producer_macro.rs similarity index 58% rename from crates/tests/cgp-tests/tests/handler_tests/producer_macro.rs rename to crates/tests/cgp-tests/tests/handlers/producer_macro.rs index b256e0ba..e7cd8a49 100644 --- a/crates/tests/cgp-tests/tests/handler_tests/producer_macro.rs +++ b/crates/tests/cgp-tests/tests/handlers/producer_macro.rs @@ -1,7 +1,20 @@ +//! `#[cgp_producer]`: turning an input-free function into a `Producer` provider. +//! +//! A function with no input annotated with `#[cgp_producer]` becomes a +//! `Producer` — production driven by only the context and a `Code` tag. CGP +//! promotes it across the rest of the computation family, so the same definition +//! is callable as `produce`, `compute`, `try_compute`, `compute_async`, and +//! `handle` (plus the by-ref variants); the promoted forms simply ignore the +//! input value. +//! +//! The error type wiring on `App` is incidental scaffolding, so it uses the +//! plain `delegate_components!`. +//! +//! See docs/reference/components/producer.md. + use cgp::core::error::ErrorTypeProviderComponent; use cgp::extra::handler::{ComputerRef, HandlerRef, TryComputerRef}; use cgp::prelude::*; -use cgp_macro_test_util::snapshot_delegate_components; use futures::executor::block_on; #[cgp_producer] @@ -11,27 +24,10 @@ pub fn magic_number() -> u64 { pub struct App; -snapshot_delegate_components! { - delegate_components! { - App { - ErrorTypeProviderComponent: - UseType, - } - } - - expand_producer_macro_app(output) { - insta::assert_snapshot!(output, @" - impl DelegateComponent for App { - type Delegate = UseType; - } - impl< - __Context__, - __Params__, - > IsProviderFor for App - where - UseType: IsProviderFor, - {} - ") +delegate_components! { + App { + ErrorTypeProviderComponent: + UseType, } } diff --git a/crates/tests/cgp-tests/tests/handlers_tests.rs b/crates/tests/cgp-tests/tests/handlers_tests.rs new file mode 100644 index 00000000..d5e104c0 --- /dev/null +++ b/crates/tests/cgp-tests/tests/handlers_tests.rs @@ -0,0 +1,18 @@ +//! Entrypoint for the `handlers` concept. +//! +//! Covers CGP's computation family: defining `Computer`/`Producer` providers +//! from functions with `#[cgp_computer]`/`#[cgp_producer]`, the automatic +//! promotion between the synchronous `Computer`, the input-free `Producer`, and +//! the async, fallible `Handler`, and composing handlers into pipelines with the +//! `PipeHandlers` combinator. +//! +//! This concept does *not* own `#[cgp_component]`, `#[cgp_provider]`, +//! `check_components!`, or `delegate_components!` snapshots — those live in their +//! owning targets — so the scaffolding here uses the plain macros. +//! +//! See docs/reference/components/computer.md, +//! docs/reference/components/producer.md, docs/reference/components/handler.md, +//! and docs/reference/providers/handler_combinators.md. +#![allow(dead_code)] + +pub mod handlers; diff --git a/crates/tests/cgp-tests/tests/higher_order_providers/mod.rs b/crates/tests/cgp-tests/tests/higher_order_providers/mod.rs new file mode 100644 index 00000000..c500454b --- /dev/null +++ b/crates/tests/cgp-tests/tests/higher_order_providers/mod.rs @@ -0,0 +1,9 @@ +//! One unit test per file, each self-contained at module scope. + +// `#[use_provider]` snapshots (this concept owns them). +pub mod use_provider_fn; +pub mod use_provider_impl; + +// The scaling pattern end-to-end: an outer calculator wraps an inner one. +pub mod rectangle_or_circle; +pub mod scaled_area; diff --git a/crates/tests/cgp-tests/tests/higher_order_providers/rectangle_or_circle.rs b/crates/tests/cgp-tests/tests/higher_order_providers/rectangle_or_circle.rs new file mode 100644 index 00000000..6a335135 --- /dev/null +++ b/crates/tests/cgp-tests/tests/higher_order_providers/rectangle_or_circle.rs @@ -0,0 +1,79 @@ +//! One context can carry the fields for several calculators at once and call each +//! provider explicitly, in addition to its wired `area()`. +//! +//! `IsThisRectangleOrCircle` has `width`, `height`, and `radius`, so both the +//! `RectangleAreaCalculator` and `CircleAreaCalculator` providers — and the +//! `rectangle_area`/`circle_area` `#[cgp_fn]`s — apply to it. The `#[cgp_fn]` +//! expansions are owned by `implicit_arguments`, so they are written plainly here. +//! +//! See docs/concepts/higher-order-providers.md and docs/reference/macros/cgp_fn.md. + +use core::f64::consts::PI; + +use cgp::prelude::*; + +#[cgp_component(AreaCalculator)] +pub trait CanCalculateArea { + fn area(&self) -> f64; +} + +#[cgp_impl(new RectangleAreaCalculator)] +impl AreaCalculator { + fn area(&self, #[implicit] width: f64, #[implicit] height: f64) -> f64 { + width * height + } +} + +#[cgp_impl(new CircleAreaCalculator)] +impl AreaCalculator { + fn area(&self, #[implicit] radius: f64) -> f64 { + PI * radius * radius + } +} + +#[cgp_fn] +pub fn rectangle_area(&self, #[implicit] width: f64, #[implicit] height: f64) -> f64 { + width * height +} + +#[cgp_fn] +pub fn circle_area(&self, #[implicit] radius: f64) -> f64 { + PI * radius * radius +} + +#[derive(HasField)] +pub struct IsThisRectangleOrCircle { + pub width: f64, + pub height: f64, + pub radius: f64, +} + +impl CanCalculateArea for IsThisRectangleOrCircle { + fn area(&self) -> f64 { + CircleAreaCalculator::area(self) + } +} + +#[test] +fn test_rectangle_or_circle() { + let rectangle_or_circle = IsThisRectangleOrCircle { + width: 2.0, + height: 3.0, + radius: 4.0, + }; + + let area = rectangle_or_circle.area(); + assert_eq!(area, 16.0 * PI); + + let rectangle_area = RectangleAreaCalculator::area(&rectangle_or_circle); + assert_eq!(rectangle_area, 6.0); + + let circle_area = CircleAreaCalculator::area(&rectangle_or_circle); + assert_eq!(circle_area, 16.0 * PI); + + let rectangle_area = rectangle_or_circle.rectangle_area(); + assert_eq!(rectangle_area, 6.0); + + let circle_area = rectangle_or_circle.circle_area(); + assert_eq!(circle_area, 16.0 * PI); +} diff --git a/crates/tests/cgp-tests/tests/higher_order_providers/scaled_area.rs b/crates/tests/cgp-tests/tests/higher_order_providers/scaled_area.rs new file mode 100644 index 00000000..de82437d --- /dev/null +++ b/crates/tests/cgp-tests/tests/higher_order_providers/scaled_area.rs @@ -0,0 +1,124 @@ +//! The scaling pattern wired onto several contexts: a base calculator, and the +//! higher-order `ScaledAreaCalculator` wrapping it. +//! +//! Each context picks its base calculator and, for the scaled variants, wraps it +//! with `ScaledAreaCalculator`. The wiring is a plain +//! `delegate_and_check_components!` compile+wiring check (the macro expansions are +//! owned by `basic_delegation` and `checking`); the runtime test confirms the +//! composed results. +//! +//! See docs/concepts/higher-order-providers.md. + +use core::f64::consts::PI; + +use cgp::prelude::*; + +#[cgp_component(AreaCalculator)] +pub trait CanCalculateArea { + fn area(&self) -> f64; +} + +#[cgp_impl(new RectangleAreaCalculator)] +impl AreaCalculator { + fn area(&self, #[implicit] width: f64, #[implicit] height: f64) -> f64 { + width * height + } +} + +#[cgp_impl(new CircleAreaCalculator)] +impl AreaCalculator { + fn area(&self, #[implicit] radius: f64) -> f64 { + PI * radius * radius + } +} + +#[cgp_impl(new ScaledAreaCalculator)] +#[use_provider(InnerCalculator: AreaCalculator)] +impl AreaCalculator { + fn area(&self, #[implicit] scale_factor: f64) -> f64 { + let base_area = InnerCalculator::area(self); + + base_area * scale_factor * scale_factor + } +} + +#[derive(HasField)] +pub struct PlainRectangle { + pub width: f64, + pub height: f64, +} + +delegate_and_check_components! { + PlainRectangle { + AreaCalculatorComponent: + RectangleAreaCalculator, + } +} + +#[derive(HasField)] +pub struct ScaledRectangle { + pub scale_factor: f64, + pub width: f64, + pub height: f64, +} + +delegate_and_check_components! { + ScaledRectangle { + AreaCalculatorComponent: + ScaledAreaCalculator, + } +} + +#[derive(HasField)] +pub struct PlainCircle { + pub radius: f64, +} + +delegate_and_check_components! { + PlainCircle { + AreaCalculatorComponent: + CircleAreaCalculator, + } +} + +#[derive(HasField)] +pub struct ScaledCircle { + pub scale_factor: f64, + pub radius: f64, +} + +delegate_and_check_components! { + ScaledCircle { + AreaCalculatorComponent: + ScaledAreaCalculator, + } +} + +#[test] +fn test_scaled_area() { + let rectangle = PlainRectangle { + width: 3.0, + height: 4.0, + }; + + assert_eq!(rectangle.area(), 12.0); + + let scaled_rectangle = ScaledRectangle { + scale_factor: 2.0, + width: 3.0, + height: 4.0, + }; + + let circle = PlainCircle { radius: 3.0 }; + + assert_eq!(circle.area(), 9.0 * PI); + + assert_eq!(scaled_rectangle.area(), 48.0); + + let scaled_circle = ScaledCircle { + scale_factor: 2.0, + radius: 3.0, + }; + + assert_eq!(scaled_circle.area(), 36.0 * PI); +} diff --git a/crates/tests/cgp-tests/tests/higher_order_providers/use_provider_fn.rs b/crates/tests/cgp-tests/tests/higher_order_providers/use_provider_fn.rs new file mode 100644 index 00000000..eddb0aa2 --- /dev/null +++ b/crates/tests/cgp-tests/tests/higher_order_providers/use_provider_fn.rs @@ -0,0 +1,65 @@ +//! `#[use_provider]` on a `#[cgp_fn]`: the function borrows another provider's +//! behavior instead of depending on the context directly. +//! +//! `#[use_provider(RectangleAreaCalculator: AreaCalculator)]` completes the inner +//! provider's bound (adding the `Self` argument) and moves it into the `where` +//! clause; the body then calls `RectangleAreaCalculator::area(self)`. The +//! component and the inner provider are written plainly (their expansions are +//! owned elsewhere); only the `#[use_provider]` `#[cgp_fn]` is snapshotted here. +//! +//! See docs/reference/attributes/use_provider.md and docs/reference/macros/cgp_fn.md. + +use cgp::prelude::*; +use cgp_macro_test_util::snapshot_cgp_fn; + +#[cgp_component(AreaCalculator)] +pub trait CanCalculateArea { + fn area(&self) -> f64; +} + +#[cgp_impl(new RectangleAreaCalculator)] +impl AreaCalculator { + fn area(&self, #[implicit] width: f64, #[implicit] height: f64) -> f64 { + width * height + } +} + +snapshot_cgp_fn! { + #[cgp_fn] + #[use_provider(RectangleAreaCalculator: AreaCalculator)] + fn rectangle_area(&self) -> f64 { + RectangleAreaCalculator::area(self) + } + + expand_rectangle_area(output) { + insta::assert_snapshot!(output, @" + trait RectangleArea { + fn rectangle_area(&self) -> f64; + } + impl<__Context__> RectangleArea for __Context__ + where + RectangleAreaCalculator: AreaCalculator, + { + fn rectangle_area(&self) -> f64 { + RectangleAreaCalculator::area(self) + } + } + ") + } +} + +#[derive(HasField)] +pub struct Rectangle { + pub width: f64, + pub height: f64, +} + +#[test] +fn test_use_provider() { + let rectangle = Rectangle { + width: 3.0, + height: 4.0, + }; + + assert_eq!(rectangle.rectangle_area(), 12.0); +} diff --git a/crates/tests/cgp-tests/tests/higher_order_providers/use_provider_impl.rs b/crates/tests/cgp-tests/tests/higher_order_providers/use_provider_impl.rs new file mode 100644 index 00000000..9a15018a --- /dev/null +++ b/crates/tests/cgp-tests/tests/higher_order_providers/use_provider_impl.rs @@ -0,0 +1,198 @@ +//! `#[use_provider]` on a `#[cgp_impl]`: a higher-order provider parameterized by +//! an inner provider. +//! +//! `ScaledArea` takes an inner `AreaCalculator` and scales its result. The +//! inner provider is chosen at wiring time (`ScaledArea`), so the +//! same outer provider composes with any base calculator. +//! +//! `higher_order_providers` owns the `#[cgp_impl]`-with-`#[use_provider]` +//! snapshot: the expansion moves the inner provider into the `where` clause as +//! `Inner: AreaCalculator<__Context__>` and threads it into the `IsProviderFor` +//! impl. The plain `RectangleArea` below is incidental scaffolding — its +//! `#[implicit]` expansion is snapshotted in `implicit_arguments`. +//! +//! See docs/reference/attributes/use_provider.md and +//! docs/concepts/higher-order-providers.md. + +use cgp::prelude::*; +use cgp_macro_test_util::snapshot_cgp_impl; + +#[cgp_component(AreaCalculator)] +pub trait CanCalculateArea { + fn area(&self) -> f64; +} + +#[cgp_impl(new RectangleArea)] +impl AreaCalculator { + fn area(&self, #[implicit] width: f64, #[implicit] height: f64) -> f64 { + width * height + } +} + +snapshot_cgp_impl! { + #[cgp_impl(new ScaledArea)] + #[use_provider(Inner: AreaCalculator)] + impl AreaCalculator { + fn area(&self, #[implicit] scale_factor: f64) -> f64 { + Inner::area(self) * scale_factor * scale_factor + } + } + + expand_scaled_area(output) { + insta::assert_snapshot!(output, @" + impl<__Context__, Inner> AreaCalculator<__Context__> for ScaledArea + where + __Context__: HasField< + Symbol< + 12, + Chars< + 's', + Chars< + 'c', + Chars< + 'a', + Chars< + 'l', + Chars< + 'e', + Chars< + '_', + Chars< + 'f', + Chars< + 'a', + Chars<'c', Chars<'t', Chars<'o', Chars<'r', Nil>>>>, + >, + >, + >, + >, + >, + >, + >, + >, + >, + Value = f64, + >, + Inner: AreaCalculator<__Context__>, + { + fn area(__context__: &__Context__) -> f64 { + let scale_factor: f64 = __context__ + .get_field( + ::core::marker::PhantomData::< + Symbol< + 12, + Chars< + 's', + Chars< + 'c', + Chars< + 'a', + Chars< + 'l', + Chars< + 'e', + Chars< + '_', + Chars< + 'f', + Chars< + 'a', + Chars<'c', Chars<'t', Chars<'o', Chars<'r', Nil>>>>, + >, + >, + >, + >, + >, + >, + >, + >, + >, + >, + ) + .clone(); + Inner::area(__context__) * scale_factor * scale_factor + } + } + impl<__Context__, Inner> IsProviderFor + for ScaledArea + where + __Context__: HasField< + Symbol< + 12, + Chars< + 's', + Chars< + 'c', + Chars< + 'a', + Chars< + 'l', + Chars< + 'e', + Chars< + '_', + Chars< + 'f', + Chars< + 'a', + Chars<'c', Chars<'t', Chars<'o', Chars<'r', Nil>>>>, + >, + >, + >, + >, + >, + >, + >, + >, + >, + Value = f64, + >, + Inner: IsProviderFor + + AreaCalculator<__Context__>, + {} + pub struct ScaledArea(pub ::core::marker::PhantomData<(Inner)>); + ") + } +} + +#[derive(HasField)] +pub struct Rectangle { + pub width: f64, + pub height: f64, +} + +impl CanCalculateArea for Rectangle { + fn area(&self) -> f64 { + RectangleArea::area(self) + } +} + +#[derive(HasField)] +pub struct ScaledRectangle { + pub width: f64, + pub height: f64, + pub scale_factor: f64, +} + +delegate_components! { + ScaledRectangle { + AreaCalculatorComponent: ScaledArea, + } +} + +#[test] +fn test_scaled_area() { + let rectangle = Rectangle { + width: 3.0, + height: 4.0, + }; + assert_eq!(rectangle.area(), 12.0); + + // The inner `RectangleArea` computes 12.0, then `ScaledArea` scales by 2^2. + let scaled = ScaledRectangle { + width: 3.0, + height: 4.0, + scale_factor: 2.0, + }; + assert_eq!(scaled.area(), 48.0); +} diff --git a/crates/tests/cgp-tests/tests/higher_order_providers_tests.rs b/crates/tests/cgp-tests/tests/higher_order_providers_tests.rs new file mode 100644 index 00000000..c0a2f711 --- /dev/null +++ b/crates/tests/cgp-tests/tests/higher_order_providers_tests.rs @@ -0,0 +1,13 @@ +//! Entrypoint for the `higher_order_providers` concept. +//! +//! A higher-order provider takes another provider as a generic parameter and +//! constrains it with a provider-trait bound via `#[use_provider]`, so its inner +//! behavior is chosen by wiring. This concept owns the `#[use_provider]` snapshots +//! (on both `#[cgp_fn]` and `#[cgp_impl]`) and exercises the scaling pattern where +//! an outer calculator wraps an inner one. +//! +//! See docs/reference/attributes/use_provider.md and +//! docs/concepts/higher-order-providers.md. +#![allow(dead_code)] + +pub mod higher_order_providers; diff --git a/crates/tests/cgp-tests/tests/component_tests/cgp_impl/implicit_args/import.rs b/crates/tests/cgp-tests/tests/impl_side_dependencies/fn_extend.rs similarity index 52% rename from crates/tests/cgp-tests/tests/component_tests/cgp_impl/implicit_args/import.rs rename to crates/tests/cgp-tests/tests/impl_side_dependencies/fn_extend.rs index b8fe907f..d528f573 100644 --- a/crates/tests/cgp-tests/tests/component_tests/cgp_impl/implicit_args/import.rs +++ b/crates/tests/cgp-tests/tests/impl_side_dependencies/fn_extend.rs @@ -1,35 +1,52 @@ -use cgp::prelude::*; -use cgp_macro_test_util::{snapshot_cgp_fn, snapshot_delegate_and_check_components}; +//! `#[extend(...)]` on a `#[cgp_fn]` adds a *supertrait* bound to the generated +//! trait — the only way to add a supertrait in `#[cgp_fn]`, whose `where` clauses +//! are impl-side dependencies. Here `rectangle_area` declares +//! `#[extend(HasScalarType)]` so the trait reads `RectangleArea: HasScalarType` +//! and its signatures name the abstract type as `Self::Scalar`. The snapshot pins +//! how `#[extend]` lands on the generated trait definition and impl. +//! +//! The `#[cgp_type]` scaffolding is written plainly here — its expansion is owned +//! by the `abstract_types` concept. +//! +//! See docs/concepts/impl-side-dependencies.md, +//! docs/reference/attributes/extend.md, and docs/reference/macros/cgp_fn.md. -#[cgp_component(AreaCalculator)] -pub trait CanCalculateArea { - fn area(&self) -> f64; -} +use std::ops::Mul; -#[cgp_impl(new RectangleAreaCalculator)] -#[uses(RectangleArea)] -impl AreaCalculator { - fn area(&self) -> f64 { - self.rectangle_area() - } +use cgp::prelude::*; +use cgp_macro_test_util::snapshot_cgp_fn; + +#[cgp_type] +pub trait HasScalarType { + type Scalar; } snapshot_cgp_fn! { #[cgp_fn] - pub fn rectangle_area(&self, #[implicit] width: f64, #[implicit] height: f64) -> f64 { + #[extend(HasScalarType)] + pub fn rectangle_area( + &self, + #[implicit] width: Self::Scalar, + #[implicit] height: Self::Scalar, + ) -> Self::Scalar + where + Self::Scalar: Mul + Copy, + { width * height } expand_rectangle_area(output) { insta::assert_snapshot!(output, @" - pub trait RectangleArea { - fn rectangle_area(&self) -> f64; + pub trait RectangleArea: HasScalarType { + fn rectangle_area(&self) -> Self::Scalar; } impl<__Context__> RectangleArea for __Context__ where + Self::Scalar: Mul + Copy, + Self: HasScalarType, Self: HasField< Symbol<5, Chars<'w', Chars<'i', Chars<'d', Chars<'t', Chars<'h', Nil>>>>>>, - Value = f64, + Value = Self::Scalar, > + HasField< Symbol< @@ -39,11 +56,11 @@ snapshot_cgp_fn! { Chars<'e', Chars<'i', Chars<'g', Chars<'h', Chars<'t', Nil>>>>>, >, >, - Value = f64, + Value = Self::Scalar, >, { - fn rectangle_area(&self) -> f64 { - let width: f64 = self + fn rectangle_area(&self) -> Self::Scalar { + let width: Self::Scalar = self .get_field( ::core::marker::PhantomData::< Symbol< @@ -53,7 +70,7 @@ snapshot_cgp_fn! { >, ) .clone(); - let height: f64 = self + let height: Self::Scalar = self .get_field( ::core::marker::PhantomData::< Symbol< @@ -82,35 +99,9 @@ pub struct Rectangle { pub height: f64, } -snapshot_delegate_and_check_components! { - delegate_and_check_components! { - Rectangle { - AreaCalculatorComponent: - RectangleAreaCalculator, - } - } - - expand_rectangle(output) { - insta::assert_snapshot!(output, @" - impl DelegateComponent for Rectangle { - type Delegate = RectangleAreaCalculator; - } - impl< - __Context__, - __Params__, - > IsProviderFor for Rectangle - where - RectangleAreaCalculator: IsProviderFor< - AreaCalculatorComponent, - __Context__, - __Params__, - >, - {} - trait __CanUseRectangle< - __Component__, - __Params__: ?Sized, - >: CanUseComponent<__Component__, __Params__> {} - impl __CanUseRectangle for Rectangle {} - ") - } +impl HasScalarType for Rectangle { + type Scalar = f64; } + +pub trait CheckRectangle: RectangleArea {} +impl CheckRectangle for Rectangle {} diff --git a/crates/tests/cgp-tests/tests/cgp_fn_tests/uses.rs b/crates/tests/cgp-tests/tests/impl_side_dependencies/fn_uses.rs similarity index 64% rename from crates/tests/cgp-tests/tests/cgp_fn_tests/uses.rs rename to crates/tests/cgp-tests/tests/impl_side_dependencies/fn_uses.rs index 1daf1fbd..d78cc63e 100644 --- a/crates/tests/cgp-tests/tests/cgp_fn_tests/uses.rs +++ b/crates/tests/cgp-tests/tests/impl_side_dependencies/fn_uses.rs @@ -1,66 +1,23 @@ +//! `#[uses(...)]` on a `#[cgp_fn]` imports a `Self` trait bound, read like a +//! `use` statement, so the function body can call another capability. Here +//! `scaled_rectangle_area` declares `#[uses(RectangleArea)]` and calls +//! `self.rectangle_area()`; the import lands as `Self: RectangleArea` in the +//! generated impl's `where` clause — an impl-side dependency the consumer trait +//! does not expose. +//! +//! The `rectangle_area` dependency below is a plain `#[cgp_fn]` (its full +//! expansion is owned by the `implicit_arguments` concept), so it is written +//! without a snapshot; the snapshot here pins how `#[uses]` lands. +//! +//! See docs/concepts/impl-side-dependencies.md, +//! docs/reference/attributes/uses.md, and docs/reference/macros/cgp_fn.md. + use cgp::prelude::*; use cgp_macro_test_util::snapshot_cgp_fn; -snapshot_cgp_fn! { - #[cgp_fn] - pub fn rectangle_area(&self, #[implicit] width: f64, #[implicit] height: f64) -> f64 { - width * height - } - - expand_rectangle_area(output) { - insta::assert_snapshot!(output, @" - pub trait RectangleArea { - fn rectangle_area(&self) -> f64; - } - impl<__Context__> RectangleArea for __Context__ - where - Self: HasField< - Symbol<5, Chars<'w', Chars<'i', Chars<'d', Chars<'t', Chars<'h', Nil>>>>>>, - Value = f64, - > - + HasField< - Symbol< - 6, - Chars< - 'h', - Chars<'e', Chars<'i', Chars<'g', Chars<'h', Chars<'t', Nil>>>>>, - >, - >, - Value = f64, - >, - { - fn rectangle_area(&self) -> f64 { - let width: f64 = self - .get_field( - ::core::marker::PhantomData::< - Symbol< - 5, - Chars<'w', Chars<'i', Chars<'d', Chars<'t', Chars<'h', Nil>>>>>, - >, - >, - ) - .clone(); - let height: f64 = self - .get_field( - ::core::marker::PhantomData::< - Symbol< - 6, - Chars< - 'h', - Chars< - 'e', - Chars<'i', Chars<'g', Chars<'h', Chars<'t', Nil>>>>, - >, - >, - >, - >, - ) - .clone(); - width * height - } - } - ") - } +#[cgp_fn] +pub fn rectangle_area(&self, #[implicit] width: f64, #[implicit] height: f64) -> f64 { + width * height } snapshot_cgp_fn! { diff --git a/crates/tests/cgp-tests/tests/impl_side_dependencies/impl_uses.rs b/crates/tests/cgp-tests/tests/impl_side_dependencies/impl_uses.rs new file mode 100644 index 00000000..1f042ce9 --- /dev/null +++ b/crates/tests/cgp-tests/tests/impl_side_dependencies/impl_uses.rs @@ -0,0 +1,59 @@ +//! `#[uses(...)]` on a `#[cgp_impl]` provider imports a `Self` trait bound, read +//! like a `use` statement, so the provider body can call another capability. Here +//! `RectangleAreaCalculator` declares `#[uses(RectangleArea)]` and calls +//! `self.rectangle_area()`; the import becomes an impl-side dependency +//! (`Self: RectangleArea`) that the `CanCalculateArea` consumer trait does not +//! expose. The context satisfies it by having the `width`/`height` fields that +//! `RectangleArea`'s blanket impl requires. +//! +//! The `#[uses(...)]` on the `#[cgp_impl]` is the feature under test and is +//! written plainly. The `#[cgp_fn]` dependency and the `delegate_and_check_components!` +//! wiring are incidental scaffolding (their expansions are owned by the +//! `implicit_arguments`, `basic_delegation`, and `checking` concepts), so both are +//! written as plain macros. +//! +//! See docs/concepts/impl-side-dependencies.md, +//! docs/reference/attributes/uses.md, and docs/reference/macros/cgp_impl.md. + +use cgp::prelude::*; + +#[cgp_component(AreaCalculator)] +pub trait CanCalculateArea { + fn area(&self) -> f64; +} + +#[cgp_impl(new RectangleAreaCalculator)] +#[uses(RectangleArea)] +impl AreaCalculator { + fn area(&self) -> f64 { + self.rectangle_area() + } +} + +#[cgp_fn] +pub fn rectangle_area(&self, #[implicit] width: f64, #[implicit] height: f64) -> f64 { + width * height +} + +#[derive(HasField)] +pub struct Rectangle { + pub width: f64, + pub height: f64, +} + +delegate_and_check_components! { + Rectangle { + AreaCalculatorComponent: + RectangleAreaCalculator, + } +} + +#[test] +fn test_impl_uses() { + let rectangle = Rectangle { + width: 3.0, + height: 4.0, + }; + + assert_eq!(rectangle.area(), 12.0); +} diff --git a/crates/tests/cgp-tests/tests/impl_side_dependencies/mod.rs b/crates/tests/cgp-tests/tests/impl_side_dependencies/mod.rs new file mode 100644 index 00000000..f6a251a2 --- /dev/null +++ b/crates/tests/cgp-tests/tests/impl_side_dependencies/mod.rs @@ -0,0 +1,17 @@ +//! One unit test per file. Each file is self-contained: it defines its own +//! components, providers, `#[cgp_fn]` functions, and context types at module +//! scope so that the type-level wiring of one test never leaks into another. + +// `#[uses(...)]` on `#[cgp_fn]`: imports a `Self` trait bound so the function can +// call another capability. This concept owns the snapshot showing how `#[uses]` +// lands on the generated impl's `where` clause. +pub mod fn_uses; + +// `#[extend(...)]` on `#[cgp_fn]`: adds a supertrait bound to the generated +// trait. This concept owns the snapshot showing how `#[extend]` lands on the +// generated trait definition and impl. +pub mod fn_extend; + +// `#[uses(...)]` on a `#[cgp_impl]` provider: imports a `Self` trait bound so the +// provider can call another capability. The provider is written plainly. +pub mod impl_uses; diff --git a/crates/tests/cgp-tests/tests/impl_side_dependencies_tests.rs b/crates/tests/cgp-tests/tests/impl_side_dependencies_tests.rs new file mode 100644 index 00000000..15312764 --- /dev/null +++ b/crates/tests/cgp-tests/tests/impl_side_dependencies_tests.rs @@ -0,0 +1,16 @@ +//! Entrypoint for the `impl_side_dependencies` concept. +//! +//! Covers the capabilities a provider or `#[cgp_fn]` function requires but the +//! consumer trait does not itself expose — CGP's form of dependency injection. +//! These impl-side dependencies are declared with `#[uses(...)]` (import `Self` +//! trait bounds, read like a `use` statement) and `#[extend(...)]` (add a +//! *supertrait* bound to the generated trait). This concept owns the `#[cgp_fn]` +//! snapshots that show how `#[uses]`/`#[extend]` land on the generated trait and +//! impl. +//! +//! See docs/concepts/impl-side-dependencies.md, +//! docs/reference/attributes/uses.md, docs/reference/attributes/extend.md, and +//! docs/reference/attributes/extend_where.md. +#![allow(dead_code)] + +pub mod impl_side_dependencies; diff --git a/crates/tests/cgp-tests/tests/cgp_fn_tests/call.rs b/crates/tests/cgp-tests/tests/implicit_arguments/cgp_fn_calling_fn.rs similarity index 94% rename from crates/tests/cgp-tests/tests/cgp_fn_tests/call.rs rename to crates/tests/cgp-tests/tests/implicit_arguments/cgp_fn_calling_fn.rs index 2eb15da6..b7af8dfd 100644 --- a/crates/tests/cgp-tests/tests/cgp_fn_tests/call.rs +++ b/crates/tests/cgp-tests/tests/implicit_arguments/cgp_fn_calling_fn.rs @@ -1,3 +1,11 @@ +//! One `#[cgp_fn]` capability calling another through a `where Self: …` bound. +//! +//! `scaled_rectangle_area` depends on `RectangleArea` (an impl-side dependency +//! stated as an explicit `where` clause) and calls `self.rectangle_area()`. The +//! snapshots show both generated traits and how the dependency lands on the impl. +//! +//! See docs/reference/macros/cgp_fn.md. + use cgp::prelude::*; use cgp_macro_test_util::snapshot_cgp_fn; diff --git a/crates/tests/cgp-tests/tests/cgp_fn_tests/basic.rs b/crates/tests/cgp-tests/tests/implicit_arguments/cgp_fn_custom_trait_name.rs similarity index 69% rename from crates/tests/cgp-tests/tests/cgp_fn_tests/basic.rs rename to crates/tests/cgp-tests/tests/implicit_arguments/cgp_fn_custom_trait_name.rs index 7a89d3f8..9ad805dd 100644 --- a/crates/tests/cgp-tests/tests/cgp_fn_tests/basic.rs +++ b/crates/tests/cgp-tests/tests/implicit_arguments/cgp_fn_custom_trait_name.rs @@ -1,47 +1,11 @@ +//! `#[cgp_fn(CustomName)]` overrides the generated trait name, with two +//! `#[implicit]` `f64` arguments read from the context (each `.clone()`d). +//! +//! See docs/reference/macros/cgp_fn.md. + use cgp::prelude::*; use cgp_macro_test_util::snapshot_cgp_fn; -snapshot_cgp_fn! { - #[cgp_fn] - pub fn greet(&self, #[implicit] name: &str) { - println!("Hello, {}!", name); - } - - expand_greet(output) { - insta::assert_snapshot!(output, @r#" - pub trait Greet { - fn greet(&self); - } - impl<__Context__> Greet for __Context__ - where - Self: HasField< - Symbol<4, Chars<'n', Chars<'a', Chars<'m', Chars<'e', Nil>>>>>, - Value = String, - >, - { - fn greet(&self) { - let name: &str = self - .get_field( - ::core::marker::PhantomData::< - Symbol<4, Chars<'n', Chars<'a', Chars<'m', Chars<'e', Nil>>>>>, - >, - ) - .as_str(); - println!("Hello, {}!", name); - } - } - "#) - } -} - -#[derive(HasField)] -pub struct Person { - pub name: String, -} - -pub trait CheckPerson: Greet {} -impl CheckPerson for Person {} - snapshot_cgp_fn! { #[cgp_fn(CanCalculateRectangleArea)] pub fn rectangle_area(&self, #[implicit] width: f64, #[implicit] height: f64) -> f64 { diff --git a/crates/tests/cgp-tests/tests/implicit_arguments/cgp_fn_greet.rs b/crates/tests/cgp-tests/tests/implicit_arguments/cgp_fn_greet.rs new file mode 100644 index 00000000..53b15a68 --- /dev/null +++ b/crates/tests/cgp-tests/tests/implicit_arguments/cgp_fn_greet.rs @@ -0,0 +1,52 @@ +//! `#[cgp_fn]` with a single `#[implicit]` `&str` argument. +//! +//! The implicit `name` is dropped from the signature and read from the context's +//! `name` field via `HasField`, with `.as_str()` applied automatically. The +//! `CheckPerson` bound proves any context with a `name: String` field implements +//! the generated trait. +//! +//! See docs/reference/macros/cgp_fn.md and docs/reference/attributes/implicit.md. + +use cgp::prelude::*; +use cgp_macro_test_util::snapshot_cgp_fn; + +snapshot_cgp_fn! { + #[cgp_fn] + pub fn greet(&self, #[implicit] name: &str) { + println!("Hello, {}!", name); + } + + expand_greet(output) { + insta::assert_snapshot!(output, @r#" + pub trait Greet { + fn greet(&self); + } + impl<__Context__> Greet for __Context__ + where + Self: HasField< + Symbol<4, Chars<'n', Chars<'a', Chars<'m', Chars<'e', Nil>>>>>, + Value = String, + >, + { + fn greet(&self) { + let name: &str = self + .get_field( + ::core::marker::PhantomData::< + Symbol<4, Chars<'n', Chars<'a', Chars<'m', Chars<'e', Nil>>>>>, + >, + ) + .as_str(); + println!("Hello, {}!", name); + } + } + "#) + } +} + +#[derive(HasField)] +pub struct Person { + pub name: String, +} + +pub trait CheckPerson: Greet {} +impl CheckPerson for Person {} diff --git a/crates/tests/cgp-tests/tests/cgp_fn_tests/multi.rs b/crates/tests/cgp-tests/tests/implicit_arguments/cgp_fn_multi_and_use_type.rs similarity index 86% rename from crates/tests/cgp-tests/tests/cgp_fn_tests/multi.rs rename to crates/tests/cgp-tests/tests/implicit_arguments/cgp_fn_multi_and_use_type.rs index 6771f485..d6296d3c 100644 --- a/crates/tests/cgp-tests/tests/cgp_fn_tests/multi.rs +++ b/crates/tests/cgp-tests/tests/implicit_arguments/cgp_fn_multi_and_use_type.rs @@ -1,3 +1,13 @@ +//! `#[cgp_fn]` combining explicit and `#[implicit]` arguments, generic method +//! parameters, `#[async_trait]`, and `#[use_type]` importing abstract types with +//! renaming. +//! +//! The implicit args here have abstract-type values (`FooX`, `Bar`) imported via +//! `#[use_type(>::{Foo as FooX}, …)]`, showing how implicit reads +//! and abstract-type imports combine in one function. +//! +//! See docs/reference/macros/cgp_fn.md and docs/reference/attributes/use_type.md. + use core::fmt::Display; use cgp::prelude::*; diff --git a/crates/tests/cgp-tests/tests/cgp_fn_tests/mutable.rs b/crates/tests/cgp-tests/tests/implicit_arguments/cgp_fn_mutable.rs similarity index 86% rename from crates/tests/cgp-tests/tests/cgp_fn_tests/mutable.rs rename to crates/tests/cgp-tests/tests/implicit_arguments/cgp_fn_mutable.rs index 36b96b43..ce234787 100644 --- a/crates/tests/cgp-tests/tests/cgp_fn_tests/mutable.rs +++ b/crates/tests/cgp-tests/tests/implicit_arguments/cgp_fn_mutable.rs @@ -1,3 +1,10 @@ +//! `#[cgp_fn]` with `&mut self` and a mutable `#[implicit]` argument. +//! +//! A `&mut` implicit reads through `HasFieldMut`/`get_field_mut` instead of the +//! shared `HasField` path. +//! +//! See docs/reference/macros/cgp_fn.md and docs/reference/traits/has_field.md. + use cgp::prelude::*; use cgp_macro_test_util::snapshot_cgp_fn; diff --git a/crates/tests/cgp-tests/tests/implicit_arguments/cgp_impl_implicit.rs b/crates/tests/cgp-tests/tests/implicit_arguments/cgp_impl_implicit.rs new file mode 100644 index 00000000..d1070da8 --- /dev/null +++ b/crates/tests/cgp-tests/tests/implicit_arguments/cgp_impl_implicit.rs @@ -0,0 +1,112 @@ +//! `#[implicit]` arguments inside a `#[cgp_impl]` provider. +//! +//! A provider method can take `#[implicit]` args just like `#[cgp_fn]`; the +//! provider then depends on the context's fields. `implicit_arguments` owns the +//! `#[cgp_impl]`-with-`#[implicit]` snapshot: the expansion drops the implicit +//! parameters from the signature, reads them from the context via +//! `HasField`/`get_field`, and adds the field bounds to the provider's +//! `IsProviderFor` impl. (The basic, no-implicit `#[cgp_impl]` snapshot lives in +//! `basic_delegation`.) +//! +//! See docs/reference/macros/cgp_impl.md and docs/reference/attributes/implicit.md. + +use cgp::prelude::*; +use cgp_macro_test_util::snapshot_cgp_impl; + +#[cgp_component(AreaCalculator)] +pub trait CanCalculateArea { + fn area(&self) -> f64; +} + +snapshot_cgp_impl! { + #[cgp_impl(new RectangleArea)] + impl AreaCalculator { + fn area(&self, #[implicit] width: f64, #[implicit] height: f64) -> f64 { + width * height + } + } + + expand_rectangle_area(output) { + insta::assert_snapshot!(output, @" + impl<__Context__> AreaCalculator<__Context__> for RectangleArea + where + __Context__: HasField< + Symbol<5, Chars<'w', Chars<'i', Chars<'d', Chars<'t', Chars<'h', Nil>>>>>>, + Value = f64, + > + + HasField< + Symbol< + 6, + Chars< + 'h', + Chars<'e', Chars<'i', Chars<'g', Chars<'h', Chars<'t', Nil>>>>>, + >, + >, + Value = f64, + >, + { + fn area(__context__: &__Context__) -> f64 { + let width: f64 = __context__ + .get_field( + ::core::marker::PhantomData::< + Symbol< + 5, + Chars<'w', Chars<'i', Chars<'d', Chars<'t', Chars<'h', Nil>>>>>, + >, + >, + ) + .clone(); + let height: f64 = __context__ + .get_field( + ::core::marker::PhantomData::< + Symbol< + 6, + Chars< + 'h', + Chars< + 'e', + Chars<'i', Chars<'g', Chars<'h', Chars<'t', Nil>>>>, + >, + >, + >, + >, + ) + .clone(); + width * height + } + } + impl<__Context__> IsProviderFor + for RectangleArea + where + __Context__: HasField< + Symbol<5, Chars<'w', Chars<'i', Chars<'d', Chars<'t', Chars<'h', Nil>>>>>>, + Value = f64, + > + + HasField< + Symbol< + 6, + Chars< + 'h', + Chars<'e', Chars<'i', Chars<'g', Chars<'h', Chars<'t', Nil>>>>>, + >, + >, + Value = f64, + >, + {} + pub struct RectangleArea; + ") + } +} + +#[derive(HasField)] +pub struct Rectangle { + pub width: f64, + pub height: f64, +} + +delegate_and_check_components! { + Rectangle { + AreaCalculatorComponent: + RectangleArea, + } +} diff --git a/crates/tests/cgp-tests/tests/implicit_arguments/cgp_impl_implicit_generic.rs b/crates/tests/cgp-tests/tests/implicit_arguments/cgp_impl_implicit_generic.rs new file mode 100644 index 00000000..9cd6dac2 --- /dev/null +++ b/crates/tests/cgp-tests/tests/implicit_arguments/cgp_impl_implicit_generic.rs @@ -0,0 +1,41 @@ +//! `#[implicit]` arguments in a generic `#[cgp_impl]` provider. +//! +//! The provider is generic over `Scalar`, and the implicit `width`/`height` are +//! read as `Scalar` from the context. `#[check_params(f64)]` picks the concrete +//! `Scalar` for the wiring check. +//! +//! See docs/reference/macros/cgp_impl.md and +//! docs/reference/macros/delegate_and_check_components.md. + +use core::ops::Mul; + +use cgp::prelude::*; + +#[cgp_component(AreaCalculator)] +pub trait CanCalculateArea { + fn area(&self) -> Scalar; +} + +#[cgp_impl(new RectangleArea)] +impl AreaCalculator +where + Scalar: Mul + Copy, +{ + fn area(&self, #[implicit] width: Scalar, #[implicit] height: Scalar) -> Scalar { + width * height + } +} + +#[derive(HasField)] +pub struct Rectangle { + pub width: f64, + pub height: f64, +} + +delegate_and_check_components! { + Rectangle { + #[check_params(f64)] + AreaCalculatorComponent: + RectangleArea, + } +} diff --git a/crates/tests/cgp-tests/tests/component_tests/cgp_impl/implicit_context.rs b/crates/tests/cgp-tests/tests/implicit_arguments/implicit_context.rs similarity index 68% rename from crates/tests/cgp-tests/tests/component_tests/cgp_impl/implicit_context.rs rename to crates/tests/cgp-tests/tests/implicit_arguments/implicit_context.rs index e822ee7d..3ea79a55 100644 --- a/crates/tests/cgp-tests/tests/component_tests/cgp_impl/implicit_context.rs +++ b/crates/tests/cgp-tests/tests/implicit_arguments/implicit_context.rs @@ -1,3 +1,12 @@ +//! Writing `#[cgp_impl]` providers without naming the context parameter. +//! +//! `impl FooProvider { … }` lets the macro insert the `__Context__` parameter, +//! so providers read like ordinary trait impls. The providers here range from a +//! context-free one (`ValueToString`) to ones that depend on the context +//! (`WithNamePrefix` needs `HasName`) or carry their own generics (`WithFooTag`). +//! +//! See docs/reference/macros/cgp_impl.md. + use cgp::prelude::*; #[cgp_component(FooProvider)] diff --git a/crates/tests/cgp-tests/tests/implicit_arguments/mod.rs b/crates/tests/cgp-tests/tests/implicit_arguments/mod.rs new file mode 100644 index 00000000..9d0c75d3 --- /dev/null +++ b/crates/tests/cgp-tests/tests/implicit_arguments/mod.rs @@ -0,0 +1,15 @@ +//! One unit test per file, each self-contained at module scope. + +// `#[cgp_fn]` expansion snapshots (this concept owns them): how `#[implicit]` +// arguments become `HasField` bounds and field reads. +pub mod cgp_fn_calling_fn; +pub mod cgp_fn_custom_trait_name; +pub mod cgp_fn_greet; +pub mod cgp_fn_multi_and_use_type; +pub mod cgp_fn_mutable; + +// `#[implicit]` arguments inside `#[cgp_impl]` providers, and the implicit +// context parameter. +pub mod cgp_impl_implicit; +pub mod cgp_impl_implicit_generic; +pub mod implicit_context; diff --git a/crates/tests/cgp-tests/tests/implicit_arguments_tests.rs b/crates/tests/cgp-tests/tests/implicit_arguments_tests.rs new file mode 100644 index 00000000..9ac83aec --- /dev/null +++ b/crates/tests/cgp-tests/tests/implicit_arguments_tests.rs @@ -0,0 +1,13 @@ +//! Entrypoint for the `implicit_arguments` concept. +//! +//! Covers `#[implicit]` arguments — parameters removed from a `#[cgp_fn]` or +//! `#[cgp_impl]` signature and read from the context's fields via `HasField`. +//! This concept owns the `#[cgp_fn]` expansion snapshots (showing the generated +//! `HasField` bounds and field reads), and exercises implicit args in `#[cgp_impl]` +//! providers, including the implicit context parameter. +//! +//! See docs/reference/attributes/implicit.md, docs/reference/macros/cgp_fn.md, +//! and docs/concepts/implicit-arguments.md. +#![allow(dead_code)] + +pub mod implicit_arguments; diff --git a/crates/tests/cgp-tests/src/tests/monad/err.rs b/crates/tests/cgp-tests/tests/monadic_handlers/err_monadic.rs similarity index 67% rename from crates/tests/cgp-tests/src/tests/monad/err.rs rename to crates/tests/cgp-tests/tests/monadic_handlers/err_monadic.rs index c2748953..4b648d63 100644 --- a/crates/tests/cgp-tests/src/tests/monad/err.rs +++ b/crates/tests/cgp-tests/tests/monadic_handlers/err_monadic.rs @@ -1,3 +1,16 @@ +//! Composing `Computer` handlers through the **err monad** — the dual of the ok +//! monad. +//! +//! `ErrMonadic` treats `Result` as a monad over its `Err` value: chaining +//! handlers threads each `Err` result into the next step and short-circuits on +//! the first `Ok`. The `increment` computer here uses the ordinary convention +//! (`Ok(next)` on success, `Err("overflow")` on failure). Both +//! `BindErr` (inside a plain `PipeHandlers` chain) and the +//! `PipeMonadic` combinator are exercised. +//! +//! See docs/concepts/monadic-handlers.md and +//! docs/reference/providers/monad_providers.md. + use cgp::extra::handler::PipeHandlers; use cgp::extra::monad::monadic::err::{BindErr, ErrMonadic}; use cgp::extra::monad::monadic::ident::IdentMonadic; diff --git a/crates/tests/cgp-tests/tests/monadic_handlers/mod.rs b/crates/tests/cgp-tests/tests/monadic_handlers/mod.rs new file mode 100644 index 00000000..a4351813 --- /dev/null +++ b/crates/tests/cgp-tests/tests/monadic_handlers/mod.rs @@ -0,0 +1,15 @@ +//! One unit test per file. Each file is self-contained: it defines its own +//! `#[cgp_computer]` handlers and drives them through the monadic combinators +//! at module scope, so the wiring of one test never leaks into another. + +// The ok monad: `PipeMonadic` and `BindOk` sequence handlers, +// short-circuiting on the first `Err` and threading each `Ok` value onward. +pub mod ok_monadic; + +// The err monad: the dual of the ok monad — `PipeMonadic` and +// `BindErr` thread the `Err` value onward and short-circuit on the first `Ok`. +pub mod err_monadic; + +// The ok-monad transformer `OkMonadicTrans` stacked over `ErrMonadic`, +// plus running the same pipeline via `try_compute` and the async `handle`. +pub mod ok_err_monadic_trans; diff --git a/crates/tests/cgp-tests/src/tests/monad/ok_err_trans.rs b/crates/tests/cgp-tests/tests/monadic_handlers/ok_err_monadic_trans.rs similarity index 75% rename from crates/tests/cgp-tests/src/tests/monad/ok_err_trans.rs rename to crates/tests/cgp-tests/tests/monadic_handlers/ok_err_monadic_trans.rs index 6766b395..a1af76fb 100644 --- a/crates/tests/cgp-tests/src/tests/monad/ok_err_trans.rs +++ b/crates/tests/cgp-tests/tests/monadic_handlers/ok_err_monadic_trans.rs @@ -1,3 +1,17 @@ +//! The ok-monad **transformer** `OkMonadicTrans` stacked over the err +//! monad, composing handlers whose output is a nested `Result, ..>`. +//! +//! Each handler returns `Result, &'static str>`. Stacking the ok +//! monad over `ErrMonadic` lets the outer `Ok`/inner-`Ok` layer drive +//! sequencing while the outer `Err` short-circuits the whole chain. The same +//! pipeline is then driven three ways to show the monadic handler works across +//! the handler family: the synchronous infallible `compute`, the fallible +//! `try_compute` (via a plain `OkMonadic` over an `ErrorOnly` context), and the +//! async `handle` (blocked on with `futures::executor::block_on`). +//! +//! See docs/concepts/monadic-handlers.md and +//! docs/reference/providers/monad_providers.md. + use cgp::core::error::ErrorOnly; use cgp::extra::monad::monadic::err::ErrMonadic; use cgp::extra::monad::monadic::ok::{OkMonadic, OkMonadicTrans}; diff --git a/crates/tests/cgp-tests/src/tests/monad/ok.rs b/crates/tests/cgp-tests/tests/monadic_handlers/ok_monadic.rs similarity index 65% rename from crates/tests/cgp-tests/src/tests/monad/ok.rs rename to crates/tests/cgp-tests/tests/monadic_handlers/ok_monadic.rs index 6891ed13..0ea23d9b 100644 --- a/crates/tests/cgp-tests/src/tests/monad/ok.rs +++ b/crates/tests/cgp-tests/tests/monadic_handlers/ok_monadic.rs @@ -1,3 +1,17 @@ +//! Composing `Computer` handlers through the **ok monad**. +//! +//! `OkMonadic` treats `Result` as a monad over its `Ok` value: chaining +//! handlers threads each `Ok` result into the next step and short-circuits on +//! the first `Err`. Here the `increment` computer inverts the usual convention +//! (it returns `Err(res)` on success so the `Ok` value can carry the terminal +//! "overflow" string), which makes the ok-monad's short-circuit-on-`Err` +//! behavior easy to observe. Both `BindOk` (inside a plain +//! `PipeHandlers` chain) and the `PipeMonadic` combinator are +//! exercised. +//! +//! See docs/concepts/monadic-handlers.md and +//! docs/reference/providers/monad_providers.md. + use cgp::extra::handler::PipeHandlers; use cgp::extra::monad::monadic::ident::IdentMonadic; use cgp::extra::monad::monadic::ok::{BindOk, OkMonadic}; diff --git a/crates/tests/cgp-tests/tests/monadic_handlers_tests.rs b/crates/tests/cgp-tests/tests/monadic_handlers_tests.rs new file mode 100644 index 00000000..7ca92f44 --- /dev/null +++ b/crates/tests/cgp-tests/tests/monadic_handlers_tests.rs @@ -0,0 +1,13 @@ +//! Entrypoint for the `monadic_handlers` concept. +//! +//! Covers CGP's monadic handler combinators: composing `Computer` handlers +//! through a monad with `PipeMonadic`, and the `BindOk`/`BindErr` binders that +//! sequence a handler within the ok/err monads. These are runtime tests — a +//! passing test is an actual assertion on the computed value, not just +//! successful compilation. +//! +//! See docs/concepts/monadic-handlers.md and +//! docs/reference/providers/monad_providers.md. +#![allow(dead_code)] + +pub mod monadic_handlers; diff --git a/crates/tests/cgp-tests/tests/namespace.rs b/crates/tests/cgp-tests/tests/namespace.rs deleted file mode 100644 index 97b83ada..00000000 --- a/crates/tests/cgp-tests/tests/namespace.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod namespace_tests; diff --git a/crates/tests/cgp-tests/tests/namespace_tests/mod.rs b/crates/tests/cgp-tests/tests/namespace_tests/mod.rs deleted file mode 100644 index 60b2bc01..00000000 --- a/crates/tests/cgp-tests/tests/namespace_tests/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -pub mod group; -pub mod multi_param; -pub mod namespace; -pub mod namespace_macro; -pub mod open; -pub mod redirect; diff --git a/crates/tests/cgp-tests/tests/namespace_tests/multi_param.rs b/crates/tests/cgp-tests/tests/namespace_tests/multi_param.rs deleted file mode 100644 index b89dd87d..00000000 --- a/crates/tests/cgp-tests/tests/namespace_tests/multi_param.rs +++ /dev/null @@ -1,243 +0,0 @@ -use cgp::prelude::*; -use cgp_macro_test_util::{snapshot_check_components, snapshot_delegate_components}; - -#[cgp_component(FooProvider)] -#[prefix(@app in DefaultNamespace)] -pub trait Foo<'a, T, U> { - fn foo(&self, first: &'a T, second: U); -} - -#[cgp_impl(new DummyFoo)] -impl<'a, T, U> FooProvider<'a, T, U> { - fn foo(&self, _first: &'a T, _second: U) {} -} - -pub struct AppA; - -snapshot_delegate_components! { - delegate_components! { - AppA { - open {FooProviderComponent}; - - @FooProviderComponent.String.u32: - DummyFoo, - @FooProviderComponent.bool.T: - DummyFoo, - } - } - - expand_multi_param_app_a(output) { - insta::assert_snapshot!(output, @" - impl DelegateComponent for AppA { - type Delegate = RedirectLookup>; - } - impl< - __Context__, - __Params__, - > IsProviderFor for AppA - where - RedirectLookup< - AppA, - PathCons, - >: IsProviderFor, - {} - impl< - __Wildcard__, - > DelegateComponent< - PathCons>>, - > for AppA { - type Delegate = DummyFoo; - } - impl< - __Wildcard__, - __Context__, - __Params__, - > IsProviderFor< - PathCons>>, - __Context__, - __Params__, - > for AppA - where - DummyFoo: IsProviderFor< - PathCons>>, - __Context__, - __Params__, - >, - {} - impl< - T, - __Wildcard__, - > DelegateComponent< - PathCons>>, - > for AppA { - type Delegate = DummyFoo; - } - impl< - T, - __Wildcard__, - __Context__, - __Params__, - > IsProviderFor< - PathCons>>, - __Context__, - __Params__, - > for AppA - where - DummyFoo: IsProviderFor< - PathCons>>, - __Context__, - __Params__, - >, - {} - ") - } -} - -snapshot_check_components! { - check_components! { - AppA { - FooProviderComponent: [ - <'a> (Life<'a>, String, u32), - <'a> (Life<'a>, bool, String), - ], - FooProviderComponent: - <'a> (Life<'a>, bool, bool), - } - } - - expand_check_app_a(output) { - insta::assert_snapshot!(output, @" - trait __CheckAppA< - __Component__, - __Params__: ?Sized, - >: CanUseComponent<__Component__, __Params__> {} - impl<'a> __CheckAppA, String, u32)> for AppA {} - impl<'a> __CheckAppA, bool, String)> for AppA {} - impl<'a> __CheckAppA, bool, bool)> for AppA {} - ") - } -} - -pub struct AppB; - -snapshot_delegate_components! { - delegate_components! { - AppB { - namespace DefaultNamespace; - - @app.FooProviderComponent.String.u64: - DummyFoo, - @app.FooProviderComponent.bool. T: - DummyFoo, - } - } - - expand_multi_param_app_b(output) { - insta::assert_snapshot!(output, @" - impl<__Key__, __Value__> DelegateComponent<__Key__> for AppB - where - __Key__: DefaultNamespace, - { - type Delegate = __Value__; - } - impl< - __Key__, - __Value__, - __Context__, - __Params__, - > IsProviderFor<__Key__, __Context__, __Params__> for AppB - where - __Key__: DefaultNamespace, - __Value__: IsProviderFor<__Key__, __Context__, __Params__>, - {} - impl< - __Wildcard__, - > DelegateComponent< - PathCons< - Symbol<3, Chars<'a', Chars<'p', Chars<'p', Nil>>>>, - PathCons>>, - >, - > for AppB { - type Delegate = DummyFoo; - } - impl< - __Wildcard__, - __Context__, - __Params__, - > IsProviderFor< - PathCons< - Symbol<3, Chars<'a', Chars<'p', Chars<'p', Nil>>>>, - PathCons>>, - >, - __Context__, - __Params__, - > for AppB - where - DummyFoo: IsProviderFor< - PathCons< - Symbol<3, Chars<'a', Chars<'p', Chars<'p', Nil>>>>, - PathCons>>, - >, - __Context__, - __Params__, - >, - {} - impl< - T, - __Wildcard__, - > DelegateComponent< - PathCons< - Symbol<3, Chars<'a', Chars<'p', Chars<'p', Nil>>>>, - PathCons>>, - >, - > for AppB { - type Delegate = DummyFoo; - } - impl< - T, - __Wildcard__, - __Context__, - __Params__, - > IsProviderFor< - PathCons< - Symbol<3, Chars<'a', Chars<'p', Chars<'p', Nil>>>>, - PathCons>>, - >, - __Context__, - __Params__, - > for AppB - where - DummyFoo: IsProviderFor< - PathCons< - Symbol<3, Chars<'a', Chars<'p', Chars<'p', Nil>>>>, - PathCons>>, - >, - __Context__, - __Params__, - >, - {} - ") - } -} - -snapshot_check_components! { - check_components! { - <'a> AppB { - FooProviderComponent: [ - (Life<'a>, String, u64), - (Life<'a>, bool, String), - ], - } - } - - expand_check_app_b(output) { - insta::assert_snapshot!(output, @" - trait __CheckAppB< - __Component__, - __Params__: ?Sized, - >: CanUseComponent<__Component__, __Params__> {} - impl<'a> __CheckAppB, String, u64)> for AppB {} - impl<'a> __CheckAppB, bool, String)> for AppB {} - ") - } -} diff --git a/crates/tests/cgp-tests/tests/namespace_tests/namespace_macro/mod.rs b/crates/tests/cgp-tests/tests/namespace_tests/namespace_macro/mod.rs deleted file mode 100644 index 42cb4ff2..00000000 --- a/crates/tests/cgp-tests/tests/namespace_tests/namespace_macro/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -pub mod basic; -pub mod default_impls; -pub mod extended_namespace; -pub mod multi_namespace; -pub mod symbol_path; -pub mod type_path; diff --git a/crates/tests/cgp-tests/tests/namespace_tests/open.rs b/crates/tests/cgp-tests/tests/namespace_tests/open.rs deleted file mode 100644 index 61ffd096..00000000 --- a/crates/tests/cgp-tests/tests/namespace_tests/open.rs +++ /dev/null @@ -1,432 +0,0 @@ -use cgp_macro_test_util::{ - snapshot_cgp_component, snapshot_cgp_impl, snapshot_check_components, - snapshot_delegate_components, -}; - -pub struct App; - -snapshot_cgp_component! { - #[cgp_component(FooProvider)] - pub trait Foo { - fn foo(&self, value: &T); - } - - expand_open_foo(output) { - insta::assert_snapshot!(output, @" - pub trait Foo { - fn foo(&self, value: &T); - } - impl<__Context__, T> Foo for __Context__ - where - __Context__: FooProvider<__Context__, T>, - { - fn foo(&self, value: &T) { - __Context__::foo(self, value) - } - } - pub trait FooProvider< - __Context__, - T, - >: IsProviderFor { - fn foo(__context__: &__Context__, value: &T); - } - impl<__Provider__, __Context__, T> FooProvider<__Context__, T> for __Provider__ - where - __Provider__: DelegateComponent - + IsProviderFor, - <__Provider__ as DelegateComponent< - FooProviderComponent, - >>::Delegate: FooProvider<__Context__, T>, - { - fn foo(__context__: &__Context__, value: &T) { - <__Provider__ as DelegateComponent< - FooProviderComponent, - >>::Delegate::foo(__context__, value) - } - } - pub struct FooProviderComponent; - impl<__Context__, T> FooProvider<__Context__, T> for UseContext - where - __Context__: Foo, - { - fn foo(__context__: &__Context__, value: &T) { - __Context__::foo(__context__, value) - } - } - impl<__Context__, T> IsProviderFor for UseContext - where - __Context__: Foo, - {} - impl<__Context__, T, __Components__, __Path__> FooProvider<__Context__, T> - for RedirectLookup<__Components__, __Path__> - where - __Path__: ConcatPath>, - __Components__: DelegateComponent< - <__Path__ as ConcatPath>>::Output, - >, - <__Components__ as DelegateComponent< - <__Path__ as ConcatPath>>::Output, - >>::Delegate: FooProvider<__Context__, T>, - { - fn foo(__context__: &__Context__, value: &T) { - <__Components__ as DelegateComponent< - <__Path__ as ConcatPath>>::Output, - >>::Delegate::foo(__context__, value) - } - } - impl< - __Context__, - T, - __Components__, - __Path__, - > IsProviderFor - for RedirectLookup<__Components__, __Path__> - where - __Path__: ConcatPath>, - __Components__: DelegateComponent< - <__Path__ as ConcatPath>>::Output, - >, - <__Components__ as DelegateComponent< - <__Path__ as ConcatPath>>::Output, - >>::Delegate: IsProviderFor - + FooProvider<__Context__, T>, - {} - ") - } -} - -snapshot_cgp_component! { - #[cgp_component(BarProvider)] - pub trait Bar { - fn bar(&self, value: &T); - } - - expand_open_bar(output) { - insta::assert_snapshot!(output, @" - pub trait Bar { - fn bar(&self, value: &T); - } - impl<__Context__, T> Bar for __Context__ - where - __Context__: BarProvider<__Context__, T>, - { - fn bar(&self, value: &T) { - __Context__::bar(self, value) - } - } - pub trait BarProvider< - __Context__, - T, - >: IsProviderFor { - fn bar(__context__: &__Context__, value: &T); - } - impl<__Provider__, __Context__, T> BarProvider<__Context__, T> for __Provider__ - where - __Provider__: DelegateComponent - + IsProviderFor, - <__Provider__ as DelegateComponent< - BarProviderComponent, - >>::Delegate: BarProvider<__Context__, T>, - { - fn bar(__context__: &__Context__, value: &T) { - <__Provider__ as DelegateComponent< - BarProviderComponent, - >>::Delegate::bar(__context__, value) - } - } - pub struct BarProviderComponent; - impl<__Context__, T> BarProvider<__Context__, T> for UseContext - where - __Context__: Bar, - { - fn bar(__context__: &__Context__, value: &T) { - __Context__::bar(__context__, value) - } - } - impl<__Context__, T> IsProviderFor for UseContext - where - __Context__: Bar, - {} - impl<__Context__, T, __Components__, __Path__> BarProvider<__Context__, T> - for RedirectLookup<__Components__, __Path__> - where - __Path__: ConcatPath>, - __Components__: DelegateComponent< - <__Path__ as ConcatPath>>::Output, - >, - <__Components__ as DelegateComponent< - <__Path__ as ConcatPath>>::Output, - >>::Delegate: BarProvider<__Context__, T>, - { - fn bar(__context__: &__Context__, value: &T) { - <__Components__ as DelegateComponent< - <__Path__ as ConcatPath>>::Output, - >>::Delegate::bar(__context__, value) - } - } - impl< - __Context__, - T, - __Components__, - __Path__, - > IsProviderFor - for RedirectLookup<__Components__, __Path__> - where - __Path__: ConcatPath>, - __Components__: DelegateComponent< - <__Path__ as ConcatPath>>::Output, - >, - <__Components__ as DelegateComponent< - <__Path__ as ConcatPath>>::Output, - >>::Delegate: IsProviderFor - + BarProvider<__Context__, T>, - {} - ") - } -} - -snapshot_cgp_impl! { - #[cgp_impl(new DummyFoo)] - impl FooProvider { - fn foo(&self, _value: &T) {} - } - - expand_open_dummy_foo(output) { - insta::assert_snapshot!(output, @" - impl<__Context__, T> FooProvider<__Context__, T> for DummyFoo { - fn foo(__context__: &__Context__, _value: &T) {} - } - impl<__Context__, T> IsProviderFor for DummyFoo {} - pub struct DummyFoo; - ") - } -} - -snapshot_cgp_impl! { - #[cgp_impl(new DummyBar)] - impl BarProvider { - fn bar(&self, _value: &T) {} - } - - expand_open_dummy_bar(output) { - insta::assert_snapshot!(output, @" - impl<__Context__, T> BarProvider<__Context__, T> for DummyBar { - fn bar(__context__: &__Context__, _value: &T) {} - } - impl<__Context__, T> IsProviderFor for DummyBar {} - pub struct DummyBar; - ") - } -} - -snapshot_delegate_components! { - delegate_components! { - App { - open {FooProviderComponent, BarProviderComponent}; - - // FooProviderComponent => - // @FooProviderComponent, - // BarProviderComponent => - // @BarProviderComponent, - - @FooProviderComponent.String: - DummyFoo, - @BarProviderComponent.{u32, u64, bool, usize, isize}: - DummyBar, - } - } - - expand_open_app(output) { - insta::assert_snapshot!(output, @" - impl DelegateComponent for App { - type Delegate = RedirectLookup>; - } - impl< - __Context__, - __Params__, - > IsProviderFor for App - where - RedirectLookup< - App, - PathCons, - >: IsProviderFor, - {} - impl DelegateComponent for App { - type Delegate = RedirectLookup>; - } - impl< - __Context__, - __Params__, - > IsProviderFor for App - where - RedirectLookup< - App, - PathCons, - >: IsProviderFor, - {} - impl< - __Wildcard__, - > DelegateComponent>> - for App { - type Delegate = DummyFoo; - } - impl< - __Wildcard__, - __Context__, - __Params__, - > IsProviderFor< - PathCons>, - __Context__, - __Params__, - > for App - where - DummyFoo: IsProviderFor< - PathCons>, - __Context__, - __Params__, - >, - {} - impl< - __Wildcard__, - > DelegateComponent>> - for App { - type Delegate = DummyBar; - } - impl< - __Wildcard__, - __Context__, - __Params__, - > IsProviderFor< - PathCons>, - __Context__, - __Params__, - > for App - where - DummyBar: IsProviderFor< - PathCons>, - __Context__, - __Params__, - >, - {} - impl< - __Wildcard__, - > DelegateComponent>> - for App { - type Delegate = DummyBar; - } - impl< - __Wildcard__, - __Context__, - __Params__, - > IsProviderFor< - PathCons>, - __Context__, - __Params__, - > for App - where - DummyBar: IsProviderFor< - PathCons>, - __Context__, - __Params__, - >, - {} - impl< - __Wildcard__, - > DelegateComponent>> - for App { - type Delegate = DummyBar; - } - impl< - __Wildcard__, - __Context__, - __Params__, - > IsProviderFor< - PathCons>, - __Context__, - __Params__, - > for App - where - DummyBar: IsProviderFor< - PathCons>, - __Context__, - __Params__, - >, - {} - impl< - __Wildcard__, - > DelegateComponent>> - for App { - type Delegate = DummyBar; - } - impl< - __Wildcard__, - __Context__, - __Params__, - > IsProviderFor< - PathCons>, - __Context__, - __Params__, - > for App - where - DummyBar: IsProviderFor< - PathCons>, - __Context__, - __Params__, - >, - {} - impl< - __Wildcard__, - > DelegateComponent>> - for App { - type Delegate = DummyBar; - } - impl< - __Wildcard__, - __Context__, - __Params__, - > IsProviderFor< - PathCons>, - __Context__, - __Params__, - > for App - where - DummyBar: IsProviderFor< - PathCons>, - __Context__, - __Params__, - >, - {} - ") - } -} - -snapshot_check_components! { - check_components! { - App { - FooProviderComponent: - String, - BarProviderComponent: [ - u32, - u64, - bool, - usize, - isize, - ], - } - } - - expand_check_app(output) { - insta::assert_snapshot!(output, @" - trait __CheckApp< - __Component__, - __Params__: ?Sized, - >: CanUseComponent<__Component__, __Params__> {} - impl __CheckApp for App {} - impl __CheckApp for App {} - impl __CheckApp for App {} - impl __CheckApp for App {} - impl __CheckApp for App {} - impl __CheckApp for App {} - ") - } -} diff --git a/crates/tests/cgp-tests/src/namespaces/default_impls.rs b/crates/tests/cgp-tests/tests/namespaces/default_impls.rs similarity index 90% rename from crates/tests/cgp-tests/src/namespaces/default_impls.rs rename to crates/tests/cgp-tests/tests/namespaces/default_impls.rs index d4cd123e..0970db21 100644 --- a/crates/tests/cgp-tests/src/namespaces/default_impls.rs +++ b/crates/tests/cgp-tests/tests/namespaces/default_impls.rs @@ -1,3 +1,16 @@ +//! Reusable namespace + per-type default impls used by `default_impls_wiring`. +//! +//! Defines the `Show`/`ShowImpl` component attached to `DefaultNamespace` via +//! `#[prefix(@test in DefaultNamespace)]`, provider structs registered as per-type +//! defaults with `#[default_impl(... in DefaultImpls1<...>)]`, a `DefaultShowComponents` +//! namespace table built with `cgp_namespace!`, and an inheriting `ExtendedNamespace` +//! (`cgp_namespace! { new ExtendedNamespace: DefaultNamespace { .. } }`). These +//! snapshots capture the namespace/default-impl wiring this concept owns; sibling +//! `default_impls_wiring` consumes the exported items. +//! +//! See docs/reference/macros/cgp_namespace.md, +//! docs/reference/traits/default_namespace.md, and docs/concepts/namespaces.md. + use core::fmt::Display; use cgp::core::component::DefaultImpls1; diff --git a/crates/tests/cgp-tests/tests/namespace_tests/namespace_macro/default_impls.rs b/crates/tests/cgp-tests/tests/namespaces/default_impls_wiring.rs similarity index 83% rename from crates/tests/cgp-tests/tests/namespace_tests/namespace_macro/default_impls.rs rename to crates/tests/cgp-tests/tests/namespaces/default_impls_wiring.rs index c801a4cb..069d59d3 100644 --- a/crates/tests/cgp-tests/tests/namespace_tests/namespace_macro/default_impls.rs +++ b/crates/tests/cgp-tests/tests/namespaces/default_impls_wiring.rs @@ -1,7 +1,24 @@ +//! Wiring a context through per-type default impls and the `for <..> in ..` loop. +//! +//! Each context opens `DefaultNamespace` (or the inheriting `ExtendedNamespace`) +//! and populates the `ShowImpl` component from a default-impl source: `AppA` and +//! `AppC` iterate `DefaultImpls1` (the per-type registry filled +//! by `#[default_impl]` in the `default_impls` module) and override `u64`; `AppB` +//! iterates the `DefaultShowComponents` namespace table. This exercises the +//! namespace/`for … in`/`@`-path forms of `delegate_components!` (kept as +//! snapshots) against the reusable namespaces defined in the sibling +//! `default_impls` module. +//! +//! See docs/reference/traits/default_namespace.md and +//! docs/reference/macros/delegate_components.md. + use cgp::core::component::DefaultImpls1; use cgp::prelude::*; -use cgp_macro_test_util::{snapshot_check_components, snapshot_delegate_components}; -use cgp_tests::namespaces::default_impls::{ +use cgp_macro_test_util::snapshot_delegate_components; + +// The reusable namespaces and providers live in the sibling `default_impls` +// module (moved here from `cgp_tests::namespaces::default_impls`). +use crate::namespaces::default_impls::{ DefaultShowComponents, ExtendedNamespace, ShowImplComponent, ShowWithDisplay, }; @@ -115,25 +132,12 @@ snapshot_delegate_components! { } } -snapshot_check_components! { - check_components! { - AppA { - ShowImplComponent: [ - String, - u64, - ] - } - } - - expand_check_app_a(output) { - insta::assert_snapshot!(output, @" - trait __CheckAppA< - __Component__, - __Params__: ?Sized, - >: CanUseComponent<__Component__, __Params__> {} - impl __CheckAppA for AppA {} - impl __CheckAppA for AppA {} - ") +check_components! { + AppA { + ShowImplComponent: [ + String, + u64, + ] } } @@ -212,25 +216,12 @@ snapshot_delegate_components! { } } -snapshot_check_components! { - check_components! { - AppB { - ShowImplComponent: [ - String, - u64, - ] - } - } - - expand_check_app_b(output) { - insta::assert_snapshot!(output, @" - trait __CheckAppB< - __Component__, - __Params__: ?Sized, - >: CanUseComponent<__Component__, __Params__> {} - impl __CheckAppB for AppB {} - impl __CheckAppB for AppB {} - ") +check_components! { + AppB { + ShowImplComponent: [ + String, + u64, + ] } } @@ -344,26 +335,12 @@ snapshot_delegate_components! { } } -snapshot_check_components! { - check_components! { - AppC { - ShowImplComponent: [ - String, - u64, - u32, - ] - } - } - - expand_check_app_c(output) { - insta::assert_snapshot!(output, @" - trait __CheckAppC< - __Component__, - __Params__: ?Sized, - >: CanUseComponent<__Component__, __Params__> {} - impl __CheckAppC for AppC {} - impl __CheckAppC for AppC {} - impl __CheckAppC for AppC {} - ") +check_components! { + AppC { + ShowImplComponent: [ + String, + u64, + u32, + ] } } diff --git a/crates/tests/cgp-tests/src/namespaces/extended.rs b/crates/tests/cgp-tests/tests/namespaces/extended.rs similarity index 69% rename from crates/tests/cgp-tests/src/namespaces/extended.rs rename to crates/tests/cgp-tests/tests/namespaces/extended.rs index a87df68c..f81a587d 100644 --- a/crates/tests/cgp-tests/src/namespaces/extended.rs +++ b/crates/tests/cgp-tests/tests/namespaces/extended.rs @@ -1,3 +1,14 @@ +//! An inheriting namespace that rewrites a path prefix. +//! +//! `cgp_namespace! { new ExtendedNamespace: DefaultNamespace { @cgp.core.error => @app } }` +//! inherits every `DefaultNamespace` entry and additionally rewrites the +//! `cgp.core.error` path prefix onto `app`, so a context using this namespace can +//! wire the error components under a shorter `@app.*` path. This snapshot captures +//! the namespace-inheritance wiring this concept owns; sibling +//! `extended_namespace_wiring` consumes the exported `ExtendedNamespace`. +//! +//! See docs/reference/macros/cgp_namespace.md and docs/concepts/namespaces.md. + use cgp::prelude::DefaultNamespace; use cgp_macro_test_util::snapshot_cgp_namespace; diff --git a/crates/tests/cgp-tests/tests/namespace_tests/namespace_macro/extended_namespace.rs b/crates/tests/cgp-tests/tests/namespaces/extended_namespace_wiring.rs similarity index 91% rename from crates/tests/cgp-tests/tests/namespace_tests/namespace_macro/extended_namespace.rs rename to crates/tests/cgp-tests/tests/namespaces/extended_namespace_wiring.rs index 9195e8c5..f32fd1dd 100644 --- a/crates/tests/cgp-tests/tests/namespace_tests/namespace_macro/extended_namespace.rs +++ b/crates/tests/cgp-tests/tests/namespaces/extended_namespace_wiring.rs @@ -1,9 +1,25 @@ +//! Wiring a context through the path-rewriting `ExtendedNamespace`. +//! +//! `App` opens the inheriting `ExtendedNamespace` (defined in the sibling +//! `extended` module), which rewrites the `cgp.core.error` prefix onto `app`, so +//! the error components can be wired under the shorter `@app.*` path while a +//! component outside that prefix (`@cgp.extra.handler.TryComputerComponent`) is +//! still reachable by its full path. Exercises the namespace/`@`-path form of +//! `delegate_components!` (kept as a snapshot) and confirms the wiring compiles +//! via the `CheckApp` supertrait bundle. +//! +//! See docs/reference/macros/cgp_namespace.md and +//! docs/reference/macros/delegate_components.md. + use cgp::core::error::{ErrorRaiserComponent, ErrorTypeProviderComponent, ErrorWrapperComponent}; use cgp::extra::error::RaiseFrom; use cgp::extra::handler::CanTryCompute; use cgp::prelude::*; use cgp_macro_test_util::snapshot_delegate_components; -use cgp_tests::namespaces::extended::ExtendedNamespace; + +// The path-rewriting namespace lives in the sibling `extended` module (moved here +// from `cgp_tests::namespaces::extended`). +use crate::namespaces::extended::ExtendedNamespace; pub struct App; diff --git a/crates/tests/cgp-tests/tests/namespaces/mod.rs b/crates/tests/cgp-tests/tests/namespaces/mod.rs new file mode 100644 index 00000000..6e81d1a5 --- /dev/null +++ b/crates/tests/cgp-tests/tests/namespaces/mod.rs @@ -0,0 +1,32 @@ +//! One unit test per file. Each file is self-contained: it defines its own +//! components, providers, namespaces, and context types at module scope so that +//! the type-level wiring of one test never leaks into another. + +// `cgp_namespace!` expansion snapshots (this concept owns the macro): the basic +// form, the two path-segment shapes (`Type` vs. `symbol`), and multiple +// namespaces attached to one component. +pub mod namespace_basic; +pub mod namespace_multi; +pub mod namespace_symbol_path; +pub mod namespace_type_path; + +// `#[prefix(...)]` + `namespace`/`@`-path wiring snapshots (this concept owns the +// namespace forms of `delegate_components!`): attaching components to +// `DefaultNamespace` and wiring a context through `@`-paths, `RedirectLookup` +// redirection, `open` per-value dispatch, and array/group namespace keys. +pub mod multi_param_namespace; +pub mod multi_param_open; +pub mod namespace_group; +pub mod open_dispatch; +pub mod prefix_default_namespace; +pub mod redirect_lookup; + +// Namespace inheritance and per-type default impls. `default_impls` and +// `extended` define reusable namespaces/providers (with `cgp_namespace!`, +// `#[prefix]`, and `#[default_impl]` snapshots); the `*_wiring` modules consume +// them from sibling modules to exercise the `for <..> in ..` loop, `DefaultImpls1`, +// and namespace inheritance in `delegate_components!`. +pub mod default_impls; +pub mod default_impls_wiring; +pub mod extended; +pub mod extended_namespace_wiring; diff --git a/crates/tests/cgp-tests/tests/namespaces/multi_param_namespace.rs b/crates/tests/cgp-tests/tests/namespaces/multi_param_namespace.rs new file mode 100644 index 00000000..c4c73b73 --- /dev/null +++ b/crates/tests/cgp-tests/tests/namespaces/multi_param_namespace.rs @@ -0,0 +1,139 @@ +//! Namespace `@`-path wiring for a component with a lifetime and many parameters. +//! +//! `Foo<'a, T, U>` attaches to `DefaultNamespace` via `#[prefix(@app in ..)]`, and +//! a context wires it through full paths that pin the leading path parameters, +//! including a generic key (`@app.FooProviderComponent.bool. T: DummyFoo`). This +//! is the namespace counterpart to `multi_param_open`, dispatching the same +//! multi-parameter component through a joined namespace instead of `open`. The +//! namespace `delegate_components!` snapshot is the canonical golden output; the +//! component and provider are incidental scaffolding. +//! +//! See docs/reference/traits/default_namespace.md and +//! docs/reference/macros/delegate_components.md. + +use cgp::prelude::*; +use cgp_macro_test_util::snapshot_delegate_components; + +// Incidental component, attached to `DefaultNamespace` so it can be wired by path. +#[cgp_component(FooProvider)] +#[prefix(@app in DefaultNamespace)] +pub trait Foo<'a, T, U> { + fn foo(&self, first: &'a T, second: U); +} + +// Incidental: a plain per-value provider. +#[cgp_impl(new DummyFoo)] +impl<'a, T, U> FooProvider<'a, T, U> { + fn foo(&self, _first: &'a T, _second: U) {} +} + +pub struct AppB; + +snapshot_delegate_components! { + delegate_components! { + AppB { + namespace DefaultNamespace; + + @app.FooProviderComponent.String.u64: + DummyFoo, + @app.FooProviderComponent.bool. T: + DummyFoo, + } + } + + expand_multi_param_app_b(output) { + insta::assert_snapshot!(output, @" + impl<__Key__, __Value__> DelegateComponent<__Key__> for AppB + where + __Key__: DefaultNamespace, + { + type Delegate = __Value__; + } + impl< + __Key__, + __Value__, + __Context__, + __Params__, + > IsProviderFor<__Key__, __Context__, __Params__> for AppB + where + __Key__: DefaultNamespace, + __Value__: IsProviderFor<__Key__, __Context__, __Params__>, + {} + impl< + __Wildcard__, + > DelegateComponent< + PathCons< + Symbol<3, Chars<'a', Chars<'p', Chars<'p', Nil>>>>, + PathCons>>, + >, + > for AppB { + type Delegate = DummyFoo; + } + impl< + __Wildcard__, + __Context__, + __Params__, + > IsProviderFor< + PathCons< + Symbol<3, Chars<'a', Chars<'p', Chars<'p', Nil>>>>, + PathCons>>, + >, + __Context__, + __Params__, + > for AppB + where + DummyFoo: IsProviderFor< + PathCons< + Symbol<3, Chars<'a', Chars<'p', Chars<'p', Nil>>>>, + PathCons>>, + >, + __Context__, + __Params__, + >, + {} + impl< + T, + __Wildcard__, + > DelegateComponent< + PathCons< + Symbol<3, Chars<'a', Chars<'p', Chars<'p', Nil>>>>, + PathCons>>, + >, + > for AppB { + type Delegate = DummyFoo; + } + impl< + T, + __Wildcard__, + __Context__, + __Params__, + > IsProviderFor< + PathCons< + Symbol<3, Chars<'a', Chars<'p', Chars<'p', Nil>>>>, + PathCons>>, + >, + __Context__, + __Params__, + > for AppB + where + DummyFoo: IsProviderFor< + PathCons< + Symbol<3, Chars<'a', Chars<'p', Chars<'p', Nil>>>>, + PathCons>>, + >, + __Context__, + __Params__, + >, + {} + ") + } +} + +check_components! { + <'a> AppB { + FooProviderComponent: [ + (Life<'a>, String, u64), + (Life<'a>, bool, String), + ], + } +} diff --git a/crates/tests/cgp-tests/tests/namespaces/multi_param_open.rs b/crates/tests/cgp-tests/tests/namespaces/multi_param_open.rs new file mode 100644 index 00000000..8f8b6c3a --- /dev/null +++ b/crates/tests/cgp-tests/tests/namespaces/multi_param_open.rs @@ -0,0 +1,117 @@ +//! `open` dispatch for a component with a lifetime and multiple type parameters. +//! +//! `Foo<'a, T, U>` is opened with `open { FooProviderComponent };` and dispatched +//! by its two leading path parameters, including a generic key +//! (` @FooProviderComponent.bool.T: DummyFoo`). This shows `open` handling the +//! multi-parameter path (the lifetime is lifted into `Life<'a>` in the check keys, +//! not the delegate path). The `delegate_components!` snapshot is the canonical +//! `open` golden output; the component and provider are incidental scaffolding. +//! +//! See docs/reference/macros/delegate_components.md. + +use cgp::prelude::*; +use cgp_macro_test_util::snapshot_delegate_components; + +// Incidental: a plain generic component dispatched via `open` below. +#[cgp_component(FooProvider)] +pub trait Foo<'a, T, U> { + fn foo(&self, first: &'a T, second: U); +} + +// Incidental: a plain per-value provider. +#[cgp_impl(new DummyFoo)] +impl<'a, T, U> FooProvider<'a, T, U> { + fn foo(&self, _first: &'a T, _second: U) {} +} + +pub struct AppA; + +snapshot_delegate_components! { + delegate_components! { + AppA { + open {FooProviderComponent}; + + @FooProviderComponent.String.u32: + DummyFoo, + @FooProviderComponent.bool.T: + DummyFoo, + } + } + + expand_multi_param_app_a(output) { + insta::assert_snapshot!(output, @" + impl DelegateComponent for AppA { + type Delegate = RedirectLookup>; + } + impl< + __Context__, + __Params__, + > IsProviderFor for AppA + where + RedirectLookup< + AppA, + PathCons, + >: IsProviderFor, + {} + impl< + __Wildcard__, + > DelegateComponent< + PathCons>>, + > for AppA { + type Delegate = DummyFoo; + } + impl< + __Wildcard__, + __Context__, + __Params__, + > IsProviderFor< + PathCons>>, + __Context__, + __Params__, + > for AppA + where + DummyFoo: IsProviderFor< + PathCons>>, + __Context__, + __Params__, + >, + {} + impl< + T, + __Wildcard__, + > DelegateComponent< + PathCons>>, + > for AppA { + type Delegate = DummyFoo; + } + impl< + T, + __Wildcard__, + __Context__, + __Params__, + > IsProviderFor< + PathCons>>, + __Context__, + __Params__, + > for AppA + where + DummyFoo: IsProviderFor< + PathCons>>, + __Context__, + __Params__, + >, + {} + ") + } +} + +check_components! { + AppA { + FooProviderComponent: [ + <'a> (Life<'a>, String, u32), + <'a> (Life<'a>, bool, String), + ], + FooProviderComponent: + <'a> (Life<'a>, bool, bool), + } +} diff --git a/crates/tests/cgp-tests/tests/namespace_tests/namespace_macro/basic.rs b/crates/tests/cgp-tests/tests/namespaces/namespace_basic.rs similarity index 56% rename from crates/tests/cgp-tests/tests/namespace_tests/namespace_macro/basic.rs rename to crates/tests/cgp-tests/tests/namespaces/namespace_basic.rs index 72f2f17c..9d0711fa 100644 --- a/crates/tests/cgp-tests/tests/namespace_tests/namespace_macro/basic.rs +++ b/crates/tests/cgp-tests/tests/namespaces/namespace_basic.rs @@ -1,84 +1,26 @@ +//! The basic `cgp_namespace!` form and wiring a context through a namespace. +//! +//! `cgp_namespace! { new MyNamespace { .. } }` builds a namespace trait/table, an +//! entry maps a component onto a `RedirectLookup` path, and a component attaches +//! itself to the namespace with `#[prefix(@MyBarComponent in MyNamespace)]`. A +//! context then wires through `namespace MyNamespace;` + `@`-path entries. The +//! `cgp_namespace!`, `#[prefix]`-component, and namespace `delegate_components!` +//! snapshots are the canonical golden output this concept owns; the plain `Foo` +//! component and the two provider impls are incidental scaffolding, written with +//! the plain macros (their expansion is pinned in `basic_delegation`). +//! +//! See docs/reference/macros/cgp_namespace.md and +//! docs/reference/macros/delegate_components.md. + +use cgp::prelude::*; use cgp_macro_test_util::{ - snapshot_cgp_component, snapshot_cgp_impl, snapshot_cgp_namespace, snapshot_check_components, - snapshot_delegate_components, + snapshot_cgp_component, snapshot_cgp_namespace, snapshot_delegate_components, }; -snapshot_cgp_component! { - #[cgp_component(FooProvider)] - pub trait Foo { - fn foo(&self); - } - - expand_basic_foo(output) { - insta::assert_snapshot!(output, @" - pub trait Foo { - fn foo(&self); - } - impl<__Context__> Foo for __Context__ - where - __Context__: FooProvider<__Context__>, - { - fn foo(&self) { - __Context__::foo(self) - } - } - pub trait FooProvider< - __Context__, - >: IsProviderFor { - fn foo(__context__: &__Context__); - } - impl<__Provider__, __Context__> FooProvider<__Context__> for __Provider__ - where - __Provider__: DelegateComponent - + IsProviderFor, - <__Provider__ as DelegateComponent< - FooProviderComponent, - >>::Delegate: FooProvider<__Context__>, - { - fn foo(__context__: &__Context__) { - <__Provider__ as DelegateComponent< - FooProviderComponent, - >>::Delegate::foo(__context__) - } - } - pub struct FooProviderComponent; - impl<__Context__> FooProvider<__Context__> for UseContext - where - __Context__: Foo, - { - fn foo(__context__: &__Context__) { - __Context__::foo(__context__) - } - } - impl<__Context__> IsProviderFor for UseContext - where - __Context__: Foo, - {} - impl<__Context__, __Components__, __Path__> FooProvider<__Context__> - for RedirectLookup<__Components__, __Path__> - where - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent<__Path__>>::Delegate: FooProvider<__Context__>, - { - fn foo(__context__: &__Context__) { - <__Components__ as DelegateComponent<__Path__>>::Delegate::foo(__context__) - } - } - impl< - __Context__, - __Components__, - __Path__, - > IsProviderFor - for RedirectLookup<__Components__, __Path__> - where - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent< - __Path__, - >>::Delegate: IsProviderFor - + FooProvider<__Context__>, - {} - ") - } +// Incidental: a plain component used only to be wired through the namespace. +#[cgp_component(FooProvider)] +pub trait Foo { + fn foo(&self); } snapshot_cgp_namespace! { @@ -190,38 +132,15 @@ snapshot_cgp_component! { pub struct MyFooComponent; pub struct MyBarComponent; -snapshot_cgp_impl! { - #[cgp_impl(new DummyFoo)] - impl FooProvider { - fn foo(&self) {} - } - - expand_basic_dummy_foo(output) { - insta::assert_snapshot!(output, @" - impl<__Context__> FooProvider<__Context__> for DummyFoo { - fn foo(__context__: &__Context__) {} - } - impl<__Context__> IsProviderFor for DummyFoo {} - pub struct DummyFoo; - ") - } +// Incidental: plain providers wired to the namespace below. +#[cgp_impl(new DummyFoo)] +impl FooProvider { + fn foo(&self) {} } -snapshot_cgp_impl! { - #[cgp_impl(new DummyBar)] - impl BarProvider { - fn bar(&self) {} - } - - expand_basic_dummy_bar(output) { - insta::assert_snapshot!(output, @" - impl<__Context__> BarProvider<__Context__> for DummyBar { - fn bar(__context__: &__Context__) {} - } - impl<__Context__> IsProviderFor for DummyBar {} - pub struct DummyBar; - ") - } +#[cgp_impl(new DummyBar)] +impl BarProvider { + fn bar(&self) {} } pub struct App; @@ -290,22 +209,9 @@ snapshot_delegate_components! { } } -snapshot_check_components! { - check_components! { - App { - FooProviderComponent, - BarProviderComponent, - } - } - - expand_check_app(output) { - insta::assert_snapshot!(output, @" - trait __CheckApp< - __Component__, - __Params__: ?Sized, - >: CanUseComponent<__Component__, __Params__> {} - impl __CheckApp for App {} - impl __CheckApp for App {} - ") +check_components! { + App { + FooProviderComponent, + BarProviderComponent, } } diff --git a/crates/tests/cgp-tests/tests/namespace_tests/group.rs b/crates/tests/cgp-tests/tests/namespaces/namespace_group.rs similarity index 86% rename from crates/tests/cgp-tests/tests/namespace_tests/group.rs rename to crates/tests/cgp-tests/tests/namespaces/namespace_group.rs index fa3157b1..8de8916b 100644 --- a/crates/tests/cgp-tests/tests/namespace_tests/group.rs +++ b/crates/tests/cgp-tests/tests/namespaces/namespace_group.rs @@ -1,6 +1,21 @@ -use cgp::prelude::{DefaultNamespace, cgp_component, cgp_impl, check_components}; +//! Array + group namespace keys: one entry wiring many components and values. +//! +//! Two generic components attach to `DefaultNamespace` via +//! `#[prefix(@app in DefaultNamespace)]`, and a single `delegate_components!` +//! entry wires both at once for two parameter values using array keys: +//! `@app.[FooProviderComponent, BarProviderComponent].[u64, String]: DummyImpl`. +//! The macro fans this out to one `DelegateComponent` impl per (component, value) +//! pair. The namespace `delegate_components!` snapshot is the canonical golden +//! output this concept owns; the components and `DummyImpl` are incidental +//! scaffolding written plainly. +//! +//! See docs/reference/macros/delegate_components.md and +//! docs/reference/traits/default_namespace.md. + +use cgp::prelude::*; use cgp_macro_test_util::snapshot_delegate_components; +// Incidental: two plain generic components attached to `DefaultNamespace`. #[cgp_component(FooProvider)] #[prefix(@app in DefaultNamespace)] pub trait Foo { diff --git a/crates/tests/cgp-tests/tests/namespace_tests/namespace_macro/multi_namespace.rs b/crates/tests/cgp-tests/tests/namespaces/namespace_multi.rs similarity index 70% rename from crates/tests/cgp-tests/tests/namespace_tests/namespace_macro/multi_namespace.rs rename to crates/tests/cgp-tests/tests/namespaces/namespace_multi.rs index 8e4904c3..e506374d 100644 --- a/crates/tests/cgp-tests/tests/namespace_tests/namespace_macro/multi_namespace.rs +++ b/crates/tests/cgp-tests/tests/namespaces/namespace_multi.rs @@ -1,86 +1,28 @@ +//! One component attached to two different namespaces at once. +//! +//! Two namespaces (`MyNamespace` with a type path `@MyApp.MyFooComponent`, and +//! `OtherNamespace` with a symbol path `@my_app.MyFooComponent`) are defined with +//! `cgp_namespace!`, and a single `Bar` component attaches to both by stacking two +//! `#[prefix(.. in ..)]` attributes — so its snapshot carries one namespace impl +//! per attribute. Two contexts then wire the same providers through the two +//! namespaces. The `cgp_namespace!`, `#[prefix]`-component, and namespace +//! `delegate_components!` snapshots are kept; the plain `Foo` component and the +//! provider impls are incidental scaffolding. +//! +//! See docs/reference/macros/cgp_namespace.md and +//! docs/reference/macros/delegate_components.md. + +use cgp::prelude::*; use cgp_macro_test_util::{ - snapshot_cgp_component, snapshot_cgp_impl, snapshot_cgp_namespace, snapshot_check_components, - snapshot_delegate_components, + snapshot_cgp_component, snapshot_cgp_namespace, snapshot_delegate_components, }; pub struct MyApp; -snapshot_cgp_component! { - #[cgp_component(FooProvider)] - pub trait Foo { - fn foo(&self); - } - - expand_multi_ns_foo(output) { - insta::assert_snapshot!(output, @" - pub trait Foo { - fn foo(&self); - } - impl<__Context__> Foo for __Context__ - where - __Context__: FooProvider<__Context__>, - { - fn foo(&self) { - __Context__::foo(self) - } - } - pub trait FooProvider< - __Context__, - >: IsProviderFor { - fn foo(__context__: &__Context__); - } - impl<__Provider__, __Context__> FooProvider<__Context__> for __Provider__ - where - __Provider__: DelegateComponent - + IsProviderFor, - <__Provider__ as DelegateComponent< - FooProviderComponent, - >>::Delegate: FooProvider<__Context__>, - { - fn foo(__context__: &__Context__) { - <__Provider__ as DelegateComponent< - FooProviderComponent, - >>::Delegate::foo(__context__) - } - } - pub struct FooProviderComponent; - impl<__Context__> FooProvider<__Context__> for UseContext - where - __Context__: Foo, - { - fn foo(__context__: &__Context__) { - __Context__::foo(__context__) - } - } - impl<__Context__> IsProviderFor for UseContext - where - __Context__: Foo, - {} - impl<__Context__, __Components__, __Path__> FooProvider<__Context__> - for RedirectLookup<__Components__, __Path__> - where - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent<__Path__>>::Delegate: FooProvider<__Context__>, - { - fn foo(__context__: &__Context__) { - <__Components__ as DelegateComponent<__Path__>>::Delegate::foo(__context__) - } - } - impl< - __Context__, - __Components__, - __Path__, - > IsProviderFor - for RedirectLookup<__Components__, __Path__> - where - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent< - __Path__, - >>::Delegate: IsProviderFor - + FooProvider<__Context__>, - {} - ") - } +// Incidental: a plain component used only to be wired through the namespaces. +#[cgp_component(FooProvider)] +pub trait Foo { + fn foo(&self); } snapshot_cgp_namespace! { @@ -245,38 +187,15 @@ pub struct MyFooComponent; pub struct MyBarComponent; -snapshot_cgp_impl! { - #[cgp_impl(new DummyFoo)] - impl FooProvider { - fn foo(&self) {} - } - - expand_multi_ns_dummy_foo(output) { - insta::assert_snapshot!(output, @" - impl<__Context__> FooProvider<__Context__> for DummyFoo { - fn foo(__context__: &__Context__) {} - } - impl<__Context__> IsProviderFor for DummyFoo {} - pub struct DummyFoo; - ") - } +// Incidental: plain providers wired to the namespaces below. +#[cgp_impl(new DummyFoo)] +impl FooProvider { + fn foo(&self) {} } -snapshot_cgp_impl! { - #[cgp_impl(new DummyBar)] - impl BarProvider { - fn bar(&self) {} - } - - expand_multi_ns_dummy_bar(output) { - insta::assert_snapshot!(output, @" - impl<__Context__> BarProvider<__Context__> for DummyBar { - fn bar(__context__: &__Context__) {} - } - impl<__Context__> IsProviderFor for DummyBar {} - pub struct DummyBar; - ") - } +#[cgp_impl(new DummyBar)] +impl BarProvider { + fn bar(&self) {} } pub struct App; @@ -357,23 +276,10 @@ snapshot_delegate_components! { } } -snapshot_check_components! { - check_components! { - App { - FooProviderComponent, - BarProviderComponent, - } - } - - expand_check_app(output) { - insta::assert_snapshot!(output, @" - trait __CheckApp< - __Component__, - __Params__: ?Sized, - >: CanUseComponent<__Component__, __Params__> {} - impl __CheckApp for App {} - impl __CheckApp for App {} - ") +check_components! { + App { + FooProviderComponent, + BarProviderComponent, } } @@ -501,22 +407,9 @@ snapshot_delegate_components! { } } -snapshot_check_components! { - check_components! { - OtherApp { - FooProviderComponent, - BarProviderComponent, - } - } - - expand_check_other_app(output) { - insta::assert_snapshot!(output, @" - trait __CheckOtherApp< - __Component__, - __Params__: ?Sized, - >: CanUseComponent<__Component__, __Params__> {} - impl __CheckOtherApp for OtherApp {} - impl __CheckOtherApp for OtherApp {} - ") +check_components! { + OtherApp { + FooProviderComponent, + BarProviderComponent, } } diff --git a/crates/tests/cgp-tests/tests/namespace_tests/namespace_macro/symbol_path.rs b/crates/tests/cgp-tests/tests/namespaces/namespace_symbol_path.rs similarity index 65% rename from crates/tests/cgp-tests/tests/namespace_tests/namespace_macro/symbol_path.rs rename to crates/tests/cgp-tests/tests/namespaces/namespace_symbol_path.rs index 604d8464..f4028885 100644 --- a/crates/tests/cgp-tests/tests/namespace_tests/namespace_macro/symbol_path.rs +++ b/crates/tests/cgp-tests/tests/namespaces/namespace_symbol_path.rs @@ -1,84 +1,24 @@ +//! A namespace whose path segment is a lowercase identifier (a `Symbol!` string). +//! +//! When a `cgp_namespace!` / `#[prefix]` path segment is a snake_case name like +//! `my_app`, the macro encodes it as a type-level `Symbol<..>` string rather than +//! a struct type (contrast `namespace_type_path`, where `MyApp` is a type). The +//! `cgp_namespace!`, `#[prefix]`-component, and namespace `delegate_components!` +//! snapshots pin that encoding; the plain `Foo` component and the provider impls +//! are incidental scaffolding written with the plain macros. +//! +//! See docs/reference/macros/cgp_namespace.md and +//! docs/reference/providers/redirect_lookup.md. + +use cgp::prelude::*; use cgp_macro_test_util::{ - snapshot_cgp_component, snapshot_cgp_impl, snapshot_cgp_namespace, snapshot_check_components, - snapshot_delegate_components, + snapshot_cgp_component, snapshot_cgp_namespace, snapshot_delegate_components, }; -snapshot_cgp_component! { - #[cgp_component(FooProvider)] - pub trait Foo { - fn foo(&self); - } - - expand_symbol_path_foo(output) { - insta::assert_snapshot!(output, @" - pub trait Foo { - fn foo(&self); - } - impl<__Context__> Foo for __Context__ - where - __Context__: FooProvider<__Context__>, - { - fn foo(&self) { - __Context__::foo(self) - } - } - pub trait FooProvider< - __Context__, - >: IsProviderFor { - fn foo(__context__: &__Context__); - } - impl<__Provider__, __Context__> FooProvider<__Context__> for __Provider__ - where - __Provider__: DelegateComponent - + IsProviderFor, - <__Provider__ as DelegateComponent< - FooProviderComponent, - >>::Delegate: FooProvider<__Context__>, - { - fn foo(__context__: &__Context__) { - <__Provider__ as DelegateComponent< - FooProviderComponent, - >>::Delegate::foo(__context__) - } - } - pub struct FooProviderComponent; - impl<__Context__> FooProvider<__Context__> for UseContext - where - __Context__: Foo, - { - fn foo(__context__: &__Context__) { - __Context__::foo(__context__) - } - } - impl<__Context__> IsProviderFor for UseContext - where - __Context__: Foo, - {} - impl<__Context__, __Components__, __Path__> FooProvider<__Context__> - for RedirectLookup<__Components__, __Path__> - where - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent<__Path__>>::Delegate: FooProvider<__Context__>, - { - fn foo(__context__: &__Context__) { - <__Components__ as DelegateComponent<__Path__>>::Delegate::foo(__context__) - } - } - impl< - __Context__, - __Components__, - __Path__, - > IsProviderFor - for RedirectLookup<__Components__, __Path__> - where - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent< - __Path__, - >>::Delegate: IsProviderFor - + FooProvider<__Context__>, - {} - ") - } +// Incidental: a plain component used only to be wired through the namespace. +#[cgp_component(FooProvider)] +pub trait Foo { + fn foo(&self); } snapshot_cgp_namespace! { @@ -212,38 +152,15 @@ pub struct MyFooComponent; pub struct MyBarComponent; -snapshot_cgp_impl! { - #[cgp_impl(new DummyFoo)] - impl FooProvider { - fn foo(&self) {} - } - - expand_symbol_path_dummy_foo(output) { - insta::assert_snapshot!(output, @" - impl<__Context__> FooProvider<__Context__> for DummyFoo { - fn foo(__context__: &__Context__) {} - } - impl<__Context__> IsProviderFor for DummyFoo {} - pub struct DummyFoo; - ") - } +// Incidental: plain providers wired to the namespace below. +#[cgp_impl(new DummyFoo)] +impl FooProvider { + fn foo(&self) {} } -snapshot_cgp_impl! { - #[cgp_impl(new DummyBar)] - impl BarProvider { - fn bar(&self) {} - } - - expand_symbol_path_dummy_bar(output) { - insta::assert_snapshot!(output, @" - impl<__Context__> BarProvider<__Context__> for DummyBar { - fn bar(__context__: &__Context__) {} - } - impl<__Context__> IsProviderFor for DummyBar {} - pub struct DummyBar; - ") - } +#[cgp_impl(new DummyBar)] +impl BarProvider { + fn bar(&self) {} } pub struct App; @@ -370,22 +287,9 @@ snapshot_delegate_components! { } } -snapshot_check_components! { - check_components! { - App { - FooProviderComponent, - BarProviderComponent, - } - } - - expand_check_app(output) { - insta::assert_snapshot!(output, @" - trait __CheckApp< - __Component__, - __Params__: ?Sized, - >: CanUseComponent<__Component__, __Params__> {} - impl __CheckApp for App {} - impl __CheckApp for App {} - ") +check_components! { + App { + FooProviderComponent, + BarProviderComponent, } } diff --git a/crates/tests/cgp-tests/tests/namespace_tests/namespace_macro/type_path.rs b/crates/tests/cgp-tests/tests/namespaces/namespace_type_path.rs similarity index 58% rename from crates/tests/cgp-tests/tests/namespace_tests/namespace_macro/type_path.rs rename to crates/tests/cgp-tests/tests/namespaces/namespace_type_path.rs index d432fcf5..6bfa198b 100644 --- a/crates/tests/cgp-tests/tests/namespace_tests/namespace_macro/type_path.rs +++ b/crates/tests/cgp-tests/tests/namespaces/namespace_type_path.rs @@ -1,86 +1,26 @@ +//! A namespace whose path segment is a type (a struct), not a `Symbol!` string. +//! +//! When a `cgp_namespace!` / `#[prefix]` path segment is a PascalCase name like +//! `MyApp` that resolves to a type, the macro places the type directly into the +//! `PathCons<..>` path (contrast `namespace_symbol_path`, where `my_app` becomes a +//! type-level `Symbol<..>` string). The `cgp_namespace!`, `#[prefix]`-component, +//! and namespace `delegate_components!` snapshots pin that encoding; the plain +//! `Foo` component and the provider impls are incidental scaffolding. +//! +//! See docs/reference/macros/cgp_namespace.md and +//! docs/reference/providers/redirect_lookup.md. + +use cgp::prelude::*; use cgp_macro_test_util::{ - snapshot_cgp_component, snapshot_cgp_impl, snapshot_cgp_namespace, snapshot_check_components, - snapshot_delegate_components, + snapshot_cgp_component, snapshot_cgp_namespace, snapshot_delegate_components, }; pub struct MyApp; -snapshot_cgp_component! { - #[cgp_component(FooProvider)] - pub trait Foo { - fn foo(&self); - } - - expand_type_path_foo(output) { - insta::assert_snapshot!(output, @" - pub trait Foo { - fn foo(&self); - } - impl<__Context__> Foo for __Context__ - where - __Context__: FooProvider<__Context__>, - { - fn foo(&self) { - __Context__::foo(self) - } - } - pub trait FooProvider< - __Context__, - >: IsProviderFor { - fn foo(__context__: &__Context__); - } - impl<__Provider__, __Context__> FooProvider<__Context__> for __Provider__ - where - __Provider__: DelegateComponent - + IsProviderFor, - <__Provider__ as DelegateComponent< - FooProviderComponent, - >>::Delegate: FooProvider<__Context__>, - { - fn foo(__context__: &__Context__) { - <__Provider__ as DelegateComponent< - FooProviderComponent, - >>::Delegate::foo(__context__) - } - } - pub struct FooProviderComponent; - impl<__Context__> FooProvider<__Context__> for UseContext - where - __Context__: Foo, - { - fn foo(__context__: &__Context__) { - __Context__::foo(__context__) - } - } - impl<__Context__> IsProviderFor for UseContext - where - __Context__: Foo, - {} - impl<__Context__, __Components__, __Path__> FooProvider<__Context__> - for RedirectLookup<__Components__, __Path__> - where - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent<__Path__>>::Delegate: FooProvider<__Context__>, - { - fn foo(__context__: &__Context__) { - <__Components__ as DelegateComponent<__Path__>>::Delegate::foo(__context__) - } - } - impl< - __Context__, - __Components__, - __Path__, - > IsProviderFor - for RedirectLookup<__Components__, __Path__> - where - __Components__: DelegateComponent<__Path__>, - <__Components__ as DelegateComponent< - __Path__, - >>::Delegate: IsProviderFor - + FooProvider<__Context__>, - {} - ") - } +// Incidental: a plain component used only to be wired through the namespace. +#[cgp_component(FooProvider)] +pub trait Foo { + fn foo(&self); } snapshot_cgp_namespace! { @@ -196,38 +136,15 @@ pub struct MyFooComponent; pub struct MyBarComponent; -snapshot_cgp_impl! { - #[cgp_impl(new DummyFoo)] - impl FooProvider { - fn foo(&self) {} - } - - expand_type_path_dummy_foo(output) { - insta::assert_snapshot!(output, @" - impl<__Context__> FooProvider<__Context__> for DummyFoo { - fn foo(__context__: &__Context__) {} - } - impl<__Context__> IsProviderFor for DummyFoo {} - pub struct DummyFoo; - ") - } +// Incidental: plain providers wired to the namespace below. +#[cgp_impl(new DummyFoo)] +impl FooProvider { + fn foo(&self) {} } -snapshot_cgp_impl! { - #[cgp_impl(new DummyBar)] - impl BarProvider { - fn bar(&self) {} - } - - expand_type_path_dummy_bar(output) { - insta::assert_snapshot!(output, @" - impl<__Context__> BarProvider<__Context__> for DummyBar { - fn bar(__context__: &__Context__) {} - } - impl<__Context__> IsProviderFor for DummyBar {} - pub struct DummyBar; - ") - } +#[cgp_impl(new DummyBar)] +impl BarProvider { + fn bar(&self) {} } pub struct App; @@ -308,22 +225,9 @@ snapshot_delegate_components! { } } -snapshot_check_components! { - check_components! { - App { - FooProviderComponent, - BarProviderComponent, - } - } - - expand_check_app(output) { - insta::assert_snapshot!(output, @" - trait __CheckApp< - __Component__, - __Params__: ?Sized, - >: CanUseComponent<__Component__, __Params__> {} - impl __CheckApp for App {} - impl __CheckApp for App {} - ") +check_components! { + App { + FooProviderComponent, + BarProviderComponent, } } diff --git a/crates/tests/cgp-tests/tests/namespaces/open_dispatch.rs b/crates/tests/cgp-tests/tests/namespaces/open_dispatch.rs new file mode 100644 index 00000000..c01fe24a --- /dev/null +++ b/crates/tests/cgp-tests/tests/namespaces/open_dispatch.rs @@ -0,0 +1,236 @@ +//! The `open` statement: per-value dispatch of a generic-parameter component. +//! +//! `open { FooProviderComponent, BarProviderComponent };` opens two generic +//! components for per-value wiring, then `@Component.Key: Provider` entries assign +//! a provider per value of the dispatch parameter (a brace group, +//! `@BarProviderComponent.{u32, u64, ..}: DummyBar`, shares one provider across +//! several values). `open` is a lightweight special case of `RedirectLookup`, so +//! each opened component gets a `DelegateComponent` pointing at +//! `RedirectLookup>`. The `delegate_components!` +//! snapshot is the canonical `open` golden output this concept owns; the two +//! components and their providers are incidental scaffolding written plainly. +//! +//! See docs/reference/macros/delegate_components.md and +//! docs/reference/providers/redirect_lookup.md. + +use cgp::prelude::*; +use cgp_macro_test_util::snapshot_delegate_components; + +pub struct App; + +// Incidental: two plain generic components dispatched via `open` below. +#[cgp_component(FooProvider)] +pub trait Foo { + fn foo(&self, value: &T); +} + +#[cgp_component(BarProvider)] +pub trait Bar { + fn bar(&self, value: &T); +} + +// Incidental: plain per-value providers for the two components. +#[cgp_impl(new DummyFoo)] +impl FooProvider { + fn foo(&self, _value: &T) {} +} + +#[cgp_impl(new DummyBar)] +impl BarProvider { + fn bar(&self, _value: &T) {} +} + +snapshot_delegate_components! { + delegate_components! { + App { + open {FooProviderComponent, BarProviderComponent}; + + // FooProviderComponent => + // @FooProviderComponent, + // BarProviderComponent => + // @BarProviderComponent, + + @FooProviderComponent.String: + DummyFoo, + @BarProviderComponent.{u32, u64, bool, usize, isize}: + DummyBar, + } + } + + expand_open_app(output) { + insta::assert_snapshot!(output, @" + impl DelegateComponent for App { + type Delegate = RedirectLookup>; + } + impl< + __Context__, + __Params__, + > IsProviderFor for App + where + RedirectLookup< + App, + PathCons, + >: IsProviderFor, + {} + impl DelegateComponent for App { + type Delegate = RedirectLookup>; + } + impl< + __Context__, + __Params__, + > IsProviderFor for App + where + RedirectLookup< + App, + PathCons, + >: IsProviderFor, + {} + impl< + __Wildcard__, + > DelegateComponent>> + for App { + type Delegate = DummyFoo; + } + impl< + __Wildcard__, + __Context__, + __Params__, + > IsProviderFor< + PathCons>, + __Context__, + __Params__, + > for App + where + DummyFoo: IsProviderFor< + PathCons>, + __Context__, + __Params__, + >, + {} + impl< + __Wildcard__, + > DelegateComponent>> + for App { + type Delegate = DummyBar; + } + impl< + __Wildcard__, + __Context__, + __Params__, + > IsProviderFor< + PathCons>, + __Context__, + __Params__, + > for App + where + DummyBar: IsProviderFor< + PathCons>, + __Context__, + __Params__, + >, + {} + impl< + __Wildcard__, + > DelegateComponent>> + for App { + type Delegate = DummyBar; + } + impl< + __Wildcard__, + __Context__, + __Params__, + > IsProviderFor< + PathCons>, + __Context__, + __Params__, + > for App + where + DummyBar: IsProviderFor< + PathCons>, + __Context__, + __Params__, + >, + {} + impl< + __Wildcard__, + > DelegateComponent>> + for App { + type Delegate = DummyBar; + } + impl< + __Wildcard__, + __Context__, + __Params__, + > IsProviderFor< + PathCons>, + __Context__, + __Params__, + > for App + where + DummyBar: IsProviderFor< + PathCons>, + __Context__, + __Params__, + >, + {} + impl< + __Wildcard__, + > DelegateComponent>> + for App { + type Delegate = DummyBar; + } + impl< + __Wildcard__, + __Context__, + __Params__, + > IsProviderFor< + PathCons>, + __Context__, + __Params__, + > for App + where + DummyBar: IsProviderFor< + PathCons>, + __Context__, + __Params__, + >, + {} + impl< + __Wildcard__, + > DelegateComponent>> + for App { + type Delegate = DummyBar; + } + impl< + __Wildcard__, + __Context__, + __Params__, + > IsProviderFor< + PathCons>, + __Context__, + __Params__, + > for App + where + DummyBar: IsProviderFor< + PathCons>, + __Context__, + __Params__, + >, + {} + ") + } +} + +check_components! { + App { + FooProviderComponent: + String, + BarProviderComponent: [ + u32, + u64, + bool, + usize, + isize, + ], + } +} diff --git a/crates/tests/cgp-tests/tests/namespace_tests/namespace.rs b/crates/tests/cgp-tests/tests/namespaces/prefix_default_namespace.rs similarity index 90% rename from crates/tests/cgp-tests/tests/namespace_tests/namespace.rs rename to crates/tests/cgp-tests/tests/namespaces/prefix_default_namespace.rs index 96f69787..2c27b6c6 100644 --- a/crates/tests/cgp-tests/tests/namespace_tests/namespace.rs +++ b/crates/tests/cgp-tests/tests/namespaces/prefix_default_namespace.rs @@ -1,9 +1,21 @@ +//! Attaching a component to `DefaultNamespace` and wiring a context by full path. +//! +//! `#[prefix(@app.MyComponents.FooProviderComponent in DefaultNamespace)]` gives +//! `Foo` a nested default path, so its snapshot carries a +//! `DefaultNamespace<..> for FooProviderComponent` impl targeting that +//! `RedirectLookup` path. A context then opens `namespace DefaultNamespace;` and +//! wires the CGP-shipped error components by their full dotted paths +//! (`@cgp.core.error.ErrorTypeProviderComponent`, `.ErrorRaiserComponent.String`). +//! The `#[prefix]`-component and namespace `delegate_components!` snapshots are the +//! canonical golden output this concept owns. +//! +//! See docs/reference/traits/default_namespace.md and +//! docs/reference/macros/delegate_components.md. + use cgp::core::error::{ErrorRaiserComponent, ErrorTypeProviderComponent}; use cgp::extra::error::ReturnError; use cgp::prelude::*; -use cgp_macro_test_util::{ - snapshot_cgp_component, snapshot_check_components, snapshot_delegate_components, -}; +use cgp_macro_test_util::{snapshot_cgp_component, snapshot_delegate_components}; pub struct MyComponents; @@ -254,20 +266,8 @@ snapshot_delegate_components! { } } -snapshot_check_components! { - check_components! { - App { - ErrorRaiserComponent: String, - } - } - - expand_check_app(output) { - insta::assert_snapshot!(output, @" - trait __CheckApp< - __Component__, - __Params__: ?Sized, - >: CanUseComponent<__Component__, __Params__> {} - impl __CheckApp for App {} - ") +check_components! { + App { + ErrorRaiserComponent: String, } } diff --git a/crates/tests/cgp-tests/tests/namespace_tests/redirect.rs b/crates/tests/cgp-tests/tests/namespaces/redirect_lookup.rs similarity index 83% rename from crates/tests/cgp-tests/tests/namespace_tests/redirect.rs rename to crates/tests/cgp-tests/tests/namespaces/redirect_lookup.rs index 527e4cf2..3884100a 100644 --- a/crates/tests/cgp-tests/tests/namespace_tests/redirect.rs +++ b/crates/tests/cgp-tests/tests/namespaces/redirect_lookup.rs @@ -1,8 +1,18 @@ +//! `RedirectLookup` re-routing a component lookup along a `#[prefix]` path. +//! +//! `#[prefix(@bar.baz in DefaultNamespace)]` makes `CanDoFoo`'s default lookup +//! redirect through `RedirectLookup`, +//! so its snapshot carries the `DefaultNamespace<..> for FooProviderComponent` impl +//! that names that path. A context then wires the whole `@bar.baz` prefix to a +//! provider in one entry, and the provider is reached via the redirect. The +//! `#[prefix]`-component and namespace `delegate_components!` snapshots are the +//! canonical golden output; the `TestProvider` impl is incidental scaffolding. +//! +//! See docs/reference/providers/redirect_lookup.md and +//! docs/reference/traits/default_namespace.md. + use cgp::prelude::*; -use cgp_macro_test_util::{ - snapshot_cgp_component, snapshot_cgp_impl, snapshot_check_components, - snapshot_delegate_components, -}; +use cgp_macro_test_util::{snapshot_cgp_component, snapshot_delegate_components}; snapshot_cgp_component! { #[cgp_component(FooProvider)] @@ -97,21 +107,10 @@ pub struct BarComponent; pub struct BazComponent; -snapshot_cgp_impl! { - #[cgp_impl(new TestProvider)] - impl FooProvider { - fn foo() {} - } - - expand_redirect_test_provider(output) { - insta::assert_snapshot!(output, @" - impl<__Context__> FooProvider<__Context__> for TestProvider { - fn foo() {} - } - impl<__Context__> IsProviderFor for TestProvider {} - pub struct TestProvider; - ") - } +// Incidental: a plain provider reached through the redirect. +#[cgp_impl(new TestProvider)] +impl FooProvider { + fn foo() {} } pub struct App; @@ -183,20 +182,8 @@ snapshot_delegate_components! { } } -snapshot_check_components! { - check_components! { - App { - FooProviderComponent, - } - } - - expand_check_app(output) { - insta::assert_snapshot!(output, @" - trait __CheckApp< - __Component__, - __Params__: ?Sized, - >: CanUseComponent<__Component__, __Params__> {} - impl __CheckApp for App {} - ") +check_components! { + App { + FooProviderComponent, } } diff --git a/crates/tests/cgp-tests/tests/namespaces_tests.rs b/crates/tests/cgp-tests/tests/namespaces_tests.rs new file mode 100644 index 00000000..0c3655ff --- /dev/null +++ b/crates/tests/cgp-tests/tests/namespaces_tests.rs @@ -0,0 +1,22 @@ +//! Entrypoint for the `namespaces` concept. +//! +//! Covers CGP's namespace feature: reusable, inheritable wiring tables built +//! with `cgp_namespace!`, components attached to a namespace with `#[prefix(...)]`, +//! the `namespace`/`open` statements inside `delegate_components!`, the +//! `RedirectLookup` provider that re-routes a lookup along a type-level `Path!`, +//! the `DefaultNamespace` trait for default provider resolution, and namespace +//! inheritance (`cgp_namespace! { new Child: Parent { .. } }`, `DefaultImpls1`, +//! and the `for <..> in ..` default-impl loop). +//! +//! This concept owns the canonical macro-expansion snapshots for `cgp_namespace!` +//! and for the namespace/`open`/`#[prefix]`/`@`-path forms of +//! `delegate_components!`; incidental uses of other macros are written plainly, +//! since their expansion is pinned in their owning target. +//! +//! See docs/concepts/namespaces.md, docs/reference/macros/cgp_namespace.md, +//! docs/reference/macros/delegate_components.md, +//! docs/reference/providers/redirect_lookup.md, and +//! docs/reference/traits/default_namespace.md. +#![allow(dead_code)] + +pub mod namespaces; diff --git a/docs/concepts/check-traits.md b/docs/concepts/check-traits.md index 61835757..17aead71 100644 --- a/docs/concepts/check-traits.md +++ b/docs/concepts/check-traits.md @@ -53,4 +53,4 @@ Check traits verify the wiring produced by [`delegate_components!`](../reference ## Source -The runtime traits live in [crates/core/cgp-component/src/traits/](../../crates/core/cgp-component/src/traits/) (`CanUseComponent` in `can_use_component.rs`, `IsProviderFor` in `is_provider.rs`, `DelegateComponent` in `delegate_component.rs`); the check-table macros are in [crates/macros/cgp-macro-core/src/types/check_components/](../../crates/macros/cgp-macro-core/src/types/check_components/) and [delegate_and_check_components/](../../crates/macros/cgp-macro-core/src/types/delegate_and_check_components/), with expansion snapshots in [crates/tests/cgp-tests/src/tests/check_components.rs](../../crates/tests/cgp-tests/src/tests/check_components.rs). +The runtime traits live in [crates/core/cgp-component/src/traits/](../../crates/core/cgp-component/src/traits/) (`CanUseComponent` in `can_use_component.rs`, `IsProviderFor` in `is_provider.rs`, `DelegateComponent` in `delegate_component.rs`); the check-table macros are in [crates/macros/cgp-macro-core/src/types/check_components/](../../crates/macros/cgp-macro-core/src/types/check_components/) and [delegate_and_check_components/](../../crates/macros/cgp-macro-core/src/types/delegate_and_check_components/), with expansion snapshots in [crates/tests/cgp-tests/tests/checking/](../../crates/tests/cgp-tests/tests/checking/). diff --git a/docs/concepts/dispatching.md b/docs/concepts/dispatching.md index 0bd055ac..109e2ec7 100644 --- a/docs/concepts/dispatching.md +++ b/docs/concepts/dispatching.md @@ -36,4 +36,4 @@ The providers that implement this pattern are catalogued in [`dispatch_combinato ## Source -The dispatching pattern is implemented entirely by the providers in [crates/extra/cgp-dispatch/src/providers/](../../crates/extra/cgp-dispatch/src/providers/), which build on the extractor and builder traits in [crates/core/cgp-field/src/traits/](../../crates/core/cgp-field/src/traits/) and the handler components in [crates/extra/cgp-handler/src/](../../crates/extra/cgp-handler/src/). The matcher loop reuses the monadic pipeline from [crates/extra/cgp-monad/src/](../../crates/extra/cgp-monad/src/). Tests covering both directions are in [crates/tests/cgp-tests/tests/extensible_data_tests/](../../crates/tests/cgp-tests/tests/extensible_data_tests/), and the macro that generates dispatch wiring is exercised in [crates/tests/cgp-tests/tests/dispatcher_macro_tests/](../../crates/tests/cgp-tests/tests/dispatcher_macro_tests/). +The dispatching pattern is implemented entirely by the providers in [crates/extra/cgp-dispatch/src/providers/](../../crates/extra/cgp-dispatch/src/providers/), which build on the extractor and builder traits in [crates/core/cgp-field/src/traits/](../../crates/core/cgp-field/src/traits/) and the handler components in [crates/extra/cgp-handler/src/](../../crates/extra/cgp-handler/src/). The matcher loop reuses the monadic pipeline from [crates/extra/cgp-monad/src/](../../crates/extra/cgp-monad/src/). Tests covering both directions are in [crates/tests/cgp-tests/tests/extensible_records/](../../crates/tests/cgp-tests/tests/extensible_records/) and [crates/tests/cgp-tests/tests/extensible_variants/](../../crates/tests/cgp-tests/tests/extensible_variants/), and the macro that generates dispatch wiring is exercised in [crates/tests/cgp-tests/tests/dispatching/](../../crates/tests/cgp-tests/tests/dispatching/). diff --git a/docs/concepts/extensible-records.md b/docs/concepts/extensible-records.md index 10843bc6..520cf47a 100644 --- a/docs/concepts/extensible-records.md +++ b/docs/concepts/extensible-records.md @@ -72,4 +72,4 @@ Extensible records are the product half of CGP's extensible data; the sum half i ## Source -The record traits live in [crates/core/cgp-field/src/traits/](../../crates/core/cgp-field/src/traits/) (`has_builder.rs`, `build_field.rs`, `update_field.rs`, `take_field.rs`) with the merge logic in [crates/core/cgp-field/src/impls/build_from.rs](../../crates/core/cgp-field/src/impls/build_from.rs) and the `MapType` markers in [crates/core/cgp-field/src/impls/map_type.rs](../../crates/core/cgp-field/src/impls/map_type.rs). The builder dispatchers are in [crates/extra/cgp-dispatch/src/providers/](../../crates/extra/cgp-dispatch/src/providers/). End-to-end tests are under [crates/tests/cgp-tests/tests/extensible_data_tests/records/](../../crates/tests/cgp-tests/tests/extensible_data_tests/records/). +The record traits live in [crates/core/cgp-field/src/traits/](../../crates/core/cgp-field/src/traits/) (`has_builder.rs`, `build_field.rs`, `update_field.rs`, `take_field.rs`) with the merge logic in [crates/core/cgp-field/src/impls/build_from.rs](../../crates/core/cgp-field/src/impls/build_from.rs) and the `MapType` markers in [crates/core/cgp-field/src/impls/map_type.rs](../../crates/core/cgp-field/src/impls/map_type.rs). The builder dispatchers are in [crates/extra/cgp-dispatch/src/providers/](../../crates/extra/cgp-dispatch/src/providers/). End-to-end tests are under [crates/tests/cgp-tests/tests/extensible_records/](../../crates/tests/cgp-tests/tests/extensible_records/). diff --git a/docs/concepts/extensible-variants.md b/docs/concepts/extensible-variants.md index 059db6af..907af907 100644 --- a/docs/concepts/extensible-variants.md +++ b/docs/concepts/extensible-variants.md @@ -85,4 +85,4 @@ Extensible variants are the sum half of CGP's extensible data; the product half ## Source -The variant traits live in [crates/core/cgp-field/src/traits/](../../crates/core/cgp-field/src/traits/) (`extract_field.rs`, `from_variant.rs`) with `HasExtractor`, `FinalizeExtract`, and the casting recursion in [crates/core/cgp-field/src/impls/cast.rs](../../crates/core/cgp-field/src/impls/cast.rs), and the `IsVoid` marker in [crates/core/cgp-field/src/impls/map_type.rs](../../crates/core/cgp-field/src/impls/map_type.rs). The visitor dispatchers and their monadic pipeline are in [crates/extra/cgp-dispatch/src/providers/](../../crates/extra/cgp-dispatch/src/providers/) and [crates/extra/cgp-monad/src/](../../crates/extra/cgp-monad/src/). End-to-end tests are under [crates/tests/cgp-tests/tests/extensible_data_tests/](../../crates/tests/cgp-tests/tests/extensible_data_tests/). +The variant traits live in [crates/core/cgp-field/src/traits/](../../crates/core/cgp-field/src/traits/) (`extract_field.rs`, `from_variant.rs`) with `HasExtractor`, `FinalizeExtract`, and the casting recursion in [crates/core/cgp-field/src/impls/cast.rs](../../crates/core/cgp-field/src/impls/cast.rs), and the `IsVoid` marker in [crates/core/cgp-field/src/impls/map_type.rs](../../crates/core/cgp-field/src/impls/map_type.rs). The visitor dispatchers and their monadic pipeline are in [crates/extra/cgp-dispatch/src/providers/](../../crates/extra/cgp-dispatch/src/providers/) and [crates/extra/cgp-monad/src/](../../crates/extra/cgp-monad/src/). End-to-end tests are under [crates/tests/cgp-tests/tests/extensible_variants/](../../crates/tests/cgp-tests/tests/extensible_variants/). diff --git a/docs/concepts/handlers.md b/docs/concepts/handlers.md index 219f57b0..0bbae495 100644 --- a/docs/concepts/handlers.md +++ b/docs/concepts/handlers.md @@ -40,4 +40,4 @@ The handler family builds directly on a few core constructs. The fallible varian ## Source -The handler components are defined in [crates/extra/cgp-handler/src/components/](../../crates/extra/cgp-handler/src/), with one module per corner: `computer.rs`, `async_computer.rs`, `try_compute.rs`, `handler.rs`, and `produce.rs`. The promotion combinators and the `UseInputDelegate` dispatch type live in [crates/extra/cgp-handler/src/providers/](../../crates/extra/cgp-handler/src/) and `types.rs`. The family is re-exported through `cgp::extra::handler`. Behavioral tests covering the full promotion surface are in [crates/tests/cgp-tests/tests/handler_tests/](../../crates/tests/cgp-tests/tests/handler_tests/). +The handler components are defined in [crates/extra/cgp-handler/src/components/](../../crates/extra/cgp-handler/src/), with one module per corner: `computer.rs`, `async_computer.rs`, `try_compute.rs`, `handler.rs`, and `produce.rs`. The promotion combinators and the `UseInputDelegate` dispatch type live in [crates/extra/cgp-handler/src/providers/](../../crates/extra/cgp-handler/src/) and `types.rs`. The family is re-exported through `cgp::extra::handler`. Behavioral tests covering the full promotion surface are in [crates/tests/cgp-tests/tests/handlers/](../../crates/tests/cgp-tests/tests/handlers/). diff --git a/docs/concepts/higher-order-providers.md b/docs/concepts/higher-order-providers.md index 5482a2b1..be856d42 100644 --- a/docs/concepts/higher-order-providers.md +++ b/docs/concepts/higher-order-providers.md @@ -79,4 +79,4 @@ Higher-order providers are written with [`#[cgp_impl]`](../reference/macros/cgp_ ## Source -The higher-order pattern is exercised throughout [crates/tests/cgp-tests/tests/component_tests/cgp_impl/use_provider.rs](../../crates/tests/cgp-tests/tests/component_tests/cgp_impl/use_provider.rs) and [shape.rs](../../crates/tests/cgp-tests/tests/component_tests/cgp_impl/shape.rs); `UseContext` lives in [crates/core/cgp-component/src/providers/use_context.rs](../../crates/core/cgp-component/src/providers/use_context.rs). +The higher-order pattern is exercised throughout [crates/tests/cgp-tests/tests/higher_order_providers/](../../crates/tests/cgp-tests/tests/higher_order_providers/), notably [use_provider_impl.rs](../../crates/tests/cgp-tests/tests/higher_order_providers/use_provider_impl.rs); `UseContext` lives in [crates/core/cgp-component/src/providers/use_context.rs](../../crates/core/cgp-component/src/providers/use_context.rs). diff --git a/docs/concepts/monadic-handlers.md b/docs/concepts/monadic-handlers.md index b2ef2f8b..1703d2de 100644 --- a/docs/concepts/monadic-handlers.md +++ b/docs/concepts/monadic-handlers.md @@ -44,4 +44,4 @@ Monadic handlers build directly on the handler family: the pipelines they produc ## Source -The monad layer lives in [crates/extra/cgp-monad/src/](../../crates/extra/cgp-monad/src/), re-exported as `cgp::extra::monad`. The pipeline provider is in `providers/pipe_monadic.rs`, the monad markers in `monadic/{ident,ok,err}.rs`, and the trait layer in `traits/`. The behavior described here is exercised by the tests in [crates/tests/cgp-tests/src/tests/monad/](../../crates/tests/cgp-tests/src/tests/monad/) (`ok.rs`, `err.rs`, `ok_err_trans.rs`). +The monad layer lives in [crates/extra/cgp-monad/src/](../../crates/extra/cgp-monad/src/), re-exported as `cgp::extra::monad`. The pipeline provider is in `providers/pipe_monadic.rs`, the monad markers in `monadic/{ident,ok,err}.rs`, and the trait layer in `traits/`. The behavior described here is exercised by the tests in [crates/tests/cgp-tests/tests/monadic_handlers/](../../crates/tests/cgp-tests/tests/monadic_handlers/) (`ok_monadic.rs`, `err_monadic.rs`, `ok_err_monadic_trans.rs`). diff --git a/docs/concepts/namespaces.md b/docs/concepts/namespaces.md index 837ecf90..a39e2ea1 100644 --- a/docs/concepts/namespaces.md +++ b/docs/concepts/namespaces.md @@ -86,4 +86,4 @@ Namespaces are defined with [`cgp_namespace!`](../reference/macros/cgp_namespace ## Source -The macro is in [crates/macros/cgp-macro-core/src/types/namespace/](../../crates/macros/cgp-macro-core/src/types/namespace/), with the `#[prefix(...)]` attribute in [attributes/prefix.rs](../../crates/macros/cgp-macro-core/src/types/attributes/prefix.rs). The runtime `DefaultNamespace`/`DefaultImpls1`/`DefaultImpls2` traits are in [crates/core/cgp-component/src/namespaces.rs](../../crates/core/cgp-component/src/namespaces.rs) and `RedirectLookup` in [crates/core/cgp-component/src/providers/redirect_lookup.rs](../../crates/core/cgp-component/src/providers/redirect_lookup.rs); expansion snapshots are in [crates/tests/cgp-tests/tests/namespace_tests/namespace_macro/](../../crates/tests/cgp-tests/tests/namespace_tests/namespace_macro/). +The macro is in [crates/macros/cgp-macro-core/src/types/namespace/](../../crates/macros/cgp-macro-core/src/types/namespace/), with the `#[prefix(...)]` attribute in [attributes/prefix.rs](../../crates/macros/cgp-macro-core/src/types/attributes/prefix.rs). The runtime `DefaultNamespace`/`DefaultImpls1`/`DefaultImpls2` traits are in [crates/core/cgp-component/src/namespaces.rs](../../crates/core/cgp-component/src/namespaces.rs) and `RedirectLookup` in [crates/core/cgp-component/src/providers/redirect_lookup.rs](../../crates/core/cgp-component/src/providers/redirect_lookup.rs); expansion snapshots are in [crates/tests/cgp-tests/tests/namespaces/](../../crates/tests/cgp-tests/tests/namespaces/). diff --git a/docs/reference/attributes/extend.md b/docs/reference/attributes/extend.md index 5fec54e0..c4eca859 100644 --- a/docs/reference/attributes/extend.md +++ b/docs/reference/attributes/extend.md @@ -121,4 +121,4 @@ Because `HasScalarType` is a supertrait of `RectangleArea`, the abstract `Self:: ## Source -`#[extend(...)]` is parsed in [crates/macros/cgp-macro-core/src/types/attributes/function.rs](../../../crates/macros/cgp-macro-core/src/types/attributes/function.rs) (the `extend` field of `FunctionAttributes`). For `#[cgp_fn]`, the bounds are added to the trait's supertraits and to the impl `where` clause in [crates/macros/cgp-macro-core/src/types/cgp_fn/preprocessed.rs](../../../crates/macros/cgp-macro-core/src/types/cgp_fn/preprocessed.rs). For `#[cgp_component]`, the attribute is parsed by `CgpComponentAttributes::parse` and its bounds appended to the consumer trait's supertraits in [crates/macros/cgp-macro-core/src/types/attributes/cgp_component_attributes.rs](../../../crates/macros/cgp-macro-core/src/types/attributes/cgp_component_attributes.rs). Expansion snapshots are in [crates/tests/cgp-tests/tests/cgp_fn_tests/extend.rs](../../../crates/tests/cgp-tests/tests/cgp_fn_tests/extend.rs) and the component abstract-type tests in [crates/tests/cgp-tests/tests/component_tests/abstract_types/](../../../crates/tests/cgp-tests/tests/component_tests/abstract_types/). +`#[extend(...)]` is parsed in [crates/macros/cgp-macro-core/src/types/attributes/function.rs](../../../crates/macros/cgp-macro-core/src/types/attributes/function.rs) (the `extend` field of `FunctionAttributes`). For `#[cgp_fn]`, the bounds are added to the trait's supertraits and to the impl `where` clause in [crates/macros/cgp-macro-core/src/types/cgp_fn/preprocessed.rs](../../../crates/macros/cgp-macro-core/src/types/cgp_fn/preprocessed.rs). For `#[cgp_component]`, the attribute is parsed by `CgpComponentAttributes::parse` and its bounds appended to the consumer trait's supertraits in [crates/macros/cgp-macro-core/src/types/attributes/cgp_component_attributes.rs](../../../crates/macros/cgp-macro-core/src/types/attributes/cgp_component_attributes.rs). Expansion snapshots are in [crates/tests/cgp-tests/tests/impl_side_dependencies/fn_extend.rs](../../../crates/tests/cgp-tests/tests/impl_side_dependencies/fn_extend.rs) and the component abstract-type tests in [crates/tests/cgp-tests/tests/abstract_types/](../../../crates/tests/cgp-tests/tests/abstract_types/). diff --git a/docs/reference/attributes/extend_where.md b/docs/reference/attributes/extend_where.md index 17be008a..56ad44d3 100644 --- a/docs/reference/attributes/extend_where.md +++ b/docs/reference/attributes/extend_where.md @@ -62,4 +62,4 @@ The `Scalar: Mul` bound, written in the function body, stays as ## Source -`#[extend_where(...)]` is parsed in [crates/macros/cgp-macro-core/src/types/attributes/function.rs](../../../crates/macros/cgp-macro-core/src/types/attributes/function.rs) (the `extend_where` field of `FunctionAttributes`), and its predicates are added to both the trait and impl `where` clauses in [crates/macros/cgp-macro-core/src/types/cgp_fn/preprocessed.rs](../../../crates/macros/cgp-macro-core/src/types/cgp_fn/preprocessed.rs). An expansion snapshot that exercises `#[extend_where(...)]` alongside `#[use_type]` lives in [crates/tests/cgp-tests/tests/cgp_fn_tests/nested_foreign_type.rs](../../../crates/tests/cgp-tests/tests/cgp_fn_tests/nested_foreign_type.rs), among the other `#[cgp_fn]` tests in [crates/tests/cgp-tests/tests/cgp_fn_tests/](../../../crates/tests/cgp-tests/tests/cgp_fn_tests/). +`#[extend_where(...)]` is parsed in [crates/macros/cgp-macro-core/src/types/attributes/function.rs](../../../crates/macros/cgp-macro-core/src/types/attributes/function.rs) (the `extend_where` field of `FunctionAttributes`), and its predicates are added to both the trait and impl `where` clauses in [crates/macros/cgp-macro-core/src/types/cgp_fn/preprocessed.rs](../../../crates/macros/cgp-macro-core/src/types/cgp_fn/preprocessed.rs). An expansion snapshot that exercises `#[extend_where(...)]` alongside `#[use_type]` lives in [crates/tests/cgp-tests/tests/abstract_types/use_type_fn_nested_foreign.rs](../../../crates/tests/cgp-tests/tests/abstract_types/use_type_fn_nested_foreign.rs), among the other `#[use_type]` `#[cgp_fn]` tests in [crates/tests/cgp-tests/tests/abstract_types/](../../../crates/tests/cgp-tests/tests/abstract_types/). diff --git a/docs/reference/attributes/implicit.md b/docs/reference/attributes/implicit.md index f18801e8..f6ec59d0 100644 --- a/docs/reference/attributes/implicit.md +++ b/docs/reference/attributes/implicit.md @@ -115,4 +115,4 @@ fn print_area(rect: &Rectangle) { ## Source -Implicit-argument parsing lives in [crates/macros/cgp-macro-core/src/functions/implicits/parse.rs](../../../crates/macros/cgp-macro-core/src/functions/implicits/parse.rs), which extracts `#[implicit]`-marked arguments and validates the `self`/`mut` rules. The per-argument model is in [crates/macros/cgp-macro-core/src/types/implicits/](../../../crates/macros/cgp-macro-core/src/types/implicits/): `arg_field.rs` builds the `HasField` bound and the `let` binding, and `arg_fields.rs` adds the bounds to the impl generics and prepends the bindings to the body. The field-type-to-access-mode mapping (`.clone()`, `.as_str()`, and the reference/option/slice cases) is in [crates/macros/cgp-macro-core/src/functions/field/parse.rs](../../../crates/macros/cgp-macro-core/src/functions/field/parse.rs) and [crates/macros/cgp-macro-core/src/types/getter/get_field_with_mode_expr.rs](../../../crates/macros/cgp-macro-core/src/types/getter/get_field_with_mode_expr.rs). Expansion snapshots are in [crates/tests/cgp-tests/tests/cgp_fn_tests/basic.rs](../../../crates/tests/cgp-tests/tests/cgp_fn_tests/basic.rs) and [crates/tests/cgp-tests/tests/component_tests/cgp_impl/implicit_args/](../../../crates/tests/cgp-tests/tests/component_tests/cgp_impl/implicit_args/). +Implicit-argument parsing lives in [crates/macros/cgp-macro-core/src/functions/implicits/parse.rs](../../../crates/macros/cgp-macro-core/src/functions/implicits/parse.rs), which extracts `#[implicit]`-marked arguments and validates the `self`/`mut` rules. The per-argument model is in [crates/macros/cgp-macro-core/src/types/implicits/](../../../crates/macros/cgp-macro-core/src/types/implicits/): `arg_field.rs` builds the `HasField` bound and the `let` binding, and `arg_fields.rs` adds the bounds to the impl generics and prepends the bindings to the body. The field-type-to-access-mode mapping (`.clone()`, `.as_str()`, and the reference/option/slice cases) is in [crates/macros/cgp-macro-core/src/functions/field/parse.rs](../../../crates/macros/cgp-macro-core/src/functions/field/parse.rs) and [crates/macros/cgp-macro-core/src/types/getter/get_field_with_mode_expr.rs](../../../crates/macros/cgp-macro-core/src/types/getter/get_field_with_mode_expr.rs). Expansion snapshots are in [crates/tests/cgp-tests/tests/implicit_arguments/](../../../crates/tests/cgp-tests/tests/implicit_arguments/), covering both the `#[cgp_fn]` form (`cgp_fn_greet.rs`) and the `#[cgp_impl]` form (`cgp_impl_implicit.rs`). diff --git a/docs/reference/attributes/use_provider.md b/docs/reference/attributes/use_provider.md index 46b404ee..3bcd4b93 100644 --- a/docs/reference/attributes/use_provider.md +++ b/docs/reference/attributes/use_provider.md @@ -127,4 +127,4 @@ A context can now wire `AreaCalculatorComponent` to `ScaledArea`, ## Source -The outer form is parsed by `UseProviderAttribute` in [crates/macros/cgp-macro-core/src/types/attributes/use_provider/attribute.rs](../../../crates/macros/cgp-macro-core/src/types/attributes/use_provider/attribute.rs); its `to_type_param_bounds` inserts the context type at index 0 of the trait's generic arguments, and `to_provider_bounds` builds the `where` predicate. The bounds are appended to the impl by `add_type_param_bounds` in `attributes.rs`. The attribute is collected for `#[cgp_impl]` in `types/attributes/cgp_impl_attributes.rs` and for `#[cgp_fn]` in `types/attributes/function.rs`, and applied in `types/cgp_impl/item.rs` and `types/cgp_fn/preprocessed.rs`. The expansion snapshot for the `#[cgp_fn]` outer form is in [crates/tests/cgp-tests/tests/cgp_fn_tests/use_provider.rs](../../../crates/tests/cgp-tests/tests/cgp_fn_tests/use_provider.rs); `#[cgp_impl]` usage is exercised in [crates/tests/cgp-tests/tests/component_tests/cgp_impl/use_provider.rs](../../../crates/tests/cgp-tests/tests/component_tests/cgp_impl/use_provider.rs) and `shape.rs`. +The outer form is parsed by `UseProviderAttribute` in [crates/macros/cgp-macro-core/src/types/attributes/use_provider/attribute.rs](../../../crates/macros/cgp-macro-core/src/types/attributes/use_provider/attribute.rs); its `to_type_param_bounds` inserts the context type at index 0 of the trait's generic arguments, and `to_provider_bounds` builds the `where` predicate. The bounds are appended to the impl by `add_type_param_bounds` in `attributes.rs`. The attribute is collected for `#[cgp_impl]` in `types/attributes/cgp_impl_attributes.rs` and for `#[cgp_fn]` in `types/attributes/function.rs`, and applied in `types/cgp_impl/item.rs` and `types/cgp_fn/preprocessed.rs`. The expansion snapshot for the `#[cgp_fn]` outer form is in [crates/tests/cgp-tests/tests/higher_order_providers/use_provider_fn.rs](../../../crates/tests/cgp-tests/tests/higher_order_providers/use_provider_fn.rs); `#[cgp_impl]` usage is exercised in [crates/tests/cgp-tests/tests/higher_order_providers/use_provider_impl.rs](../../../crates/tests/cgp-tests/tests/higher_order_providers/use_provider_impl.rs). diff --git a/docs/reference/attributes/use_type.md b/docs/reference/attributes/use_type.md index a7f485fe..cd083f96 100644 --- a/docs/reference/attributes/use_type.md +++ b/docs/reference/attributes/use_type.md @@ -178,4 +178,4 @@ The provider's `#[use_type]` adds `Self: HasScalarType` to its `where` clause an ## Source -The attribute is parsed by `UseTypeAttribute` in [crates/macros/cgp-macro-core/src/types/attributes/use_type/attribute.rs](../../../crates/macros/cgp-macro-core/src/types/attributes/use_type/attribute.rs), with per-type entries (`as` alias and `=` equality) in `ident.rs`. The two-phase transform — substitute then add bounds — lives in `attributes.rs` as `transform_item_trait` (supertrait for `#[cgp_component]`) and `transform_item_impl` (`where` bound for impls), and the type-equality and foreign-context resolution are in `type_predicates.rs`. The identifier substitution itself is the `SubstituteAbstractType` `VisitMut` pass in [crates/macros/cgp-macro-core/src/visitors/substitute_abstract_type.rs](../../../crates/macros/cgp-macro-core/src/visitors/substitute_abstract_type.rs), which matches single-segment, argument-free type paths. The `= ...` rejection for component traits is enforced in `types/attributes/cgp_component_attributes.rs`. Expansion snapshots covering the single, foreign (`@`), alias, equality, and cross-spec equality forms are in [crates/tests/cgp-tests/tests/cgp_fn_tests/type_equality.rs](../../../crates/tests/cgp-tests/tests/cgp_fn_tests/type_equality.rs) and [foreign_type.rs](../../../crates/tests/cgp-tests/tests/cgp_fn_tests/foreign_type.rs). +The attribute is parsed by `UseTypeAttribute` in [crates/macros/cgp-macro-core/src/types/attributes/use_type/attribute.rs](../../../crates/macros/cgp-macro-core/src/types/attributes/use_type/attribute.rs), with per-type entries (`as` alias and `=` equality) in `ident.rs`. The two-phase transform — substitute then add bounds — lives in `attributes.rs` as `transform_item_trait` (supertrait for `#[cgp_component]`) and `transform_item_impl` (`where` bound for impls), and the type-equality and foreign-context resolution are in `type_predicates.rs`. The identifier substitution itself is the `SubstituteAbstractType` `VisitMut` pass in [crates/macros/cgp-macro-core/src/visitors/substitute_abstract_type.rs](../../../crates/macros/cgp-macro-core/src/visitors/substitute_abstract_type.rs), which matches single-segment, argument-free type paths. The `= ...` rejection for component traits is enforced in `types/attributes/cgp_component_attributes.rs`. Expansion snapshots covering the single, foreign (`@`), alias, equality, and cross-spec equality forms are in [crates/tests/cgp-tests/tests/abstract_types/use_type_fn_equality.rs](../../../crates/tests/cgp-tests/tests/abstract_types/use_type_fn_equality.rs) and [use_type_fn_foreign.rs](../../../crates/tests/cgp-tests/tests/abstract_types/use_type_fn_foreign.rs). diff --git a/docs/reference/attributes/uses.md b/docs/reference/attributes/uses.md index bfec8283..37d385d0 100644 --- a/docs/reference/attributes/uses.md +++ b/docs/reference/attributes/uses.md @@ -105,4 +105,4 @@ The `#[uses(CanCalculateArea)]` attribute adds `Self: CanCalculateArea` to the g ## Source -`#[uses(...)]` parsing lives in [crates/macros/cgp-macro-core/src/types/attributes/uses.rs](../../../crates/macros/cgp-macro-core/src/types/attributes/uses.rs) (the `UsesAttributes` type and its `to_type_param_bounds`), with the attribute dispatch in [crates/macros/cgp-macro-core/src/types/attributes/function.rs](../../../crates/macros/cgp-macro-core/src/types/attributes/function.rs) for `#[cgp_fn]` and [crates/macros/cgp-macro-core/src/types/attributes/cgp_impl_attributes.rs](../../../crates/macros/cgp-macro-core/src/types/attributes/cgp_impl_attributes.rs) for `#[cgp_impl]`. The bounds are added to the impl `where` clause in [crates/macros/cgp-macro-core/src/types/cgp_fn/preprocessed.rs](../../../crates/macros/cgp-macro-core/src/types/cgp_fn/preprocessed.rs). Expansion snapshots are in [crates/tests/cgp-tests/tests/cgp_fn_tests/uses.rs](../../../crates/tests/cgp-tests/tests/cgp_fn_tests/uses.rs) and [crates/tests/cgp-tests/tests/component_tests/cgp_impl/implicit_args/import.rs](../../../crates/tests/cgp-tests/tests/component_tests/cgp_impl/implicit_args/import.rs). +`#[uses(...)]` parsing lives in [crates/macros/cgp-macro-core/src/types/attributes/uses.rs](../../../crates/macros/cgp-macro-core/src/types/attributes/uses.rs) (the `UsesAttributes` type and its `to_type_param_bounds`), with the attribute dispatch in [crates/macros/cgp-macro-core/src/types/attributes/function.rs](../../../crates/macros/cgp-macro-core/src/types/attributes/function.rs) for `#[cgp_fn]` and [crates/macros/cgp-macro-core/src/types/attributes/cgp_impl_attributes.rs](../../../crates/macros/cgp-macro-core/src/types/attributes/cgp_impl_attributes.rs) for `#[cgp_impl]`. The bounds are added to the impl `where` clause in [crates/macros/cgp-macro-core/src/types/cgp_fn/preprocessed.rs](../../../crates/macros/cgp-macro-core/src/types/cgp_fn/preprocessed.rs). Expansion snapshots are in [crates/tests/cgp-tests/tests/impl_side_dependencies/fn_uses.rs](../../../crates/tests/cgp-tests/tests/impl_side_dependencies/fn_uses.rs) and [crates/tests/cgp-tests/tests/impl_side_dependencies/impl_uses.rs](../../../crates/tests/cgp-tests/tests/impl_side_dependencies/impl_uses.rs). diff --git a/docs/reference/components/computer.md b/docs/reference/components/computer.md index 826372e9..72aa3af7 100644 --- a/docs/reference/components/computer.md +++ b/docs/reference/components/computer.md @@ -106,4 +106,4 @@ The computer components are the infallible corner of the [handler family](../../ ## Source -The synchronous computers `Computer`/`ComputerRef` are defined in [crates/extra/cgp-handler/src/components/computer.rs](../../../crates/extra/cgp-handler/src/components/computer.rs), and the async computers `AsyncComputer`/`AsyncComputerRef` in [crates/extra/cgp-handler/src/components/async_computer.rs](../../../crates/extra/cgp-handler/src/components/async_computer.rs). The `UseInputDelegate` dispatch type is in [crates/extra/cgp-handler/src/types.rs](../../../crates/extra/cgp-handler/src/types.rs). The components are re-exported through `cgp::extra::handler`. Behavioral tests exercising computers and their promotions are in [crates/tests/cgp-tests/tests/handler_tests/computer_macro.rs](../../../crates/tests/cgp-tests/tests/handler_tests/computer_macro.rs). +The synchronous computers `Computer`/`ComputerRef` are defined in [crates/extra/cgp-handler/src/components/computer.rs](../../../crates/extra/cgp-handler/src/components/computer.rs), and the async computers `AsyncComputer`/`AsyncComputerRef` in [crates/extra/cgp-handler/src/components/async_computer.rs](../../../crates/extra/cgp-handler/src/components/async_computer.rs). The `UseInputDelegate` dispatch type is in [crates/extra/cgp-handler/src/types.rs](../../../crates/extra/cgp-handler/src/types.rs). The components are re-exported through `cgp::extra::handler`. Behavioral tests exercising computers and their promotions are in [crates/tests/cgp-tests/tests/handlers/computer_macro.rs](../../../crates/tests/cgp-tests/tests/handlers/computer_macro.rs). diff --git a/docs/reference/components/handler.md b/docs/reference/components/handler.md index 93b06b75..8f9b1918 100644 --- a/docs/reference/components/handler.md +++ b/docs/reference/components/handler.md @@ -84,4 +84,4 @@ The function `run_with` works for any context that wires a handler for the given ## Source -`Handler` and `HandlerRef` are defined in [crates/extra/cgp-handler/src/components/handler.rs](../../../crates/extra/cgp-handler/src/components/handler.rs). The `ReturnInput` provider is in [crates/extra/cgp-handler/src/providers/return_input.rs](../../../crates/extra/cgp-handler/src/providers/return_input.rs), and the promotion combinators that lift simpler providers into `Handler` are in [crates/extra/cgp-handler/src/providers/](../../../crates/extra/cgp-handler/src/). The components are re-exported through `cgp::extra::handler`. Behavioral tests exercising handlers and their promotions are in [crates/tests/cgp-tests/tests/handler_tests/handler_macro.rs](../../../crates/tests/cgp-tests/tests/handler_tests/handler_macro.rs). +`Handler` and `HandlerRef` are defined in [crates/extra/cgp-handler/src/components/handler.rs](../../../crates/extra/cgp-handler/src/components/handler.rs). The `ReturnInput` provider is in [crates/extra/cgp-handler/src/providers/return_input.rs](../../../crates/extra/cgp-handler/src/providers/return_input.rs), and the promotion combinators that lift simpler providers into `Handler` are in [crates/extra/cgp-handler/src/providers/](../../../crates/extra/cgp-handler/src/). The components are re-exported through `cgp::extra::handler`. Behavioral tests exercising handlers and their promotions are in [crates/tests/cgp-tests/tests/handlers/handler_macro.rs](../../../crates/tests/cgp-tests/tests/handlers/handler_macro.rs). diff --git a/docs/reference/components/has_type.md b/docs/reference/components/has_type.md index c49f9101..5a00d4f8 100644 --- a/docs/reference/components/has_type.md +++ b/docs/reference/components/has_type.md @@ -72,4 +72,4 @@ where ## Source -The trait, the `TypeProvider` provider trait, and the `TypeOf` alias are defined in [crates/core/cgp-type/src/traits/has_type.rs](../../../crates/core/cgp-type/src/traits/has_type.rs). The `UseType` provider and its `TypeProvider` impl are in [crates/core/cgp-type/src/impls/use_type.rs](../../../crates/core/cgp-type/src/impls/use_type.rs). The `#[cgp_type]` macro that builds named components on this substrate lives in [crates/macros/cgp-macro-core/src/types/cgp_type/](../../../crates/macros/cgp-macro-core/src/types/cgp_type/). Behavioral and expansion-snapshot tests are in [crates/tests/cgp-tests/tests/component_tests/abstract_types/](../../../crates/tests/cgp-tests/tests/component_tests/abstract_types/). +The trait, the `TypeProvider` provider trait, and the `TypeOf` alias are defined in [crates/core/cgp-type/src/traits/has_type.rs](../../../crates/core/cgp-type/src/traits/has_type.rs). The `UseType` provider and its `TypeProvider` impl are in [crates/core/cgp-type/src/impls/use_type.rs](../../../crates/core/cgp-type/src/impls/use_type.rs). The `#[cgp_type]` macro that builds named components on this substrate lives in [crates/macros/cgp-macro-core/src/types/cgp_type/](../../../crates/macros/cgp-macro-core/src/types/cgp_type/). Behavioral and expansion-snapshot tests are in [crates/tests/cgp-tests/tests/abstract_types/](../../../crates/tests/cgp-tests/tests/abstract_types/). diff --git a/docs/reference/components/producer.md b/docs/reference/components/producer.md index 4b39aca8..4144b4fb 100644 --- a/docs/reference/components/producer.md +++ b/docs/reference/components/producer.md @@ -80,4 +80,4 @@ Here `MagicNumber` produces `42` from the `Code` tag alone, and `App` delegates ## Source -`Producer` is defined in [crates/extra/cgp-handler/src/components/produce.rs](../../../crates/extra/cgp-handler/src/components/produce.rs). The `Promote` combinator that lifts it into a `Computer` is in [crates/extra/cgp-handler/src/providers/promote.rs](../../../crates/extra/cgp-handler/src/providers/promote.rs), and the `PromoteProducer` table in [crates/extra/cgp-handler/src/providers/promote_all.rs](../../../crates/extra/cgp-handler/src/providers/promote_all.rs). The component is re-exported through `cgp::extra::handler`. Behavioral tests exercising a producer and its promotions are in [crates/tests/cgp-tests/tests/handler_tests/producer_macro.rs](../../../crates/tests/cgp-tests/tests/handler_tests/producer_macro.rs). +`Producer` is defined in [crates/extra/cgp-handler/src/components/produce.rs](../../../crates/extra/cgp-handler/src/components/produce.rs). The `Promote` combinator that lifts it into a `Computer` is in [crates/extra/cgp-handler/src/providers/promote.rs](../../../crates/extra/cgp-handler/src/providers/promote.rs), and the `PromoteProducer` table in [crates/extra/cgp-handler/src/providers/promote_all.rs](../../../crates/extra/cgp-handler/src/providers/promote_all.rs). The component is re-exported through `cgp::extra::handler`. Behavioral tests exercising a producer and its promotions are in [crates/tests/cgp-tests/tests/handlers/producer_macro.rs](../../../crates/tests/cgp-tests/tests/handlers/producer_macro.rs). diff --git a/docs/reference/components/runner.md b/docs/reference/components/runner.md index 0ea8bc1f..f121a184 100644 --- a/docs/reference/components/runner.md +++ b/docs/reference/components/runner.md @@ -103,4 +103,4 @@ The `Runner` family is most often paired with [`HasRuntime`](has_runtime.md): a ## Source -`CanRun` / `Runner` and `CanSendRun` / `SendRunner` are defined together in [crates/extra/cgp-run/src/lib.rs](../../../crates/extra/cgp-run/src/lib.rs), reached from the facade as `cgp::extra::run`. The `#[cgp_component]` and `#[derive_delegate]` expansions they rely on live under [crates/macros/cgp-macro-core/src/](../../../crates/macros/cgp-macro-core/src/). The end-to-end usage — a `UseDelegate` runner table, a `SpawnAndRun` provider, and a concrete `SendRunner` proxy that spawns a `Send` future — is exercised in [crates/tests/cgp-tests/src/tests/async/spawn.rs](../../../crates/tests/cgp-tests/src/tests/async/spawn.rs). +`CanRun` / `Runner` and `CanSendRun` / `SendRunner` are defined together in [crates/extra/cgp-run/src/lib.rs](../../../crates/extra/cgp-run/src/lib.rs), reached from the facade as `cgp::extra::run`. The `#[cgp_component]` and `#[derive_delegate]` expansions they rely on live under [crates/macros/cgp-macro-core/src/](../../../crates/macros/cgp-macro-core/src/). The end-to-end usage — a `UseDelegate` runner table, a `SpawnAndRun` provider, and a concrete `SendRunner` proxy that spawns a `Send` future — is exercised in [crates/tests/cgp-tests/tests/async_and_send/spawn.rs](../../../crates/tests/cgp-tests/tests/async_and_send/spawn.rs). diff --git a/docs/reference/components/try_computer.md b/docs/reference/components/try_computer.md index 82a33839..61e89933 100644 --- a/docs/reference/components/try_computer.md +++ b/docs/reference/components/try_computer.md @@ -94,4 +94,4 @@ The provider `ParseU64` returns its `u64` output or the context's abstract error ## Source -`TryComputer` and `TryComputerRef` are defined in [crates/extra/cgp-handler/src/components/try_compute.rs](../../../crates/extra/cgp-handler/src/components/try_compute.rs). The `ReturnInput` provider is in [crates/extra/cgp-handler/src/providers/return_input.rs](../../../crates/extra/cgp-handler/src/providers/return_input.rs), and the promotion combinators in [crates/extra/cgp-handler/src/providers/](../../../crates/extra/cgp-handler/src/). The components are re-exported through `cgp::extra::handler`. Behavioral tests exercising the fallible computers and their promotions are in [crates/tests/cgp-tests/tests/handler_tests/computer_macro.rs](../../../crates/tests/cgp-tests/tests/handler_tests/computer_macro.rs). +`TryComputer` and `TryComputerRef` are defined in [crates/extra/cgp-handler/src/components/try_compute.rs](../../../crates/extra/cgp-handler/src/components/try_compute.rs). The `ReturnInput` provider is in [crates/extra/cgp-handler/src/providers/return_input.rs](../../../crates/extra/cgp-handler/src/providers/return_input.rs), and the promotion combinators in [crates/extra/cgp-handler/src/providers/](../../../crates/extra/cgp-handler/src/). The components are re-exported through `cgp::extra::handler`. Behavioral tests exercising the fallible computers and their promotions are in [crates/tests/cgp-tests/tests/handlers/computer_macro.rs](../../../crates/tests/cgp-tests/tests/handlers/computer_macro.rs). diff --git a/docs/reference/derives/derive_build_field.md b/docs/reference/derives/derive_build_field.md index b44db841..c80f673f 100644 --- a/docs/reference/derives/derive_build_field.md +++ b/docs/reference/derives/derive_build_field.md @@ -129,4 +129,4 @@ Each step changes the partial type, and only after the last field is set does th ## Source -The derive entry point is `derive_build_field` in [crates/macros/cgp-macro-lib/src/derive_build_field.rs](../../../crates/macros/cgp-macro-lib/src/derive_build_field.rs), which builds an `ItemCgpRecord` and calls `to_build_field_items()`. That method, in [crates/macros/cgp-macro-core/src/types/cgp_data/record.rs](../../../crates/macros/cgp-macro-core/src/types/cgp_data/record.rs), composes the helpers in the [`derive_builder/`](../../../crates/macros/cgp-macro-core/src/types/cgp_data/derive_builder/) submodule (`builder_struct.rs`, `has_builder_impl.rs`, `into_builder_impl.rs`, `partial_data.rs`, `finalize_build_impl.rs`, `update_field_impls.rs`, `has_field_impls.rs`). The `BuildField` and `FinalizeBuild` traits are in [crates/core/cgp-field/src/traits/build_field.rs](../../../crates/core/cgp-field/src/traits/build_field.rs), `UpdateField` in `update_field.rs`, `HasBuilder`/`IntoBuilder` in `has_builder.rs`, `PartialData` in `partial_data.rs`, and the `MapType` markers in `crates/core/cgp-field/src/impls/map_type.rs`. Tests are in [crates/tests/cgp-tests/tests/extensible_data_tests/records/](../../../crates/tests/cgp-tests/tests/extensible_data_tests/records/). +The derive entry point is `derive_build_field` in [crates/macros/cgp-macro-lib/src/derive_build_field.rs](../../../crates/macros/cgp-macro-lib/src/derive_build_field.rs), which builds an `ItemCgpRecord` and calls `to_build_field_items()`. That method, in [crates/macros/cgp-macro-core/src/types/cgp_data/record.rs](../../../crates/macros/cgp-macro-core/src/types/cgp_data/record.rs), composes the helpers in the [`derive_builder/`](../../../crates/macros/cgp-macro-core/src/types/cgp_data/derive_builder/) submodule (`builder_struct.rs`, `has_builder_impl.rs`, `into_builder_impl.rs`, `partial_data.rs`, `finalize_build_impl.rs`, `update_field_impls.rs`, `has_field_impls.rs`). The `BuildField` and `FinalizeBuild` traits are in [crates/core/cgp-field/src/traits/build_field.rs](../../../crates/core/cgp-field/src/traits/build_field.rs), `UpdateField` in `update_field.rs`, `HasBuilder`/`IntoBuilder` in `has_builder.rs`, `PartialData` in `partial_data.rs`, and the `MapType` markers in `crates/core/cgp-field/src/impls/map_type.rs`. Tests are in [crates/tests/cgp-tests/tests/extensible_records/](../../../crates/tests/cgp-tests/tests/extensible_records/). diff --git a/docs/reference/derives/derive_cgp_data.md b/docs/reference/derives/derive_cgp_data.md index 5d3fa903..10c67d27 100644 --- a/docs/reference/derives/derive_cgp_data.md +++ b/docs/reference/derives/derive_cgp_data.md @@ -197,4 +197,4 @@ These two views are what higher-level constructs build on: builders and field-di ## Source -The derive entry point is `derive_cgp_data` in [crates/macros/cgp-macro-lib/src/cgp_data.rs](../../../crates/macros/cgp-macro-lib/src/cgp_data.rs), which parses the input into `ItemCgpData` and calls `to_items()`. The dispatch on struct vs. enum is in [crates/macros/cgp-macro-core/src/types/cgp_data/item.rs](../../../crates/macros/cgp-macro-core/src/types/cgp_data/item.rs); the record path is in `record.rs` and the variant path in `variant.rs` in the same directory, which delegate to the `derive_has_fields/`, `derive_builder/`, `derive_extractor/`, and `derive_from_variant.rs` submodules. The runtime traits are in [crates/core/cgp-field/src/traits/](../../../crates/core/cgp-field/src/traits/) and the `MapType` markers in `crates/core/cgp-field/src/impls/map_type.rs`. Expansion snapshots and behavioral tests live in [crates/tests/cgp-tests/tests/extensible_data_tests/](../../../crates/tests/cgp-tests/tests/extensible_data_tests/), notably `records/` and `variants/`. +The derive entry point is `derive_cgp_data` in [crates/macros/cgp-macro-lib/src/cgp_data.rs](../../../crates/macros/cgp-macro-lib/src/cgp_data.rs), which parses the input into `ItemCgpData` and calls `to_items()`. The dispatch on struct vs. enum is in [crates/macros/cgp-macro-core/src/types/cgp_data/item.rs](../../../crates/macros/cgp-macro-core/src/types/cgp_data/item.rs); the record path is in `record.rs` and the variant path in `variant.rs` in the same directory, which delegate to the `derive_has_fields/`, `derive_builder/`, `derive_extractor/`, and `derive_from_variant.rs` submodules. The runtime traits are in [crates/core/cgp-field/src/traits/](../../../crates/core/cgp-field/src/traits/) and the `MapType` markers in `crates/core/cgp-field/src/impls/map_type.rs`. Expansion snapshots and behavioral tests live in [crates/tests/cgp-tests/tests/extensible_records/](../../../crates/tests/cgp-tests/tests/extensible_records/) and [crates/tests/cgp-tests/tests/extensible_variants/](../../../crates/tests/cgp-tests/tests/extensible_variants/). diff --git a/docs/reference/derives/derive_cgp_record.md b/docs/reference/derives/derive_cgp_record.md index 96cc306b..ab8e826c 100644 --- a/docs/reference/derives/derive_cgp_record.md +++ b/docs/reference/derives/derive_cgp_record.md @@ -134,4 +134,4 @@ If a field were left unset before `finalize_build`, the call would not compile ## Source -The derive entry point is `derive_cgp_record` in [crates/macros/cgp-macro-lib/src/cgp_record.rs](../../../crates/macros/cgp-macro-lib/src/cgp_record.rs), which parses an `ItemCgpRecord` and calls `to_items()`. The record codegen is in [crates/macros/cgp-macro-core/src/types/cgp_data/record.rs](../../../crates/macros/cgp-macro-core/src/types/cgp_data/record.rs), which composes `derive_has_field_impls_from_struct`, `derive_has_fields_impls_from_struct`, and the builder helpers in the `derive_builder/` submodule. The runtime traits (`HasBuilder`, `IntoBuilder`, `PartialData`, `FinalizeBuild`, `UpdateField`, `BuildField`, `HasFields`, `FromFields`, `ToFields`) live in [crates/core/cgp-field/src/traits/](../../../crates/core/cgp-field/src/traits/). Expansion snapshots and behavioral tests are in [crates/tests/cgp-tests/tests/extensible_data_tests/records/](../../../crates/tests/cgp-tests/tests/extensible_data_tests/records/). +The derive entry point is `derive_cgp_record` in [crates/macros/cgp-macro-lib/src/cgp_record.rs](../../../crates/macros/cgp-macro-lib/src/cgp_record.rs), which parses an `ItemCgpRecord` and calls `to_items()`. The record codegen is in [crates/macros/cgp-macro-core/src/types/cgp_data/record.rs](../../../crates/macros/cgp-macro-core/src/types/cgp_data/record.rs), which composes `derive_has_field_impls_from_struct`, `derive_has_fields_impls_from_struct`, and the builder helpers in the `derive_builder/` submodule. The runtime traits (`HasBuilder`, `IntoBuilder`, `PartialData`, `FinalizeBuild`, `UpdateField`, `BuildField`, `HasFields`, `FromFields`, `ToFields`) live in [crates/core/cgp-field/src/traits/](../../../crates/core/cgp-field/src/traits/). Expansion snapshots and behavioral tests are in [crates/tests/cgp-tests/tests/extensible_records/](../../../crates/tests/cgp-tests/tests/extensible_records/). diff --git a/docs/reference/derives/derive_cgp_variant.md b/docs/reference/derives/derive_cgp_variant.md index 3efa0d8b..cf1626ed 100644 --- a/docs/reference/derives/derive_cgp_variant.md +++ b/docs/reference/derives/derive_cgp_variant.md @@ -120,4 +120,4 @@ Because each `extract_field` narrows the remainder type, the compiler knows afte ## Source -The derive entry point is `derive_cgp_variant` in [crates/macros/cgp-macro-lib/src/cgp_variant.rs](../../../crates/macros/cgp-macro-lib/src/cgp_variant.rs), which parses an `ItemCgpVariant` and calls `to_items()`. The variant codegen is in [crates/macros/cgp-macro-core/src/types/cgp_data/variant.rs](../../../crates/macros/cgp-macro-core/src/types/cgp_data/variant.rs), which composes `derive_has_fields_impls_from_enum`, `derive_from_variant_from_enum`, and the extractor helpers in the `derive_extractor/` submodule. The runtime traits (`HasExtractor`, `HasExtractorRef`, `HasExtractorMut`, `ExtractField`, `FinalizeExtract`, `FromVariant`, `HasFields`, `FromFields`, `ToFields`) live in [crates/core/cgp-field/src/traits/](../../../crates/core/cgp-field/src/traits/). Expansion snapshots and behavioral tests are in [crates/tests/cgp-tests/tests/extensible_data_tests/variants/](../../../crates/tests/cgp-tests/tests/extensible_data_tests/variants/). +The derive entry point is `derive_cgp_variant` in [crates/macros/cgp-macro-lib/src/cgp_variant.rs](../../../crates/macros/cgp-macro-lib/src/cgp_variant.rs), which parses an `ItemCgpVariant` and calls `to_items()`. The variant codegen is in [crates/macros/cgp-macro-core/src/types/cgp_data/variant.rs](../../../crates/macros/cgp-macro-core/src/types/cgp_data/variant.rs), which composes `derive_has_fields_impls_from_enum`, `derive_from_variant_from_enum`, and the extractor helpers in the `derive_extractor/` submodule. The runtime traits (`HasExtractor`, `HasExtractorRef`, `HasExtractorMut`, `ExtractField`, `FinalizeExtract`, `FromVariant`, `HasFields`, `FromFields`, `ToFields`) live in [crates/core/cgp-field/src/traits/](../../../crates/core/cgp-field/src/traits/). Expansion snapshots and behavioral tests are in [crates/tests/cgp-tests/tests/extensible_variants/](../../../crates/tests/cgp-tests/tests/extensible_variants/). diff --git a/docs/reference/derives/derive_extract_field.md b/docs/reference/derives/derive_extract_field.md index 66667637..490db8c8 100644 --- a/docs/reference/derives/derive_extract_field.md +++ b/docs/reference/derives/derive_extract_field.md @@ -146,4 +146,4 @@ The derive only accepts enums whose every variant is a single-field tuple varian ## Source -The derive entry point is `derive_extract_field` in [crates/macros/cgp-macro-lib/src/derive_extract_field.rs](../../../crates/macros/cgp-macro-lib/src/derive_extract_field.rs), which builds an `ItemCgpVariant` and calls `to_extract_field_items()`. That method, in [crates/macros/cgp-macro-core/src/types/cgp_data/variant.rs](../../../crates/macros/cgp-macro-core/src/types/cgp_data/variant.rs), composes the helpers in the [`derive_extractor/`](../../../crates/macros/cgp-macro-core/src/types/cgp_data/derive_extractor/) submodule (`extractor_enum.rs`, `has_extractor_impl.rs`, `partial_data.rs`, `finalize_extract_impl.rs`, `extract_field_impls.rs`). The `ExtractField`, `HasExtractor`/`HasExtractorRef`/`HasExtractorMut`, `FinalizeExtract`, and `FinalizeExtractResult` traits are in [crates/core/cgp-field/src/traits/extract_field.rs](../../../crates/core/cgp-field/src/traits/extract_field.rs), `PartialData` in `partial_data.rs`, and the `MapType`/`MapTypeRef` markers in `crates/core/cgp-field/src/impls/`. Tests are in [crates/tests/cgp-tests/tests/extensible_data_tests/variants/](../../../crates/tests/cgp-tests/tests/extensible_data_tests/variants/). +The derive entry point is `derive_extract_field` in [crates/macros/cgp-macro-lib/src/derive_extract_field.rs](../../../crates/macros/cgp-macro-lib/src/derive_extract_field.rs), which builds an `ItemCgpVariant` and calls `to_extract_field_items()`. That method, in [crates/macros/cgp-macro-core/src/types/cgp_data/variant.rs](../../../crates/macros/cgp-macro-core/src/types/cgp_data/variant.rs), composes the helpers in the [`derive_extractor/`](../../../crates/macros/cgp-macro-core/src/types/cgp_data/derive_extractor/) submodule (`extractor_enum.rs`, `has_extractor_impl.rs`, `partial_data.rs`, `finalize_extract_impl.rs`, `extract_field_impls.rs`). The `ExtractField`, `HasExtractor`/`HasExtractorRef`/`HasExtractorMut`, `FinalizeExtract`, and `FinalizeExtractResult` traits are in [crates/core/cgp-field/src/traits/extract_field.rs](../../../crates/core/cgp-field/src/traits/extract_field.rs), `PartialData` in `partial_data.rs`, and the `MapType`/`MapTypeRef` markers in `crates/core/cgp-field/src/impls/`. Tests are in [crates/tests/cgp-tests/tests/extensible_variants/](../../../crates/tests/cgp-tests/tests/extensible_variants/). diff --git a/docs/reference/derives/derive_from_variant.md b/docs/reference/derives/derive_from_variant.md index 26c5a477..2fe52c6c 100644 --- a/docs/reference/derives/derive_from_variant.md +++ b/docs/reference/derives/derive_from_variant.md @@ -86,4 +86,4 @@ The derive only accepts enums whose every variant is a single-field tuple varian ## Source -The derive entry point is `derive_from_variant` in [crates/macros/cgp-macro-lib/src/derive_from_variant.rs](../../../crates/macros/cgp-macro-lib/src/derive_from_variant.rs), which builds an `ItemCgpVariant` and calls `to_from_variant_impls()`. That method, in [crates/macros/cgp-macro-core/src/types/cgp_data/variant.rs](../../../crates/macros/cgp-macro-core/src/types/cgp_data/variant.rs), delegates to `derive_from_variant_from_enum` in [crates/macros/cgp-macro-core/src/types/cgp_data/derive_from_variant.rs](../../../crates/macros/cgp-macro-core/src/types/cgp_data/derive_from_variant.rs). The `FromVariant` trait is in [crates/core/cgp-field/src/traits/from_variant.rs](../../../crates/core/cgp-field/src/traits/from_variant.rs). Tests are in [crates/tests/cgp-tests/tests/extensible_data_tests/variants/](../../../crates/tests/cgp-tests/tests/extensible_data_tests/variants/). +The derive entry point is `derive_from_variant` in [crates/macros/cgp-macro-lib/src/derive_from_variant.rs](../../../crates/macros/cgp-macro-lib/src/derive_from_variant.rs), which builds an `ItemCgpVariant` and calls `to_from_variant_impls()`. That method, in [crates/macros/cgp-macro-core/src/types/cgp_data/variant.rs](../../../crates/macros/cgp-macro-core/src/types/cgp_data/variant.rs), delegates to `derive_from_variant_from_enum` in [crates/macros/cgp-macro-core/src/types/cgp_data/derive_from_variant.rs](../../../crates/macros/cgp-macro-core/src/types/cgp_data/derive_from_variant.rs). The `FromVariant` trait is in [crates/core/cgp-field/src/traits/from_variant.rs](../../../crates/core/cgp-field/src/traits/from_variant.rs). Tests are in [crates/tests/cgp-tests/tests/extensible_variants/](../../../crates/tests/cgp-tests/tests/extensible_variants/). diff --git a/docs/reference/macros/async_trait.md b/docs/reference/macros/async_trait.md index 69fc09b2..7cbac40a 100644 --- a/docs/reference/macros/async_trait.md +++ b/docs/reference/macros/async_trait.md @@ -135,5 +135,5 @@ The generated future carries no `Send` bound. Because the rewrite produces a bar ## Source -The macro entry point is `async_trait` in [crates/macros/cgp-async-macro/src/lib.rs](../../../crates/macros/cgp-async-macro/src/lib.rs), a `#[proc_macro_attribute]` that discards its attribute arguments and forwards the annotated item to `impl_async`. The rewrite lives in [crates/macros/cgp-async-macro/src/impl_async.rs](../../../crates/macros/cgp-async-macro/src/impl_async.rs): it parses the item as a `syn::ItemTrait`, and on success walks the trait's methods, replacing each `async` signature's output with `-> impl ::core::future::Future` and clearing the `async` keyword; if the item does not parse as a trait, it is returned unchanged. The macro is re-exported into the prelude from [crates/main/cgp-core/src/prelude.rs](../../../crates/main/cgp-core/src/prelude.rs), so `use cgp::prelude::*;` brings it into scope. Its interaction with `#[cgp_fn]` is exercised by the expansion snapshot in [crates/tests/cgp-tests/tests/cgp_fn_tests/async.rs](../../../crates/tests/cgp-tests/tests/cgp_fn_tests/async.rs), and async providers spawned onto a runtime appear in [crates/tests/cgp-tests/src/tests/async/spawn.rs](../../../crates/tests/cgp-tests/src/tests/async/spawn.rs). +The macro entry point is `async_trait` in [crates/macros/cgp-async-macro/src/lib.rs](../../../crates/macros/cgp-async-macro/src/lib.rs), a `#[proc_macro_attribute]` that discards its attribute arguments and forwards the annotated item to `impl_async`. The rewrite lives in [crates/macros/cgp-async-macro/src/impl_async.rs](../../../crates/macros/cgp-async-macro/src/impl_async.rs): it parses the item as a `syn::ItemTrait`, and on success walks the trait's methods, replacing each `async` signature's output with `-> impl ::core::future::Future` and clearing the `async` keyword; if the item does not parse as a trait, it is returned unchanged. The macro is re-exported into the prelude from [crates/main/cgp-core/src/prelude.rs](../../../crates/main/cgp-core/src/prelude.rs), so `use cgp::prelude::*;` brings it into scope. Its interaction with `#[cgp_fn]` is exercised by the expansion snapshot in [crates/tests/cgp-tests/tests/async_and_send/cgp_fn_async.rs](../../../crates/tests/cgp-tests/tests/async_and_send/cgp_fn_async.rs), and async providers spawned onto a runtime appear in [crates/tests/cgp-tests/tests/async_and_send/spawn.rs](../../../crates/tests/cgp-tests/tests/async_and_send/spawn.rs). diff --git a/docs/reference/macros/blanket_trait.md b/docs/reference/macros/blanket_trait.md index 6c6811bc..e4c464c9 100644 --- a/docs/reference/macros/blanket_trait.md +++ b/docs/reference/macros/blanket_trait.md @@ -167,4 +167,4 @@ fn run(ctx: &Context) { ## Source -The macro entry point is `blanket_trait` in [crates/macros/cgp-macro-lib/src/blanket_trait.rs](../../../crates/macros/cgp-macro-lib/src/blanket_trait.rs), which parses the optional context identifier (defaulting to `__Context__` when the attribute argument is empty) and the trait, then runs `item.to_items()?`. The logic lives in [crates/macros/cgp-macro-core/src/types/blanket_trait.rs](../../../crates/macros/cgp-macro-core/src/types/blanket_trait.rs): `to_item_impl` walks the trait items, forwards each default method/const body and associated-type assignment into the impl, lifts associated types into impl generics, moves associated-type bounds into the `where` clause, and appends the trait's supertraits as the `__Context__: ...` predicate. The `Self`-to-parameter rewriting for associated types is done by `RemoveSelfPathVisitor` in [crates/macros/cgp-macro-core/src/visitors/](../../../crates/macros/cgp-macro-core/src/visitors/). Behavioral and expansion-snapshot tests are in [crates/tests/cgp-tests/src/tests/blanket_trait.rs](../../../crates/tests/cgp-tests/src/tests/blanket_trait.rs), covering the empty, method, and associated-type-with-and-without-bounds cases. +The macro entry point is `blanket_trait` in [crates/macros/cgp-macro-lib/src/blanket_trait.rs](../../../crates/macros/cgp-macro-lib/src/blanket_trait.rs), which parses the optional context identifier (defaulting to `__Context__` when the attribute argument is empty) and the trait, then runs `item.to_items()?`. The logic lives in [crates/macros/cgp-macro-core/src/types/blanket_trait.rs](../../../crates/macros/cgp-macro-core/src/types/blanket_trait.rs): `to_item_impl` walks the trait items, forwards each default method/const body and associated-type assignment into the impl, lifts associated types into impl generics, moves associated-type bounds into the `where` clause, and appends the trait's supertraits as the `__Context__: ...` predicate. The `Self`-to-parameter rewriting for associated types is done by `RemoveSelfPathVisitor` in [crates/macros/cgp-macro-core/src/visitors/](../../../crates/macros/cgp-macro-core/src/visitors/). Behavioral and expansion-snapshot tests are in [crates/tests/cgp-tests/tests/blanket_traits/](../../../crates/tests/cgp-tests/tests/blanket_traits/), covering the empty, method, and associated-type-with-and-without-bounds cases. diff --git a/docs/reference/macros/cgp_auto_dispatch.md b/docs/reference/macros/cgp_auto_dispatch.md index 8a23a7ae..5beaef74 100644 --- a/docs/reference/macros/cgp_auto_dispatch.md +++ b/docs/reference/macros/cgp_auto_dispatch.md @@ -135,4 +135,4 @@ The macro rejects trait methods with non-lifetime generic parameters, so a dispa ## Source -The macro is the `cgp_auto_dispatch` entry point in [crates/macros/cgp-extra-macro-lib/src/entrypoints/cgp_auto_dispatch.rs](../../../crates/macros/cgp-extra-macro-lib/src/entrypoints/cgp_auto_dispatch.rs), forwarded from the proc-macro shim in [crates/macros/cgp-extra-macro/src/lib.rs](../../../crates/macros/cgp-extra-macro/src/lib.rs) and re-exported through [crates/main/cgp-extra/src/prelude.rs](../../../crates/main/cgp-extra/src/prelude.rs). The matchers it generates live in [crates/extra/cgp-dispatch/src/providers/matchers/](../../../crates/extra/cgp-dispatch/src/providers/matchers/). Tests are in [crates/tests/cgp-tests/tests/dispatcher_macro_tests/](../../../crates/tests/cgp-tests/tests/dispatcher_macro_tests/), including the `shape.rs` cases covering by-reference, by-mutable-reference, by-value, multi-argument, and async methods. +The macro is the `cgp_auto_dispatch` entry point in [crates/macros/cgp-extra-macro-lib/src/entrypoints/cgp_auto_dispatch.rs](../../../crates/macros/cgp-extra-macro-lib/src/entrypoints/cgp_auto_dispatch.rs), forwarded from the proc-macro shim in [crates/macros/cgp-extra-macro/src/lib.rs](../../../crates/macros/cgp-extra-macro/src/lib.rs) and re-exported through [crates/main/cgp-extra/src/prelude.rs](../../../crates/main/cgp-extra/src/prelude.rs). The matchers it generates live in [crates/extra/cgp-dispatch/src/providers/matchers/](../../../crates/extra/cgp-dispatch/src/providers/matchers/). Tests are in [crates/tests/cgp-tests/tests/dispatching/](../../../crates/tests/cgp-tests/tests/dispatching/), including the `auto_dispatch_shape.rs` cases covering by-reference, by-mutable-reference, by-value, multi-argument, and async methods. diff --git a/docs/reference/macros/cgp_auto_getter.md b/docs/reference/macros/cgp_auto_getter.md index e28068d8..9bcf895a 100644 --- a/docs/reference/macros/cgp_auto_getter.md +++ b/docs/reference/macros/cgp_auto_getter.md @@ -158,4 +158,4 @@ The explicit form is more verbose but requires no understanding of `HasField` or ## Source -The macro entry point is `cgp_auto_getter` in [crates/macros/cgp-macro-lib/src/cgp_auto_getter.rs](../../../crates/macros/cgp-macro-lib/src/cgp_auto_getter.rs), which rejects any attribute argument and runs `ItemCgpAutoGetter::preprocess(...).to_items()`. The logic lives in [crates/macros/cgp-macro-core/src/types/cgp_auto_getter/](../../../crates/macros/cgp-macro-core/src/types/cgp_auto_getter/): `item.rs` sets the `__Context__` context identifier and drives field parsing, and `blanket.rs` builds the single blanket impl. Getter parsing — including the `&str`/`Option<&T>`/`&[T]`/owned shorthands and the associated-type rules — is shared with `#[cgp_getter]` in [crates/macros/cgp-macro-core/src/functions/getter/parse.rs](../../../crates/macros/cgp-macro-core/src/functions/getter/parse.rs), [crates/macros/cgp-macro-core/src/functions/field/parse.rs](../../../crates/macros/cgp-macro-core/src/functions/field/parse.rs), and [crates/macros/cgp-macro-core/src/types/getter/](../../../crates/macros/cgp-macro-core/src/types/getter/). Behavioral and expansion-snapshot tests are in [crates/tests/cgp-tests/tests/getter.rs](../../../crates/tests/cgp-tests/tests/getter.rs) and the `getter_tests/` modules beside it (notably `assoc_type/auto_getter.rs` for the associated-type form and `string.rs` for the `&str` shorthand). +The macro entry point is `cgp_auto_getter` in [crates/macros/cgp-macro-lib/src/cgp_auto_getter.rs](../../../crates/macros/cgp-macro-lib/src/cgp_auto_getter.rs), which rejects any attribute argument and runs `ItemCgpAutoGetter::preprocess(...).to_items()`. The logic lives in [crates/macros/cgp-macro-core/src/types/cgp_auto_getter/](../../../crates/macros/cgp-macro-core/src/types/cgp_auto_getter/): `item.rs` sets the `__Context__` context identifier and drives field parsing, and `blanket.rs` builds the single blanket impl. Getter parsing — including the `&str`/`Option<&T>`/`&[T]`/owned shorthands and the associated-type rules — is shared with `#[cgp_getter]` in [crates/macros/cgp-macro-core/src/functions/getter/parse.rs](../../../crates/macros/cgp-macro-core/src/functions/getter/parse.rs), [crates/macros/cgp-macro-core/src/functions/field/parse.rs](../../../crates/macros/cgp-macro-core/src/functions/field/parse.rs), and [crates/macros/cgp-macro-core/src/types/getter/](../../../crates/macros/cgp-macro-core/src/types/getter/). Behavioral and expansion-snapshot tests are in [crates/tests/cgp-tests/tests/getters/](../../../crates/tests/cgp-tests/tests/getters/) (notably `assoc_type_auto_getter.rs` for the associated-type form and `string_auto.rs` for the `&str` shorthand). diff --git a/docs/reference/macros/cgp_computer.md b/docs/reference/macros/cgp_computer.md index 10e41def..780c3b6b 100644 --- a/docs/reference/macros/cgp_computer.md +++ b/docs/reference/macros/cgp_computer.md @@ -145,4 +145,4 @@ Because the function returns a plain `u64`, the `try_compute` and `handle` forms ## Source -The macro entrypoint is [crates/macros/cgp-extra-macro/src/lib.rs](../../../crates/macros/cgp-extra-macro/src/lib.rs), forwarding to the implementation in [crates/macros/cgp-extra-macro-lib/src/entrypoints/cgp_computer.rs](../../../crates/macros/cgp-extra-macro-lib/src/entrypoints/cgp_computer.rs); the `Result`-versus-value detection is the `MaybeResultType` parser in [crates/macros/cgp-extra-macro-lib/src/parse/maybe_result.rs](../../../crates/macros/cgp-extra-macro-lib/src/parse/maybe_result.rs). The base `Computer`/`AsyncComputer` traits are defined in [crates/extra/cgp-handler/src/components/](../../../crates/extra/cgp-handler/src/components/), and the promotion bundles in [crates/extra/cgp-handler/src/providers/promote_all.rs](../../../crates/extra/cgp-handler/src/providers/promote_all.rs). Behavioral and snapshot tests live in [crates/tests/cgp-tests/tests/handler_tests/computer_macro.rs](../../../crates/tests/cgp-tests/tests/handler_tests/computer_macro.rs). +The macro entrypoint is [crates/macros/cgp-extra-macro/src/lib.rs](../../../crates/macros/cgp-extra-macro/src/lib.rs), forwarding to the implementation in [crates/macros/cgp-extra-macro-lib/src/entrypoints/cgp_computer.rs](../../../crates/macros/cgp-extra-macro-lib/src/entrypoints/cgp_computer.rs); the `Result`-versus-value detection is the `MaybeResultType` parser in [crates/macros/cgp-extra-macro-lib/src/parse/maybe_result.rs](../../../crates/macros/cgp-extra-macro-lib/src/parse/maybe_result.rs). The base `Computer`/`AsyncComputer` traits are defined in [crates/extra/cgp-handler/src/components/](../../../crates/extra/cgp-handler/src/components/), and the promotion bundles in [crates/extra/cgp-handler/src/providers/promote_all.rs](../../../crates/extra/cgp-handler/src/providers/promote_all.rs). Behavioral and snapshot tests live in [crates/tests/cgp-tests/tests/handlers/computer_macro.rs](../../../crates/tests/cgp-tests/tests/handlers/computer_macro.rs). diff --git a/docs/reference/macros/cgp_fn.md b/docs/reference/macros/cgp_fn.md index a9891669..a8bd0753 100644 --- a/docs/reference/macros/cgp_fn.md +++ b/docs/reference/macros/cgp_fn.md @@ -170,4 +170,4 @@ Because `Rectangle` derives `HasField` and carries `width`, `height`, and `scale ## Source -The macro entry point is `cgp_fn` in [crates/macros/cgp-macro-lib/src/cgp_fn.rs](../../../crates/macros/cgp-macro-lib/src/cgp_fn.rs), which parses the optional trait-name identifier and the function, then runs `item.preprocess()?.to_items()?`. The logic lives in [crates/macros/cgp-macro-core/src/types/cgp_fn/](../../../crates/macros/cgp-macro-core/src/types/cgp_fn/): `item.rs` performs the PascalCase default-name derivation (`to_camel_case_str`), implicit-argument extraction, and attribute parsing; `preprocessed.rs` builds the trait in `to_item_trait` and the blanket impl in `to_item_impl`, including the generics/`where`-clause split and the insertion of the leading `__Context__` parameter. Implicit-argument handling is in [crates/macros/cgp-macro-core/src/types/implicits/](../../../crates/macros/cgp-macro-core/src/types/implicits/), and the companion-attribute parsing is in [crates/macros/cgp-macro-core/src/types/attributes/function.rs](../../../crates/macros/cgp-macro-core/src/types/attributes/function.rs). Behavioral and expansion-snapshot tests are in [crates/tests/cgp-tests/tests/cgp_fn_tests/](../../../crates/tests/cgp-tests/tests/cgp_fn_tests/), covering the basic form (`basic.rs`), generics (`generics.rs`, `impl_generics.rs`), and each companion attribute (`uses.rs`, `use_type.rs`, `use_provider.rs`, `extend.rs`). +The macro entry point is `cgp_fn` in [crates/macros/cgp-macro-lib/src/cgp_fn.rs](../../../crates/macros/cgp-macro-lib/src/cgp_fn.rs), which parses the optional trait-name identifier and the function, then runs `item.preprocess()?.to_items()?`. The logic lives in [crates/macros/cgp-macro-core/src/types/cgp_fn/](../../../crates/macros/cgp-macro-core/src/types/cgp_fn/): `item.rs` performs the PascalCase default-name derivation (`to_camel_case_str`), implicit-argument extraction, and attribute parsing; `preprocessed.rs` builds the trait in `to_item_trait` and the blanket impl in `to_item_impl`, including the generics/`where`-clause split and the insertion of the leading `__Context__` parameter. Implicit-argument handling is in [crates/macros/cgp-macro-core/src/types/implicits/](../../../crates/macros/cgp-macro-core/src/types/implicits/), and the companion-attribute parsing is in [crates/macros/cgp-macro-core/src/types/attributes/function.rs](../../../crates/macros/cgp-macro-core/src/types/attributes/function.rs). Behavioral and expansion-snapshot tests are spread across the concept directories: the basic form and implicit arguments in [crates/tests/cgp-tests/tests/implicit_arguments/](../../../crates/tests/cgp-tests/tests/implicit_arguments/), generics in [crates/tests/cgp-tests/tests/generic_components/](../../../crates/tests/cgp-tests/tests/generic_components/), the `#[uses]` and `#[extend]` companion attributes in [crates/tests/cgp-tests/tests/impl_side_dependencies/](../../../crates/tests/cgp-tests/tests/impl_side_dependencies/), `#[use_type]` in [crates/tests/cgp-tests/tests/abstract_types/](../../../crates/tests/cgp-tests/tests/abstract_types/), and `#[use_provider]` in [crates/tests/cgp-tests/tests/higher_order_providers/](../../../crates/tests/cgp-tests/tests/higher_order_providers/). diff --git a/docs/reference/macros/cgp_getter.md b/docs/reference/macros/cgp_getter.md index d0bfd389..c2637f90 100644 --- a/docs/reference/macros/cgp_getter.md +++ b/docs/reference/macros/cgp_getter.md @@ -150,4 +150,4 @@ The direct implementation is the most transparent option and shows that a `#[cgp ## Source -The macro entry point is `cgp_getter` in [crates/macros/cgp-macro-lib/src/cgp_getter.rs](../../../crates/macros/cgp-macro-lib/src/cgp_getter.rs), which derives the default provider name (strip `Has`, append `Getter`), runs the `#[cgp_component]` `preprocess → eval` pipeline, then converts the result into `ItemCgpGetter` and emits the extra provider impls. The logic lives in [crates/macros/cgp-macro-core/src/types/cgp_getter/](../../../crates/macros/cgp-macro-core/src/types/cgp_getter/): `item.rs` assembles the items, `use_field.rs` builds the `UseField` impl with the free `__Tag__` parameter, `to_use_fields_impl.rs` builds the `UseFields` impl keyed by method name, and `with_provider.rs` builds the `WithProvider` impl. Getter-method parsing and the return-type shorthands are shared with `#[cgp_auto_getter]` in [crates/macros/cgp-macro-core/src/functions/getter/parse.rs](../../../crates/macros/cgp-macro-core/src/functions/getter/parse.rs) and [crates/macros/cgp-macro-core/src/types/getter/](../../../crates/macros/cgp-macro-core/src/types/getter/). Behavioral and expansion-snapshot tests are in [crates/tests/cgp-tests/tests/getter.rs](../../../crates/tests/cgp-tests/tests/getter.rs) and the `getter_tests/` modules beside it (notably `string.rs` for the full `UseField`/`UseFields`/`WithProvider` expansion). +The macro entry point is `cgp_getter` in [crates/macros/cgp-macro-lib/src/cgp_getter.rs](../../../crates/macros/cgp-macro-lib/src/cgp_getter.rs), which derives the default provider name (strip `Has`, append `Getter`), runs the `#[cgp_component]` `preprocess → eval` pipeline, then converts the result into `ItemCgpGetter` and emits the extra provider impls. The logic lives in [crates/macros/cgp-macro-core/src/types/cgp_getter/](../../../crates/macros/cgp-macro-core/src/types/cgp_getter/): `item.rs` assembles the items, `use_field.rs` builds the `UseField` impl with the free `__Tag__` parameter, `to_use_fields_impl.rs` builds the `UseFields` impl keyed by method name, and `with_provider.rs` builds the `WithProvider` impl. Getter-method parsing and the return-type shorthands are shared with `#[cgp_auto_getter]` in [crates/macros/cgp-macro-core/src/functions/getter/parse.rs](../../../crates/macros/cgp-macro-core/src/functions/getter/parse.rs) and [crates/macros/cgp-macro-core/src/types/getter/](../../../crates/macros/cgp-macro-core/src/types/getter/). Behavioral and expansion-snapshot tests are in [crates/tests/cgp-tests/tests/getters/](../../../crates/tests/cgp-tests/tests/getters/) (notably `string.rs` for the full `UseField`/`UseFields`/`WithProvider` expansion). diff --git a/docs/reference/macros/cgp_impl.md b/docs/reference/macros/cgp_impl.md index 841d6624..3123667b 100644 --- a/docs/reference/macros/cgp_impl.md +++ b/docs/reference/macros/cgp_impl.md @@ -177,4 +177,4 @@ The call `rect.area()` resolves through the consumer blanket impl to `Rectangle` ## Source -The macro entry point is `cgp_impl` in [crates/macros/cgp-macro-lib/src/cgp_impl.rs](../../../crates/macros/cgp-macro-lib/src/cgp_impl.rs), which lowers the impl and emits the lowered provider impl alongside any default impls. The logic lives in [crates/macros/cgp-macro-core/src/types/cgp_impl/](../../../crates/macros/cgp-macro-core/src/types/cgp_impl/): attribute argument parsing (the `new` keyword, provider type, optional component type) in `args.rs`; the lowering that extracts implicit arguments, applies companion attributes, and inserts the `__Context__` parameter when `for` is omitted in `item.rs`; the rewrite of `self`/`Self` and the handoff to `#[cgp_provider]` in `lowered.rs`; and the bare-impl passthrough for `#[cgp_impl(Self)]` in `provider_or_bare.rs`. The `self`/`Self` rewriting is performed by the `ReplaceSelfType`, `ReplaceSelfReceiver`, and `ReplaceSelfValue` visitors in [crates/macros/cgp-macro-core/src/visitors/](../../../crates/macros/cgp-macro-core/src/visitors/). Expansion snapshots are in [crates/tests/cgp-tests](../../../crates/tests/cgp-tests), notably `tests/component_tests/cgp_impl/`. +The macro entry point is `cgp_impl` in [crates/macros/cgp-macro-lib/src/cgp_impl.rs](../../../crates/macros/cgp-macro-lib/src/cgp_impl.rs), which lowers the impl and emits the lowered provider impl alongside any default impls. The logic lives in [crates/macros/cgp-macro-core/src/types/cgp_impl/](../../../crates/macros/cgp-macro-core/src/types/cgp_impl/): attribute argument parsing (the `new` keyword, provider type, optional component type) in `args.rs`; the lowering that extracts implicit arguments, applies companion attributes, and inserts the `__Context__` parameter when `for` is omitted in `item.rs`; the rewrite of `self`/`Self` and the handoff to `#[cgp_provider]` in `lowered.rs`; and the bare-impl passthrough for `#[cgp_impl(Self)]` in `provider_or_bare.rs`. The `self`/`Self` rewriting is performed by the `ReplaceSelfType`, `ReplaceSelfReceiver`, and `ReplaceSelfValue` visitors in [crates/macros/cgp-macro-core/src/visitors/](../../../crates/macros/cgp-macro-core/src/visitors/). Expansion snapshots are in [crates/tests/cgp-tests](../../../crates/tests/cgp-tests), notably `tests/basic_delegation/provider_macro.rs` for the canonical provider form and `tests/implicit_arguments/` for the `#[implicit]`-argument and implicit-context variants. diff --git a/docs/reference/macros/cgp_namespace.md b/docs/reference/macros/cgp_namespace.md index 72b9f81b..23a9fe88 100644 --- a/docs/reference/macros/cgp_namespace.md +++ b/docs/reference/macros/cgp_namespace.md @@ -210,4 +210,4 @@ Inheritance composes the same way at the namespace level: `ExtendedNamespace: De ## Source -The macro entry point is `cgp_namespace` in [crates/macros/cgp-macro-lib/src/cgp_namespace.rs](../../../crates/macros/cgp-macro-lib/src/cgp_namespace.rs), which parses a `NamespaceTable` and calls `.eval()`. The logic lives in [crates/macros/cgp-macro-core/src/types/namespace/](../../../crates/macros/cgp-macro-core/src/types/namespace/): `table.rs` parses the header (`new`, namespace name, optional `: parent`) and builds the trait, struct, per-entry impls, and the parent-inheritance impl; `inherit.rs` builds the path-rewriting inheritance entry; `eval.rs` holds the emitted `EvaluatedNamespaceTable`. The `#[prefix(...)]` attribute that attaches a component to a namespace is parsed in [crates/macros/cgp-macro-core/src/types/attributes/prefix.rs](../../../crates/macros/cgp-macro-core/src/types/attributes/prefix.rs), and the matching `RedirectLookup` provider impl is emitted by [crates/macros/cgp-macro-core/src/types/cgp_component/evaluated/to_redirect_lookup_impl.rs](../../../crates/macros/cgp-macro-core/src/types/cgp_component/evaluated/to_redirect_lookup_impl.rs). The runtime traits `DefaultNamespace`/`DefaultImpls1`/`DefaultImpls2` are in [crates/core/cgp-component/src/namespaces.rs](../../../crates/core/cgp-component/src/namespaces.rs) and `RedirectLookup` in [crates/core/cgp-component/src/providers/redirect_lookup.rs](../../../crates/core/cgp-component/src/providers/redirect_lookup.rs). Expansion snapshots covering the basic, symbol-path, multi-namespace, extended, and default-impls cases are in [crates/tests/cgp-tests/tests/namespace_tests/namespace_macro/](../../../crates/tests/cgp-tests/tests/namespace_tests/namespace_macro/) and [crates/tests/cgp-tests/src/namespaces/](../../../crates/tests/cgp-tests/src/namespaces/). +The macro entry point is `cgp_namespace` in [crates/macros/cgp-macro-lib/src/cgp_namespace.rs](../../../crates/macros/cgp-macro-lib/src/cgp_namespace.rs), which parses a `NamespaceTable` and calls `.eval()`. The logic lives in [crates/macros/cgp-macro-core/src/types/namespace/](../../../crates/macros/cgp-macro-core/src/types/namespace/): `table.rs` parses the header (`new`, namespace name, optional `: parent`) and builds the trait, struct, per-entry impls, and the parent-inheritance impl; `inherit.rs` builds the path-rewriting inheritance entry; `eval.rs` holds the emitted `EvaluatedNamespaceTable`. The `#[prefix(...)]` attribute that attaches a component to a namespace is parsed in [crates/macros/cgp-macro-core/src/types/attributes/prefix.rs](../../../crates/macros/cgp-macro-core/src/types/attributes/prefix.rs), and the matching `RedirectLookup` provider impl is emitted by [crates/macros/cgp-macro-core/src/types/cgp_component/evaluated/to_redirect_lookup_impl.rs](../../../crates/macros/cgp-macro-core/src/types/cgp_component/evaluated/to_redirect_lookup_impl.rs). The runtime traits `DefaultNamespace`/`DefaultImpls1`/`DefaultImpls2` are in [crates/core/cgp-component/src/namespaces.rs](../../../crates/core/cgp-component/src/namespaces.rs) and `RedirectLookup` in [crates/core/cgp-component/src/providers/redirect_lookup.rs](../../../crates/core/cgp-component/src/providers/redirect_lookup.rs). Expansion snapshots covering the basic, symbol-path, multi-namespace, extended, and default-impls cases are in [crates/tests/cgp-tests/tests/namespaces/](../../../crates/tests/cgp-tests/tests/namespaces/). diff --git a/docs/reference/macros/cgp_new_provider.md b/docs/reference/macros/cgp_new_provider.md index d121a58c..627b446d 100644 --- a/docs/reference/macros/cgp_new_provider.md +++ b/docs/reference/macros/cgp_new_provider.md @@ -115,4 +115,4 @@ This is equivalent to writing `pub struct RectangleArea;` followed by the same i ## Source -The macro entry point is `cgp_new_provider` in [crates/macros/cgp-macro-lib/src/cgp_new_provider.rs](../../../crates/macros/cgp-macro-lib/src/cgp_new_provider.rs); it parses the same `ProviderArgs`, sets `new` to enabled, and then runs the identical lowering as [`#[cgp_provider]`](cgp_provider.md). All of the generation logic — including the struct declaration emitted when `new` is set — lives in [crates/macros/cgp-macro-core/src/types/cgp_provider/](../../../crates/macros/cgp-macro-core/src/types/cgp_provider/); the struct shape is built in `item.rs` (`to_provider_struct`). Expansion snapshots that exercise `#[cgp_new_provider]`, including the generic-struct case, are in [crates/tests/cgp-tests](../../../crates/tests/cgp-tests), notably `src/tests/compose.rs` and `src/tests/async/spawn.rs`. +The macro entry point is `cgp_new_provider` in [crates/macros/cgp-macro-lib/src/cgp_new_provider.rs](../../../crates/macros/cgp-macro-lib/src/cgp_new_provider.rs); it parses the same `ProviderArgs`, sets `new` to enabled, and then runs the identical lowering as [`#[cgp_provider]`](cgp_provider.md). All of the generation logic — including the struct declaration emitted when `new` is set — lives in [crates/macros/cgp-macro-core/src/types/cgp_provider/](../../../crates/macros/cgp-macro-core/src/types/cgp_provider/); the struct shape is built in `item.rs` (`to_provider_struct`). The `#[cgp_impl(new …)]` sugar that desugars to `#[cgp_new_provider]` (declaring the provider struct alongside the impl) is snapshotted in [crates/tests/cgp-tests/tests/basic_delegation/provider_macro.rs](../../../crates/tests/cgp-tests/tests/basic_delegation/provider_macro.rs), and `#[cgp_new_provider]` is exercised directly in [crates/tests/cgp-tests/tests/dispatching/compose.rs](../../../crates/tests/cgp-tests/tests/dispatching/compose.rs) and [crates/tests/cgp-tests/tests/async_and_send/spawn.rs](../../../crates/tests/cgp-tests/tests/async_and_send/spawn.rs). diff --git a/docs/reference/macros/cgp_producer.md b/docs/reference/macros/cgp_producer.md index 93c31091..d33a9997 100644 --- a/docs/reference/macros/cgp_producer.md +++ b/docs/reference/macros/cgp_producer.md @@ -118,4 +118,4 @@ The computer and handler forms accept an input argument and ignore it, since the ## Source -The macro entrypoint is [crates/macros/cgp-extra-macro/src/lib.rs](../../../crates/macros/cgp-extra-macro/src/lib.rs), forwarding to the implementation in [crates/macros/cgp-extra-macro-lib/src/entrypoints/cgp_producer.rs](../../../crates/macros/cgp-extra-macro-lib/src/entrypoints/cgp_producer.rs). The `Producer` trait is defined in [crates/extra/cgp-handler/src/components/produce.rs](../../../crates/extra/cgp-handler/src/components/produce.rs), and the `PromoteProducer` bundle in [crates/extra/cgp-handler/src/providers/promote_all.rs](../../../crates/extra/cgp-handler/src/providers/promote_all.rs). Behavioral and snapshot tests live in [crates/tests/cgp-tests/tests/handler_tests/producer_macro.rs](../../../crates/tests/cgp-tests/tests/handler_tests/producer_macro.rs). +The macro entrypoint is [crates/macros/cgp-extra-macro/src/lib.rs](../../../crates/macros/cgp-extra-macro/src/lib.rs), forwarding to the implementation in [crates/macros/cgp-extra-macro-lib/src/entrypoints/cgp_producer.rs](../../../crates/macros/cgp-extra-macro-lib/src/entrypoints/cgp_producer.rs). The `Producer` trait is defined in [crates/extra/cgp-handler/src/components/produce.rs](../../../crates/extra/cgp-handler/src/components/produce.rs), and the `PromoteProducer` bundle in [crates/extra/cgp-handler/src/providers/promote_all.rs](../../../crates/extra/cgp-handler/src/providers/promote_all.rs). Behavioral and snapshot tests live in [crates/tests/cgp-tests/tests/handlers/producer_macro.rs](../../../crates/tests/cgp-tests/tests/handlers/producer_macro.rs). diff --git a/docs/reference/macros/cgp_provider.md b/docs/reference/macros/cgp_provider.md index 72c68741..a944739e 100644 --- a/docs/reference/macros/cgp_provider.md +++ b/docs/reference/macros/cgp_provider.md @@ -138,4 +138,4 @@ In most code, the same provider would be written more concisely with [`#[cgp_imp ## Source -The macro entry point is `cgp_provider` in [crates/macros/cgp-macro-lib/src/cgp_provider.rs](../../../crates/macros/cgp-macro-lib/src/cgp_provider.rs), which parses the optional component argument, lowers the impl, and emits the result. The logic lives in [crates/macros/cgp-macro-core/src/types/cgp_provider/](../../../crates/macros/cgp-macro-core/src/types/cgp_provider/): attribute argument parsing (the optional `new` keyword and component type) in `args.rs`; the lowering that derives the component default, the provider struct, and the `IsProviderFor` impl in `item.rs`; the emitted-token assembly in `lower.rs`; and the splitting of provider-trait arguments into context and `Params` tuple in `provider_impl_args.rs`. The `IsProviderFor` derivation itself is in [crates/macros/cgp-macro-core/src/types/provider_impl.rs](../../../crates/macros/cgp-macro-core/src/types/provider_impl.rs). Expansion snapshots are in [crates/tests/cgp-tests](../../../crates/tests/cgp-tests), notably `src/tests/compose.rs` and `src/tests/async/spawn.rs`. +The macro entry point is `cgp_provider` in [crates/macros/cgp-macro-lib/src/cgp_provider.rs](../../../crates/macros/cgp-macro-lib/src/cgp_provider.rs), which parses the optional component argument, lowers the impl, and emits the result. The logic lives in [crates/macros/cgp-macro-core/src/types/cgp_provider/](../../../crates/macros/cgp-macro-core/src/types/cgp_provider/): attribute argument parsing (the optional `new` keyword and component type) in `args.rs`; the lowering that derives the component default, the provider struct, and the `IsProviderFor` impl in `item.rs`; the emitted-token assembly in `lower.rs`; and the splitting of provider-trait arguments into context and `Params` tuple in `provider_impl_args.rs`. The `IsProviderFor` derivation itself is in [crates/macros/cgp-macro-core/src/types/provider_impl.rs](../../../crates/macros/cgp-macro-core/src/types/provider_impl.rs). Expansion snapshots of the generated provider impl are in [crates/tests/cgp-tests/tests/generic_components/](../../../crates/tests/cgp-tests/tests/generic_components/) (the const-generic and lifetime provider cases), and `#[cgp_provider]` is exercised directly in [crates/tests/cgp-tests/tests/dispatching/compose.rs](../../../crates/tests/cgp-tests/tests/dispatching/compose.rs) and [crates/tests/cgp-tests/tests/async_and_send/spawn.rs](../../../crates/tests/cgp-tests/tests/async_and_send/spawn.rs). diff --git a/docs/reference/macros/cgp_type.md b/docs/reference/macros/cgp_type.md index 6c2aaa10..a7857808 100644 --- a/docs/reference/macros/cgp_type.md +++ b/docs/reference/macros/cgp_type.md @@ -128,4 +128,4 @@ This direct form is only marginally longer than the wired form and is the most a ## Source -The macro entry point is `cgp_type` in [crates/macros/cgp-macro-lib/src/cgp_type.rs](../../../crates/macros/cgp-macro-lib/src/cgp_type.rs), which extracts the single associated type, derives the default `{Type}TypeProvider` provider name from the associated type's identifier, runs the `#[cgp_component]` `preprocess → eval` pipeline, and converts the result into `ItemCgpType`. The logic lives in [crates/macros/cgp-macro-core/src/types/cgp_type/item.rs](../../../crates/macros/cgp-macro-core/src/types/cgp_type/item.rs), which validates the trait shape (`extract_item_type_from_trait`) and builds the `UseType` and `WithProvider` provider impls. The runtime `HasType`, `TypeProvider`, and `UseType` definitions are in [crates/core/cgp-type/src/](../../../crates/core/cgp-type/src/) (`traits/has_type.rs` and `impls/use_type.rs`). Behavioral and expansion-snapshot tests are in [crates/tests/cgp-tests/tests/component_tests/abstract_types/](../../../crates/tests/cgp-tests/tests/component_tests/abstract_types/) (notably `basic.rs`). +The macro entry point is `cgp_type` in [crates/macros/cgp-macro-lib/src/cgp_type.rs](../../../crates/macros/cgp-macro-lib/src/cgp_type.rs), which extracts the single associated type, derives the default `{Type}TypeProvider` provider name from the associated type's identifier, runs the `#[cgp_component]` `preprocess → eval` pipeline, and converts the result into `ItemCgpType`. The logic lives in [crates/macros/cgp-macro-core/src/types/cgp_type/item.rs](../../../crates/macros/cgp-macro-core/src/types/cgp_type/item.rs), which validates the trait shape (`extract_item_type_from_trait`) and builds the `UseType` and `WithProvider` provider impls. The runtime `HasType`, `TypeProvider`, and `UseType` definitions are in [crates/core/cgp-type/src/](../../../crates/core/cgp-type/src/) (`traits/has_type.rs` and `impls/use_type.rs`). Behavioral and expansion-snapshot tests are in [crates/tests/cgp-tests/tests/abstract_types/](../../../crates/tests/cgp-tests/tests/abstract_types/) (notably `cgp_type_macro.rs`). diff --git a/docs/reference/macros/check_components.md b/docs/reference/macros/check_components.md index 84213143..a38327b1 100644 --- a/docs/reference/macros/check_components.md +++ b/docs/reference/macros/check_components.md @@ -213,4 +213,4 @@ This verifies `MyApp: CanCalculateAreaOfShape` and `MyApp: CanCalcula ## Source -The macro entry point is `check_components` in [crates/macros/cgp-macro-lib/src/check_components.rs](../../../crates/macros/cgp-macro-lib/src/check_components.rs), which parses `CheckComponentsTables` and emits their items. The logic lives in [crates/macros/cgp-macro-core/src/types/check_components/](../../../crates/macros/cgp-macro-core/src/types/check_components/): table parsing, the `#[check_trait]`/`#[check_providers]` attributes, the `__Check{Context}` name derivation, and the choice between `CanUseComponent` and `IsProviderFor` supertraits are all in `table.rs`; key and value parsing (including array syntax) in `key.rs` and `value.rs`; and the cartesian-product expansion of entries in `entry.rs`. Expansion snapshots are in [crates/tests/cgp-tests/src/tests/check_components.rs](../../../crates/tests/cgp-tests/src/tests/check_components.rs). +The macro entry point is `check_components` in [crates/macros/cgp-macro-lib/src/check_components.rs](../../../crates/macros/cgp-macro-lib/src/check_components.rs), which parses `CheckComponentsTables` and emits their items. The logic lives in [crates/macros/cgp-macro-core/src/types/check_components/](../../../crates/macros/cgp-macro-core/src/types/check_components/): table parsing, the `#[check_trait]`/`#[check_providers]` attributes, the `__Check{Context}` name derivation, and the choice between `CanUseComponent` and `IsProviderFor` supertraits are all in `table.rs`; key and value parsing (including array syntax) in `key.rs` and `value.rs`; and the cartesian-product expansion of entries in `entry.rs`. Expansion snapshots are in [crates/tests/cgp-tests/tests/checking/](../../../crates/tests/cgp-tests/tests/checking/). diff --git a/docs/reference/macros/delegate_and_check_components.md b/docs/reference/macros/delegate_and_check_components.md index a876cf92..a37b7c54 100644 --- a/docs/reference/macros/delegate_and_check_components.md +++ b/docs/reference/macros/delegate_and_check_components.md @@ -182,4 +182,4 @@ delegate_and_check_components! { ## Source -The macro entry point is `delegate_and_check_components` in [crates/macros/cgp-macro-lib/src/delegate_and_check_components.rs](../../../crates/macros/cgp-macro-lib/src/delegate_and_check_components.rs), which parses the table, evaluates the delegation half via the shared `DelegateTable`, derives a `CheckComponentsTable` from the keys, and emits both. The logic lives in [crates/macros/cgp-macro-core/src/types/delegate_and_check_components/](../../../crates/macros/cgp-macro-core/src/types/delegate_and_check_components/): the `__CanUse{Context}` default name and `#[check_trait]` handling in `item.rs`, the `#[check_params]`/`#[skip_check]` parsing and their mutual exclusion in `check_params.rs`, the per-key conversion to check entries in `key_with_check_params.rs`, and the walk over delegation entries in `to_keys_with_check_params.rs`. It reuses the `DelegateTable` from [delegate_component/](../../../crates/macros/cgp-macro-core/src/types/delegate_component/) and the `CheckComponentsTable` from [check_components/](../../../crates/macros/cgp-macro-core/src/types/check_components/). Expansion snapshots are in [crates/tests/cgp-tests/src/tests/delegate_and_check_components.rs](../../../crates/tests/cgp-tests/src/tests/delegate_and_check_components.rs) and [crates/tests/cgp-tests/src/tests/check_components.rs](../../../crates/tests/cgp-tests/src/tests/check_components.rs). +The macro entry point is `delegate_and_check_components` in [crates/macros/cgp-macro-lib/src/delegate_and_check_components.rs](../../../crates/macros/cgp-macro-lib/src/delegate_and_check_components.rs), which parses the table, evaluates the delegation half via the shared `DelegateTable`, derives a `CheckComponentsTable` from the keys, and emits both. The logic lives in [crates/macros/cgp-macro-core/src/types/delegate_and_check_components/](../../../crates/macros/cgp-macro-core/src/types/delegate_and_check_components/): the `__CanUse{Context}` default name and `#[check_trait]` handling in `item.rs`, the `#[check_params]`/`#[skip_check]` parsing and their mutual exclusion in `check_params.rs`, the per-key conversion to check entries in `key_with_check_params.rs`, and the walk over delegation entries in `to_keys_with_check_params.rs`. It reuses the `DelegateTable` from [delegate_component/](../../../crates/macros/cgp-macro-core/src/types/delegate_component/) and the `CheckComponentsTable` from [check_components/](../../../crates/macros/cgp-macro-core/src/types/check_components/). Expansion snapshots are in [crates/tests/cgp-tests/tests/checking/](../../../crates/tests/cgp-tests/tests/checking/). diff --git a/docs/reference/macros/delegate_components.md b/docs/reference/macros/delegate_components.md index 684b7fb8..44fbe584 100644 --- a/docs/reference/macros/delegate_components.md +++ b/docs/reference/macros/delegate_components.md @@ -253,4 +253,4 @@ delegate_components! { ## Source -The macro entry point is `delegate_components` in [crates/macros/cgp-macro-lib/src/delegate_components.rs](../../../crates/macros/cgp-macro-lib/src/delegate_components.rs), which parses a `DelegateTable`, validates that no attributes are present, evaluates it, and emits the tokens. The logic lives in [crates/macros/cgp-macro-core/src/types/delegate_component/](../../../crates/macros/cgp-macro-core/src/types/delegate_component/): the top-level table and the `new` keyword in `table/main.rs`, key parsing (single, array/`Multi`, path) in `key/`, value parsing including the nested-table form in `value/`, the statement forms (`open`, `namespace`, `for`) in `statement/` — with the `open` statement and its `RedirectLookup` expansion in `statement/open.rs` — and the `DelegateComponent`/`IsProviderFor` impl construction in `mapping/eval.rs`. Attribute rejection is in `validate_attributes.rs`. Expansion snapshots covering the single-entry, array, and generic-table forms are in [crates/tests/cgp-tests/tests/component_tests/delegate_components/](../../../crates/tests/cgp-tests/tests/component_tests/delegate_components/), and the namespace-header form (`namespace …;`, `@`-path keys) is exercised by the namespace snapshots in [crates/tests/cgp-tests/tests/namespace_tests/namespace_macro/](../../../crates/tests/cgp-tests/tests/namespace_tests/namespace_macro/). +The macro entry point is `delegate_components` in [crates/macros/cgp-macro-lib/src/delegate_components.rs](../../../crates/macros/cgp-macro-lib/src/delegate_components.rs), which parses a `DelegateTable`, validates that no attributes are present, evaluates it, and emits the tokens. The logic lives in [crates/macros/cgp-macro-core/src/types/delegate_component/](../../../crates/macros/cgp-macro-core/src/types/delegate_component/): the top-level table and the `new` keyword in `table/main.rs`, key parsing (single, array/`Multi`, path) in `key/`, value parsing including the nested-table form in `value/`, the statement forms (`open`, `namespace`, `for`) in `statement/` — with the `open` statement and its `RedirectLookup` expansion in `statement/open.rs` — and the `DelegateComponent`/`IsProviderFor` impl construction in `mapping/eval.rs`. Attribute rejection is in `validate_attributes.rs`. Expansion snapshots covering the single-entry, array, and generic-table forms are in [crates/tests/cgp-tests/tests/basic_delegation/](../../../crates/tests/cgp-tests/tests/basic_delegation/), and the namespace-header form (`namespace …;`, `@`-path keys) is exercised by the namespace snapshots in [crates/tests/cgp-tests/tests/namespaces/](../../../crates/tests/cgp-tests/tests/namespaces/). diff --git a/docs/reference/macros/path.md b/docs/reference/macros/path.md index 84cc29f7..0f81ad90 100644 --- a/docs/reference/macros/path.md +++ b/docs/reference/macros/path.md @@ -93,4 +93,4 @@ Either way the path is the same `PathCons` list; the macro and the namespace sim ## Source -The macro entry point is `Path` in [crates/macros/cgp-macro-lib/src/path.rs](../../../crates/macros/cgp-macro-lib/src/path.rs), which parses the body into a `UniPath` and emits its tokens. The parsing and codegen live in [crates/macros/cgp-macro-core/src/types/path/](../../../crates/macros/cgp-macro-core/src/types/path/): `unipath.rs` requires the leading `@`, parses the dot-separated segments, and right-folds them with `PathCons` onto `Nil`; `path_element.rs` decides per segment whether a lowercase, non-primitive identifier becomes a `Symbol` or the segment stays a named type. The runtime spine `PathCons` is defined in [crates/core/cgp-base-types/src/types/path.rs](../../../crates/core/cgp-base-types/src/types/path.rs), and the `RedirectLookup` provider that walks a path is in [crates/core/cgp-component/src/providers/redirect_lookup.rs](../../../crates/core/cgp-component/src/providers/redirect_lookup.rs). The `@`-path forms are exercised by the namespace snapshot tests in [crates/tests/cgp-tests/tests/namespace_tests/namespace_macro/](../../../crates/tests/cgp-tests/tests/namespace_tests/namespace_macro/). +The macro entry point is `Path` in [crates/macros/cgp-macro-lib/src/path.rs](../../../crates/macros/cgp-macro-lib/src/path.rs), which parses the body into a `UniPath` and emits its tokens. The parsing and codegen live in [crates/macros/cgp-macro-core/src/types/path/](../../../crates/macros/cgp-macro-core/src/types/path/): `unipath.rs` requires the leading `@`, parses the dot-separated segments, and right-folds them with `PathCons` onto `Nil`; `path_element.rs` decides per segment whether a lowercase, non-primitive identifier becomes a `Symbol` or the segment stays a named type. The runtime spine `PathCons` is defined in [crates/core/cgp-base-types/src/types/path.rs](../../../crates/core/cgp-base-types/src/types/path.rs), and the `RedirectLookup` provider that walks a path is in [crates/core/cgp-component/src/providers/redirect_lookup.rs](../../../crates/core/cgp-component/src/providers/redirect_lookup.rs). The `@`-path forms are exercised by the namespace snapshot tests in [crates/tests/cgp-tests/tests/namespaces/](../../../crates/tests/cgp-tests/tests/namespaces/). diff --git a/docs/reference/macros/symbol.md b/docs/reference/macros/symbol.md index 6c86d53c..9882da59 100644 --- a/docs/reference/macros/symbol.md +++ b/docs/reference/macros/symbol.md @@ -85,4 +85,4 @@ When a struct derives [`HasField`](../derives/derive_has_field.md), each named f ## Source -The macro entry point is `Symbol` in [crates/macros/cgp-macro-lib/src/symbol.rs](../../../crates/macros/cgp-macro-lib/src/symbol.rs), which forwards to the `Symbol` construct in [crates/macros/cgp-macro-core/src/types/field/symbol.rs](../../../crates/macros/cgp-macro-core/src/types/field/symbol.rs) — its `ToTokens` impl performs the right-fold over the characters, computes `LEN` from `str::len()`, and wraps the result in `Symbol`. The runtime types are defined in [crates/core/cgp-base-types/src/types/symbol.rs](../../../crates/core/cgp-base-types/src/types/symbol.rs) (`Symbol`) and [crates/core/cgp-base-types/src/types/chars.rs](../../../crates/core/cgp-base-types/src/types/chars.rs) (`Chars`), with `Nil` in [crates/core/cgp-base-types/src/types/nil.rs](../../../crates/core/cgp-base-types/src/types/nil.rs). Tests covering display round-tripping and multi-byte strings are in [crates/tests/cgp-tests/src/tests/symbol.rs](../../../crates/tests/cgp-tests/src/tests/symbol.rs). +The macro entry point is `Symbol` in [crates/macros/cgp-macro-lib/src/symbol.rs](../../../crates/macros/cgp-macro-lib/src/symbol.rs), which forwards to the `Symbol` construct in [crates/macros/cgp-macro-core/src/types/field/symbol.rs](../../../crates/macros/cgp-macro-core/src/types/field/symbol.rs) — its `ToTokens` impl performs the right-fold over the characters, computes `LEN` from `str::len()`, and wraps the result in `Symbol`. The runtime types are defined in [crates/core/cgp-base-types/src/types/symbol.rs](../../../crates/core/cgp-base-types/src/types/symbol.rs) (`Symbol`) and [crates/core/cgp-base-types/src/types/chars.rs](../../../crates/core/cgp-base-types/src/types/chars.rs) (`Chars`), with `Nil` in [crates/core/cgp-base-types/src/types/nil.rs](../../../crates/core/cgp-base-types/src/types/nil.rs). Tests covering display round-tripping and multi-byte strings are in [crates/tests/cgp-tests/tests/field_access/symbol.rs](../../../crates/tests/cgp-tests/tests/field_access/symbol.rs). diff --git a/docs/reference/providers/dispatch_combinators.md b/docs/reference/providers/dispatch_combinators.md index 0c38591e..59f40422 100644 --- a/docs/reference/providers/dispatch_combinators.md +++ b/docs/reference/providers/dispatch_combinators.md @@ -266,4 +266,4 @@ The matchers stand on the enum-deconstruction traits in [`extract_field`](../tra ## Source -The provider structs live under [crates/extra/cgp-dispatch/src/providers/](../../../crates/extra/cgp-dispatch/src/providers/): the matchers in `with_handlers/` (`match_with_handlers.rs`, `match_with_handlers_ref.rs`, `match_with_handlers_mut.rs`, `match_first_with_handlers*.rs`, `build_with_handlers.rs`), the matcher loop alias in `dispatchers/dispatch_matchers.rs`, the field/variant adapters in `field_matchers/` (`extract_field.rs`, `extract_first_field.rs`, `extract_handle.rs`, `field_value.rs`, `first_field_value.rs`), the convenience matchers and the `ToFieldHandlers`/`HasFieldHandlers`/`MapFieldHandler` machinery in `matchers/` (`match_with_field_handlers.rs`, `match_first_with_field_handlers.rs`, `to_field_handlers.rs`), and the builders in `field_builders/` (`build_and_set_field.rs`, `build_and_merge.rs`) and `builders/build_and_merge_outputs.rs`. The prelude re-exports the value-handler matchers from [crates/main/cgp-extra/src/prelude.rs](../../../crates/main/cgp-extra/src/prelude.rs); the remaining structs are reached through `cgp::extra::dispatch`. Tests exercising them are in [crates/tests/cgp-tests/tests/extensible_data_tests/variants/](../../../crates/tests/cgp-tests/tests/extensible_data_tests/variants/) (matchers) and [records/](../../../crates/tests/cgp-tests/tests/extensible_data_tests/records/) (builders). +The provider structs live under [crates/extra/cgp-dispatch/src/providers/](../../../crates/extra/cgp-dispatch/src/providers/): the matchers in `with_handlers/` (`match_with_handlers.rs`, `match_with_handlers_ref.rs`, `match_with_handlers_mut.rs`, `match_first_with_handlers*.rs`, `build_with_handlers.rs`), the matcher loop alias in `dispatchers/dispatch_matchers.rs`, the field/variant adapters in `field_matchers/` (`extract_field.rs`, `extract_first_field.rs`, `extract_handle.rs`, `field_value.rs`, `first_field_value.rs`), the convenience matchers and the `ToFieldHandlers`/`HasFieldHandlers`/`MapFieldHandler` machinery in `matchers/` (`match_with_field_handlers.rs`, `match_first_with_field_handlers.rs`, `to_field_handlers.rs`), and the builders in `field_builders/` (`build_and_set_field.rs`, `build_and_merge.rs`) and `builders/build_and_merge_outputs.rs`. The prelude re-exports the value-handler matchers from [crates/main/cgp-extra/src/prelude.rs](../../../crates/main/cgp-extra/src/prelude.rs); the remaining structs are reached through `cgp::extra::dispatch`. Tests exercising them are in [crates/tests/cgp-tests/tests/extensible_variants/](../../../crates/tests/cgp-tests/tests/extensible_variants/) (matchers) and [extensible_records/](../../../crates/tests/cgp-tests/tests/extensible_records/) (builders). diff --git a/docs/reference/providers/handler_combinators.md b/docs/reference/providers/handler_combinators.md index 873110f9..4ea2b21e 100644 --- a/docs/reference/providers/handler_combinators.md +++ b/docs/reference/providers/handler_combinators.md @@ -217,4 +217,4 @@ The combinators are providers of the handler component traits: [`Computer`](../c ## Source -The combinators are defined in `cgp-handler` under [crates/extra/cgp-handler/src/providers/](../../../crates/extra/cgp-handler/src/providers/): `compose.rs` (`ComposeHandlers`), `pipe.rs` (`PipeHandlers` and the internal `ComposeProviders` fold), `return_input.rs` (`ReturnInput`), `promote.rs` (`Promote`), `promote_async.rs` (`PromoteAsync`), `promote_ref.rs` (`PromoteRef`), `try_promote.rs` (`TryPromote`), and `promote_all.rs` (the `PromoteComputer`, `PromoteTryComputer`, `PromoteProducer`, `PromoteAsyncComputer`, and `PromoteHandler` bundles). `UseInputDelegate` is defined in [crates/extra/cgp-handler/src/types.rs](../../../crates/extra/cgp-handler/src/types.rs), and its provider impls are generated from the `#[derive_delegate(UseInputDelegate)]` directive on the handler component traits in [crates/extra/cgp-handler/src/components/](../../../crates/extra/cgp-handler/src/components/). Behavioral tests exercising the combinators live in [crates/tests/cgp-tests/tests/handler_tests/](../../../crates/tests/cgp-tests/tests/handler_tests/), notably `pipe.rs`. +The combinators are defined in `cgp-handler` under [crates/extra/cgp-handler/src/providers/](../../../crates/extra/cgp-handler/src/providers/): `compose.rs` (`ComposeHandlers`), `pipe.rs` (`PipeHandlers` and the internal `ComposeProviders` fold), `return_input.rs` (`ReturnInput`), `promote.rs` (`Promote`), `promote_async.rs` (`PromoteAsync`), `promote_ref.rs` (`PromoteRef`), `try_promote.rs` (`TryPromote`), and `promote_all.rs` (the `PromoteComputer`, `PromoteTryComputer`, `PromoteProducer`, `PromoteAsyncComputer`, and `PromoteHandler` bundles). `UseInputDelegate` is defined in [crates/extra/cgp-handler/src/types.rs](../../../crates/extra/cgp-handler/src/types.rs), and its provider impls are generated from the `#[derive_delegate(UseInputDelegate)]` directive on the handler component traits in [crates/extra/cgp-handler/src/components/](../../../crates/extra/cgp-handler/src/components/). Behavioral tests exercising the combinators live in [crates/tests/cgp-tests/tests/handlers/](../../../crates/tests/cgp-tests/tests/handlers/), notably `pipe.rs`. diff --git a/docs/reference/providers/monad_providers.md b/docs/reference/providers/monad_providers.md index 48435422..54007f08 100644 --- a/docs/reference/providers/monad_providers.md +++ b/docs/reference/providers/monad_providers.md @@ -101,4 +101,4 @@ PipeMonadic::::try_co ## Source -The pipeline provider, `TryPromoteProviders`, and the internal `BindProviders` fold are in [crates/extra/cgp-monad/src/providers/pipe_monadic.rs](../../../crates/extra/cgp-monad/src/providers/pipe_monadic.rs). The monad markers and bind providers are in [crates/extra/cgp-monad/src/monadic/](../../../crates/extra/cgp-monad/src/monadic/) — `ident.rs` for `IdentMonadic`, `ok.rs` for `OkMonadic` / `OkMonadicTrans` / `BindOk`, and `err.rs` for `ErrMonadic` / `ErrMonadicTrans` / `BindErr`. Usage is exercised in [crates/tests/cgp-tests/src/tests/monad/](../../../crates/tests/cgp-tests/src/tests/monad/) (`ok.rs`, `err.rs`, `ok_err_trans.rs`). +The pipeline provider, `TryPromoteProviders`, and the internal `BindProviders` fold are in [crates/extra/cgp-monad/src/providers/pipe_monadic.rs](../../../crates/extra/cgp-monad/src/providers/pipe_monadic.rs). The monad markers and bind providers are in [crates/extra/cgp-monad/src/monadic/](../../../crates/extra/cgp-monad/src/monadic/) — `ident.rs` for `IdentMonadic`, `ok.rs` for `OkMonadic` / `OkMonadicTrans` / `BindOk`, and `err.rs` for `ErrMonadic` / `ErrMonadicTrans` / `BindErr`. Usage is exercised in [crates/tests/cgp-tests/tests/monadic_handlers/](../../../crates/tests/cgp-tests/tests/monadic_handlers/) (`ok.rs`, `err.rs`, `ok_err_trans.rs`). diff --git a/docs/reference/providers/redirect_lookup.md b/docs/reference/providers/redirect_lookup.md index a5977d50..930e3e24 100644 --- a/docs/reference/providers/redirect_lookup.md +++ b/docs/reference/providers/redirect_lookup.md @@ -78,4 +78,4 @@ This registers `TestProvider` under the path `bar`/`baz` in `App`'s default name ## Source -The struct is defined in [crates/core/cgp-component/src/providers/redirect_lookup.rs](../../../crates/core/cgp-component/src/providers/redirect_lookup.rs), and the related `DefaultNamespace` trait in [crates/core/cgp-component/src/namespaces.rs](../../../crates/core/cgp-component/src/namespaces.rs). The `RedirectLookup` provider impl is generated by `to_redirect_lookup_impl` in [crates/macros/cgp-macro-core/src/types/cgp_component/evaluated/to_redirect_lookup_impl.rs](../../../crates/macros/cgp-macro-core/src/types/cgp_component/evaluated/to_redirect_lookup_impl.rs), which appends generic parameters through `ConcatPath`. The namespace delegates that target `RedirectLookup` are produced by the `#[prefix]` attribute in [crates/macros/cgp-macro-core/src/types/attributes/prefix.rs](../../../crates/macros/cgp-macro-core/src/types/attributes/prefix.rs) and the redirect mapping in [crates/macros/cgp-macro-core/src/types/delegate_component/mapping/redirect.rs](../../../crates/macros/cgp-macro-core/src/types/delegate_component/mapping/redirect.rs). Expansion snapshots showing the generated impl and the namespace wiring are in [crates/tests/cgp-tests/tests/namespace_tests/redirect.rs](../../../crates/tests/cgp-tests/tests/namespace_tests/redirect.rs). +The struct is defined in [crates/core/cgp-component/src/providers/redirect_lookup.rs](../../../crates/core/cgp-component/src/providers/redirect_lookup.rs), and the related `DefaultNamespace` trait in [crates/core/cgp-component/src/namespaces.rs](../../../crates/core/cgp-component/src/namespaces.rs). The `RedirectLookup` provider impl is generated by `to_redirect_lookup_impl` in [crates/macros/cgp-macro-core/src/types/cgp_component/evaluated/to_redirect_lookup_impl.rs](../../../crates/macros/cgp-macro-core/src/types/cgp_component/evaluated/to_redirect_lookup_impl.rs), which appends generic parameters through `ConcatPath`. The namespace delegates that target `RedirectLookup` are produced by the `#[prefix]` attribute in [crates/macros/cgp-macro-core/src/types/attributes/prefix.rs](../../../crates/macros/cgp-macro-core/src/types/attributes/prefix.rs) and the redirect mapping in [crates/macros/cgp-macro-core/src/types/delegate_component/mapping/redirect.rs](../../../crates/macros/cgp-macro-core/src/types/delegate_component/mapping/redirect.rs). Expansion snapshots showing the generated impl and the namespace wiring are in [crates/tests/cgp-tests/tests/namespaces/](../../../crates/tests/cgp-tests/tests/namespaces/). diff --git a/docs/reference/providers/use_context.md b/docs/reference/providers/use_context.md index f9084131..5d4b9a69 100644 --- a/docs/reference/providers/use_context.md +++ b/docs/reference/providers/use_context.md @@ -85,4 +85,4 @@ Note that `UseContext` only acts as a default when a higher-order provider's str ## Source -The struct is defined in [crates/core/cgp-component/src/providers/use_context.rs](../../../crates/core/cgp-component/src/providers/use_context.rs), which also declares the `WithContext` alias. The `UseContext` provider impl is generated by `to_use_context_impl` in [crates/macros/cgp-macro-core/src/types/cgp_component/evaluated/to_use_context_impl.rs](../../../crates/macros/cgp-macro-core/src/types/cgp_component/evaluated/to_use_context_impl.rs), which forwards each provider-trait method to the consumer trait via `trait_items_to_delegated_impl_items`. Expansion snapshots showing the generated `UseContext` impl appear in [crates/tests/cgp-tests/tests/getter_tests/string.rs](../../../crates/tests/cgp-tests/tests/getter_tests/string.rs) and [crates/tests/cgp-tests/tests/component_tests/cgp_component/default_impl.rs](../../../crates/tests/cgp-tests/tests/component_tests/cgp_component/default_impl.rs). +The struct is defined in [crates/core/cgp-component/src/providers/use_context.rs](../../../crates/core/cgp-component/src/providers/use_context.rs), which also declares the `WithContext` alias. The `UseContext` provider impl is generated by `to_use_context_impl` in [crates/macros/cgp-macro-core/src/types/cgp_component/evaluated/to_use_context_impl.rs](../../../crates/macros/cgp-macro-core/src/types/cgp_component/evaluated/to_use_context_impl.rs), which forwards each provider-trait method to the consumer trait via `trait_items_to_delegated_impl_items`. Expansion snapshots showing the generated `UseContext` impl appear in [crates/tests/cgp-tests/tests/getters/](../../../crates/tests/cgp-tests/tests/getters/) and [crates/tests/cgp-tests/tests/basic_delegation/](../../../crates/tests/cgp-tests/tests/basic_delegation/). diff --git a/docs/reference/providers/use_default.md b/docs/reference/providers/use_default.md index 558f75e5..bf25b45d 100644 --- a/docs/reference/providers/use_default.md +++ b/docs/reference/providers/use_default.md @@ -107,4 +107,4 @@ delegate_components! { ## Source -The struct is defined in [crates/core/cgp-component/src/providers/use_default.rs](../../../crates/core/cgp-component/src/providers/use_default.rs) and re-exported in [crates/core/cgp-component/src/providers/mod.rs](../../../crates/core/cgp-component/src/providers/mod.rs); the file contains only the bare struct, with no macro-generated impls. The empty-body `#[cgp_impl]` pattern that wires components to a default-only marker is exercised in [crates/tests/cgp-tests/tests/component_tests/cgp_component/default_impl.rs](../../../crates/tests/cgp-tests/tests/component_tests/cgp_component/default_impl.rs). +The struct is defined in [crates/core/cgp-component/src/providers/use_default.rs](../../../crates/core/cgp-component/src/providers/use_default.rs) and re-exported in [crates/core/cgp-component/src/providers/mod.rs](../../../crates/core/cgp-component/src/providers/mod.rs); the file contains only the bare struct, with no macro-generated impls. The empty-body `#[cgp_impl]` pattern that wires components to a default-only marker is exercised in [crates/tests/cgp-tests/tests/basic_delegation/](../../../crates/tests/cgp-tests/tests/basic_delegation/). diff --git a/docs/reference/providers/use_delegate.md b/docs/reference/providers/use_delegate.md index 58d60035..0634810c 100644 --- a/docs/reference/providers/use_delegate.md +++ b/docs/reference/providers/use_delegate.md @@ -95,4 +95,4 @@ The wiring reads in two layers. `MyApp` delegates `AreaCalculatorComponent` to ` ## Source -The struct is defined in [crates/core/cgp-component/src/providers/use_delegate.rs](../../../crates/core/cgp-component/src/providers/use_delegate.rs). The `UseDelegate` provider impl is generated from the `#[derive_delegate]` directive parsed in [crates/macros/cgp-macro-core/src/types/attributes/](../../../crates/macros/cgp-macro-core/src/types/attributes/) and emitted by the `#[cgp_component]` pipeline in [crates/macros/cgp-macro-core/src/types/cgp_component/](../../../crates/macros/cgp-macro-core/src/types/cgp_component/). The nested-table wiring is handled by `delegate_components!` in [crates/macros/cgp-macro-core/src/types/delegate_component/](../../../crates/macros/cgp-macro-core/src/types/delegate_component/). Behavioral tests exercising `UseDelegate` dispatch live in [crates/tests/cgp-tests/src/tests/use_delegate/](../../../crates/tests/cgp-tests/src/tests/use_delegate/). +The struct is defined in [crates/core/cgp-component/src/providers/use_delegate.rs](../../../crates/core/cgp-component/src/providers/use_delegate.rs). The `UseDelegate` provider impl is generated from the `#[derive_delegate]` directive parsed in [crates/macros/cgp-macro-core/src/types/attributes/](../../../crates/macros/cgp-macro-core/src/types/attributes/) and emitted by the `#[cgp_component]` pipeline in [crates/macros/cgp-macro-core/src/types/cgp_component/](../../../crates/macros/cgp-macro-core/src/types/cgp_component/). The nested-table wiring is handled by `delegate_components!` in [crates/macros/cgp-macro-core/src/types/delegate_component/](../../../crates/macros/cgp-macro-core/src/types/delegate_component/). Behavioral tests exercising `UseDelegate` dispatch live in [crates/tests/cgp-tests/tests/dispatching/](../../../crates/tests/cgp-tests/tests/dispatching/). diff --git a/docs/reference/providers/use_field.md b/docs/reference/providers/use_field.md index 8a7dc62d..1e568560 100644 --- a/docs/reference/providers/use_field.md +++ b/docs/reference/providers/use_field.md @@ -91,4 +91,4 @@ Both forms read `first_name`; `UseField` is the idiomatic choice for binding a g ## Source -The `UseField` struct, its `WithField` alias, and the `FieldGetter`, `MutFieldGetter`, and `TypeProvider` impls are in [crates/core/cgp-field/src/impls/use_field.rs](../../../crates/core/cgp-field/src/impls/use_field.rs). The `HasField`, `FieldGetter`, and (in `has_field_mut.rs`) `HasFieldMut`, `MutFieldGetter` traits are in [crates/core/cgp-field/src/traits/](../../../crates/core/cgp-field/src/traits/). The `#[cgp_getter]`-generated `UseField` impl is built in [crates/macros/cgp-macro-core/src/types/cgp_getter/use_field.rs](../../../crates/macros/cgp-macro-core/src/types/cgp_getter/use_field.rs). Behavioral and expansion tests are in [crates/tests/cgp-tests/tests/getter.rs](../../../crates/tests/cgp-tests/tests/getter.rs) and the `getter_tests/` modules beside it. +The `UseField` struct, its `WithField` alias, and the `FieldGetter`, `MutFieldGetter`, and `TypeProvider` impls are in [crates/core/cgp-field/src/impls/use_field.rs](../../../crates/core/cgp-field/src/impls/use_field.rs). The `HasField`, `FieldGetter`, and (in `has_field_mut.rs`) `HasFieldMut`, `MutFieldGetter` traits are in [crates/core/cgp-field/src/traits/](../../../crates/core/cgp-field/src/traits/). The `#[cgp_getter]`-generated `UseField` impl is built in [crates/macros/cgp-macro-core/src/types/cgp_getter/use_field.rs](../../../crates/macros/cgp-macro-core/src/types/cgp_getter/use_field.rs). Behavioral and expansion tests are in [crates/tests/cgp-tests/tests/getters/](../../../crates/tests/cgp-tests/tests/getters/). diff --git a/docs/reference/providers/use_fields.md b/docs/reference/providers/use_fields.md index 4b09d3d5..2efd93f7 100644 --- a/docs/reference/providers/use_fields.md +++ b/docs/reference/providers/use_fields.md @@ -86,4 +86,4 @@ Because `App` wires `FooGetterComponent` to `UseFields`, the generated impl read ## Source -The struct is defined in [crates/core/cgp-component/src/providers/use_fields.rs](../../../crates/core/cgp-component/src/providers/use_fields.rs). The `UseFields` impl is built by `to_use_fields_impl` in [crates/macros/cgp-macro-core/src/types/cgp_getter/to_use_fields_impl.rs](../../../crates/macros/cgp-macro-core/src/types/cgp_getter/to_use_fields_impl.rs), which keys each method on its name via `Symbol::from_ident` and emits a matching `HasField` bound. Expansion snapshots showing the generated `UseFields` impl alongside the `UseField` and `WithProvider` impls are in [crates/tests/cgp-tests/tests/getter_tests/string.rs](../../../crates/tests/cgp-tests/tests/getter_tests/string.rs). +The struct is defined in [crates/core/cgp-component/src/providers/use_fields.rs](../../../crates/core/cgp-component/src/providers/use_fields.rs). The `UseFields` impl is built by `to_use_fields_impl` in [crates/macros/cgp-macro-core/src/types/cgp_getter/to_use_fields_impl.rs](../../../crates/macros/cgp-macro-core/src/types/cgp_getter/to_use_fields_impl.rs), which keys each method on its name via `Symbol::from_ident` and emits a matching `HasField` bound. Expansion snapshots showing the generated `UseFields` impl alongside the `UseField` and `WithProvider` impls are in [crates/tests/cgp-tests/tests/getters/](../../../crates/tests/cgp-tests/tests/getters/). diff --git a/docs/reference/providers/use_type.md b/docs/reference/providers/use_type.md index 8af10ea1..ee190243 100644 --- a/docs/reference/providers/use_type.md +++ b/docs/reference/providers/use_type.md @@ -96,4 +96,4 @@ Both forms produce the same result — `App::Scalar` is `f64` — which is why ` ## Source -The `UseType` struct, its `WithType` alias, and the built-in `TypeProvider` impl are in [crates/core/cgp-type/src/impls/use_type.rs](../../../crates/core/cgp-type/src/impls/use_type.rs). The `HasType` consumer trait, the `TypeProvider` provider trait, and the `TypeOf` alias are in [crates/core/cgp-type/src/traits/has_type.rs](../../../crates/core/cgp-type/src/traits/has_type.rs). The `#[cgp_type]`-generated `UseType` impl is built in [crates/macros/cgp-macro-core/src/types/cgp_type/item.rs](../../../crates/macros/cgp-macro-core/src/types/cgp_type/item.rs). Behavioral and expansion tests live in [crates/tests/cgp-tests/tests/component_tests/abstract_types/](../../../crates/tests/cgp-tests/tests/component_tests/abstract_types/). +The `UseType` struct, its `WithType` alias, and the built-in `TypeProvider` impl are in [crates/core/cgp-type/src/impls/use_type.rs](../../../crates/core/cgp-type/src/impls/use_type.rs). The `HasType` consumer trait, the `TypeProvider` provider trait, and the `TypeOf` alias are in [crates/core/cgp-type/src/traits/has_type.rs](../../../crates/core/cgp-type/src/traits/has_type.rs). The `#[cgp_type]`-generated `UseType` impl is built in [crates/macros/cgp-macro-core/src/types/cgp_type/item.rs](../../../crates/macros/cgp-macro-core/src/types/cgp_type/item.rs). Behavioral and expansion tests live in [crates/tests/cgp-tests/tests/abstract_types/](../../../crates/tests/cgp-tests/tests/abstract_types/). diff --git a/docs/reference/providers/with_provider.md b/docs/reference/providers/with_provider.md index ea1b546e..25c405b6 100644 --- a/docs/reference/providers/with_provider.md +++ b/docs/reference/providers/with_provider.md @@ -93,4 +93,4 @@ delegate_components! { ## Source -The struct is defined in [crates/core/cgp-component/src/providers/with_provider.rs](../../../crates/core/cgp-component/src/providers/with_provider.rs), and the `WithContext` alias in [crates/core/cgp-component/src/providers/use_context.rs](../../../crates/core/cgp-component/src/providers/use_context.rs). The remaining aliases are defined beside their inner providers: `WithType` and `WithDelegatedType` in [crates/core/cgp-type/src/impls/use_type.rs](../../../crates/core/cgp-type/src/impls/use_type.rs) and [use_delegated_type.rs](../../../crates/core/cgp-type/src/impls/use_delegated_type.rs), and `WithField` and `WithFieldRef` in [crates/core/cgp-field/src/impls/use_field.rs](../../../crates/core/cgp-field/src/impls/use_field.rs) and [use_ref.rs](../../../crates/core/cgp-field/src/impls/use_ref.rs). The component `WithProvider` impls are generated by `#[cgp_type]` in [crates/macros/cgp-macro-core/src/types/cgp_type/item.rs](../../../crates/macros/cgp-macro-core/src/types/cgp_type/item.rs) and by `#[cgp_getter]` in [crates/macros/cgp-macro-core/src/types/cgp_getter/with_provider.rs](../../../crates/macros/cgp-macro-core/src/types/cgp_getter/with_provider.rs). The generated getter `WithProvider` impl is shown in the expansion snapshot in [crates/tests/cgp-tests/tests/getter_tests/string.rs](../../../crates/tests/cgp-tests/tests/getter_tests/string.rs). +The struct is defined in [crates/core/cgp-component/src/providers/with_provider.rs](../../../crates/core/cgp-component/src/providers/with_provider.rs), and the `WithContext` alias in [crates/core/cgp-component/src/providers/use_context.rs](../../../crates/core/cgp-component/src/providers/use_context.rs). The remaining aliases are defined beside their inner providers: `WithType` and `WithDelegatedType` in [crates/core/cgp-type/src/impls/use_type.rs](../../../crates/core/cgp-type/src/impls/use_type.rs) and [use_delegated_type.rs](../../../crates/core/cgp-type/src/impls/use_delegated_type.rs), and `WithField` and `WithFieldRef` in [crates/core/cgp-field/src/impls/use_field.rs](../../../crates/core/cgp-field/src/impls/use_field.rs) and [use_ref.rs](../../../crates/core/cgp-field/src/impls/use_ref.rs). The component `WithProvider` impls are generated by `#[cgp_type]` in [crates/macros/cgp-macro-core/src/types/cgp_type/item.rs](../../../crates/macros/cgp-macro-core/src/types/cgp_type/item.rs) and by `#[cgp_getter]` in [crates/macros/cgp-macro-core/src/types/cgp_getter/with_provider.rs](../../../crates/macros/cgp-macro-core/src/types/cgp_getter/with_provider.rs). The generated getter `WithProvider` impl is shown in the expansion snapshot in [crates/tests/cgp-tests/tests/getters/](../../../crates/tests/cgp-tests/tests/getters/). diff --git a/docs/reference/traits/can_use_component.md b/docs/reference/traits/can_use_component.md index c38a3b22..79f0096d 100644 --- a/docs/reference/traits/can_use_component.md +++ b/docs/reference/traits/can_use_component.md @@ -95,4 +95,4 @@ where ## Source -The trait and its sole blanket impl are defined in [crates/core/cgp-component/src/traits/can_use_component.rs](../../../crates/core/cgp-component/src/traits/can_use_component.rs) and re-exported through [crates/core/cgp-component/src/macro_prelude.rs](../../../crates/core/cgp-component/src/macro_prelude.rs). The checks that assert it are generated by `check_components!`, whose codegen lives in [crates/macros/cgp-macro-core/src/types/check_components/](../../../crates/macros/cgp-macro-core/src/types/check_components/) — `table.rs` chooses `CanUseComponent` versus `IsProviderFor` as the check trait's supertrait. Expansion snapshots are in [crates/tests/cgp-tests/src/tests/check_components.rs](../../../crates/tests/cgp-tests/src/tests/check_components.rs) and [crates/tests/cgp-tests/tests/namespace_tests/namespace_macro/default_impls.rs](../../../crates/tests/cgp-tests/tests/namespace_tests/namespace_macro/default_impls.rs). +The trait and its sole blanket impl are defined in [crates/core/cgp-component/src/traits/can_use_component.rs](../../../crates/core/cgp-component/src/traits/can_use_component.rs) and re-exported through [crates/core/cgp-component/src/macro_prelude.rs](../../../crates/core/cgp-component/src/macro_prelude.rs). The checks that assert it are generated by `check_components!`, whose codegen lives in [crates/macros/cgp-macro-core/src/types/check_components/](../../../crates/macros/cgp-macro-core/src/types/check_components/) — `table.rs` chooses `CanUseComponent` versus `IsProviderFor` as the check trait's supertrait. Expansion snapshots are in [crates/tests/cgp-tests/tests/checking/](../../../crates/tests/cgp-tests/tests/checking/) and [crates/tests/cgp-tests/tests/namespaces/](../../../crates/tests/cgp-tests/tests/namespaces/). diff --git a/docs/reference/traits/cast.md b/docs/reference/traits/cast.md index ee89b48b..9898543e 100644 --- a/docs/reference/traits/cast.md +++ b/docs/reference/traits/cast.md @@ -105,4 +105,4 @@ The casting traits sit on top of the extensible-data primitives: variant casts r ## Source -`CanUpcast`, `CanDowncast`, and `CanDowncastFields`, together with the internal `FieldsExtractor` recursion that drives them, are defined in [crates/core/cgp-field/src/impls/cast.rs](../../../crates/core/cgp-field/src/impls/cast.rs). `CanBuildFrom` and its internal `FieldsBuilder` recursion are in [crates/core/cgp-field/src/impls/build_from.rs](../../../crates/core/cgp-field/src/impls/build_from.rs). The underlying extractor and builder traits are under [crates/core/cgp-field/src/traits/](../../../crates/core/cgp-field/src/traits/) (`extract_field.rs`, `from_variant.rs`, `has_builder.rs`). End-to-end tests of upcasting, downcasting, and building-from live in the extensible-data suites under [crates/tests/cgp-tests/tests/extensible_data_tests/](../../../crates/tests/cgp-tests/tests/extensible_data_tests/). +`CanUpcast`, `CanDowncast`, and `CanDowncastFields`, together with the internal `FieldsExtractor` recursion that drives them, are defined in [crates/core/cgp-field/src/impls/cast.rs](../../../crates/core/cgp-field/src/impls/cast.rs). `CanBuildFrom` and its internal `FieldsBuilder` recursion are in [crates/core/cgp-field/src/impls/build_from.rs](../../../crates/core/cgp-field/src/impls/build_from.rs). The underlying extractor and builder traits are under [crates/core/cgp-field/src/traits/](../../../crates/core/cgp-field/src/traits/) (`extract_field.rs`, `from_variant.rs`, `has_builder.rs`). End-to-end tests of upcasting, downcasting, and building-from live in the extensible-data suites under [crates/tests/cgp-tests/tests/extensible_records/](../../../crates/tests/cgp-tests/tests/extensible_records/) and [crates/tests/cgp-tests/tests/extensible_variants/](../../../crates/tests/cgp-tests/tests/extensible_variants/). diff --git a/docs/reference/traits/default_namespace.md b/docs/reference/traits/default_namespace.md index b7084260..2a9b0d6f 100644 --- a/docs/reference/traits/default_namespace.md +++ b/docs/reference/traits/default_namespace.md @@ -98,4 +98,4 @@ Pointing a `for in DefaultShowComponents { … }` loop at this nam ## Source -The three traits are defined in [crates/core/cgp-component/src/namespaces.rs](../../../crates/core/cgp-component/src/namespaces.rs), with `DefaultNamespace` re-exported through [crates/core/cgp-component/src/macro_prelude.rs](../../../crates/core/cgp-component/src/macro_prelude.rs). The namespace macro that builds the namespace trait and its inheritance impl lives in [crates/macros/cgp-macro-core/src/types/namespace/](../../../crates/macros/cgp-macro-core/src/types/namespace/); the `#[default_impl(... in DefaultImpls1<...>)]` attribute that registers a per-type default is parsed and lowered in [crates/macros/cgp-macro-core/src/types/attributes/default_impl/](../../../crates/macros/cgp-macro-core/src/types/attributes/default_impl/). The `namespace` header and `for … in` loop are handled by the `delegate_components!` codegen in [crates/macros/cgp-macro-core/src/types/delegate_component/](../../../crates/macros/cgp-macro-core/src/types/delegate_component/). Expansion snapshots covering `DefaultNamespace`, `DefaultImpls1`, the `for … in` loop, and namespace inheritance are in [crates/tests/cgp-tests/src/namespaces/default_impls.rs](../../../crates/tests/cgp-tests/src/namespaces/default_impls.rs), [crates/tests/cgp-tests/src/namespaces/extended.rs](../../../crates/tests/cgp-tests/src/namespaces/extended.rs), and [crates/tests/cgp-tests/tests/namespace_tests/namespace_macro/default_impls.rs](../../../crates/tests/cgp-tests/tests/namespace_tests/namespace_macro/default_impls.rs). +The three traits are defined in [crates/core/cgp-component/src/namespaces.rs](../../../crates/core/cgp-component/src/namespaces.rs), with `DefaultNamespace` re-exported through [crates/core/cgp-component/src/macro_prelude.rs](../../../crates/core/cgp-component/src/macro_prelude.rs). The namespace macro that builds the namespace trait and its inheritance impl lives in [crates/macros/cgp-macro-core/src/types/namespace/](../../../crates/macros/cgp-macro-core/src/types/namespace/); the `#[default_impl(... in DefaultImpls1<...>)]` attribute that registers a per-type default is parsed and lowered in [crates/macros/cgp-macro-core/src/types/attributes/default_impl/](../../../crates/macros/cgp-macro-core/src/types/attributes/default_impl/). The `namespace` header and `for … in` loop are handled by the `delegate_components!` codegen in [crates/macros/cgp-macro-core/src/types/delegate_component/](../../../crates/macros/cgp-macro-core/src/types/delegate_component/). Expansion snapshots covering `DefaultNamespace`, `DefaultImpls1`, the `for … in` loop, and namespace inheritance are in [crates/tests/cgp-tests/tests/namespaces/](../../../crates/tests/cgp-tests/tests/namespaces/). diff --git a/docs/reference/traits/delegate_component.md b/docs/reference/traits/delegate_component.md index 6a0aa726..1be42df1 100644 --- a/docs/reference/traits/delegate_component.md +++ b/docs/reference/traits/delegate_component.md @@ -108,4 +108,4 @@ The bound `AreaComponents: DelegateComponent` is the "get", and `>::Output; ## Source -`StaticFormat` and its `Chars`/`Nil` impls are defined in [crates/core/cgp-base-types/src/traits/static_format.rs](../../../crates/core/cgp-base-types/src/traits/static_format.rs); the `Display` impls that delegate to it are on the [`Chars`](../types/chars.md) and [`Symbol`](../macros/symbol.md) types in [crates/core/cgp-base-types/src/types/](../../../crates/core/cgp-base-types/src/types/). `StaticString`, with its const-evaluated UTF-8 decoding, is in [crates/core/cgp-field/src/traits/static_string.rs](../../../crates/core/cgp-field/src/traits/static_string.rs). `ConcatPath` is in [crates/core/cgp-base-types/src/traits/concat_path.rs](../../../crates/core/cgp-base-types/src/traits/concat_path.rs), and `PathCons` in [crates/core/cgp-base-types/src/types/path.rs](../../../crates/core/cgp-base-types/src/types/path.rs). Tests covering display round-tripping and const decoding of multi-byte strings are in [crates/tests/cgp-tests/src/tests/symbol.rs](../../../crates/tests/cgp-tests/src/tests/symbol.rs). +`StaticFormat` and its `Chars`/`Nil` impls are defined in [crates/core/cgp-base-types/src/traits/static_format.rs](../../../crates/core/cgp-base-types/src/traits/static_format.rs); the `Display` impls that delegate to it are on the [`Chars`](../types/chars.md) and [`Symbol`](../macros/symbol.md) types in [crates/core/cgp-base-types/src/types/](../../../crates/core/cgp-base-types/src/types/). `StaticString`, with its const-evaluated UTF-8 decoding, is in [crates/core/cgp-field/src/traits/static_string.rs](../../../crates/core/cgp-field/src/traits/static_string.rs). `ConcatPath` is in [crates/core/cgp-base-types/src/traits/concat_path.rs](../../../crates/core/cgp-base-types/src/traits/concat_path.rs), and `PathCons` in [crates/core/cgp-base-types/src/types/path.rs](../../../crates/core/cgp-base-types/src/types/path.rs). Tests covering display round-tripping and const decoding of multi-byte strings are in [crates/tests/cgp-tests/tests/field_access/](../../../crates/tests/cgp-tests/tests/field_access/). diff --git a/docs/reference/types/chars.md b/docs/reference/types/chars.md index 8da131e7..2406b2a5 100644 --- a/docs/reference/types/chars.md +++ b/docs/reference/types/chars.md @@ -71,4 +71,4 @@ Because the encoding is a list, an empty string is `Symbol<0, Nil>` — a `Symbo ## Source -The runtime types are defined in [crates/core/cgp-base-types/src/types/chars.rs](../../../crates/core/cgp-base-types/src/types/chars.rs) (`Chars`) and [crates/core/cgp-base-types/src/types/symbol.rs](../../../crates/core/cgp-base-types/src/types/symbol.rs) (`Symbol`), with `Nil` in [crates/core/cgp-base-types/src/types/nil.rs](../../../crates/core/cgp-base-types/src/types/nil.rs). The `StaticFormat` impls that drive `Display` are in [crates/core/cgp-base-types/src/traits/static_format.rs](../../../crates/core/cgp-base-types/src/traits/static_format.rs), and the const-decoding `StaticString` impl that consumes `LEN` is in [crates/core/cgp-field/src/traits/static_string.rs](../../../crates/core/cgp-field/src/traits/static_string.rs). The constructing macro is [`Symbol!`](../macros/symbol.md). Tests covering display round-tripping and multi-byte strings are in [crates/tests/cgp-tests/src/tests/symbol.rs](../../../crates/tests/cgp-tests/src/tests/symbol.rs). +The runtime types are defined in [crates/core/cgp-base-types/src/types/chars.rs](../../../crates/core/cgp-base-types/src/types/chars.rs) (`Chars`) and [crates/core/cgp-base-types/src/types/symbol.rs](../../../crates/core/cgp-base-types/src/types/symbol.rs) (`Symbol`), with `Nil` in [crates/core/cgp-base-types/src/types/nil.rs](../../../crates/core/cgp-base-types/src/types/nil.rs). The `StaticFormat` impls that drive `Display` are in [crates/core/cgp-base-types/src/traits/static_format.rs](../../../crates/core/cgp-base-types/src/traits/static_format.rs), and the const-decoding `StaticString` impl that consumes `LEN` is in [crates/core/cgp-field/src/traits/static_string.rs](../../../crates/core/cgp-field/src/traits/static_string.rs). The constructing macro is [`Symbol!`](../macros/symbol.md). Tests covering display round-tripping and multi-byte strings are in [crates/tests/cgp-tests/tests/field_access/](../../../crates/tests/cgp-tests/tests/field_access/). diff --git a/docs/reference/types/life.md b/docs/reference/types/life.md index a5d84964..2d644066 100644 --- a/docs/reference/types/life.md +++ b/docs/reference/types/life.md @@ -54,4 +54,4 @@ Every impl that wires this component — whether through `UseContext`, a `UseFie ## Source -The type is defined in [crates/core/cgp-field/src/types/life.rs](../../../crates/core/cgp-field/src/types/life.rs). The macro logic that wraps a trait's lifetime parameters in `Life` when building the `IsProviderFor` argument tuple is in [crates/macros/cgp-macro-core/src/functions/is_provider_params.rs](../../../crates/macros/cgp-macro-core/src/functions/is_provider_params.rs), with related placement in [crates/macros/cgp-macro-core/src/types/empty_struct.rs](../../../crates/macros/cgp-macro-core/src/types/empty_struct.rs) and [crates/macros/cgp-macro-core/src/types/cgp_provider/provider_impl_args.rs](../../../crates/macros/cgp-macro-core/src/types/cgp_provider/provider_impl_args.rs). The generated wiring for a lifetime-carrying component is exercised by the snapshot tests in [crates/tests/cgp-tests/tests/component_tests/cgp_component/lifetime.rs](../../../crates/tests/cgp-tests/tests/component_tests/cgp_component/lifetime.rs). +The type is defined in [crates/core/cgp-field/src/types/life.rs](../../../crates/core/cgp-field/src/types/life.rs). The macro logic that wraps a trait's lifetime parameters in `Life` when building the `IsProviderFor` argument tuple is in [crates/macros/cgp-macro-core/src/functions/is_provider_params.rs](../../../crates/macros/cgp-macro-core/src/functions/is_provider_params.rs), with related placement in [crates/macros/cgp-macro-core/src/types/empty_struct.rs](../../../crates/macros/cgp-macro-core/src/types/empty_struct.rs) and [crates/macros/cgp-macro-core/src/types/cgp_provider/provider_impl_args.rs](../../../crates/macros/cgp-macro-core/src/types/cgp_provider/provider_impl_args.rs). The generated wiring for a lifetime-carrying component is exercised by the snapshot tests in [crates/tests/cgp-tests/tests/generic_components/](../../../crates/tests/cgp-tests/tests/generic_components/).