Skip to main content

synapto_interface/
command.rs

1#![doc = include_str!("command.md")]
2
3use crate::llm::LLMSafe;
4
5#[async_trait::async_trait]
6pub trait Command: Send + Sync + 'static {
7    type Arguments: schemars::JsonSchema
8        + serde::de::DeserializeOwned
9        + LLMSafe
10        + Send
11        + Sync
12        + 'static;
13    const NAME: &'static str;
14    async fn execute(&self, args: Self::Arguments) -> Result<(), String>;
15}
16
17#[async_trait::async_trait]
18pub trait ErasedCommand: Send + Sync + 'static {
19    fn name(&self) -> &'static str;
20    fn schema(&self) -> schemars::Schema;
21    async fn erased_execute(&self, args: serde_json::Value) -> Result<(), String>;
22}
23
24#[async_trait::async_trait]
25impl<T> ErasedCommand for T
26where
27    T: Command,
28{
29    fn name(&self) -> &'static str {
30        <T as Command>::NAME
31    }
32    fn schema(&self) -> schemars::Schema {
33        schemars::schema_for!(<T as Command>::Arguments)
34    }
35    async fn erased_execute(&self, args: serde_json::Value) -> Result<(), String> {
36        let parsed_args = serde_json::from_value(args).map_err(|e| e.to_string())?;
37        <T as Command>::execute(self, parsed_args).await
38    }
39}
40
41#[derive(Default)]
42pub struct CommandRegistryBuilder {
43    pub commands:
44        std::sync::RwLock<std::collections::HashMap<String, std::sync::Arc<dyn ErasedCommand>>>,
45}
46
47impl CommandRegistryBuilder {
48    pub fn register<T>(&self, command: T)
49    where
50        T: ErasedCommand + 'static,
51    {
52        let command_arc: std::sync::Arc<dyn ErasedCommand> = std::sync::Arc::new(command);
53        self.register_erased(command_arc);
54    }
55    pub fn register_erased(&self, command: std::sync::Arc<dyn ErasedCommand>) {
56        self.commands
57            .write()
58            .unwrap_or_else(|e| panic!("Failed to acquire write lock on commands: {:?}", e))
59            .insert(command.name().to_string(), command);
60    }
61}