Skip to main content

everruns_core/capabilities/
current_time.rs

1//! CurrentTime Capability - provides tools to get current date and time
2
3use super::{Capability, CapabilityStatus};
4use crate::tool_types::ToolHints;
5use crate::tools::{Tool, ToolExecutionResult};
6use async_trait::async_trait;
7use serde_json::Value;
8
9/// CurrentTime capability - provides tools to get current date and time
10pub struct CurrentTimeCapability;
11
12impl Capability for CurrentTimeCapability {
13    fn id(&self) -> &str {
14        "current_time"
15    }
16
17    fn name(&self) -> &str {
18        "Current Time"
19    }
20
21    fn description(&self) -> &str {
22        "Adds a tool to get the current date and time in various formats and timezones."
23    }
24
25    fn status(&self) -> CapabilityStatus {
26        CapabilityStatus::Available
27    }
28
29    fn icon(&self) -> Option<&str> {
30        Some("clock")
31    }
32
33    fn category(&self) -> Option<&str> {
34        Some("Utilities")
35    }
36
37    fn tools(&self) -> Vec<Box<dyn Tool>> {
38        vec![Box::new(GetCurrentTimeTool)]
39    }
40}
41
42// ============================================================================
43// Tool: get_current_time
44// ============================================================================
45
46/// Tool that returns the current date and time
47pub struct GetCurrentTimeTool;
48
49#[async_trait]
50impl Tool for GetCurrentTimeTool {
51    fn name(&self) -> &str {
52        "get_current_time"
53    }
54
55    fn display_name(&self) -> Option<&str> {
56        Some("Get Current Time")
57    }
58
59    fn description(&self) -> &str {
60        "Get the current date and time. Can return time in different formats and timezones."
61    }
62
63    fn parameters_schema(&self) -> Value {
64        serde_json::json!({
65            "type": "object",
66            "properties": {
67                "timezone": {
68                    "type": "string",
69                    "description": "Timezone to return the time in (e.g., 'UTC', 'America/New_York', 'Europe/London'). Defaults to UTC."
70                },
71                "format": {
72                    "type": "string",
73                    "enum": ["iso8601", "unix", "human"],
74                    "description": "Output format: 'iso8601' for ISO 8601 format, 'unix' for Unix timestamp, 'human' for human-readable format. Defaults to 'iso8601'."
75                }
76            },
77            "additionalProperties": false
78        })
79    }
80
81    fn hints(&self) -> ToolHints {
82        ToolHints::default()
83            .with_readonly(true)
84            .with_idempotent(true)
85    }
86
87    async fn execute(&self, arguments: Value) -> ToolExecutionResult {
88        let format = arguments
89            .get("format")
90            .and_then(|v| v.as_str())
91            .unwrap_or("iso8601");
92
93        let _timezone = arguments
94            .get("timezone")
95            .and_then(|v| v.as_str())
96            .unwrap_or("UTC");
97
98        // Note: For simplicity, we're using UTC. Full timezone support would require
99        // the chrono-tz crate which adds significant dependencies.
100        let now = chrono::Utc::now();
101
102        let result = match format {
103            "unix" => serde_json::json!({
104                "timestamp": now.timestamp(),
105                "format": "unix",
106                "timezone": "UTC"
107            }),
108            "human" => serde_json::json!({
109                "datetime": now.format("%A, %B %d, %Y at %H:%M:%S UTC").to_string(),
110                "format": "human",
111                "timezone": "UTC"
112            }),
113            _ => serde_json::json!({
114                "datetime": now.to_rfc3339(),
115                "format": "iso8601",
116                "timezone": "UTC"
117            }),
118        };
119
120        ToolExecutionResult::success(result)
121    }
122}
123
124#[cfg(test)]
125mod tests {
126    use super::*;
127    use crate::capabilities::CapabilityRegistry;
128
129    #[test]
130    fn test_capability_metadata() {
131        let cap = CurrentTimeCapability;
132
133        assert_eq!(cap.id(), "current_time");
134        assert_eq!(cap.name(), "Current Time");
135        assert_eq!(cap.icon(), Some("clock"));
136        assert_eq!(cap.category(), Some("Utilities"));
137        assert_eq!(cap.status(), CapabilityStatus::Available);
138    }
139
140    #[test]
141    fn test_capability_has_tools() {
142        let cap = CurrentTimeCapability;
143        let tools = cap.tools();
144
145        assert_eq!(tools.len(), 1);
146        assert_eq!(tools[0].name(), "get_current_time");
147    }
148
149    #[test]
150    fn test_capability_no_system_prompt() {
151        let cap = CurrentTimeCapability;
152        assert!(cap.system_prompt_addition().is_none());
153    }
154
155    #[test]
156    fn test_capability_in_registry() {
157        let registry = CapabilityRegistry::with_builtins();
158        let cap = registry.get("current_time").unwrap();
159
160        assert_eq!(cap.id(), "current_time");
161        assert_eq!(cap.tools().len(), 1);
162    }
163
164    #[tokio::test]
165    async fn test_get_current_time_iso8601() {
166        let tool = GetCurrentTimeTool;
167        let result = tool.execute(serde_json::json!({})).await;
168
169        if let ToolExecutionResult::Success(value) = result {
170            assert!(value.get("datetime").is_some());
171            assert_eq!(value.get("format").unwrap().as_str().unwrap(), "iso8601");
172            assert_eq!(value.get("timezone").unwrap().as_str().unwrap(), "UTC");
173        } else {
174            panic!("Expected success");
175        }
176    }
177
178    #[tokio::test]
179    async fn test_get_current_time_unix() {
180        let tool = GetCurrentTimeTool;
181        let result = tool.execute(serde_json::json!({"format": "unix"})).await;
182
183        if let ToolExecutionResult::Success(value) = result {
184            assert!(value.get("timestamp").is_some());
185            assert_eq!(value.get("format").unwrap().as_str().unwrap(), "unix");
186        } else {
187            panic!("Expected success");
188        }
189    }
190
191    #[tokio::test]
192    async fn test_get_current_time_human() {
193        let tool = GetCurrentTimeTool;
194        let result = tool.execute(serde_json::json!({"format": "human"})).await;
195
196        if let ToolExecutionResult::Success(value) = result {
197            assert!(value.get("datetime").is_some());
198            assert_eq!(value.get("format").unwrap().as_str().unwrap(), "human");
199            let datetime = value.get("datetime").unwrap().as_str().unwrap();
200            assert!(datetime.contains("at"));
201        } else {
202            panic!("Expected success");
203        }
204    }
205}