odp-core 0.1.1

Transport-independent protocol primitives for the Offering Discovery Protocol.
Documentation
# odp-core

Transport-independent protocol primitives for the Offering Discovery Protocol.

This crate owns ODP wire models, validation, Resource Identity and reference handling, pagination
models, and protocol-level errors. It does not depend on an asynchronous runtime, HTTP client,
Directory behavior, Agent orchestration, or a Service framework.

Normative JSON Schemas are embedded in the crate and compiled without network access. Parsing a
wire document performs JSON Schema validation, typed deserialization, and the semantic checks that
cannot be expressed by JSON Schema alone.

```rust
use odp_core::parse_service_document;

let data = br#"{
  "description": "An AI-enabled plant store.",
  "http": { "endpoint_base": "/odp" },
  "language": "en",
  "localizations": ["en"],
  "name": "Indica Flowers",
  "odp_version": "1.0",
  "operations": [
    { "authentication": "not-required", "name": "get-offering" },
    { "authentication": "not-required", "name": "list-offerings" }
  ]
}"#;

let document = parse_service_document(data)?;
assert_eq!(document.name, "Indica Flowers");
# Ok::<(), odp_core::ParseError>(())
```

`ServiceProtocols::trust` carries generic trust-protocol advertisements. TAP support is represented
by `TrustProtocol { name: Protocol::Tap }`.

`parse_service_document` is the strict current-version Service parser. Agents use
`parse_agent_service_document`, which filters unrecognized enrollment, payment, and trust
descriptors while retaining strict validation for recognized descriptors.

Validation failures retain structured paths and schema keywords so applications can present more
than a generic parse failure:

```rust
use odp_core::{ParseError, parse_service_document};

let error = parse_service_document(br#"{}"#).unwrap_err();
if let ParseError::Validation(validation) = error {
    for issue in validation.issues {
        println!("{}: {}", issue.path, issue.message);
    }
}
```

Resource identifiers are local to a Service. Compose a stable identity with the canonical Service
origin, and resolve origin-relative resource links against that same origin:

```rust
use odp_core::{ResourceIdentity, ResourceType, resolve_resource_reference};

let identity = ResourceIdentity::new(
    "https://shop.example/.well-known/odp",
    ResourceType::Offering,
    "rubber-plant",
)?;
let image = resolve_resource_reference("/images/rubber-plant.webp", &identity.service)?;

assert_eq!(identity.service, "https://shop.example");
assert_eq!(image.as_str(), "https://shop.example/images/rubber-plant.webp");
# Ok::<(), odp_core::ReferenceError>(())
```

Unknown additive members are retained in each model's `additional` map. Use
`parse_problem_response` for an ODP error response when the HTTP status must agree with the Problem
Details body.

See the [workspace guide](../../README.md) and the
[ODP specification](https://www.offeringprotocol.org/).