use crate::types::Generation;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum FaultClass {
Transport,
Process,
Protocol,
Timeout,
Downstream,
Resource,
Replay,
Rollout,
Invariant,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum RecoveryDirective {
RetryInPlace,
RestartAndReplay,
AbortRequest,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
#[serde(transparent)]
pub struct FaultCode(String);
impl FaultCode {
pub fn try_new(code: impl Into<String>) -> Result<Self, crate::types::InvariantViolation> {
let code = code.into();
if code.is_empty()
|| !code
.bytes()
.all(|byte| byte.is_ascii_lowercase() || byte == b'_' || byte.is_ascii_digit())
{
return Err(crate::types::InvariantViolation::new(
"fault code must be non-empty lowercase ascii snake_case",
));
}
Ok(Self(code))
}
#[must_use]
pub fn as_str(&self) -> &str {
self.0.as_str()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct Fault {
pub generation: Generation,
pub class: FaultClass,
pub code: FaultCode,
pub directive: RecoveryDirective,
pub detail: String,
}
impl Fault {
#[must_use]
pub fn new(
generation: Generation,
class: FaultClass,
code: FaultCode,
directive: RecoveryDirective,
detail: impl Into<String>,
) -> Self {
Self {
generation,
class,
code,
directive,
detail: detail.into(),
}
}
}
#[cfg(test)]
mod tests {
use super::FaultCode;
#[test]
fn fault_code_rejects_non_snake_case() {
assert!(FaultCode::try_new("broken_pipe").is_ok());
assert!(FaultCode::try_new("BrokenPipe").is_err());
assert!(FaultCode::try_new("").is_err());
}
}