use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::fmt;
pub const ENVELOPE_VERSION: u32 = 1;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum ErrorCode {
#[serde(rename = "KEEL-E001")]
PolicyInvalid,
#[serde(rename = "KEEL-E002")]
TargetUnknown,
#[serde(rename = "KEEL-E003")]
EnvelopeDecode,
#[serde(rename = "KEEL-E004")]
EnvelopeVersion,
#[serde(rename = "KEEL-E005")]
UnsupportedConfiguration,
#[serde(rename = "KEEL-E010")]
AttemptsExhausted,
#[serde(rename = "KEEL-E011")]
Timeout,
#[serde(rename = "KEEL-E012")]
BreakerOpen,
#[serde(rename = "KEEL-E013")]
RateBudgetExceeded,
#[serde(rename = "KEEL-E014")]
NonIdempotentNotRetried,
#[serde(rename = "KEEL-E015")]
NonRetryableError,
#[serde(rename = "KEEL-E020")]
CacheCodec,
#[serde(rename = "KEEL-E030")]
FlowLeaseHeld,
#[serde(rename = "KEEL-E031")]
FlowNondeterminism,
#[serde(rename = "KEEL-E032")]
FlowDead,
#[serde(rename = "KEEL-E040")]
Internal,
}
impl ErrorCode {
pub fn as_str(self) -> &'static str {
match self {
ErrorCode::PolicyInvalid => "KEEL-E001",
ErrorCode::TargetUnknown => "KEEL-E002",
ErrorCode::EnvelopeDecode => "KEEL-E003",
ErrorCode::EnvelopeVersion => "KEEL-E004",
ErrorCode::UnsupportedConfiguration => "KEEL-E005",
ErrorCode::AttemptsExhausted => "KEEL-E010",
ErrorCode::Timeout => "KEEL-E011",
ErrorCode::BreakerOpen => "KEEL-E012",
ErrorCode::RateBudgetExceeded => "KEEL-E013",
ErrorCode::NonIdempotentNotRetried => "KEEL-E014",
ErrorCode::NonRetryableError => "KEEL-E015",
ErrorCode::CacheCodec => "KEEL-E020",
ErrorCode::FlowLeaseHeld => "KEEL-E030",
ErrorCode::FlowNondeterminism => "KEEL-E031",
ErrorCode::FlowDead => "KEEL-E032",
ErrorCode::Internal => "KEEL-E040",
}
}
}
impl fmt::Display for ErrorCode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct KeelError {
pub code: ErrorCode,
pub message: String,
}
impl fmt::Display for KeelError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}: {}", self.code, self.message)
}
}
impl std::error::Error for KeelError {}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ErrorClass {
Conn,
Timeout,
Http,
Cancelled,
Other,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Request {
pub v: u32,
pub target: String,
pub op: String,
pub idempotent: bool,
#[serde(default)]
pub args_hash: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "status", rename_all = "snake_case")]
pub enum AttemptResult {
Ok {
payload: Value,
},
Error {
class: ErrorClass,
#[serde(default, skip_serializing_if = "Option::is_none")]
http_status: Option<u16>,
#[serde(default, skip_serializing_if = "Option::is_none")]
retry_after_ms: Option<u64>,
#[serde(default)]
message: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
original: Option<Value>,
},
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OutcomeError {
pub code: ErrorCode,
pub class: ErrorClass,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub http_status: Option<u16>,
pub message: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub original: Option<Value>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum BreakerState {
Closed,
Open,
HalfOpen,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Outcome {
pub v: u32,
pub result: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub payload: Option<Value>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub error: Option<OutcomeError>,
pub attempts: u32,
pub from_cache: bool,
pub waits_ms: Vec<u64>,
pub throttled: bool,
pub throttle_wait_ms: u64,
pub breaker: BreakerState,
pub trace_id: String,
}
pub trait KeelCore {
fn configure(&mut self, policy_json: &Value) -> Result<(), KeelError>;
fn execute(
&mut self,
request: &Request,
effect: &mut dyn FnMut(u32) -> AttemptResult,
) -> Outcome;
fn report(&self) -> Value;
fn advance_clock(&mut self, _ms: u64) {}
}