allure-rust-commons 1.2.0

Core Allure runtime model, lifecycle, and result writer for Rust integrations.
Documentation
# allure-rust-commons

`allure-rust-commons` provides the core runtime, lifecycle, model, writer, and facade APIs
used to produce Allure-compatible result files.

Use this crate when you want to build your own adapter for a custom test runner or framework.

## Add the crate

```bash
cargo add allure-rust-commons
```

## Basic usage

Create a results writer, initialize a runtime, then start and stop a test through the lifecycle
or the facade API.

```rust
use allure_rust_commons::{AllureFacade, AllureRuntime, FileSystemResultsWriter, Status};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let writer = FileSystemResultsWriter::new("target/allure-results")?;
    let runtime = AllureRuntime::new(writer);
    let lifecycle = runtime.lifecycle();
    let allure = AllureFacade::with_lifecycle(lifecycle);

    allure.start_test_with_full_name("login_works", "auth::login_works");
    allure.feature("Authentication");
    allure.parameter("browser", "firefox");
    allure.stage("open login page");
    allure.log_step("open login page");
    allure.attachment("request.json", "application/json", br#"{"ok":true}"#);
    allure.end_test(Status::Passed, None);

    Ok(())
}
```

## Thread-bound convenience functions

Adapters can bind an `AllureFacade` to the current thread while a test runs. After that, users can
import framework-neutral functions from this crate instead of passing an `AllureFacade` through
every helper.

```rust
use allure_rust_commons::{
    feature, log_step, parameter, push_current_allure, stage, AllureFacade, AllureRuntime,
    FileSystemResultsWriter, Status,
};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let writer = FileSystemResultsWriter::new("target/allure-results")?;
    let runtime = AllureRuntime::new(writer);
    let allure = AllureFacade::with_lifecycle(runtime.lifecycle());

    allure.start_test_with_full_name("login_works", "auth::login_works");
    let _current = push_current_allure(&allure);

    feature("Authentication");
    parameter("browser", "firefox");
    stage("open login page");
    log_step("open login page");

    allure.end_test(Status::Passed, None);
    Ok(())
}
```

## Macro-free cargo test usage

When attribute macros are not allowed, wrap a regular Rust `#[test]` with `allure::test`. The
test name is inferred from the current cargo-test thread, and the closure gets the same
thread-bound runtime context used by the convenience functions.

```rust
use allure_rust_commons as allure;

#[test]
fn login_works() {
    allure::test(|| {
        allure::feature("Authentication");
        allure::parameter("browser", "firefox");
        allure::stage("prepare data");
        let user = "alice";
        allure::stage("submit credentials");
        allure::step("send request", || {
            allure::attachment("request.json", "application/json", br#"{"user":"alice"}"#);
        });
        allure::stage("verify result");
        assert_eq!(user, "alice");
    });
}
```

Use `test_named` for a custom display name, or `test_with(TestOptions::new(...), || { ... })`
when an adapter or helper can provide exact metadata such as full name, title path, manifest dir,
module path, labels, or IDs. Async tests can use `test_async`, `test_named_async`, or
`test_with_async`.

Adapters that need Rust test-style outcome handling can use `test_with_outcome` or
`test_with_outcome_async`. These wrappers report values implementing `AllureTestOutcome`, including
`()`, nested `Result<T, E>` where `T` is also an Allure outcome, and `ExitCode`, while still
returning the original value to the test harness.

## Runtime facade

The thread-bound facade exports framework-neutral helpers for the common Allure concepts:

- Metadata: `display_name`, `description`, `description_html`, `history_id`, `test_case_id`,
  `id`, `allure_id`
- Labels and links: `label`, `labels`, `epic`, `feature`, `story`, `suite`, `parent_suite`,
  `sub_suite`, `owner`, `severity`, `tag`, `tags`, `link`, `links`, `issue`, `tms`
- Parameters: `parameter`, `parameter_excluded`, `parameter_mode`, `parameter_with_options`
- Steps: `log_step`, `log_step_with`, `step`, `stage`
- Evidence: `attachment`, `attachment_path`, `attach_trace`, `http_exchange`, and
  `http_exchange_named`
- Run-level diagnostics: `global_attachment`, `global_attachment_path`, `global_error`,
  `global_error_with_trace`

Runtime attachment and HTTP exchange helpers create an ordered evidence step by default. Adapter
internals that need exact active-owner attachment placement can use the reporter module:

```rust
use allure_rust_commons::{reporter, AllureFacade, HttpExchange};

fn attach_from_adapter(allure: &AllureFacade) {
    reporter::attachment(allure, "trace.txt", "text/plain", "raw trace");
    reporter::http_exchange(allure, HttpExchange::new("GET", "https://example.invalid"));
}
```

## HTTP exchange attachments

Use `HttpExchange` when an integration captures request/response evidence for Allure viewers or
API coverage tools.

```rust
use allure_rust_commons::{
    AllureFacade, HttpExchange, HttpExchangeBody, HttpExchangeBodyEncoding, HttpExchangeResponse,
};

fn attach_order_exchange(allure: &AllureFacade) {
    let mut exchange = HttpExchange::new("POST", "https://api.example.com/v1/orders");
    exchange.request.body = Some(HttpExchangeBody {
        content_type: Some("application/json".to_string()),
        encoding: Some(HttpExchangeBodyEncoding::Utf8),
        value: Some(r#"{"name":"demo"}"#.to_string()),
        size: Some(15),
        truncated: Some(false),
        ..Default::default()
    });
    exchange.response = Some(HttpExchangeResponse {
        status: Some(201),
        status_text: Some("Created".to_string()),
        ..Default::default()
    });

    allure.http_exchange(exchange);
}
```

## Common building blocks

- `AllureRuntime`: owns the configured results writer.
- `AllureLifecycle`: manages in-progress tests and steps.
- `AllureFacade`: ergonomic helper for labels, links, parameters, steps, attachments, identity,
  and run-level diagnostics.
- Macro-free test runners: `test`, `test_named`, `test_with`, and async equivalents.
- Thread-bound functions delegate to the current facade set by `push_current_allure`.
- `FileSystemResultsWriter`: persists JSON result files and attachments.
- `config`: a process-wide env/runtime config singleton plus cached Cargo metadata labels and
  title-path helpers.
- `model`: raw Allure data structures for custom integrations.

## Configuration Helpers

`AllureLifecycle::start_test_case` automatically adds global labels from `ALLURE_LABEL_*` and
`allure.label.*` environment variables. Integrations can call `apply_config_labels` to add labels
from `[package.metadata.allure]` in `Cargo.toml`, and `apply_common_runtime_labels` to add shared
runtime labels such as `language`, `host`, and `thread`.

Environment-derived settings such as `ALLURE_RESULTS_DIR`, global labels, host override, thread
override, and assertion-logging override are loaded once per process through `global_config()`.
Assertion logging is enabled by default and can be disabled with `ALLURE_LOG_ASSERTS=false` or
`[package.metadata.allure] log_asserts = false`. Cargo metadata is cached separately per manifest
directory.

## Output location

The writer creates the target directory if it does not already exist:

```rust
let writer = FileSystemResultsWriter::new("target/allure-results")?;
```