use libc::{c_char, c_int};
use std::{borrow::Cow, ffi::CStr, os::raw::c_void, ptr};
use weechat_sys::{t_gui_buffer, t_weechat_plugin, WEECHAT_RC_OK};
use crate::{buffer::Buffer, Args, LossyCString, ReturnCode, Weechat};
use super::Hook;
pub struct Command {
_hook: Hook,
_hook_data: Box<CommandHookData>,
}
pub trait CommandCallback {
fn callback(&mut self, weechat: &Weechat, buffer: &Buffer, arguments: Args);
}
impl<T: FnMut(&Weechat, &Buffer, Args) + 'static> CommandCallback for T {
fn callback(&mut self, weechat: &Weechat, buffer: &Buffer, arguments: Args) {
self(weechat, buffer, arguments)
}
}
#[derive(Default)]
pub struct CommandSettings {
name: String,
description: String,
arguments: Vec<String>,
argument_descriptoin: String,
completion: Vec<String>,
}
impl CommandSettings {
pub fn new<P: Into<String>>(name: P) -> Self {
CommandSettings {
name: name.into(),
..Default::default()
}
}
pub fn description<D: Into<String>>(mut self, descritpion: D) -> Self {
self.description = descritpion.into();
self
}
pub fn add_argument<T: Into<String>>(mut self, argument: T) -> Self {
self.arguments.push(argument.into());
self
}
pub fn arguments_description<T: Into<String>>(mut self, descritpion: T) -> Self {
self.argument_descriptoin = descritpion.into();
self
}
pub fn add_completion<T: Into<String>>(mut self, completion: T) -> Self {
self.completion.push(completion.into());
self
}
}
struct CommandHookData {
callback: Box<dyn CommandCallback>,
weechat_ptr: *mut t_weechat_plugin,
}
pub struct CommandRun {
_hook: Hook,
_hook_data: Box<CommandRunHookData>,
}
pub trait CommandRunCallback {
fn callback(&mut self, weechat: &Weechat, buffer: &Buffer, command: Cow<str>) -> ReturnCode;
}
impl<T: FnMut(&Weechat, &Buffer, Cow<str>) -> ReturnCode + 'static> CommandRunCallback for T {
fn callback(&mut self, weechat: &Weechat, buffer: &Buffer, command: Cow<str>) -> ReturnCode {
self(weechat, buffer, command)
}
}
struct CommandRunHookData {
callback: Box<dyn CommandRunCallback>,
weechat_ptr: *mut t_weechat_plugin,
}
impl CommandRun {
pub fn new(command: &str, callback: impl CommandRunCallback + 'static) -> Result<Self, ()> {
unsafe extern "C" fn c_hook_cb(
pointer: *const c_void,
_data: *mut c_void,
buffer: *mut t_gui_buffer,
command: *const std::os::raw::c_char,
) -> c_int {
let hook_data: &mut CommandRunHookData = { &mut *(pointer as *mut CommandRunHookData) };
let cb = &mut hook_data.callback;
let weechat = Weechat::from_ptr(hook_data.weechat_ptr);
let buffer = weechat.buffer_from_ptr(buffer);
let command = CStr::from_ptr(command).to_string_lossy();
cb.callback(&weechat, &buffer, command) as isize as i32
}
Weechat::check_thread();
let weechat = unsafe { Weechat::weechat() };
let data = Box::new(CommandRunHookData {
callback: Box::new(callback),
weechat_ptr: weechat.ptr,
});
let data_ref = Box::leak(data);
let hook_command_run = weechat.get().hook_command_run.unwrap();
let command = LossyCString::new(command);
let hook_ptr = unsafe {
hook_command_run(
weechat.ptr,
command.as_ptr(),
Some(c_hook_cb),
data_ref as *const _ as *const c_void,
ptr::null_mut(),
)
};
let hook_data = unsafe { Box::from_raw(data_ref) };
if hook_ptr.is_null() {
Err(())
} else {
let hook = Hook {
ptr: hook_ptr,
weechat_ptr: weechat.ptr,
};
Ok(CommandRun {
_hook: hook,
_hook_data: hook_data,
})
}
}
}
impl Command {
pub fn new(
command_settings: CommandSettings,
callback: impl CommandCallback + 'static,
) -> Result<Command, ()> {
unsafe extern "C" fn c_hook_cb(
pointer: *const c_void,
_data: *mut c_void,
buffer: *mut t_gui_buffer,
argc: i32,
argv: *mut *mut c_char,
_argv_eol: *mut *mut c_char,
) -> c_int {
let hook_data: &mut CommandHookData = { &mut *(pointer as *mut CommandHookData) };
let weechat = Weechat::from_ptr(hook_data.weechat_ptr);
let buffer = weechat.buffer_from_ptr(buffer);
let cb = &mut hook_data.callback;
let args = Args::new(argc, argv);
cb.callback(&weechat, &buffer, args);
WEECHAT_RC_OK
}
Weechat::check_thread();
let weechat = unsafe { Weechat::weechat() };
let name = LossyCString::new(command_settings.name);
let description = LossyCString::new(command_settings.description);
let args = LossyCString::new(command_settings.arguments.join("||"));
let args_description = LossyCString::new(command_settings.argument_descriptoin);
let completion = LossyCString::new(command_settings.completion.join("||"));
let data = Box::new(CommandHookData {
callback: Box::new(callback),
weechat_ptr: weechat.ptr,
});
let data_ref = Box::leak(data);
let hook_command = weechat.get().hook_command.unwrap();
let hook_ptr = unsafe {
hook_command(
weechat.ptr,
name.as_ptr(),
description.as_ptr(),
args.as_ptr(),
args_description.as_ptr(),
completion.as_ptr(),
Some(c_hook_cb),
data_ref as *const _ as *const c_void,
ptr::null_mut(),
)
};
let hook_data = unsafe { Box::from_raw(data_ref) };
let hook = Hook {
ptr: hook_ptr,
weechat_ptr: weechat.ptr,
};
if hook_ptr.is_null() {
Err(())
} else {
Ok(Command {
_hook: hook,
_hook_data: hook_data,
})
}
}
}