use async_trait::async_trait;
use serde::{Deserialize, Serialize};
#[async_trait]
pub trait UserInterface: Send + Sync {
fn has_ui(&self) -> bool;
async fn notify(&self, message: &str, level: NotifyLevel);
async fn confirm(&self, title: &str, message: &str) -> Option<bool>;
async fn select(&self, title: &str, options: &[SelectOption]) -> Option<usize> {
self.select_with_context(title, "", options).await
}
async fn select_with_context(
&self,
title: &str,
context: &str,
options: &[SelectOption],
) -> Option<usize>;
async fn multi_select_with_context(
&self,
title: &str,
context: &str,
options: &[SelectOption],
) -> Option<Vec<usize>> {
self.select_with_context(title, context, options)
.await
.map(|index| vec![index])
}
async fn input(&self, title: &str, placeholder: &str) -> Option<String> {
self.input_with_context(title, "", placeholder).await
}
async fn input_with_context(
&self,
title: &str,
context: &str,
placeholder: &str,
) -> Option<String>;
async fn set_status(&self, key: &str, text: Option<&str>);
async fn set_widget(&self, key: &str, content: Option<WidgetContent>);
async fn custom(&self, component: ComponentSpec) -> Option<serde_json::Value>;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum NotifyLevel {
Info,
Warning,
Error,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SelectOption {
pub label: String,
pub description: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum WidgetContent {
Lines(Vec<String>),
Component(ComponentSpec),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ComponentSpec {
pub component_type: String,
pub props: serde_json::Value,
pub children: Vec<ComponentSpec>,
}
pub struct NullInterface;
#[async_trait]
impl UserInterface for NullInterface {
fn has_ui(&self) -> bool {
false
}
async fn notify(&self, _message: &str, _level: NotifyLevel) {}
async fn confirm(&self, _title: &str, _message: &str) -> Option<bool> {
None
}
async fn select_with_context(
&self,
_title: &str,
_context: &str,
_options: &[SelectOption],
) -> Option<usize> {
None
}
async fn multi_select_with_context(
&self,
_title: &str,
_context: &str,
_options: &[SelectOption],
) -> Option<Vec<usize>> {
None
}
async fn input_with_context(
&self,
_title: &str,
_context: &str,
_placeholder: &str,
) -> Option<String> {
None
}
async fn set_status(&self, _key: &str, _text: Option<&str>) {}
async fn set_widget(&self, _key: &str, _content: Option<WidgetContent>) {}
async fn custom(&self, _component: ComponentSpec) -> Option<serde_json::Value> {
None
}
}