// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

#![cfg_attr(coverage_nightly, feature(coverage_attribute))]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![doc(html_logo_url = "https://media.githubusercontent.com/media/microsoft/oxidizer/refs/heads/main/crates/seatbelt/logo.png")]
#![doc(html_favicon_url = "https://media.githubusercontent.com/media/microsoft/oxidizer/refs/heads/main/crates/seatbelt/favicon.ico")]
#![cfg_attr(
    not(all(feature = "retry", feature = "timeout", feature = "breaker", feature = "metrics", feature = "logs")),
    expect(
        rustdoc::broken_intra_doc_links,
        reason = "too ugly to make 'live links' possible with the combination of features"
    )
)]

//! Resilience and recovery mechanisms for fallible operations.
//!
//! # Quick Start
//!
//! Add resilience to fallible operations, such as RPC calls over the network, with just a few lines of code.
//! **Retry** handles transient failures and **Timeout** prevents operations from hanging indefinitely:
//!
//! ```rust
//! # #[cfg(all(feature = "retry", feature = "timeout"))]
//! # {
//! # use std::time::Duration;
//! # use tick::Clock;
//! use layered::{Execute, Service, Stack};
//! use seatbelt::retry::Retry;
//! use seatbelt::timeout::Timeout;
//! use seatbelt::{RecoveryInfo, ResilienceContext};
//!
//! # async fn main(clock: Clock) {
//! let context = ResilienceContext::new(&clock);
//! let service = (
//!     // Retry middleware: Automatically retries failed operations
//!     Retry::layer("retry", &context)
//!         .clone_input()
//!         .recovery_with(|output: &String, _| match output.as_str() {
//!             "temporary_error" => RecoveryInfo::retry(),
//!             "operation timed out" => RecoveryInfo::retry(),
//!             _ => RecoveryInfo::never(),
//!         }),
//!     // Timeout middleware: Cancels operations that take too long
//!     Timeout::layer("timeout", &context)
//!         .timeout_output(|_| "operation timed out".to_string())
//!         .timeout(Duration::from_secs(30)),
//!     // Your core business logic
//!     Execute::new(my_string_operation),
//! )
//!     .into_service();
//!
//! let result = service.execute("input data".to_string()).await;
//! # }
//! # async fn my_string_operation(input: String) -> String {
//! #     // Simulate processing that transforms the input string
//! #     format!("processed: {}", input)
//! # }
//! # }
//! ```
//!
//! # Why?
//!
//! Communicating over a network is inherently fraught with problems. The network can go down at any time,
//! sometimes for a millisecond or two. The endpoint you're connecting to may crash or be rebooted,
//! network configuration may change from under you, etc. To deliver a robust experience to users, and to
//! achieve `5` or more `9s` of availability, it is imperative to implement robust resilience patterns to
//! mask these transient failures.
//!
//! This crate provides production-ready resilience middleware with excellent telemetry for building
//! robust distributed systems that can automatically handle timeouts, retries, and other failure
//! scenarios.
//!
//! - **Production-ready** - Battle-tested middleware with sensible defaults and comprehensive
//!   configuration options.
//! - **Excellent telemetry** - Built-in support for metrics and structured logging to monitor
//!   resilience behavior in production.
//! - **Runtime agnostic** - Works seamlessly across any async runtime. Use the same resilience
//!   patterns across different projects and migrate between runtimes without changes.
//!
//! # Overview
//!
//! This crate uses the [`layered`] crate for composing middleware. The middleware layers
//! can be stacked together using tuples and built into a service using the [`Stack`][layered::Stack] trait.
//!
//! Resilience middleware also requires [`Clock`][tick::Clock] from the [`tick`] crate for timing
//! operations like delays, timeouts, and backoff calculations. The clock is passed through
//! [`ResilienceContext`] when creating middleware layers.
//!
//! ## Core Types
//!
//! - [`ResilienceContext`] - Holds shared state for resilience middleware, including the clock.
//! - [`RecoveryInfo`] - Classifies errors as recoverable (transient) or non-recoverable (permanent).
//! - [`Recovery`] - A trait for types that can determine their recoverability.
//!
//! ## Built-in Middleware
//!
//! This crate provides built-in resilience middleware that you can use out of the box. See the documentation
//! for each module for details on how to use them.
//!
//! - [`timeout`] - Middleware that cancels long-running operations.
//! - [`retry`] - Middleware that automatically retries failed operations.
//! - [`breaker`] - Middleware that prevents cascading failures.
//!
//! # Tower Compatibility
//!
//! All resilience middleware are compatible with the Tower ecosystem when the `tower-service`
//! feature is enabled. This allows you to use `tower::ServiceBuilder` to compose middleware stacks:
//!
//! ```rust
//! # use std::time::Duration;
//! # use tick::Clock;
//! use seatbelt::retry::Retry;
//! use seatbelt::timeout::Timeout;
//! use seatbelt::{RecoveryInfo, ResilienceContext};
//! use tower::ServiceBuilder;
//!
//! # async fn example(clock: Clock) {
//! let context: ResilienceContext<String, Result<String, String>> =
//!     ResilienceContext::new(&clock);
//!
//! let service = ServiceBuilder::new()
//!     .layer(
//!         Retry::layer("my_retry", &context)
//!             .clone_input()
//!             .recovery_with(|result: &Result<String, String>, _| match result {
//!                 Ok(_) => RecoveryInfo::never(),
//!                 Err(_) => RecoveryInfo::retry(),
//!             }),
//!     )
//!     .layer(
//!         Timeout::layer("my_timeout", &context)
//!             .timeout(Duration::from_secs(30))
//!             .timeout_error(|_| "operation timed out".to_string()),
//!     )
//!     .service_fn(|input: String| async move {
//!         Ok::<_, String>(format!("processed: {input}"))
//!     });
//! # }
//! ```
//!
//! # Features
//!
//! This crate provides several optional features that can be enabled in your `Cargo.toml`:
//!
//! - **`timeout`** - Enables the [`timeout`] middleware for canceling long-running operations.
//! - **`retry`** - Enables the [`retry`] middleware for automatically retrying failed operations with
//!   configurable backoff strategies, jitter, and recovery classification.
//! - **`breaker`** - Enables the [`breaker`] middleware for preventing cascading failures.
//! - **`metrics`** - Exposes the OpenTelemetry metrics API for collecting and reporting metrics.
//! - **`logs`** - Enables structured logging for resilience middleware using the `tracing` crate.
//! - **`tower-service`** - Enables [`tower_service::Service`] trait implementations for all
//!   resilience middleware.

#[doc(inline)]
pub use recoverable::{Recovery, RecoveryInfo, RecoveryKind};

mod context;
pub use context::ResilienceContext;

mod shared;
pub use crate::shared::{NotSet, Set};

#[cfg(any(feature = "timeout", test))]
pub mod timeout;

#[cfg(any(feature = "retry", test))]
pub mod retry;

#[cfg(any(feature = "breaker", test))]
pub mod breaker;

#[cfg(any(feature = "retry", feature = "breaker", test))]
mod rnd;

#[cfg(any(feature = "retry", feature = "breaker", feature = "timeout", test))]
pub(crate) mod utils;

#[cfg(any(feature = "metrics", test))]
mod metrics;

#[cfg_attr(coverage_nightly, coverage(off))]
#[cfg(test)]
pub(crate) mod testing;
