<div align="center">
<img src="./logo.png" alt="Recoverable Logo" width="96">
# Recoverable
[](https://crates.io/crates/recoverable)
[](https://docs.rs/recoverable)
[](https://crates.io/crates/recoverable)
[](https://github.com/microsoft/oxidizer/actions/workflows/main.yml)
[](https://codecov.io/gh/microsoft/oxidizer)
[](../../LICENSE)
<a href="../.."><img src="../../logo.svg" alt="This crate was developed as part of the Oxidizer project" width="20"></a>
</div>
Recovery information and classification for resilience patterns.
## Why
This crate provides types for classifying conditions based on their **recoverability state**,
enabling consistent recovery behavior across different error types and resilience middleware.
## Recovery Information
The recovery information describes whether recovering from an operation might help, not whether
the operation succeeded or failed. Both successful operations and permanent failures
should use [`RecoveryInfo::never`][__link0] since recovery is not necessary or desirable.
## Core Types
* [`RecoveryInfo`][__link1]: Classifies conditions as recoverable (transient) or non-recoverable (permanent/successful).
* [`Recovery`][__link2]: A trait for types that can determine their recoverability.
* [`RecoveryKind`][__link3]: An enum representing the kind of recovery that can be attempted.
## Examples
### Recovery Error
```rust
use recoverable::{Recovery, RecoveryInfo, RecoveryKind};
#[derive(Debug)]
enum DatabaseError {
ConnectionTimeout,
InvalidCredentials,
TableNotFound,
}
impl Recovery for DatabaseError {
fn recovery(&self) -> RecoveryInfo {
match self {
// Transient failure - might succeed if retried
DatabaseError::ConnectionTimeout => RecoveryInfo::retry(),
// Permanent failures - retrying won't help
DatabaseError::InvalidCredentials => RecoveryInfo::never(),
DatabaseError::TableNotFound => RecoveryInfo::never(),
}
}
}
let error = DatabaseError::ConnectionTimeout;
assert_eq!(error.recovery().kind(), RecoveryKind::Retry);
// For successful operations, also use never() since retry is unnecessary
let success_result: Result<(), DatabaseError> = Ok(());
// If we had a wrapper type for success, it would also return RecoveryInfo::never()
```
### Retry Delay
You can specify when to retry an operation using the `delay` method:
```rust
use std::time::Duration;
use recoverable::{RecoveryInfo, RecoveryKind};
// Retry with a 30-second delay (e.g., from a Retry-After header)
let recovery = RecoveryInfo::retry().delay(Duration::from_secs(30));
assert_eq!(recovery.kind(), RecoveryKind::Retry);
assert_eq!(recovery.get_delay(), Some(Duration::from_secs(30)));
// Immediate retry
let immediate = RecoveryInfo::retry().delay(Duration::ZERO);
assert_eq!(immediate.get_delay(), Some(Duration::ZERO));
```
<hr/>
<sub>
This crate was developed as part of <a href="../..">The Oxidizer Project</a>. Browse this crate's <a href="https://github.com/microsoft/oxidizer/tree/main/crates/recoverable">source code</a>.
</sub>
[__cargo_doc2readme_dependencies_info]: ggGkYW0CYXSEGy4k8ldDFPOhG2VNeXtD5nnKG6EPY6OfW5wBG8g18NOFNdxpYXKEG4cFLMVQymhvG3_1rzbl1X55G-vZhEWC9_13GwjdQK0PrVchYWSBgmtyZWNvdmVyYWJsZWUwLjEuMA
[__link0]: https://docs.rs/recoverable/0.1.0/recoverable/?search=RecoveryInfo::never
[__link1]: https://docs.rs/recoverable/0.1.0/recoverable/struct.RecoveryInfo.html
[__link2]: https://docs.rs/recoverable/0.1.0/recoverable/trait.Recovery.html
[__link3]: https://docs.rs/recoverable/0.1.0/recoverable/enum.RecoveryKind.html