use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ActionParamDef {
pub name: String,
pub description: String,
#[serde(default)]
pub required: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ScenarioActionCategory {
#[default]
NodeExpand,
NodeStateChange,
}
impl ScenarioActionCategory {
pub fn to_core(&self) -> swarm_engine_core::actions::ActionCategory {
match self {
Self::NodeExpand => swarm_engine_core::actions::ActionCategory::NodeExpand,
Self::NodeStateChange => swarm_engine_core::actions::ActionCategory::NodeStateChange,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ScenarioActionDef {
pub name: String,
pub description: String,
#[serde(default)]
pub category: ScenarioActionCategory,
#[serde(default)]
pub params: Vec<ActionParamDef>,
#[serde(default)]
pub example: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct ScenarioActions {
#[serde(default)]
pub actions: Vec<ScenarioActionDef>,
}
impl ScenarioActions {
pub fn to_core_config(&self) -> swarm_engine_core::actions::ActionsConfig {
use swarm_engine_core::actions::{ActionDef, ActionsConfig, ParamDef};
let mut config = ActionsConfig::new();
for action_def in &self.actions {
let mut def = ActionDef::new(&action_def.name, &action_def.description)
.category(action_def.category.to_core())
.group("default");
for param in &action_def.params {
let param_def = if param.required {
ParamDef::required(¶m.name, ¶m.description)
} else {
ParamDef::optional(¶m.name, ¶m.description)
};
def = def.param(param_def);
}
if let Some(ref ex) = action_def.example {
def = def.example(ex);
}
config.add_action(&action_def.name, def);
}
config
}
pub fn action_names(&self) -> Vec<String> {
self.actions.iter().map(|a| a.name.clone()).collect()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_scenario_action_category_default() {
let category = ScenarioActionCategory::default();
assert_eq!(category, ScenarioActionCategory::NodeExpand);
}
#[test]
fn test_scenario_actions_default() {
let actions = ScenarioActions::default();
assert!(actions.actions.is_empty());
assert!(actions.action_names().is_empty());
}
#[test]
fn test_scenario_actions_deserialize_toml() {
let toml_str = r#"
[[actions]]
name = "grep"
description = "Search for patterns in files"
category = "node_expand"
[[actions.params]]
name = "pattern"
description = "Search pattern"
required = true
[[actions]]
name = "read"
description = "Read file contents"
category = "node_state_change"
"#;
let actions: ScenarioActions = toml::from_str(toml_str).unwrap();
assert_eq!(actions.actions.len(), 2);
assert_eq!(actions.action_names(), vec!["grep", "read"]);
let grep = &actions.actions[0];
assert_eq!(grep.name, "grep");
assert_eq!(grep.category, ScenarioActionCategory::NodeExpand);
assert_eq!(grep.params.len(), 1);
assert!(grep.params[0].required);
}
#[test]
fn test_scenario_actions_to_core_config() {
let actions = ScenarioActions {
actions: vec![ScenarioActionDef {
name: "test_action".to_string(),
description: "A test action".to_string(),
category: ScenarioActionCategory::NodeExpand,
params: vec![ActionParamDef {
name: "param1".to_string(),
description: "A parameter".to_string(),
required: true,
}],
example: Some(r#"{"action": "test_action"}"#.to_string()),
}],
};
let core_config = actions.to_core_config();
let action_def = core_config.get("test_action");
assert!(action_def.is_some());
}
}