use alloc::{format, string::String, vec::Vec};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use serde_json::Value;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
pub enum DispatchMode {
Await,
FireAndForget,
}
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct RuntimeDispatchRequest {
pub target: String,
pub operation: String,
pub mode: DispatchMode,
pub input: Value,
pub deadline_ms: Option<u64>,
}
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct RuntimeDispatchResponse {
pub ok: bool,
pub output: Value,
#[cfg_attr(feature = "serde", serde(default))]
pub events: Vec<Value>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
pub error: Option<DispatchError>,
}
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct DispatchError {
pub code: String,
pub message: String,
}
pub fn request_topic(runtime: &str) -> String {
format!("greentic.{runtime}.request.v1")
}
pub fn response_topic(runtime: &str) -> String {
format!("greentic.{runtime}.response.v1")
}
#[cfg(all(test, feature = "serde"))]
#[allow(clippy::unwrap_used, clippy::expect_used)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn request_round_trips_through_json() {
let req = RuntimeDispatchRequest {
target: "dep-123".into(),
operation: "create_invoice".into(),
mode: DispatchMode::Await,
input: json!({ "amount": 10 }),
deadline_ms: Some(30_000),
};
let encoded = serde_json::to_value(&req).unwrap();
let decoded: RuntimeDispatchRequest = serde_json::from_value(encoded).unwrap();
assert_eq!(decoded, req);
}
#[test]
fn topic_helpers_use_runtime_name() {
assert_eq!(request_topic("sorla"), "greentic.sorla.request.v1");
assert_eq!(response_topic("sorla"), "greentic.sorla.response.v1");
}
}