macro_rules! topics {
(
$(use HelpTopic $topic:ident for $desc:literal;)+
) => {
pub mod topics {
$(
#[doc = concat!(
"# ",
$desc,
"\n\nTo access this help from Lua, call `help '",
stringify!($topic),
"'`.\n\n```txt\n",
include_str!(
concat!("help_system/topics/", stringify!($topic), ".txt")
))]
pub mod $topic {
use crate::userscript_api::help_system::HelpTopic;
#[doc = "Userscript API help topic definition."]
pub struct Topic;
impl HelpTopic for Topic {
fn name(&self) -> &'static str {
stringify!($topic)
}
fn short_description(&self) -> &'static str {
$desc
}
fn content(&self) -> &'static str {
include_str!(
concat!("help_system/topics/", stringify!($topic), ".txt")
)
}
}
}
)+
}
use crate::userscript_api::help_system::topics::{
$($topic),+
};
pub struct HelpSystem {
topics: HashMap<String, Box<dyn HelpTopic>>,
}
impl HelpSystem {
#[must_use]
pub fn new() -> Self {
Self {
topics: HashMap::with_capacity(50),
}
}
pub fn topic(&mut self, topic: Box<dyn HelpTopic>) -> &mut Self {
self.topics.insert(topic.name().to_owned(), topic);
self
}
}
impl UserData for HelpSystem {
fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
methods.add_meta_method("__call", |_, this: &HelpSystem, topic: Option<String>| {
if let Some(topic) = topic {
if let Some(topic) = this.topics.get(topic.trim()) {
let content: &str = topic.content();
println!("{content}");
if !content.ends_with('\n') {
println!();
}
Ok(())
} else {
Err(Error::topic_not_found(&topic).into_lua_err())
}
} else {
println!(include_str!("help_system/topics/__generic.txt"));
Ok(())
}
});
methods.add_method("topics", |_, this: &HelpSystem, ()| {
println!("\nThe following help topics are available:\n");
for (name, topic) in &this.topics {
let name: &str = name.trim();
let description: &str = topic.short_description().trim();
println!("{name:<16} - {description:<50}");
}
println!("\nTo get help on a particular topic, use help 'topic'\n");
Ok(())
});
}
}
impl ApiObject for HelpSystem {
fn name(&self) -> &'static str {
"help"
}
}
impl Default for HelpSystem {
fn default() -> Self {
let mut help_system: HelpSystem = Self::new();
$(
help_system.topic(Box::new($topic::Topic));
)+
help_system
}
}
};
}
pub(crate) use topics;
macro_rules! impl_ping {
($($actor:ident),+) => {
use kameo::message::{Context, Message};
$(
impl Message<Ping> for $actor {
type Reply = ();
async fn handle(&mut self, _: Ping, _: Context<'_, Self, Self::Reply>) -> Self::Reply {}
}
)+
};
}
pub(crate) use impl_ping;