workflow_terminal/
cli.rs

1//!
2//! Cli trait for implementing a user-side command-line processor.
3//!
4
5use crate::error::Error;
6use crate::parse;
7pub use crate::result::Result;
8use crate::terminal::Terminal;
9use async_trait::async_trait;
10use downcast::{downcast_sync, AnySync};
11use std::{
12    collections::HashMap,
13    sync::{Arc, Mutex, MutexGuard},
14};
15pub use workflow_terminal_macros::{declare_handler, register_handlers, Handler};
16
17#[async_trait]
18pub trait Cli: Sync + Send {
19    fn init(self: Arc<Self>, _term: &Arc<Terminal>) -> Result<()> {
20        Ok(())
21    }
22    async fn digest(self: Arc<Self>, term: Arc<Terminal>, cmd: String) -> Result<()>;
23    async fn complete(
24        self: Arc<Self>,
25        term: Arc<Terminal>,
26        cmd: String,
27    ) -> Result<Option<Vec<String>>>;
28    fn prompt(&self) -> Option<String>;
29}
30
31pub trait Context: Sync + Send + AnySync {
32    fn term(&self) -> Arc<Terminal>;
33}
34downcast_sync!(dyn Context);
35downcast_sync!(dyn Context + Sync + Send);
36
37impl From<&dyn Context> for Arc<Terminal> {
38    fn from(ctx: &dyn Context) -> Arc<Terminal> {
39        ctx.term()
40    }
41}
42
43#[async_trait]
44pub trait Handler: Sync + Send + AnySync {
45    fn verb(&self, _ctx: &Arc<dyn Context>) -> Option<&'static str> {
46        None
47    }
48    fn condition(&self, ctx: &Arc<dyn Context>) -> bool {
49        self.verb(ctx).is_some()
50    }
51    fn help(&self, _ctx: &Arc<dyn Context>) -> &'static str {
52        ""
53    }
54    fn dyn_help(&self, _ctx: &Arc<dyn Context>) -> String {
55        "".to_owned()
56    }
57    async fn complete(&self, _ctx: &Arc<dyn Context>, _cmd: &str) -> Result<Option<Vec<String>>> {
58        Ok(None)
59    }
60    async fn start(self: Arc<Self>, _ctx: &Arc<dyn Context>) -> Result<()> {
61        Ok(())
62    }
63    async fn stop(self: Arc<Self>, _ctx: &Arc<dyn Context>) -> Result<()> {
64        Ok(())
65    }
66    async fn handle(
67        self: Arc<Self>,
68        ctx: &Arc<dyn Context>,
69        argv: Vec<String>,
70        cmd: &str,
71    ) -> Result<()>;
72}
73
74downcast_sync!(dyn Handler);
75
76pub fn get_handler_help(handler: Arc<dyn Handler>, ctx: &Arc<dyn Context>) -> String {
77    let s = handler.help(ctx);
78    if s.is_empty() {
79        handler.dyn_help(ctx)
80    } else {
81        s.to_string()
82    }
83}
84
85#[derive(Default)]
86struct Inner {
87    handlers: HashMap<String, Arc<dyn Handler>>,
88}
89
90#[derive(Default)]
91pub struct HandlerCli {
92    inner: Arc<Mutex<Inner>>,
93}
94
95impl HandlerCli {
96    pub fn new() -> Self {
97        Self {
98            inner: Arc::new(Mutex::new(Inner::default())),
99        }
100    }
101
102    fn inner(&self) -> MutexGuard<Inner> {
103        self.inner.lock().unwrap()
104    }
105
106    pub fn collect(&self) -> Vec<Arc<dyn Handler>> {
107        self.inner().handlers.values().cloned().collect::<Vec<_>>()
108    }
109
110    pub fn get(&self, name: &str) -> Option<Arc<dyn Handler>> {
111        self.inner().handlers.get(name).cloned()
112    }
113
114    pub fn register<T, H>(&self, ctx: &Arc<T>, handler: H)
115    where
116        T: Context + Sized,
117        H: Handler + Send + Sync + 'static,
118    {
119        let ctx: Arc<dyn Context> = ctx.clone();
120        match handler.verb(&ctx) {
121            Some(name) if handler.condition(&ctx) => {
122                self.inner()
123                    .handlers
124                    .insert(name.to_lowercase(), Arc::new(handler));
125            }
126            _ => {}
127        }
128    }
129
130    pub fn register_arc<T, H>(&self, ctx: &Arc<T>, handler: &Arc<H>)
131    where
132        T: Context + Sized,
133        H: Handler + Send + Sync + 'static,
134    {
135        let ctx: Arc<dyn Context> = ctx.clone();
136        match handler.verb(&ctx) {
137            Some(name) if handler.condition(&ctx) => {
138                self.inner()
139                    .handlers
140                    .insert(name.to_lowercase(), handler.clone());
141            }
142            _ => {}
143        }
144    }
145
146    pub fn unregister(&self, name: &str) -> Option<Arc<dyn Handler>> {
147        self.inner().handlers.remove(name)
148    }
149
150    pub fn clear(&self) -> Result<()> {
151        self.inner().handlers.clear();
152        Ok(())
153    }
154
155    pub async fn start<T>(&self, ctx: &Arc<T>) -> Result<()>
156    where
157        T: Context + Sized,
158    {
159        let ctx: Arc<dyn Context> = ctx.clone();
160        let handlers = self.collect();
161        for handler in handlers.iter() {
162            handler.clone().start(&ctx).await?;
163        }
164        Ok(())
165    }
166
167    pub async fn stop<T>(&self, ctx: &Arc<T>) -> Result<()>
168    where
169        T: Context + Sized,
170    {
171        let handlers = self.collect();
172        let ctx: Arc<dyn Context> = ctx.clone();
173        for handler in handlers.into_iter() {
174            handler.clone().start(&ctx).await?;
175        }
176        Ok(())
177    }
178
179    pub async fn execute<T>(&self, ctx: &Arc<T>, cmd: &str) -> Result<()>
180    where
181        T: Context + Sized,
182    {
183        let ctx: Arc<dyn Context> = ctx.clone();
184
185        let argv = parse(cmd);
186        let action = argv[0].to_lowercase();
187
188        let handler = self.get(action.as_str());
189        if let Some(handler) = handler {
190            handler
191                .clone()
192                .handle(&ctx, argv[1..].to_vec(), cmd)
193                .await?;
194            Ok(())
195        } else {
196            Err(Error::CommandNotFound(action))
197        }
198    }
199
200    pub async fn complete<T>(&self, ctx: &Arc<T>, cmd: &str) -> Result<Option<Vec<String>>>
201    where
202        T: Context + Sized,
203    {
204        let ctx: Arc<dyn Context> = ctx.clone();
205
206        let argv = parse(cmd);
207        let action = argv[0].to_lowercase();
208
209        let handler = self.get(action.as_str());
210        if let Some(handler) = handler {
211            Ok(handler.clone().complete(&ctx, cmd).await?)
212        } else {
213            Err(Error::CommandNotFound(action))
214        }
215    }
216}