use std::sync::LazyLock;
use super::handlers_admin::{
cmd_autoconnect, cmd_ignore, cmd_image, cmd_kill, cmd_log, cmd_oper, cmd_preview, cmd_reload,
cmd_mentions, cmd_script, cmd_server, cmd_spellcheck, cmd_stats, cmd_unignore, cmd_wallops,
};
use super::handlers_dcc::cmd_dcc;
use super::handlers_irc::{
cmd_admin, cmd_away, cmd_ban, cmd_connect, cmd_cycle, cmd_deop, cmd_devoice, cmd_disconnect,
cmd_except, cmd_info, cmd_invex, cmd_invite, cmd_join, cmd_kick, cmd_kickban, cmd_links,
cmd_list, cmd_lusers, cmd_me, cmd_mode, cmd_msg, cmd_names, cmd_nick, cmd_notice, cmd_op,
cmd_part, cmd_query, cmd_quote, cmd_reop, cmd_time, cmd_topic, cmd_unban, cmd_unexcept,
cmd_uninvex, cmd_unreop, cmd_version, cmd_voice, cmd_who, cmd_whois, cmd_whowas, cmd_wii,
};
use super::handlers_ui::{
cmd_alias, cmd_clear, cmd_close, cmd_detach, cmd_help, cmd_items, cmd_quit, cmd_shell,
cmd_unalias,
};
use super::types::{CommandCategory, CommandDef};
static COMMANDS: LazyLock<Vec<(&'static str, CommandDef)>> = LazyLock::new(|| {
vec![
(
"connect",
CommandDef {
handler: cmd_connect,
description: "Connect to a server",
aliases: &["c"],
category: CommandCategory::Connection,
},
),
(
"disconnect",
CommandDef {
handler: cmd_disconnect,
description: "Disconnect from current server",
aliases: &[],
category: CommandCategory::Connection,
},
),
(
"quit",
CommandDef {
handler: cmd_quit,
description: "Quit the client",
aliases: &["exit"],
category: CommandCategory::Connection,
},
),
(
"detach",
CommandDef {
handler: cmd_detach,
description: "Detach from terminal (keep running)",
aliases: &["dt"],
category: CommandCategory::Connection,
},
),
(
"server",
CommandDef {
handler: cmd_server,
description: "Manage server configurations",
aliases: &[],
category: CommandCategory::Connection,
},
),
(
"dcc",
CommandDef {
handler: cmd_dcc,
description: "DCC CHAT commands (chat, close, list, reject)",
aliases: &[],
category: CommandCategory::Connection,
},
),
(
"join",
CommandDef {
handler: cmd_join,
description: "Join a channel",
aliases: &["j"],
category: CommandCategory::Channel,
},
),
(
"part",
CommandDef {
handler: cmd_part,
description: "Leave a channel",
aliases: &["leave"],
category: CommandCategory::Channel,
},
),
(
"cycle",
CommandDef {
handler: cmd_cycle,
description: "Part and rejoin a channel",
aliases: &["rejoin"],
category: CommandCategory::Channel,
},
),
(
"topic",
CommandDef {
handler: cmd_topic,
description: "View or set channel topic",
aliases: &["t"],
category: CommandCategory::Channel,
},
),
(
"kick",
CommandDef {
handler: cmd_kick,
description: "Kick a user from channel",
aliases: &["k"],
category: CommandCategory::Channel,
},
),
(
"invite",
CommandDef {
handler: cmd_invite,
description: "Invite a user to a channel",
aliases: &[],
category: CommandCategory::Channel,
},
),
(
"mode",
CommandDef {
handler: cmd_mode,
description: "Query or set modes",
aliases: &[],
category: CommandCategory::Channel,
},
),
(
"op",
CommandDef {
handler: cmd_op,
description: "Give operator status",
aliases: &[],
category: CommandCategory::Channel,
},
),
(
"deop",
CommandDef {
handler: cmd_deop,
description: "Remove operator status",
aliases: &[],
category: CommandCategory::Channel,
},
),
(
"voice",
CommandDef {
handler: cmd_voice,
description: "Give voice status",
aliases: &["v"],
category: CommandCategory::Channel,
},
),
(
"devoice",
CommandDef {
handler: cmd_devoice,
description: "Remove voice status",
aliases: &[],
category: CommandCategory::Channel,
},
),
(
"ban",
CommandDef {
handler: cmd_ban,
description: "Add ban or show ban list",
aliases: &["b"],
category: CommandCategory::Channel,
},
),
(
"unban",
CommandDef {
handler: cmd_unban,
description: "Remove ban(s) by number, mask, or wildcard (* = all)",
aliases: &[],
category: CommandCategory::Channel,
},
),
(
"kb",
CommandDef {
handler: cmd_kickban,
description: "Kickban a user (*!*ident@host)",
aliases: &["kickban"],
category: CommandCategory::Channel,
},
),
(
"except",
CommandDef {
handler: cmd_except,
description: "Add exception or show list",
aliases: &[],
category: CommandCategory::Channel,
},
),
(
"unexcept",
CommandDef {
handler: cmd_unexcept,
description: "Remove an exception",
aliases: &[],
category: CommandCategory::Channel,
},
),
(
"invex",
CommandDef {
handler: cmd_invex,
description: "Add invite exception or show list",
aliases: &[],
category: CommandCategory::Channel,
},
),
(
"uninvex",
CommandDef {
handler: cmd_uninvex,
description: "Remove an invite exception",
aliases: &[],
category: CommandCategory::Channel,
},
),
(
"reop",
CommandDef {
handler: cmd_reop,
description: "Add reop entry or show list",
aliases: &[],
category: CommandCategory::Channel,
},
),
(
"unreop",
CommandDef {
handler: cmd_unreop,
description: "Remove a reop entry",
aliases: &[],
category: CommandCategory::Channel,
},
),
(
"names",
CommandDef {
handler: cmd_names,
description: "Request NAMES list",
aliases: &[],
category: CommandCategory::Channel,
},
),
(
"msg",
CommandDef {
handler: cmd_msg,
description: "Send a private message",
aliases: &["m"],
category: CommandCategory::Messaging,
},
),
(
"query",
CommandDef {
handler: cmd_query,
description: "Open a query with a user",
aliases: &["q"],
category: CommandCategory::Messaging,
},
),
(
"me",
CommandDef {
handler: cmd_me,
description: "Send a CTCP ACTION",
aliases: &["action"],
category: CommandCategory::Messaging,
},
),
(
"notice",
CommandDef {
handler: cmd_notice,
description: "Send a notice",
aliases: &[],
category: CommandCategory::Messaging,
},
),
(
"nick",
CommandDef {
handler: cmd_nick,
description: "Change nickname",
aliases: &[],
category: CommandCategory::Messaging,
},
),
(
"away",
CommandDef {
handler: cmd_away,
description: "Set or clear away status",
aliases: &[],
category: CommandCategory::Messaging,
},
),
(
"help",
CommandDef {
handler: cmd_help,
description: "Show help for commands",
aliases: &["?"],
category: CommandCategory::Info,
},
),
(
"whois",
CommandDef {
handler: cmd_whois,
description: "WHOIS query on a user",
aliases: &["wi"],
category: CommandCategory::Info,
},
),
(
"wii",
CommandDef {
handler: cmd_wii,
description: "WHOIS with idle time",
aliases: &[],
category: CommandCategory::Info,
},
),
(
"version",
CommandDef {
handler: cmd_version,
description: "CTCP VERSION or server version",
aliases: &["ver"],
category: CommandCategory::Info,
},
),
(
"list",
CommandDef {
handler: cmd_list,
description: "List channels on server",
aliases: &[],
category: CommandCategory::Info,
},
),
(
"who",
CommandDef {
handler: cmd_who,
description: "WHO query",
aliases: &[],
category: CommandCategory::Info,
},
),
(
"whowas",
CommandDef {
handler: cmd_whowas,
description: "WHOWAS query on a nick",
aliases: &[],
category: CommandCategory::Info,
},
),
(
"info",
CommandDef {
handler: cmd_info,
description: "Request server info",
aliases: &[],
category: CommandCategory::Info,
},
),
(
"admin",
CommandDef {
handler: cmd_admin,
description: "Request server admin info",
aliases: &[],
category: CommandCategory::Info,
},
),
(
"lusers",
CommandDef {
handler: cmd_lusers,
description: "Request server user statistics",
aliases: &[],
category: CommandCategory::Info,
},
),
(
"time",
CommandDef {
handler: cmd_time,
description: "Request server time",
aliases: &[],
category: CommandCategory::Info,
},
),
(
"links",
CommandDef {
handler: cmd_links,
description: "List server links",
aliases: &[],
category: CommandCategory::Info,
},
),
(
"set",
CommandDef {
handler: super::settings::cmd_set,
description: "View or change settings",
aliases: &[],
category: CommandCategory::Configuration,
},
),
(
"reload",
CommandDef {
handler: cmd_reload,
description: "Reload theme and config",
aliases: &[],
category: CommandCategory::Configuration,
},
),
(
"spellcheck",
CommandDef {
handler: cmd_spellcheck,
description: "Spell checker status and control",
aliases: &["spell"],
category: CommandCategory::Configuration,
},
),
(
"mentions",
CommandDef {
handler: cmd_mentions,
description: "Switch to the mentions buffer",
aliases: &[],
category: CommandCategory::Info,
},
),
(
"ignore",
CommandDef {
handler: cmd_ignore,
description: "Add or list ignore rules",
aliases: &[],
category: CommandCategory::Configuration,
},
),
(
"unignore",
CommandDef {
handler: cmd_unignore,
description: "Remove an ignore rule",
aliases: &[],
category: CommandCategory::Configuration,
},
),
(
"alias",
CommandDef {
handler: cmd_alias,
description: "Define or list command aliases",
aliases: &[],
category: CommandCategory::Configuration,
},
),
(
"unalias",
CommandDef {
handler: cmd_unalias,
description: "Remove a command alias",
aliases: &[],
category: CommandCategory::Configuration,
},
),
(
"autoconnect",
CommandDef {
handler: cmd_autoconnect,
description: "Toggle server autoconnect",
aliases: &[],
category: CommandCategory::Configuration,
},
),
(
"items",
CommandDef {
handler: cmd_items,
description: "Manage statusbar items",
aliases: &[],
category: CommandCategory::Configuration,
},
),
(
"log",
CommandDef {
handler: cmd_log,
description: "Log status and search",
aliases: &[],
category: CommandCategory::Configuration,
},
),
(
"script",
CommandDef {
handler: cmd_script,
description: "Manage Lua scripts",
aliases: &[],
category: CommandCategory::Configuration,
},
),
(
"oper",
CommandDef {
handler: cmd_oper,
description: "Authenticate as IRC operator",
aliases: &[],
category: CommandCategory::Other,
},
),
(
"kill",
CommandDef {
handler: cmd_kill,
description: "Disconnect a user (oper only)",
aliases: &[],
category: CommandCategory::Other,
},
),
(
"wallops",
CommandDef {
handler: cmd_wallops,
description: "Send message to all opers",
aliases: &[],
category: CommandCategory::Other,
},
),
(
"stats",
CommandDef {
handler: cmd_stats,
description: "Request server statistics",
aliases: &[],
category: CommandCategory::Other,
},
),
(
"clear",
CommandDef {
handler: cmd_clear,
description: "Clear active buffer",
aliases: &[],
category: CommandCategory::Other,
},
),
(
"close",
CommandDef {
handler: cmd_close,
description: "Close active buffer",
aliases: &["wc"],
category: CommandCategory::Other,
},
),
(
"quote",
CommandDef {
handler: cmd_quote,
description: "Send a raw IRC command",
aliases: &["raw"],
category: CommandCategory::Other,
},
),
(
"preview",
CommandDef {
handler: cmd_preview,
description: "Preview an image URL",
aliases: &[],
category: CommandCategory::Other,
},
),
(
"image",
CommandDef {
handler: cmd_image,
description: "Image cache management",
aliases: &["img"],
category: CommandCategory::Other,
},
),
(
"shell",
CommandDef {
handler: cmd_shell,
description: "Open an embedded terminal",
aliases: &["sh"],
category: CommandCategory::Other,
},
),
]
});
pub fn get_commands() -> &'static [(&'static str, CommandDef)] {
&COMMANDS
}
pub fn get_command_names() -> &'static [&'static str] {
static NAMES: LazyLock<Vec<&'static str>> = LazyLock::new(|| {
let commands = get_commands();
let mut names: Vec<&'static str> = Vec::new();
for &(name, ref def) in commands {
names.push(name);
for alias in def.aliases {
names.push(alias);
}
}
names.sort_unstable();
names.dedup();
names
});
&NAMES
}
#[allow(dead_code)]
pub fn resolve_alias(name: &str) -> Option<&'static str> {
let commands = get_commands();
for &(cmd_name, ref def) in commands {
if cmd_name == name {
return Some(cmd_name);
}
for alias in def.aliases {
if *alias == name {
return Some(cmd_name);
}
}
}
None
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn all_commands_have_description() {
for &(name, ref def) in get_commands() {
assert!(!def.description.is_empty(), "/{name} missing description");
}
}
#[test]
fn command_names_includes_aliases() {
let names = get_command_names();
assert!(names.contains(&"connect"));
assert!(names.contains(&"c"));
assert!(names.contains(&"j"));
assert!(names.contains(&"?"));
assert!(names.contains(&"exit"));
}
#[test]
fn resolve_alias_works() {
assert_eq!(resolve_alias("c"), Some("connect"));
assert_eq!(resolve_alias("j"), Some("join"));
assert_eq!(resolve_alias("?"), Some("help"));
assert_eq!(resolve_alias("connect"), Some("connect"));
assert_eq!(resolve_alias("nonexistent"), None);
}
#[test]
fn categories_cover_all_commands() {
for &(name, ref def) in get_commands() {
assert!(
!def.category.label().is_empty(),
"/{name} has empty category label"
);
}
}
#[test]
fn server_query_commands_registered() {
let commands = get_commands();
let names: Vec<&str> = commands.iter().map(|(name, _)| *name).collect();
assert!(names.contains(&"info"), "/info command not registered");
assert!(names.contains(&"admin"), "/admin command not registered");
assert!(names.contains(&"lusers"), "/lusers command not registered");
assert!(names.contains(&"time"), "/time command not registered");
assert!(names.contains(&"stats"), "/stats command not registered");
assert!(names.contains(&"links"), "/links command not registered");
}
#[test]
fn server_query_commands_in_correct_category() {
let commands = get_commands();
for &(name, ref def) in commands {
match name {
"info" | "admin" | "lusers" | "time" | "links" => {
assert!(
matches!(def.category, CommandCategory::Info),
"/{name} should be in Info category, got {:?}",
def.category
);
}
_ => {}
}
}
}
#[test]
fn no_duplicate_aliases() {
let mut all_names: Vec<&str> = Vec::new();
for &(name, ref def) in get_commands() {
assert!(!all_names.contains(&name), "Duplicate command name: {name}");
all_names.push(name);
for alias in def.aliases {
assert!(!all_names.contains(alias), "Duplicate alias: {alias}");
all_names.push(alias);
}
}
}
}