use std::sync::Arc;
use serde::Serialize;
use crate::runtime::handlers::{MultiEntryContext, SingleEntryContext};
use crate::runtime::state::ContextMenuHandler;
use crate::runtime::handle::ExtensionHandle;
use crate::error::Error;
#[derive(Serialize)]
#[serde(bound(serialize = ""))]
#[serde(rename_all = "camelCase")]
pub struct ContextMenuItem<C> {
id: String,
label: String,
#[serde(skip)]
handler: ContextMenuHandler<C>,
}
impl<C> ContextMenuItem<C> {
pub fn new<F, Fut>(id: impl Into<String>, label: impl Into<String>, handler: F) -> Self
where
F: Fn(C, ExtensionHandle) -> Fut + Send + Sync + 'static,
Fut: Future<Output = Result<(), Error>> + Send + 'static,
{
Self {
id: id.into(),
label: label.into(),
handler: Arc::new(move |ctx, handle| {
let fut = handler(ctx, handle);
Box::pin(async move { fut.await.map_err(Error::into_jrpc) })
}),
}
}
}
#[derive(Serialize)]
#[serde(bound(serialize = ""))]
#[serde(rename_all = "camelCase")]
pub struct ContextMenuSubmenu<C> {
label: String,
children: Vec<ContextMenuNode<C>>,
}
impl<C> ContextMenuSubmenu<C> {
pub fn new(label: impl Into<String>) -> Self {
Self {
label: label.into(),
children: Vec::new()
}
}
pub fn with_children(mut self, children: &mut Vec<ContextMenuNode<C>>) -> Self {
self.children.append(children);
self
}
pub fn with_child(mut self, child: ContextMenuNode<C>) -> Self {
self.children.push(child);
self
}
}
#[derive(Serialize)]
#[serde(bound(serialize = ""))]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum ContextMenuNode<C> {
Submenu(ContextMenuSubmenu<C>),
Item(ContextMenuItem<C>),
}
impl<C> ContextMenuNode<C> {
pub(crate) fn extract_handlers(&self) -> Vec<(String, ContextMenuHandler<C>)> {
match self {
Self::Item(item) => vec![(item.id.clone(), Arc::clone(&item.handler))],
Self::Submenu(sub) => sub
.children
.iter()
.flat_map(|child| child.extract_handlers())
.collect(),
}
}
}
impl<C> From<ContextMenuItem<C>> for ContextMenuNode<C> {
fn from(item: ContextMenuItem<C>) -> Self {
ContextMenuNode::Item(item)
}
}
impl<C> From<ContextMenuSubmenu<C>> for ContextMenuNode<C> {
fn from(submenu: ContextMenuSubmenu<C>) -> Self {
ContextMenuNode::Submenu(submenu)
}
}
pub type ContextMenuItemSingle = ContextMenuItem<SingleEntryContext>;
pub type ContextMenuSubmenuSingle = ContextMenuSubmenu<SingleEntryContext>;
pub type ContextMenuNodeSingle = ContextMenuNode<SingleEntryContext>;
pub type ContextMenuItemMulti = ContextMenuItem<MultiEntryContext>;
pub type ContextMenuSubmenuMulti = ContextMenuSubmenu<MultiEntryContext>;
pub type ContextMenuNodeMulti = ContextMenuNode<MultiEntryContext>;