zeph_commands/handlers/
scheduler.rs1use std::future::Future;
7use std::pin::Pin;
8
9use crate::context::CommandContext;
10use crate::{CommandError, CommandHandler, CommandOutput, SlashCategory};
11
12pub struct SchedulerCommand;
16
17impl CommandHandler<CommandContext<'_>> for SchedulerCommand {
18 fn name(&self) -> &'static str {
19 "/scheduler"
20 }
21
22 fn description(&self) -> &'static str {
23 "List scheduled tasks"
24 }
25
26 fn args_hint(&self) -> &'static str {
27 "[list]"
28 }
29
30 fn category(&self) -> SlashCategory {
31 SlashCategory::Advanced
32 }
33
34 fn feature_gate(&self) -> Option<&'static str> {
35 Some("scheduler")
36 }
37
38 fn requires_auth(&self) -> bool {
39 true
40 }
41
42 fn handle<'a>(
43 &'a self,
44 ctx: &'a mut CommandContext<'_>,
45 args: &'a str,
46 ) -> Pin<Box<dyn Future<Output = Result<CommandOutput, CommandError>> + Send + 'a>> {
47 use tracing::Instrument as _;
48 let span = tracing::info_span!("commands.scheduler.handle");
49 Box::pin(
50 async move {
51 if !args.is_empty() && args != "list" {
52 return Err(CommandError::new(
53 "Unknown /scheduler subcommand. Available: /scheduler list",
54 ));
55 }
56 match ctx.agent.list_scheduled_tasks().await? {
57 Some(msg) if msg.is_empty() => Ok(CommandOutput::Silent),
58 Some(msg) => Ok(CommandOutput::Message(msg)),
59 None => Ok(CommandOutput::Message(
60 "Scheduler is not enabled or list_tasks tool is unavailable.".to_owned(),
61 )),
62 }
63 }
64 .instrument(span),
65 )
66 }
67}
68
69#[cfg(test)]
70mod tests {
71 use super::*;
72 use crate::handlers::test_helpers::{MockDebug, MockMessages, MockSession, make_ctx};
73 use crate::sink::NullSink;
74
75 #[test]
76 fn scheduler_name_and_description() {
77 assert_eq!(SchedulerCommand.name(), "/scheduler");
78 assert!(!SchedulerCommand.description().is_empty());
79 }
80
81 #[tokio::test]
82 async fn scheduler_none_returns_not_enabled_message() {
83 let mut sink = NullSink;
85 let mut debug = MockDebug;
86 let mut messages = MockMessages;
87 let session = MockSession;
88 let mut agent = crate::NullAgent;
89 let mut ctx = make_ctx(&mut sink, &mut debug, &mut messages, &session, &mut agent);
90 let out = SchedulerCommand.handle(&mut ctx, "").await.unwrap();
91 let CommandOutput::Message(msg) = out else {
92 panic!("expected Message")
93 };
94 assert!(msg.contains("not enabled") || msg.contains("unavailable"));
95 }
96
97 #[tokio::test]
98 async fn scheduler_unknown_subcommand_returns_error() {
99 let mut sink = NullSink;
100 let mut debug = MockDebug;
101 let mut messages = MockMessages;
102 let session = MockSession;
103 let mut agent = crate::NullAgent;
104 let mut ctx = make_ctx(&mut sink, &mut debug, &mut messages, &session, &mut agent);
105 let err = SchedulerCommand
106 .handle(&mut ctx, "start")
107 .await
108 .unwrap_err();
109 assert!(err.to_string().contains("Unknown") || err.to_string().contains("Available"));
110 }
111}