#![allow(clippy::result_large_err)]
pub(crate) mod adapters;
pub(crate) mod bootstrap;
pub mod ports;
mod config;
mod error;
mod handler_error;
mod request_id;
pub mod org_isolation;
pub mod org_policy;
pub mod audit;
pub mod etag;
pub mod extract;
pub mod pagination;
#[cfg(feature = "testing")]
pub mod testing;
#[cfg(feature = "http-client")]
pub mod http_client;
#[cfg(feature = "metrics")]
pub mod metrics;
pub use audit::{
AuditAnnotation, AuditAnnotationSlot, AuditEvent, AuditFilter, AuditLayer, AuditService,
AuditSink, AuditSinkError, TracingAuditSink,
};
pub use bootstrap::{BootstrapCtx, ServiceBootstrap, ShutdownHookFn};
pub use config::{BootstrapConfig, CorsConfig, LogFormat, RateLimitConfig, RateLimitKind};
pub use error::{Error, Result};
pub use etag::{ETag, IfMatch, IfNoneMatch, check_if_match, etag_from_updated_at};
pub use handler_error::{
ApiError, CreatedAtResponse, CreatedResponse, ErrorCode, EtaggedHandlerResponse, HandlerError,
HandlerListResponse, HandlerResponse, ProblemJson, UnconstrainedResponse, ValidationError,
created, created_at, created_under, etagged, listed, listed_page, ok,
};
#[cfg(feature = "rfc-types")]
pub use handler_error::RfcOk;
#[cfg(feature = "test-util")]
pub use audit::ChannelAuditSink;
#[cfg(feature = "nats")]
pub use audit::NatsJetStreamAuditSink;
pub use org_isolation::{
OrgContextExtractor, OrgContextSource, OrgIsolationLayer, OrgIsolationService,
};
pub use org_policy::{AncestryOrgPolicy, OrgPolicy};
pub use pagination::{
CursorPaginatedResponse, CursorPagination, CursorPaginationParams, KeysetPaginatedResponse,
KeysetPaginationParams, PaginatedResponse, PaginationParams, SortDirection, SortParams,
};
#[cfg(feature = "cursor")]
pub use pagination::{Cursor, CursorError};
#[cfg(feature = "ratelimit")]
pub use adapters::security::rate_limit::{RateLimitBackend, RateLimitExtractor};
#[cfg(feature = "validation")]
pub use extract::Valid;
pub use ports::auth::AuthProvider;
pub use ports::health::ReadinessCheckFn;
pub use ports::rate_limit::RateLimitProvider;
#[cfg(feature = "telemetry")]
pub use ports::telemetry::{BasicTelemetryProvider, TelemetryProvider};
pub use api_bones::org_context::OrganizationContext;
pub use api_bones::org_id::{OrgId, OrgPath};
pub use api_bones::common::{ResourceId, Timestamp};
pub use api_bones::ratelimit::RateLimitInfo;
pub use api_bones::AuditInfo;
pub use api_bones::{ApiResponse, ApiResponseBuilder, ResponseMeta};
pub use api_bones::{BulkItemResult, BulkRequest, BulkResponse};
pub use api_bones::{CorrelationId, CorrelationIdError};
pub use api_bones::{IdempotencyKey, IdempotencyKeyError};
pub use api_bones::{Link, Links};
pub use api_bones::{RequestId, RequestIdParseError};
pub use api_bones::{Slug, SlugError};
#[cfg(feature = "openapi")]
#[macro_export]
macro_rules! generate_openapi_binary {
($api_doc:ty) => {
$crate::generate_openapi_binary!($api_doc, "/health");
};
($api_doc:ty, $health_path:expr) => {
fn main() {
use utoipa::OpenApi as _;
use $crate::openapi::{merge_health_paths, to_3_0_pretty_json};
let mut doc = <$api_doc>::openapi();
merge_health_paths(&mut doc, $health_path);
match to_3_0_pretty_json(&doc) {
Ok(json) => println!("{json}"),
Err(e) => {
eprintln!("generate-openapi: serialize failed: {e}");
std::process::exit(1);
}
}
}
};
}
#[cfg(feature = "openapi")]
pub mod openapi;
pub mod reexports {
pub use api_bones;
}
#[cfg(test)]
mod tests {
use crate::error::Error;
#[test]
fn error_display_covers_all_variants() {
assert!(Error::Config("x".into()).to_string().contains("x"));
assert!(Error::Telemetry("x".into()).to_string().contains("x"));
assert!(Error::Database("x".into()).to_string().contains("x"));
assert!(Error::Bind("x".into()).to_string().contains("x"));
assert!(Error::Serve("x".into()).to_string().contains("x"));
}
#[cfg(feature = "telemetry")]
#[test]
fn init_basic_tracing_is_idempotent() {
crate::adapters::observability::telemetry::init_basic_tracing();
crate::adapters::observability::telemetry::init_basic_tracing();
}
}