#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum HelpSection {
BuiltIn,
Channel,
Skill,
Custom,
}
impl HelpSection {
pub fn title(self) -> &'static str {
match self {
HelpSection::BuiltIn => "SLASH COMMANDS",
HelpSection::Channel => "CHANNEL COMMANDS (Telegram/Discord/Slack)",
HelpSection::Skill => "SKILLS",
HelpSection::Custom => "CUSTOM COMMANDS",
}
}
pub fn all() -> [HelpSection; 4] {
[
HelpSection::BuiltIn,
HelpSection::Channel,
HelpSection::Skill,
HelpSection::Custom,
]
}
}
#[derive(Debug, Clone)]
pub struct HelpRow {
pub name: String,
pub description: String,
pub section: HelpSection,
}
impl HelpRow {
pub fn matches(&self, needle: &str) -> bool {
if needle.is_empty() {
return true;
}
let needle = needle.to_lowercase();
self.name.to_lowercase().contains(&needle)
|| self.description.to_lowercase().contains(&needle)
}
}
pub fn load() -> Vec<HelpRow> {
let mut rows = Vec::new();
for cmd in super::SLASH_COMMANDS {
rows.push(HelpRow {
name: cmd.name.to_string(),
description: cmd.description.to_string(),
section: HelpSection::BuiltIn,
});
}
for cmd in super::CHANNEL_COMMANDS {
rows.push(HelpRow {
name: cmd.name.to_string(),
description: cmd.description.to_string(),
section: HelpSection::Channel,
});
}
for skill in crate::brain::skills::load_all_skills() {
rows.push(HelpRow {
name: skill.slash_name.clone(),
description: skill.description.clone(),
section: HelpSection::Skill,
});
}
let brain_path = crate::brain::BrainLoader::resolve_path();
let mut user_cmds = crate::brain::CommandLoader::from_brain_path(&brain_path).load();
user_cmds.sort_by(|a, b| a.name.cmp(&b.name));
for cmd in user_cmds {
rows.push(HelpRow {
name: cmd.name,
description: cmd.description,
section: HelpSection::Custom,
});
}
rows
}
pub fn section_matches<'a>(
rows: &'a [HelpRow],
section: HelpSection,
needle: &str,
) -> Vec<&'a HelpRow> {
rows.iter()
.filter(|r| r.section == section && r.matches(needle))
.collect()
}
pub fn match_count(rows: &[HelpRow], needle: &str) -> usize {
rows.iter().filter(|r| r.matches(needle)).count()
}
pub fn max_scroll(content_rows: usize, viewport_rows: usize) -> usize {
content_rows.saturating_sub(viewport_rows)
}