A rust framework for interacting with fluxer instances
Something went wrong. Try again.
5.4 kB · 180 lines
Rust
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181use std::{collections::HashMap, pin::Pin};
use anyhow::Result;
use crate::{ api::FluxerApiHandler, error::{CommandHandlerError, FluxerRsError}, serde::types::message::MessageData,};
pub type Command = Box<dyn PinnedCommandTrait>;
pub struct CommandHandler { prefix: String, map: HashMap<String, Command>,}
pub struct CommandFeedback<'a> { pub data: &'a MessageData, pub args: Vec<&'a str>,}
/// A wrapper trait high level commands must implement to be able to be registered and stored in the command handler's hashmap/// Mainly used as a quality of life so users don't have to manually pin the returned futurepub trait CommandTrait: Send + Sync + 'static { fn execute<'a>( &'a self, api: &'a FluxerApiHandler, feedback: &'a CommandFeedback<'a>, ) -> impl Future<Output = Result<(), FluxerRsError>> + Send + 'a;}
/// The main trait that is actually used to store high level commands in the hashmappub trait PinnedCommandTrait: Send + Sync { fn execute<'a>( &'a self, api: &'a FluxerApiHandler, // TODO // Maybe make it owned instead of a reference feedback: &'a CommandFeedback<'a>, ) -> Pin<Box<dyn Future<Output = Result<(), FluxerRsError>> + Send + 'a>>;}
/// Helper function for people that prefer to use the register command function instead of the macropub fn cmd(cmd_name: &str, cmd_handler: impl CommandTrait) -> (String, Command) { (cmd_name.into(), Box::new(cmd_handler))}
/// The bridge between the wrapper and the main command traitimpl<C: CommandTrait> PinnedCommandTrait for C { fn execute<'a>( &'a self, api: &'a FluxerApiHandler, feedback: &'a CommandFeedback<'a>, ) -> Pin<Box<dyn Future<Output = Result<(), FluxerRsError>> + Send + 'a>> { Box::pin(CommandTrait::execute(self, api, feedback)) }}
impl CommandHandler { pub fn init(prefix: impl Into<String>) -> Self { CommandHandler { prefix: prefix.into(), map: HashMap::new(), } }
/// Register a single command into the command handler pub fn register_command( &mut self, command_name: impl Into<String>, handler: impl CommandTrait, ) { self.map.insert(command_name.into(), Box::new(handler)); }
/// Register a list of commands into the command handler pub fn register_commands(&mut self, commands: Vec<(String, Command)>) { for (command_name, handler) in commands { self.map.insert(command_name, handler); } }
pub async fn handle( &self, data: &MessageData, api: &FluxerApiHandler, ) -> Result<(), FluxerRsError> { let content = match &data.content { Some(data) => data, None => return Ok(()), };
// Checking whether the message content really starts with the prefix and if it's not just the prefix itself if !content.starts_with(&self.prefix) || content.len() < 2 { return Ok(()); }
let (cmd, args) = Self::command_data(&self.prefix, content)?;
let split_args = Self::split_args(args);
match self.map.get(cmd) { Some(handler) => { handler .execute( api, &CommandFeedback { data, args: split_args, }, ) .await } None => Err(FluxerRsError::CommandHandlerError( crate::error::CommandHandlerError::UnknownCommand(args.to_string()), )), } }
pub fn command_data<'a>( prefix: &str, content: &'a str, ) -> Result<(&'a str, &'a str), FluxerRsError> { content .strip_prefix(prefix) .ok_or_else(|| CommandHandlerError::Custom("Failed to remove command prefix".into())) .map(|stripped_body| match stripped_body.split_once(" ") { Some((cmd, args)) => (cmd, args), None => (stripped_body, ""), }) .map_err(FluxerRsError::from) }
/// Helper function for argument parsing fn split_args(body: &str) -> Vec<&str> { let mut args: Vec<&str> = Vec::new();
let mut quote: bool = false; let mut last_char: char = ' '; let body = body.trim(); let mut start: usize = 0;
for (i, elem) in body.char_indices() { if !quote && last_char == '\"' && elem != ' ' { start = i; }
match elem { ' ' => { if !quote && last_char != '\"' { args.push(&body[start..i]); }
if !quote { start = i + 1; } } '\"' => { if !quote { start = i + 1; } else { args.push(&body[start..i]); }
quote = !quote; } _ => {} }
if i + 1 == body.len() && elem != '\"' { args.push(&body[start..i + 1]); }
last_char = elem; }
args }}