use std::sync::Arc;
use async_trait::async_trait;
use serde_json::{json, Value};
use tokio_util::sync::CancellationToken;
use zelos::actions::ActionsError;
use zelos::{Action, ActionExecuteResult, ActionSchema, ActionsClient, ActionsRegistry};
struct AddNumbers;
#[async_trait]
impl Action for AddNumbers {
async fn execute(&self, params: Value) -> Result<ActionExecuteResult, ActionsError> {
let x = params
.get("x")
.and_then(Value::as_f64)
.ok_or_else(|| ActionsError::ExecutionError("missing or invalid 'x'".to_string()))?;
let y = params
.get("y")
.and_then(Value::as_f64)
.ok_or_else(|| ActionsError::ExecutionError("missing or invalid 'y'".to_string()))?;
Ok(ActionExecuteResult::done(&json!({ "sum": x + y })))
}
fn get_schema_json(
&self,
_current_values: Option<Value>,
) -> Result<(String, String, String), ActionsError> {
Ok(
ActionSchema::new("Add Numbers", "Add two numbers and return their sum")
.number("x", |f| f.title("X").required())
.number("y", |f| f.title("Y").required())
.to_schema_json(),
)
}
}
struct CheckThreshold;
#[async_trait]
impl Action for CheckThreshold {
async fn execute(&self, params: Value) -> Result<ActionExecuteResult, ActionsError> {
let value = params.get("value").and_then(Value::as_f64).ok_or_else(|| {
ActionsError::ExecutionError("missing or invalid 'value'".to_string())
})?;
let threshold = params
.get("threshold")
.and_then(Value::as_f64)
.ok_or_else(|| {
ActionsError::ExecutionError("missing or invalid 'threshold'".to_string())
})?;
let result = json!({ "value": value, "threshold": threshold });
if value <= threshold {
Ok(ActionExecuteResult::passed(&result))
} else {
Ok(ActionExecuteResult::failed(&result))
}
}
fn get_schema_json(
&self,
_current_values: Option<Value>,
) -> Result<(String, String, String), ActionsError> {
Ok(ActionSchema::new(
"Check Threshold",
"Check whether a value is within a threshold",
)
.number("value", |f| f.title("Value").required())
.number("threshold", |f| f.title("Threshold").required())
.to_schema_json())
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
tracing_subscriber::fmt::init();
let url = std::env::var("ZELOS_URL").unwrap_or_else(|_| "grpc://127.0.0.1:2300".to_string());
println!("Connecting to Zelos agent at: {}", url);
let registry = Arc::new(ActionsRegistry::new());
registry.register("add".to_string(), Arc::new(AddNumbers));
registry.register("check_threshold".to_string(), Arc::new(CheckThreshold));
let client = ActionsClient::new_with_url(url)?;
let cancellation_token = CancellationToken::new();
println!("Serving actions as 'rust-example/add' and 'rust-example/check_threshold'");
println!("Press Ctrl-C to stop.");
tokio::select! {
result = client.serve("rust-example".to_string(), registry, cancellation_token.clone()) => {
result?;
}
_ = tokio::signal::ctrl_c() => {
println!("Shutting down...");
cancellation_token.cancel();
}
}
Ok(())
}