use std::{collections::HashMap, future::Future, pin::Pin, sync::Arc};
use crate::{Menu, proto};
pub struct List {
pub items: Vec<ListItem>,
pub style: Option<ListStyle>,
_priv: (),
}
impl List {
pub fn new(items: Vec<ListItem>) -> Self {
Self {
items,
style: None,
_priv: (),
}
}
#[must_use = "builder method consumes self"]
pub fn as_grid_with_columns(mut self, columns: u32) -> Self {
self.style = Some(ListStyle::GridWithColumns(columns));
self
}
#[must_use = "builder method consumes self"]
pub fn as_grid(mut self) -> Self {
self.style = Some(ListStyle::Grid);
self
}
#[must_use = "builder method consumes self"]
pub fn as_rows(mut self) -> Self {
self.style = Some(ListStyle::Rows);
self
}
}
#[non_exhaustive]
pub enum ListStyle {
Rows,
Grid,
GridWithColumns(u32),
}
impl ListStyle {
pub(crate) fn into_proto(self) -> proto::query_response::ListStyle {
use proto::query_response::ListStyle as Proto;
match self {
ListStyle::Rows => Proto::Rows(()),
ListStyle::Grid => Proto::Grid(()),
ListStyle::GridWithColumns(columns) => Proto::GridWithColumns(columns),
}
}
}
#[derive(Clone)]
pub struct ListItem {
pub title: String,
pub description: String,
pub icon: Option<Icon>,
pub(crate) commands: ListItemCallbacks,
}
impl ListItem {
pub fn new(title: impl Into<String>) -> Self {
let title = title.into();
Self {
title: title.clone(),
icon: None,
description: String::new(),
commands: ListItemCallbacks::new(title),
}
}
#[must_use = "builder method consumes self"]
pub fn with_description(mut self, desc: impl Into<String>) -> Self {
self.description = desc.into();
self
}
#[must_use = "builder method consumes self"]
pub fn with_icon(mut self, icon: Option<Icon>) -> Self {
self.icon = icon;
self
}
#[must_use = "builder method consumes self"]
pub fn with_icon_name(mut self, name: impl Into<String>) -> Self {
self.icon = Some(Icon::Name(name.into()));
self
}
#[must_use = "builder method consumes self"]
pub fn with_icon_text(mut self, text: impl Into<String>) -> Self {
self.icon = Some(Icon::Text(text.into()));
self
}
#[doc(hidden)]
#[must_use]
pub fn add_command(mut self, name: &'static str, callback: ActivationFunction) -> Self {
self.commands.add_command(name, callback);
self
}
}
#[derive(Debug, Clone)]
pub enum Icon {
Name(String),
Text(String),
}
impl Icon {
pub(crate) fn into_proto(self) -> proto::list_item::Icon {
use proto::list_item::Icon as Proto;
match self {
Self::Name(name) => Proto::Name(name),
Self::Text(text) => Proto::Text(text),
}
}
}
type DynFuture<T> = Pin<Box<dyn Future<Output = T>>>;
type ActivationFunction = Arc<dyn Fn(Menu) -> DynFuture<()> + Send + Sync>;
#[derive(Clone)]
pub(crate) struct ListItemCallbacks {
commands: HashMap<&'static str, ActivationFunction>,
item_title: String,
}
impl ListItemCallbacks {
pub(crate) fn new(title: String) -> Self {
Self {
commands: HashMap::default(),
item_title: title,
}
}
pub(crate) fn add_command(&mut self, name: &'static str, callback: ActivationFunction) {
self.commands.insert(name, callback);
}
pub(crate) async fn call_command(&self, name: &str, menu: Menu) {
if let Some(cmd) = self.commands.get(name) {
crate::rank::register_usage(&self.item_title);
cmd(menu).await;
}
}
pub(crate) fn ids(&self) -> impl Iterator<Item = &'static str> + use<'_> {
self.commands.keys().copied()
}
}