Skip to main content

api_bones/
lib.rs

1//! # api-bones
2//!
3//! Opinionated REST API types: errors (RFC 9457), pagination, health checks, and more.
4//!
5//! ## `no_std` support
6//!
7//! This crate is `#![no_std]` when the default `std` feature is disabled.
8//!
9//! | Features enabled         | Available types                                    |
10//! |--------------------------|-----------------------------------------------------|
11//! | *(none)*                 | Pure-`core` types: `ErrorCode`, `HealthStatus`, `PaginationParams`, `SortDirection` |
12//! | `alloc`                  | All types that use `String`/`Vec`/`Arc`             |
13//! | `std` *(default)*        | Full feature set including `HashMap`-backed types   |
14//!
15//! ```toml
16//! # no_std + alloc (WASM, embedded with allocator)
17//! api-bones = { version = "...", default-features = false, features = ["alloc"] }
18//!
19//! # pure no_std (core types only)
20//! api-bones = { version = "...", default-features = false }
21//! ```
22//!
23//! ## Core type: [`ApiError`]
24//!
25//! Every service serializes errors into [RFC 9457](https://www.rfc-editor.org/rfc/rfc9457)
26//! Problem Details format:
27//!
28//! ```json
29//! {
30//!   "type": "urn:api-bones:error:resource-not-found",
31//!   "title": "Resource Not Found",
32//!   "status": 404,
33//!   "detail": "Booking 123 not found"
34//! }
35//! ```
36//!
37//! ```rust
38//! use api_bones::{ApiError, ErrorCode};
39//!
40//! fn find_booking(id: u64) -> Result<(), ApiError> {
41//!     Err(ApiError::not_found(format!("booking {id} not found")))
42//! }
43//! ```
44//!
45//! ## Feature flags (selection)
46//!
47//! | Feature    | What it enables                                      |
48//! |------------|------------------------------------------------------|
49//! | `schemars` | [`schemars::JsonSchema`] derive on all public types  |
50//! | `utoipa`   | [`utoipa::ToSchema`] derive on all public types      |
51//!
52//! Enable `schemars` in your `Cargo.toml`:
53//!
54//! ```toml
55//! api-bones = { version = "3", features = ["schemars"] }
56//! ```
57//!
58//! ## Add as dependency
59//!
60//! ```toml
61//! [dependencies]
62//! api-bones = "3"
63//! ```
64
65#![cfg_attr(not(feature = "std"), no_std)]
66
67// When `std` is not available but `alloc` is, bring the alloc crate into scope.
68// Under `std`, the `alloc` crate is re-exported by `std` so no explicit import
69// is needed.
70#[cfg(all(not(feature = "std"), feature = "alloc"))]
71extern crate alloc;
72
73// Modules that require heap allocation (String / Vec / Arc).
74#[cfg(any(feature = "std", feature = "alloc"))]
75pub mod audit;
76#[cfg(any(feature = "std", feature = "alloc"))]
77pub mod bulk;
78#[cfg(any(feature = "std", feature = "alloc"))]
79pub mod cache;
80#[cfg(all(any(feature = "std", feature = "alloc"), feature = "uuid"))]
81pub mod correlation_id;
82#[cfg(any(feature = "std", feature = "alloc"))]
83pub mod cors;
84#[cfg(feature = "base64")]
85pub mod cursor;
86#[cfg(any(feature = "std", feature = "alloc"))]
87pub mod deprecated;
88#[cfg(any(feature = "std", feature = "alloc"))]
89pub mod etag;
90#[cfg(any(feature = "std", feature = "alloc"))]
91pub mod header_id;
92#[cfg(all(any(feature = "std", feature = "alloc"), feature = "uuid"))]
93pub mod idempotency;
94#[cfg(any(feature = "std", feature = "alloc"))]
95pub mod links;
96#[cfg(all(any(feature = "std", feature = "alloc"), feature = "uuid"))]
97pub mod org_id;
98#[cfg(any(feature = "std", feature = "alloc"))]
99pub mod range;
100#[cfg(all(any(feature = "std", feature = "alloc"), feature = "uuid"))]
101pub mod request_id;
102#[cfg(any(feature = "std", feature = "alloc"))]
103pub mod response;
104#[cfg(any(feature = "std", feature = "alloc"))]
105pub mod slug;
106#[cfg(all(any(feature = "std", feature = "alloc"), feature = "uuid"))]
107pub mod traceparent;
108#[cfg(any(feature = "std", feature = "alloc"))]
109pub mod url;
110#[cfg(any(feature = "std", feature = "alloc"))]
111pub mod vary;
112pub mod version;
113
114// Modules available in all configurations; individual types inside are gated
115// where they require `alloc` or `std`.
116pub mod common;
117pub mod error;
118pub mod health;
119pub mod method;
120pub mod pagination;
121pub mod query;
122pub mod ratelimit;
123pub mod retry;
124pub mod status;
125
126#[cfg(any(feature = "std", feature = "alloc"))]
127pub mod content_type;
128
129#[cfg(feature = "http")]
130pub mod header;
131
132#[cfg(all(feature = "serde", any(feature = "std", feature = "alloc")))]
133pub mod serde;
134
135#[cfg(feature = "fake")]
136mod fake_impls;
137
138#[cfg(feature = "icalendar")]
139pub mod calendar;
140
141// OpenAPI helpers: Example<T> and DeprecatedField (issues #119, #120).
142pub mod openapi;
143
144#[cfg(feature = "connect")]
145pub mod connect;
146
147#[cfg(feature = "opentelemetry")]
148pub mod propagation;
149
150pub mod has_id;
151pub use has_id::HasId;
152
153#[cfg(any(feature = "std", feature = "alloc"))]
154pub use audit::{
155    AuditInfo, DeviceLease, DeviceLeaseKind, Principal, PrincipalId, PrincipalKind,
156    PrincipalParseError, ResolvedPrincipal,
157};
158#[cfg(any(feature = "std", feature = "alloc"))]
159pub use bulk::{BulkItemResult, BulkRequest, BulkResponse};
160#[cfg(feature = "uuid")]
161pub use common::ResourceId;
162#[cfg(feature = "uuid")]
163pub use common::new_resource_id;
164#[cfg(feature = "chrono")]
165pub use common::parse_timestamp;
166// Timestamp is chrono::DateTime when chrono is on (no alloc needed),
167// or String when chrono is off (needs alloc or std).
168#[cfg(any(feature = "std", feature = "alloc"))]
169pub use cache::CacheControl;
170#[cfg(any(feature = "chrono", feature = "std", feature = "alloc"))]
171pub use common::Timestamp;
172#[cfg(any(feature = "std", feature = "alloc"))]
173pub use content_type::ContentType;
174#[cfg(all(any(feature = "std", feature = "alloc"), feature = "uuid"))]
175pub use correlation_id::{CorrelationId, CorrelationIdError};
176#[cfg(any(feature = "std", feature = "alloc"))]
177pub use cors::{CorsHeaders, CorsOrigin};
178#[cfg(feature = "base64")]
179pub use cursor::{Cursor, CursorError};
180#[cfg(any(feature = "std", feature = "alloc"))]
181pub use deprecated::Deprecated;
182pub use error::ErrorCode;
183#[cfg(any(feature = "std", feature = "alloc"))]
184pub use error::ErrorTypeMode;
185#[cfg(any(feature = "std", feature = "alloc"))]
186pub use error::HttpError;
187#[cfg(all(any(feature = "std", feature = "alloc"), feature = "serde"))]
188pub use error::ProblemJson;
189#[cfg(any(feature = "std", feature = "alloc"))]
190pub use error::{ApiError, ValidationError};
191#[cfg(feature = "std")]
192pub use error::{error_type_mode, set_error_type_mode, urn_namespace};
193#[cfg(any(feature = "std", feature = "alloc"))]
194pub use etag::{ETag, IfMatch, IfNoneMatch};
195#[cfg(feature = "http")]
196pub use header::{HeaderName, HeaderValue};
197#[cfg(any(feature = "std", feature = "alloc"))]
198pub use header_id::HeaderId;
199pub use health::HealthStatus;
200#[cfg(feature = "std")]
201pub use health::ReadinessResponse;
202#[cfg(any(feature = "std", feature = "alloc"))]
203pub use health::{HealthCheck, LivenessResponse};
204#[cfg(all(any(feature = "std", feature = "alloc"), feature = "uuid"))]
205pub use idempotency::{IdempotencyKey, IdempotencyKeyError};
206#[cfg(any(feature = "std", feature = "alloc"))]
207pub use links::{Link, Links};
208pub use method::HttpMethod;
209#[cfg(all(
210    any(feature = "std", feature = "alloc"),
211    feature = "uuid",
212    feature = "http"
213))]
214pub use org_id::OrgIdHeaderError;
215#[cfg(all(any(feature = "std", feature = "alloc"), feature = "uuid"))]
216pub use org_id::{OrgId, OrgIdError, OrgPath};
217pub use pagination::PaginationParams;
218#[cfg(any(feature = "std", feature = "alloc"))]
219pub use pagination::{
220    CursorPaginatedResponse, CursorPagination, CursorPaginationParams, PaginatedResponse,
221};
222#[cfg(any(feature = "std", feature = "alloc"))]
223pub use pagination::{KeysetPaginatedResponse, KeysetPaginationParams};
224pub use query::SortDirection;
225#[cfg(any(feature = "std", feature = "alloc"))]
226pub use query::{FilterEntry, FilterOp, FilterParams, SearchParams, SortParams};
227#[cfg(any(feature = "std", feature = "alloc"))]
228pub use range::{ByteRange, ContentRange, ParseRangeError, RangeHeader};
229#[cfg(any(feature = "std", feature = "alloc"))]
230pub use ratelimit::RateLimitInfo;
231#[cfg(all(any(feature = "std", feature = "alloc"), feature = "uuid"))]
232pub use request_id::{RequestId, RequestIdError, RequestIdParseError};
233#[cfg(any(feature = "std", feature = "alloc"))]
234pub use response::{ApiResponse, ApiResponseBuilder, ResponseMeta};
235pub use retry::{BackoffStrategy, Idempotent, RetryPolicy};
236#[cfg(any(feature = "std", feature = "alloc"))]
237pub use retry::{RetryAfter, RetryAfterParseError};
238#[cfg(any(feature = "std", feature = "alloc"))]
239pub use slug::{Slug, SlugError};
240pub use status::StatusCode;
241#[cfg(all(any(feature = "std", feature = "alloc"), feature = "uuid"))]
242pub use traceparent::{SamplingFlags, SpanId, TraceContext, TraceContextError, TraceId};
243#[cfg(any(feature = "std", feature = "alloc"))]
244pub use url::{QueryBuilder, UrlBuilder};
245#[cfg(any(feature = "std", feature = "alloc"))]
246pub use vary::Vary;
247pub use version::{ApiVersion, ApiVersionParseError, SemverTriple};