agentguard_core/
action.rs1use serde::{Deserialize, Serialize};
4use std::fmt;
5
6#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
11#[non_exhaustive]
12pub struct AgentAction {
13 pub tool: String,
15 #[serde(default, skip_serializing_if = "Option::is_none")]
17 pub operation: Option<String>,
18}
19
20impl AgentAction {
21 pub fn tool(name: impl Into<String>) -> Self {
23 Self {
24 tool: name.into(),
25 operation: None,
26 }
27 }
28
29 pub fn tool_op(name: impl Into<String>, op: impl Into<String>) -> Self {
31 Self {
32 tool: name.into(),
33 operation: Some(op.into()),
34 }
35 }
36
37 #[deprecated(
39 since = "0.2.1",
40 note = "Use the Display impl to write into an existing buffer (no allocation)."
41 )]
42 pub fn action_uid(&self) -> String {
43 match &self.operation {
44 Some(op) => format!("Action::\"ToolCall::{}::{}\"", self.tool, op),
45 None => format!("Action::\"ToolCall::{}\"", self.tool),
46 }
47 }
48
49 pub fn action_id(&self) -> String {
51 match &self.operation {
52 Some(op) => format!("ToolCall::{}::{}", self.tool, op),
53 None => format!("ToolCall::{}", self.tool),
54 }
55 }
56}
57
58impl fmt::Display for AgentAction {
59 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
63 match &self.operation {
64 Some(op) => write!(f, "Action::\"ToolCall::{}::{}\"", self.tool, op),
65 None => write!(f, "Action::\"ToolCall::{}\"", self.tool),
66 }
67 }
68}
69
70#[cfg(test)]
71mod tests {
72 use super::*;
73
74 #[test]
75 fn tool_action_uid() {
76 let a = AgentAction::tool("send_email");
77 assert_eq!(format!("{}", a), "Action::\"ToolCall::send_email\"");
78 assert_eq!(a.action_id(), "ToolCall::send_email");
79 }
80
81 #[test]
82 fn tool_op_action_uid() {
83 let a = AgentAction::tool_op("s3", "PutObject");
84 assert_eq!(format!("{}", a), "Action::\"ToolCall::s3::PutObject\"");
85 assert_eq!(a.action_id(), "ToolCall::s3::PutObject");
86 }
87
88 #[test]
89 fn action_without_operation_omits_double_colon() {
90 let a = AgentAction::tool("search");
91 assert_eq!(a.action_id(), "ToolCall::search");
93 assert!(!a.action_id().contains("::ToolCall::"));
94 }
95
96 #[test]
97 fn action_serde_round_trip() {
98 let a = AgentAction::tool_op("email", "send");
99 let json = serde_json::to_string(&a).unwrap();
100 let parsed: AgentAction = serde_json::from_str(&json).unwrap();
101 assert_eq!(parsed, a);
102 }
103
104 #[test]
105 fn action_without_operation_serializes_no_op_field() {
106 let a = AgentAction::tool("solo");
107 let json = serde_json::to_value(&a).unwrap();
108 assert!(json.get("operation").is_none());
111 }
112}