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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ members = [

"crates/tests/cgp-tests",
"crates/tests/cgp-macro-tests",
"crates/tests/cgp-no-std-tests",
]

[workspace.package]
Expand Down
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,20 @@ use cgp::prelude::*;
CGP requires **Rust 1.89+** and runs on the **stable** toolchain, and needs neither a nightly toolchain nor a fork. The
[quickstart](https://contextgeneric.dev/docs/quickstart) takes it from there.

### `no_std`

The `cgp` crate builds for a target without `std`, and its default features need neither `std` nor
`alloc`. Turn on the `alloc` feature for the providers that need an allocator, such as
`DebugError` and `DisplayError`:

```toml
[dependencies]
cgp = { version = "0.8.0", default-features = false, features = ["alloc"] }
```

The error backends are separate crates: `cgp-error-anyhow` and `cgp-error-std` need `alloc`, and
`cgp-error-eyre` needs `std`.

## A quick look

Real operations depend on infrastructure such as a database or object storage, and the usual ways of
Expand Down
1 change: 1 addition & 0 deletions crates/core/cgp-base-types/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! This crate provides CGP's type-level primitives: symbols, lists, sums, and paths.
//!
//! See [`Symbol!`](https://contextgeneric.dev/docs/reference/macros/symbol), [`Cons`](https://contextgeneric.dev/docs/reference/types/cons), [`Nil`](https://contextgeneric.dev/docs/reference/types/nil), [`Chars`](https://contextgeneric.dev/docs/reference/types/chars), [`PathCons`](https://contextgeneric.dev/docs/reference/types/path_cons), [`StaticFormat`](https://contextgeneric.dev/docs/reference/traits/formatting/static_format), and [`ConcatPath`](https://contextgeneric.dev/docs/reference/traits/formatting/concat_path).
#![no_std]

pub mod macro_prelude;
pub mod traits;
Expand Down
4 changes: 3 additions & 1 deletion crates/extra/cgp-error-extra/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ categories = { workspace = true }
description = "CGP's generic error providers, which raise, return, discard, and format an error."

[features]
default = [ "alloc" ]
default = []
# The `DebugError` and `DisplayError` providers need an allocator, so they are
# behind this feature. A `no_std` build without one leaves it off.
alloc = []

[dependencies]
Expand Down
3 changes: 2 additions & 1 deletion crates/extra/cgp-error-extra/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#![no_std]
//! This crate provides CGP's generic error providers, which raise, return, discard, and format an
//! error.
//! error. The `DebugError` and `DisplayError` providers need an allocator, so they are behind the
//! `alloc` feature.
//!
//! See [`RaiseFrom`](https://contextgeneric.dev/docs/reference/providers/error/raise_from), [`ReturnError`](https://contextgeneric.dev/docs/reference/providers/error/return_error), [`RaiseInfallible`](https://contextgeneric.dev/docs/reference/providers/error/raise_infallible), [`PanicOnError`](https://contextgeneric.dev/docs/reference/providers/error/panic_on_error), [`DiscardDetail`](https://contextgeneric.dev/docs/reference/providers/error/discard_detail), [`DebugError`](https://contextgeneric.dev/docs/reference/providers/error/debug_error), and [`DisplayError`](https://contextgeneric.dev/docs/reference/providers/error/display_error).

Expand Down
1 change: 1 addition & 0 deletions crates/extra/cgp-runtime/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! This crate provides CGP's runtime abstraction, which lets a context choose its async runtime.
//!
//! See [`HasRuntime`](https://contextgeneric.dev/docs/reference/components/has_runtime) and [`HasRuntimeType`](https://contextgeneric.dev/docs/reference/components/has_runtime_type).
#![no_std]

mod traits;

Expand Down
1 change: 1 addition & 0 deletions crates/main/cgp-base/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
//! the macros.
//!
//! See [`#[cgp_component]`](https://contextgeneric.dev/docs/reference/macros/cgp_component), [`Symbol!`](https://contextgeneric.dev/docs/reference/macros/symbol), and [`Cons`](https://contextgeneric.dev/docs/reference/types/cons).
#![no_std]

pub mod macro_prelude;

Expand Down
8 changes: 6 additions & 2 deletions crates/main/cgp-extra/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,12 @@ categories = { workspace = true }
description = "CGP's extra layer through cgp::extra: handlers, monads, dispatch, runtime, runners, the optional-field layer, and the generic error providers."

[features]
default = [ "full" ]
full = [ "cgp-error-extra/alloc" ]
default = []
# The `alloc` feature turns on the providers that need an allocator, such as
# `DebugError` and `DisplayError`. A `no_std` build without an allocator leaves it
# off, and `default-features = false` needs neither `std` nor `alloc`.
alloc = [ "cgp-error-extra/alloc" ]
full = [ "alloc" ]

[dependencies]
cgp-core = { workspace = true }
Expand Down
3 changes: 2 additions & 1 deletion crates/main/cgp-extra/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#![no_std]
//! This crate re-exports CGP's extra layer through `cgp::extra`: handlers, monads, dispatch,
//! runtime, runners, the optional-field layer, and the generic error providers.
//! runtime, runners, the optional-field layer, and the generic error providers. The `alloc` feature
//! turns on the providers that need an allocator.
//!
//! See [`CanHandle`](https://contextgeneric.dev/docs/reference/components/handler/handler), [`PipeHandlers`](https://contextgeneric.dev/docs/reference/providers/handler/pipe_handlers), and [`RaiseFrom`](https://contextgeneric.dev/docs/reference/providers/error/raise_from).

Expand Down
6 changes: 6 additions & 0 deletions crates/main/cgp/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ keywords = { workspace = true }
categories = { workspace = true }
description = { workspace = true }

[features]
default = []
# Turn on the providers that need an allocator. A `no_std` build without one leaves
# this off, and `default-features = false` needs neither `std` nor `alloc`.
alloc = [ "cgp-extra/alloc" ]

[dependencies]
cgp-core = { workspace = true }
cgp-extra = { workspace = true }
19 changes: 18 additions & 1 deletion crates/tests/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,11 @@ where the canonical macro-expansion snapshots live.
refuses during expansion, and for pinning the invalid tokens a macro currently
emits.

A third category lives in another repository: the cases where a macro *accepts*
**`cgp-no-std-tests`** builds the `cgp` crate for a target without `std`, so a `std` or `alloc` leak
in the dependency tree fails the build. It is `#![no_std]`, uses the macros, and gates an alloc-using
provider behind its `alloc` feature.

A fourth category lives in another repository: the cases where a macro *accepts*
input whose *expansion* then fails to compile are UI fixtures in
[`cargo-cgp`](https://github.com/contextgeneric/cargo-cgp/blob/main/tests/README.md),
so each is pinned as the readable error the tool renders for it. AGENTS.md's
Expand Down Expand Up @@ -67,6 +71,19 @@ cargo insta test -p cgp-tests --review # review snapshot diffs interact
cargo insta test -p cgp-tests --accept # accept intended snapshot changes
```

The `no_std` check builds for a target whose sysroot has no `std`, so it fails if any crate in the
tree needs one. Install the target once, then run both paths:

```sh
rustup target add x86_64-unknown-none
cargo check -p cgp-no-std-tests --target x86_64-unknown-none
cargo check -p cgp-no-std-tests --features alloc --target x86_64-unknown-none
```

The first proves that `cgp` with default features needs neither `std` nor `alloc`; the second turns on
the `alloc` feature and compiles the provider that needs an allocator. A `std` use planted in the
crate fails the first command with "cannot find module or crate `std`".

Many tests assert the exact code a macro generates, through the `snapshot_*!`
macros from `cgp-macro-test-util`: each emits the real generated code into the
module *and* generates a `#[test]` asserting a pretty-printed inline `insta`
Expand Down
22 changes: 22 additions & 0 deletions crates/tests/cgp-no-std-tests/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
[package]
name = "cgp-no-std-tests"
version = "0.8.0-alpha"
edition = { workspace = true }
rust-version = { workspace = true }
license = { workspace = true }
repository = { workspace = true }
authors = { workspace = true }
homepage = { workspace = true }
keywords = { workspace = true }
categories = { workspace = true }
description = "Builds the cgp crate for a target without std, with and without alloc."
publish = false

[features]
default = []
# Turn on the providers that need an allocator, so the same crate checks the
# `alloc` path as well as the plain `no_std` one.
alloc = [ "cgp/alloc" ]

[dependencies]
cgp = { workspace = true }
82 changes: 82 additions & 0 deletions crates/tests/cgp-no-std-tests/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
//! Builds the `cgp` crate for a target without `std`.
//!
//! The crate is `#![no_std]` and uses the macros, so a build for a target that has no `std` fails if
//! any crate in the tree needs it. The `alloc` feature turns on the providers that need an
//! allocator, so the same crate checks both paths:
//!
//! ```sh
//! cargo check -p cgp-no-std-tests --target x86_64-unknown-none
//! cargo check -p cgp-no-std-tests --features alloc --target x86_64-unknown-none
//! ```
//!
//! See cgp-knowledge-base/cgp/implementation/README.md and the `no_std` inventory in the campaign
//! records.

#![no_std]

#[cfg(feature = "alloc")]
extern crate alloc;

use cgp::prelude::*;

/// A component, so the build exercises the component macros.
#[cgp_component(Greeter)]
pub trait CanGreet {
/// Greets.
fn greet(&self) -> &'static str;
}

/// A provider for the component.
#[cgp_impl(new GreetHello)]
impl Greeter {
fn greet(&self) -> &'static str {
"Hello"
}
}

/// A function-style trait, so the build exercises `#[cgp_fn]`.
///
/// The implicit argument is a `u32` rather than a `&str`, because the `&str` form
/// lowers to a `String` field bound, which needs `alloc`.
#[cgp_fn]
pub fn double_count(&self, #[implicit] count: u32) -> u32 {
count * 2
}

/// A context that wires the component.
pub struct App;

delegate_components! {
App {
GreeterComponent: GreetHello,
}
}

check_components! {
App {
GreeterComponent,
}
}

/// A type-level string, so the build exercises the type-level primitives.
pub type NameTag = Symbol!("name");

/// A provider that needs an allocator, compiled only with the `alloc` feature.
#[cfg(feature = "alloc")]
#[cgp_impl(new GreetWithAlloc)]
impl Greeter {
fn greet(&self) -> &'static str {
let greeting = alloc::string::String::from("Hello");
alloc::boxed::Box::leak(greeting.into_boxed_str())
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn the_context_greets() {
assert_eq!(App.greet(), "Hello");
}
}
Loading