1#![allow(clippy::result_large_err)]
28
29pub(crate) mod adapters;
32pub(crate) mod bootstrap;
33pub mod ports;
34
35mod config;
36mod error;
37mod handler_error;
38mod request_id;
39
40pub mod org_isolation;
41pub mod org_policy;
42
43pub mod audit;
44pub mod etag;
45pub mod extract;
46pub mod pagination;
47
48#[cfg(feature = "testing")]
49pub mod testing;
50
51#[cfg(feature = "http-client")]
52pub mod http_client;
53
54#[cfg(feature = "metrics")]
55pub mod metrics;
56
57pub use audit::{
60 AuditAnnotation, AuditAnnotationSlot, AuditEvent, AuditFilter, AuditLayer, AuditService,
61 AuditSink, AuditSinkError, TracingAuditSink,
62};
63pub use bootstrap::{BootstrapCtx, ServiceBootstrap, ShutdownHookFn};
64pub use config::{BootstrapConfig, CorsConfig, LogFormat, RateLimitConfig, RateLimitKind};
65pub use error::{Error, Result};
66pub use etag::{ETag, IfMatch, IfNoneMatch, check_if_match, etag_from_updated_at};
67pub use handler_error::{
68 ApiError, CreatedAtResponse, CreatedResponse, ErrorCode, EtaggedHandlerResponse, HandlerError,
69 HandlerListResponse, HandlerResponse, ProblemJson, UnconstrainedResponse, ValidationError,
70 created, created_at, created_under, etagged, listed, listed_page, ok,
71};
72
73#[cfg(feature = "rfc-types")]
74pub use handler_error::RfcOk;
75
76#[cfg(feature = "test-util")]
77pub use audit::ChannelAuditSink;
78
79#[cfg(feature = "nats")]
80pub use audit::NatsJetStreamAuditSink;
81
82pub use org_isolation::{
83 OrgContextExtractor, OrgContextSource, OrgIsolationLayer, OrgIsolationService,
84};
85pub use org_policy::{AncestryOrgPolicy, OrgPolicy};
86
87pub use pagination::{
88 CursorPaginatedResponse, CursorPagination, CursorPaginationParams, KeysetPaginatedResponse,
89 KeysetPaginationParams, PaginatedResponse, PaginationParams, SortDirection, SortParams,
90};
91
92#[cfg(feature = "cursor")]
93pub use pagination::{Cursor, CursorError};
94
95#[cfg(feature = "ratelimit")]
96pub use adapters::security::rate_limit::{RateLimitBackend, RateLimitExtractor};
97
98#[cfg(feature = "validation")]
99pub use extract::Valid;
100
101pub use ports::auth::AuthProvider;
102pub use ports::health::ReadinessCheckFn;
103pub use ports::rate_limit::RateLimitProvider;
104#[cfg(feature = "telemetry")]
105pub use ports::telemetry::{BasicTelemetryProvider, TelemetryProvider};
106
107pub use api_bones::org_context::OrganizationContext;
110pub use api_bones::org_id::{OrgId, OrgPath};
111
112pub use api_bones::common::{ResourceId, Timestamp};
113pub use api_bones::ratelimit::RateLimitInfo;
114
115pub use api_bones::AuditInfo;
116pub use api_bones::{ApiResponse, ApiResponseBuilder, ResponseMeta};
117pub use api_bones::{BulkItemResult, BulkRequest, BulkResponse};
118pub use api_bones::{CorrelationId, CorrelationIdError};
119pub use api_bones::{IdempotencyKey, IdempotencyKeyError};
120pub use api_bones::{Link, Links};
121pub use api_bones::{RequestId, RequestIdParseError};
122pub use api_bones::{Slug, SlugError};
123
124#[cfg(feature = "openapi")]
140#[macro_export]
141macro_rules! generate_openapi_binary {
142 ($api_doc:ty) => {
143 $crate::generate_openapi_binary!($api_doc, "/health");
144 };
145 ($api_doc:ty, $health_path:expr) => {
146 fn main() {
147 use utoipa::OpenApi as _;
148 use $crate::openapi::{merge_health_paths, to_3_0_pretty_json};
149 let mut doc = <$api_doc>::openapi();
150 merge_health_paths(&mut doc, $health_path);
151 match to_3_0_pretty_json(&doc) {
152 Ok(json) => println!("{json}"),
153 Err(e) => {
154 eprintln!("generate-openapi: serialize failed: {e}");
155 std::process::exit(1);
156 }
157 }
158 }
159 };
160}
161
162#[cfg(feature = "openapi")]
164pub mod openapi;
165
166pub mod reexports {
168 pub use api_bones;
169}
170
171#[cfg(test)]
172mod tests {
173 use crate::error::Error;
174
175 #[test]
176 fn error_display_covers_all_variants() {
177 assert!(Error::Config("x".into()).to_string().contains("x"));
178 assert!(Error::Telemetry("x".into()).to_string().contains("x"));
179 assert!(Error::Database("x".into()).to_string().contains("x"));
180 assert!(Error::Bind("x".into()).to_string().contains("x"));
181 assert!(Error::Serve("x".into()).to_string().contains("x"));
182 }
183
184 #[cfg(feature = "telemetry")]
185 #[test]
186 fn init_basic_tracing_is_idempotent() {
187 crate::adapters::observability::telemetry::init_basic_tracing();
188 crate::adapters::observability::telemetry::init_basic_tracing();
189 }
190}