Minimal Rust-facing provider micro-library for Observer.
If you are new to this surface, start with HOWTO.md before reading the individual snippets and starters.
This wraps the existing low-level Rust provider crates with the same human-first DX shape already used in C and TypeScript:
- authors write
describe!(...),test!(...),it!(...), andexpect(...) - default identity is derived deterministically from explicit suite path plus test title
- optional
id = ...provides a refactor-stable override when needed - observation is bounded and opt-in through
ctx.observe().* - direct host and embedded
observedispatch are owned by the library
The common path stays human-first. The deterministic boundary stays explicit.
HOWTO.md: detailed user manual covering authoring, determinism, host transport, inventory derivation, and end-to-end workflowsrc/lib.rs: public APIexamples/example_smoke.rs: tiny collection and execution exampleexamples/host_example.rs: tiny directlistandrunhost exampleexamples/host_embed_example.rs: own-main styleobservenamespace examplestarter/: runnable project-shaped example with Cargo build, provider host build, inventory derivation, suite run, and snapshot verificationstarter-embedded/: runnable app-shaped example where the application keepsmain()and routesobserve ...through its own CLIstarter-embedded-failure/: runnable failing companion for the embedded app-shaped pathstarter-failure/: runnable failing companion showing the same provider flow with one intentionally failing exported test
If you want the full end-to-end workflow rather than isolated snippets, start with starter/, then read starter-embedded/, and then compare those with starter-failure/ and starter-embedded-failure/.
use observer_rust_lib::{collect_tests, describe, expect, test};
fn connect() -> bool {
true
}
let tests = collect_tests(|| {
describe!("database", {
test!("access to the database", |ctx| {
ctx.stdout("ok\n");
expect(connect()).to_be_truthy();
});
});
})?;When id is omitted, Observer derives a deterministic identity from suite path, test title, and duplicate occurrence order.
If a test wants a refactor-stable identity, it opts into id explicitly:
test!("access to the database", id = "database/access", |ctx| {
expect(true).to_be_truthy();
});If a test wants to emit observational data, it uses the author context directly:
test!("access to the database", id = "database/access", |ctx| {
assert!(ctx.observe().metric("wall_time_ns", 104233.0));
assert!(ctx.observe().vector("request_latency_ns", &[1000.0, 1100.0, 980.0]));
assert!(ctx.observe().tag("resource_path", "fixtures/config.json"));
expect(true).to_be_truthy();
});- explicit
id, when present, must be non-empty - resolved canonical identities must be unique
- resolved targets must be unique
- deterministic sorting is by canonical name, then target
In this first cut, the resolved identity is used for both canonical name and target.
cargo test -p observer-rust-libcargo run -q -p observer-rust-lib --example example_smokecargo run -q -p observer-rust-lib --example host_example -- list
cargo run -q -p observer-rust-lib --example host_example -- observe --target pkg::smoke --timeout-ms 1000
cargo run -q -p observer-rust-lib --example host_example -- run --target pkg::fail --timeout-ms 1000The library owns the standard provider host transport for Rust too. A direct host can stay nearly trivial:
use observer_rust_lib::{collect_tests, describe, expect, observer_host_main, test};
fn main() {
let tests = collect_tests(|| {
describe!("pkg", {
test!("smoke test", id = "pkg::smoke", |ctx| {
ctx.stdout("ok\n");
expect(true).to_be_truthy();
});
});
})
.expect("collection should validate");
let exit_code = match observer_host_main("rust", &tests) {
Ok(()) => 0,
Err(error) => {
eprintln!("{error}");
2
}
};
std::process::exit(exit_code);
}For developer-facing usage, prefer observe. run remains available for compatibility with the standardized outer provider contract.
If a project already owns its CLI, the library can also serve an embedded observe namespace:
use observer_rust_lib::{collect_tests, describe, expect, observer_host_dispatch_embedded, test};
fn app_main() {
println!("app main");
}
fn main() {
let tests = collect_tests(|| {
describe!("pkg", {
test!("embedded smoke test", id = "pkg::embedded-smoke", |ctx| {
ctx.stdout("ok\n");
expect(true).to_be_truthy();
});
});
})
.expect("collection should validate");
let args = std::env::args().collect::<Vec<_>>();
if args.get(1).map(String::as_str) == Some("observe") {
let exit_code = match observer_host_dispatch_embedded("rust", "observe", &tests, args) {
Ok(()) => 0,
Err(error) => {
eprintln!("{error}");
2
}
};
std::process::exit(exit_code);
}
app_main();
}Compile and run that path with:
cargo run -q -p observer-rust-lib --example host_embed_example -- observe list
cargo run -q -p observer-rust-lib --example host_embed_example -- observe --target pkg::embedded-smoke --timeout-ms 1000
cargo run -q -p observer-rust-lib --example host_embed_example