Skip to content
Merged
140 changes: 131 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,138 @@
# Context-Generic Programming (CGP)

# `cgp` - Context-Generic Programming in Rust

[![Apache 2.0 Licensed](https://img.shields.io/badge/license-Apache_2.0-blue.svg)](https://github.com/contextgeneric/cgp/blob/master/LICENSE)
[![Apache 2.0 Licensed](https://img.shields.io/badge/license-Apache_2.0-blue.svg)](https://github.com/contextgeneric/cgp/blob/main/LICENSE)
[![Crates.io](https://img.shields.io/crates/v/cgp.svg)](https://crates.io/crates/cgp)
[![Tests](https://github.com/contextgeneric/cgp/actions/workflows/tests.yml/badge.svg)](https://github.com/contextgeneric/cgp/actions/workflows/tests.yml)
![Rust Stable](https://img.shields.io/badge/rustc-stable-blue.svg)
![Rust 1.81+](https://img.shields.io/badge/rustc-1.81+-blue.svg)
![Rust 1.89+](https://img.shields.io/badge/rustc-1.89+-blue.svg)

**A Rust language extension for reusable, swappable trait implementations — checked at compile time, with zero runtime cost.**

Context-generic programming (CGP) is a library, built on stable Rust, that lets one interface have many interchangeable implementations and lets each *context* — an application, a test, a deployment — choose which one it uses. The choice is written in one readable place and resolved entirely during compilation, so it compiles down to direct calls: there is no runtime container, no reflection, and nothing left in the binary for an implementation a context does not use.

> [!IMPORTANT]
> The `main` branch tracks the upcoming **v0.8.0** release (published as `0.8.0-alpha` pre-release crates). The current stable release on crates.io is **v0.7.0**, which is **not compatible** with this branch — v0.8.0 changes and removes syntax that v0.7.0 used. **All documentation in this repository describes v0.8.0 only.** For v0.7.0, refer to its published crate documentation instead.

**[Website](https://contextgeneric.dev/) · [crates.io](https://crates.io/crates/cgp)**

## Key features

CGP's headline promises are few and deliberately concrete.

- **One interface, many implementations.** Write many interchangeable implementations of the same interface and choose between them per context — including the overlapping and orphan implementations Rust normally forbids, made safe because every choice is explicit and local.
- **Zero-cost abstraction.** Everything is resolved at compile time and compiles down to direct calls, so the flexibility costs nothing at runtime and unused implementations never reach the binary.
- **Type-safe wiring.** All wiring is checked at compile time, so a missing dependency is a build error rather than a runtime failure — in safe Rust, with no `dyn`, `Any`, or reflection.
- **Abstract over every dependency.** Write core logic that names its error type, runtime, and I/O abstractly and let each context supply the concrete choice, which keeps the core `no_std`-friendly — from embedded systems to WebAssembly.
- **Still ordinary Rust.** CGP is a superset of ordinary traits that you adopt one piece at a time: providers read like normal impls and implicit arguments like normal parameters, so a codebase can use it in one corner and stay otherwise vanilla.

## A quick look

Real operations depend on infrastructure — a database, object storage — and the usual ways of supplying it do not scale. Passing each dependency as an argument threads long lists through every layer, while grouping them into a struct and adding methods locks the logic to that one concrete type, so it cannot be reused for a test, an embedded build, or a different backend. CGP lets you write the operation once against the capabilities a context provides.

```rust
use cgp::prelude::*;

// A context is a plain data struct.
#[derive(HasField)]
pub struct App {
pub database: PgPool,
pub storage_client: Client,
pub bucket_id: String,
}

// Write the operation once. `#[implicit]` reads `database` from the context;
// `user_id` stays an ordinary argument. Any context carrying a matching
// `database` field gains `get_user` — no wiring, no trait impls to write.
#[cgp_fn]
#[async_trait]
pub async fn get_user(
&self,
#[implicit] database: &PgPool,
user_id: &UserId,
) -> anyhow::Result<User> {
// ...query `database` for the user row
}
```

`get_user` reads like an ordinary async function, yet it works on any context with a matching field — checked at compile time, so a context missing `database` is a build error rather than a runtime failure. When a step needs *more than one* implementation, promote it to a component and let each context pick a provider:

```rust
// One interface...
#[async_trait]
#[cgp_component(StorageObjectFetcher)]
pub trait CanFetchStorageObject {
async fn fetch_storage_object(&self, object_id: &str) -> anyhow::Result<Vec<u8>>;
}

// ...with as many implementations as you need...
#[cgp_impl(new FetchS3Object)]
impl StorageObjectFetcher { /* fetch the object from Amazon S3 */ }

#[cgp_impl(new FetchGCloudObject)]
impl StorageObjectFetcher { /* fetch the object from Google Cloud Storage */ }

// ...and each context picks one, resolved at compile time.
delegate_components! { App { StorageObjectFetcherComponent: FetchS3Object } }
delegate_components! { GCloudApp { StorageObjectFetcherComponent: FetchGCloudObject } }
```

`App` fetches from Amazon S3, and `GCloudApp` — a context carrying a Google Cloud storage client instead — fetches from Google Cloud. Neither pays for `dyn` or runtime dispatch, the choice is a single greppable line, and code that calls `self.fetch_storage_object(..)` never changes. (Method bodies are elided here for brevity.)

## Installation

Add the latest stable release with Cargo:

```bash
cargo add cgp
```

then bring everything into scope with a single import:

```rust
use cgp::prelude::*;
```

CGP requires **Rust 1.89+** and runs on the **stable** toolchain — no nightly, no fork.

> [!NOTE]
> `cargo add cgp` installs the current stable release, **v0.7.0**. The examples in this repository target **v0.8.0**; to follow them today, depend on the pre-release from `main`:
> ```toml
> cgp = { git = "https://github.com/contextgeneric/cgp" }
> ```
> Once v0.8.0 is released, `cargo add cgp` will install it.

## When to use CGP — and when not

CGP is a tool with a boundary, and it earns its cost only past that boundary. The rule of thumb is to **reach for CGP when a capability needs more than one implementation and the choice belongs to the context — not before.** Concretely:

- **One implementation?** Use a plain trait or function. CGP would be over-engineering.
- **A small, closed set of variants?** Use an `enum` and a `match`.
- **Implementations chosen at runtime** — plugins, config-driven selection, heterogeneous collections? Use `dyn Trait`; CGP resolves everything at compile time and gives up that runtime openness by design.
- **One implementation must be globally unique** — a single `Ord` for a map key, say? A coherent trait is the right tool; CGP's per-context choice is deliberately not global.

CGP is a *superset* of ordinary traits, so adopting it is a climb from the plainer tool rather than a rejection of it. You can use it exactly where it pays and leave the rest of a codebase unchanged.

## Documentation

Learn CGP at the project website, **[contextgeneric.dev](https://contextgeneric.dev/)**, which hosts the guides, worked examples, and project blog.

> [!NOTE]
> The crates' API docs on `docs.rs` are still sparse. The website is the place to start while the inline documentation is filled in.

## Project status

CGP works today on stable Rust and already offers a broad feature set: components and providers, abstract types, extensible records and variants, a handler and computation family, namespaces, and modular error handling. It is also young — the paradigm and its ecosystem are still evolving, and v0.8.0 is a pre-release — so adopting it is a real decision rather than a foregone one.

Two properties make it low-risk to try. Because CGP is a superset of ordinary traits, it can be introduced in one corner of a codebase and stepped back from without a rewrite; and because it imposes no runtime, it does not lock a project into a framework's lifecycle. If you are evaluating it for a team, start small, and lean on the worked examples on the [website](https://contextgeneric.dev/) to gauge the learning curve honestly.

## How the project is organized

CGP is a collection of small, layered crates, arranged so that low-level primitives stay independent of the high-level facade. Most users depend only on the single **`cgp`** crate, which re-exports the core and extra functionality through `cgp::prelude`. Pluggable error backends are opt-in as separate crates — `cgp-error-anyhow`, `cgp-error-eyre`, and `cgp-error-std`. Browse [`crates/`](crates) for the full layout.

## Overview
## Contributing

The `cgp` project contains a collection of micro Rust crates that empowers _context-generic programming_ (CGP), a new modular programming paradigm in Rust.
Contributions, questions, and bug reports are welcome. Open an [issue](https://github.com/contextgeneric/cgp/issues) or a pull request, and see the [website](https://contextgeneric.dev/) to get oriented first. Unless stated otherwise, contributions are made under the terms of the Apache-2.0 license below.

To learn more about context-generic programming, check out the our website [contextgeneric.dev](https://contextgeneric.dev/), and our book [Context-Generic Programming Patterns](https://patterns.contextgeneric.dev/).
## License

[!WARNING]
At the moment, the `cgp` crate its constructs are mostly undocumented within Rustdoc. The best way to understand CGP is to read the book [Context-Generic Programming Patterns](https://patterns.contextgeneric.dev/).
Licensed under the [Apache License, Version 2.0](LICENSE).
6 changes: 5 additions & 1 deletion docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ The knowledge base also serves as a contract. When an agent changes how a macro

## How it is organized

The knowledge base is divided into five top-level sections, and will grow to contain more as the need arises.
The knowledge base is divided into several top-level sections, described below in no particular order, and will grow to contain more as the need arises. Each section answers a different question about CGP, so a reader picks the one that matches their need rather than reading them in sequence.

The [reference/](reference/README.md) directory holds one document per CGP construct — one for `cgp_component`, one for `cgp_impl`, one for `delegate_components`, and so on. Each document is self-contained and explains a single construct completely: its purpose, its accepted syntax, the exact code it desugars to, worked examples, and links to the constructs it relates to. The [reference index](reference/README.md) lists every construct and tracks which ones are documented.

Expand All @@ -22,6 +22,10 @@ The [guides/](guides/README.md) directory holds the guides to *writing* CGP —

The [errors/](errors/README.md) directory catalogs the compiler errors CGP produces *after* codegen — input a macro accepts and lowers to Rust that then fails to compile — organized by the kind of error rather than by the macro that produced it. Each document records the anatomy of one class: the mistake that triggers it, the shape of the diagnostic, whether the root cause is present in the output, and where it sits when it is. The catalog is built around the distinction between errors that *surface* their root cause and those the compiler *hides*, and it is the canonical documentation for the post-codegen compile-fail fixtures, so it exists both to guide debugging agents and to map the ground a `cargo-cgp`-style tool would post-process. Failures a macro raises by *rejecting* its input stay with the macro's implementation document instead.

The [related-work/](related-work/README.md) directory looks outward instead of inward: each document takes an external concept, framework, or language feature that resembles CGP — dependency injection, implicit parameters, and so on — explains it faithfully and with citations, weighs what its users like and dislike about it, and positions CGP against it. These documents exist to serve future user-facing writing, giving an agent who must explain CGP to readers of a particular background the honest comparison and the positioning strategy to build on the intuitions those readers already hold.

The [communication-strategy/](communication-strategy/README.md) directory turns that outward-facing material into guidance for *writing* about CGP to the public — landing pages, tutorials, articles, blog posts, social-media threads. Where a related-work document compares CGP to one external idea, a communication-strategy document generalizes across those comparisons into audience-level strategy: which readers exist and what each already believes, which content and hooks gain the most attention, which misunderstandings CGP reliably provokes and how to preempt them, and what tone keeps everything written about CGP reading as one voice. An agent preparing any public-facing writing reads here first, then draws on the matching related-work document for the depth of a particular comparison.

## How to use it

An agent working on CGP should read the relevant reference document before changing a construct, and should consult the `/cgp` skill for the conceptual framing that ties constructs together. The reference documents assume familiarity with the vocabulary the `/cgp` skill establishes — consumer traits, provider traits, providers, wiring, and so on — and focus on precise per-construct semantics rather than re-teaching the paradigm. Read the two together: the skill for the shape of the forest, the reference for the individual trees.
Loading
Loading