use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use crate::{Result, Object, Trait};
pub type ActionId = uuid::Uuid;
#[derive(Debug, Clone)]
pub struct ActionContext {
pub objects: HashMap<String, Object>,
pub parameters: HashMap<String, serde_json::Value>,
pub metadata: HashMap<String, String>,
}
impl ActionContext {
#[inline]
pub fn new() -> Self {
Self {
objects: HashMap::new(),
parameters: HashMap::new(),
metadata: HashMap::new(),
}
}
#[inline]
pub fn with_capacity(expected_objects: usize, expected_parameters: usize) -> Self {
Self {
objects: HashMap::with_capacity(expected_objects),
parameters: HashMap::with_capacity(expected_parameters),
metadata: HashMap::new(),
}
}
#[inline]
pub fn add_object(&mut self, name: impl Into<String>, object: Object) {
self.objects.insert(name.into(), object);
}
#[inline]
pub fn get_object(&self, name: &str) -> Option<&Object> {
self.objects.get(name)
}
#[inline]
pub fn get_objects(&self, names: &[&str]) -> HashMap<String, &Object> {
names.iter()
.filter_map(|name| self.objects.get(*name).map(|obj| (name.to_string(), obj)))
.collect()
}
#[inline]
pub fn add_parameter(&mut self, name: impl Into<String>, value: serde_json::Value) {
self.parameters.insert(name.into(), value);
}
#[inline]
pub fn get_parameter(&self, name: &str) -> Option<&serde_json::Value> {
self.parameters.get(name)
}
#[inline]
pub fn add_metadata(&mut self, key: impl Into<String>, value: impl Into<String>) {
self.metadata.insert(key.into(), value.into());
}
#[inline]
pub fn get_metadata(&self, key: &str) -> Option<&String> {
self.metadata.get(key)
}
#[inline]
pub fn object_count(&self) -> usize {
self.objects.len()
}
#[inline]
pub fn parameter_count(&self) -> usize {
self.parameters.len()
}
#[inline]
pub fn metadata_count(&self) -> usize {
self.metadata.len()
}
#[inline]
pub fn reserve_objects(&mut self, additional: usize) {
self.objects.reserve(additional);
}
#[inline]
pub fn reserve_parameters(&mut self, additional: usize) {
self.parameters.reserve(additional);
}
#[inline]
pub fn clear_objects(&mut self) {
self.objects.clear();
}
#[inline]
pub fn clear_parameters(&mut self) {
self.parameters.clear();
}
#[inline]
pub fn clear_metadata(&mut self) {
self.metadata.clear();
}
}
impl Default for ActionContext {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ActionResult {
pub success: bool,
pub trait_updates: Vec<Trait>,
pub messages: Vec<String>,
pub data: HashMap<String, serde_json::Value>,
}
impl ActionResult {
#[inline]
pub fn success() -> Self {
Self {
success: true,
trait_updates: Vec::new(),
messages: Vec::new(),
data: HashMap::new(),
}
}
#[inline]
pub fn failure(message: impl Into<String>) -> Self {
Self {
success: false,
trait_updates: Vec::new(),
messages: vec![message.into()],
data: HashMap::new(),
}
}
pub fn success_with_capacity(trait_capacity: usize, message_capacity: usize, data_capacity: usize) -> Self {
Self {
success: true,
trait_updates: Vec::with_capacity(trait_capacity),
messages: Vec::with_capacity(message_capacity),
data: HashMap::with_capacity(data_capacity),
}
}
#[inline]
pub fn add_trait_update(&mut self, trait_obj: Trait) {
self.trait_updates.push(trait_obj);
}
#[inline]
pub fn add_trait_updates(&mut self, trait_updates: impl IntoIterator<Item = Trait>) {
self.trait_updates.extend(trait_updates);
}
#[inline]
pub fn add_message(&mut self, message: impl Into<String>) {
self.messages.push(message.into());
}
#[inline]
pub fn add_messages(&mut self, messages: impl IntoIterator<Item = String>) {
self.messages.extend(messages);
}
#[inline]
pub fn add_data(&mut self, key: impl Into<String>, value: serde_json::Value) {
self.data.insert(key.into(), value);
}
#[inline]
pub fn reserve_capacity(&mut self, trait_updates: usize, messages: usize) {
self.trait_updates.reserve(trait_updates);
self.messages.reserve(messages);
}
#[inline]
pub fn is_success(&self) -> bool {
self.success
}
#[inline]
pub fn is_failure(&self) -> bool {
!self.success
}
#[inline]
pub fn trait_update_count(&self) -> usize {
self.trait_updates.len()
}
#[inline]
pub fn message_count(&self) -> usize {
self.messages.len()
}
#[inline]
pub fn data_count(&self) -> usize {
self.data.len()
}
#[inline]
pub fn clear_trait_updates(&mut self) {
self.trait_updates.clear();
}
#[inline]
pub fn clear_messages(&mut self) {
self.messages.clear();
}
#[inline]
pub fn clear_data(&mut self) {
self.data.clear();
}
}
#[async_trait]
pub trait Action: Send + Sync {
fn name(&self) -> &str;
fn description(&self) -> &str;
async fn execute(&self, context: ActionContext) -> Result<ActionResult>;
fn required_traits(&self) -> Vec<String> {
Vec::new()
}
fn optional_traits(&self) -> Vec<String> {
Vec::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_action_context() {
let mut context = ActionContext::new();
let test_object = Object::new("test", "type");
context.add_object("test_obj", test_object);
context.add_parameter("param", serde_json::json!("value"));
context.add_metadata("key", "value");
assert!(context.get_object("test_obj").is_some());
assert!(context.get_parameter("param").is_some());
assert_eq!(context.get_metadata("key"), Some(&"value".to_string()));
}
#[test]
fn test_action_result() {
let mut result = ActionResult::success();
result.add_message("Test message");
result.add_data("key", serde_json::json!("value"));
assert!(result.is_success());
assert_eq!(result.messages.len(), 1);
assert_eq!(result.data.len(), 1);
}
}