#![allow(clippy::not_unsafe_ptr_arg_deref)]
use std::ffi::{c_char, CStr};
use crate::{
bindings::cvar::command::{
CCommand, COMMAND_COMPLETION_ITEM_LENGTH, COMMAND_COMPLETION_MAXITEMS,
},
errors::CompletionError,
mid::utils::set_c_char_array,
};
#[derive(Debug, Default)]
pub struct CCommandResult {
args: Vec<String>,
command: String,
}
pub struct CommandCompletion<'a> {
suggestions: &'a mut [[i8; COMMAND_COMPLETION_ITEM_LENGTH as usize]],
suggestions_left: u32,
}
pub struct CurrentCommand<'a> {
pub cmd: &'a str,
pub partial: &'a str,
}
impl CCommandResult {
pub unsafe fn new(ccommand: *const CCommand) -> Self {
let ccommand = match unsafe { ccommand.as_ref() } {
Some(c) => c,
None => return Self::default(),
};
if ccommand.m_nArgv0Size == 0 {
Self::default()
} else {
let buffer = ccommand.m_pArgSBuffer.as_ptr();
let whole_command = unsafe { CStr::from_ptr(buffer).to_string_lossy().to_string() };
let mut whole_command = whole_command.split_whitespace();
let command = whole_command.next().unwrap_or_default().into();
let args = whole_command.map(|a| a.to_string()).collect();
Self { args, command }
}
}
pub fn pop_arg(&mut self) -> Option<String> {
self.args.pop()
}
pub fn get_arg(&self, index: usize) -> Option<&str> {
self.args.get(index).map(|s| &**s)
}
pub fn get_args(&self) -> &[String] {
&self.args
}
pub fn get_command(&self) -> &str {
&self.command
}
}
impl From<*mut [c_char; COMMAND_COMPLETION_ITEM_LENGTH as usize]> for CommandCompletion<'_> {
fn from(commands: *mut [c_char; COMMAND_COMPLETION_ITEM_LENGTH as usize]) -> Self {
Self {
suggestions: unsafe {
std::slice::from_raw_parts_mut(commands, COMMAND_COMPLETION_MAXITEMS as usize)
},
suggestions_left: COMMAND_COMPLETION_MAXITEMS,
}
}
}
impl CommandCompletion<'_> {
pub fn push(&mut self, new: &str) -> Result<(), CompletionError> {
if self.suggestions_left == 0 {
return Err(CompletionError::NoCompletionSlotsLeft);
}
set_c_char_array(
&mut self.suggestions[(COMMAND_COMPLETION_MAXITEMS - self.suggestions_left) as usize],
new,
);
self.suggestions_left -= 1;
Ok(())
}
pub const fn commands_used(&self) -> i32 {
(COMMAND_COMPLETION_MAXITEMS - self.suggestions_left) as i32
}
}
impl CurrentCommand<'_> {
pub fn new(partial: *const c_char) -> Option<Self> {
let partial = unsafe { CStr::from_ptr(partial).to_str() }.ok()?;
let (name, cmd) = partial.split_once(' ').unwrap_or((partial, ""));
Some(Self {
cmd: name,
partial: cmd,
})
}
}
#[cfg(test)]
mod test {
use crate::rrplug;
#[rrplug::completion]
fn completion_test(current: CurrentCommand, suggestions: CommandCompletion) {
suggestions
.push(format!("{} {}", current.cmd, "test").as_str())
.unwrap();
}
}