use serde::{Deserialize, Serialize};
use serde_json::Value;
pub mod compensation_policy {
pub const NONE: &str = "none";
pub const ON_FAILURE: &str = "on_failure";
pub const ALWAYS: &str = "always";
}
pub mod aggregate_strategy {
pub const MERGE: &str = "merge";
pub const WEIGHTED_FIRST_K: &str = "weighted_first_k";
pub const MERGE_ALL: &str = "merge_all";
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DagNode {
pub id: String,
pub action: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub target_nid: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub inputs: Option<serde_json::Map<String, Value>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub depends_on: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub compensate_action: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub compensate_params_mapping: Option<serde_json::Map<String, Value>>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BackoffStrategy {
Fixed,
Linear,
Exponential,
}
impl BackoffStrategy {
pub fn compute_delay_ms(self, base_ms: u64, max_ms: u64, attempt: u32) -> u64 {
let raw = match self {
BackoffStrategy::Fixed => base_ms,
BackoffStrategy::Linear => base_ms * (attempt as u64 + 1),
BackoffStrategy::Exponential => base_ms * (1u64 << attempt),
};
raw.min(max_ms)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TaskState {
Pending,
Running,
Completed,
Failed,
Cancelled,
Compensating,
Compensated,
}
impl TaskState {
pub fn from_str(s: &str) -> Option<Self> {
match s {
"pending" => Some(TaskState::Pending),
"running" => Some(TaskState::Running),
"completed" => Some(TaskState::Completed),
"failed" => Some(TaskState::Failed),
"cancelled" => Some(TaskState::Cancelled),
"compensating" => Some(TaskState::Compensating),
"compensated" => Some(TaskState::Compensated),
_ => None,
}
}
pub fn is_terminal(self) -> bool {
matches!(self, TaskState::Completed | TaskState::Failed | TaskState::Cancelled | TaskState::Compensated)
}
}
#[derive(Debug, Clone)]
pub struct NopTaskStatus {
raw: serde_json::Map<String, Value>,
}
impl NopTaskStatus {
pub fn from_dict(raw: serde_json::Map<String, Value>) -> Self {
NopTaskStatus { raw }
}
pub fn task_id(&self) -> &str {
self.raw
.get("task_id")
.and_then(Value::as_str)
.unwrap_or("")
}
pub fn state(&self) -> Option<TaskState> {
self.raw
.get("state")
.and_then(Value::as_str)
.and_then(TaskState::from_str)
}
pub fn is_terminal(&self) -> bool {
self.state().map(TaskState::is_terminal).unwrap_or(false)
}
pub fn error_code(&self) -> Option<&str> {
self.raw.get("error_code").and_then(Value::as_str)
}
pub fn error_message(&self) -> Option<&str> {
self.raw.get("error_message").and_then(Value::as_str)
}
pub fn node_results(&self) -> Option<&serde_json::Map<String, Value>> {
self.raw.get("node_results").and_then(Value::as_object)
}
pub fn raw(&self) -> &serde_json::Map<String, Value> {
&self.raw
}
}
impl std::fmt::Display for NopTaskStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"NopTaskStatus(task_id={}, state={:?})",
self.task_id(),
self.state()
)
}
}