use crate::commands::{CommandBaseKind, CommandFlags};
use sdk_raw::test_support::{mock_vtable, unexpected_call};
use std::ffi::{CStr, c_char};
use std::ptr::null_mut;
#[repr(C)]
struct MockCvar {
interface: sys::ICvar,
head: *mut sys::ConCommandBase,
vars: Vec<*mut sys::ConVar>,
}
unsafe extern "C" fn find_var(this: *mut sys::ICvar, name: *const c_char) -> *mut sys::ConVar {
let (name, cvar) = unsafe { (CStr::from_ptr(name), &*this.cast::<MockCvar>()) };
cvar.vars
.iter()
.copied()
.find(|&var| {
let listed = unsafe { CStr::from_ptr((*var)._base.m_pszName) };
listed.to_bytes().eq_ignore_ascii_case(name.to_bytes())
})
.unwrap_or(null_mut())
}
unsafe extern "C" fn get_commands(this: *mut sys::ICvar) -> *mut sys::ConCommandBase {
unsafe { (*this.cast::<MockCvar>()).head }
}
unsafe extern "C" fn get_name(this: *const sys::ConCommandBase) -> *const c_char {
unsafe { (&raw const (*this).m_pszName).read() }
}
pub unsafe extern "C" fn is_command(_: *const sys::ConCommandBase) -> bool {
true
}
unsafe extern "C" fn is_registered(this: *const sys::ConCommandBase) -> bool {
unsafe { (&raw const (*this).m_bRegistered).read() }
}
pub unsafe extern "C" fn is_variable(_: *const sys::ConCommandBase) -> bool {
false
}
pub fn mock_base(
name: &'static CStr,
kind: CommandBaseKind,
flags: CommandFlags,
next: *mut sys::ConCommandBase,
) -> sys::ConCommandBase {
let vtable = unsafe {
mock_vtable::<sys::ConCommandBase__bindgen_vtable>(unexpected_call as *const (), |vtable| {
(&raw mut (*vtable).ConCommandBase_IsCommand).write(match kind {
CommandBaseKind::Command => is_command,
CommandBaseKind::Variable => is_variable,
});
(&raw mut (*vtable).ConCommandBase_GetName).write(get_name);
(&raw mut (*vtable).ConCommandBase_IsRegistered).write(is_registered);
})
};
sys::ConCommandBase {
vtable_: Box::leak(vtable),
m_pNext: next,
m_bRegistered: true,
m_pszName: name.as_ptr(),
m_pszHelpString: c"".as_ptr(),
m_nFlags: flags.bits(),
}
}
pub fn mock_command(
name: &'static CStr,
next: *mut sys::ConCommandBase,
) -> *mut sys::ConCommandBase {
let base = mock_base(name, CommandBaseKind::Command, CommandFlags::NONE, next);
Box::into_raw(Box::new(base))
}
pub fn mock_cvar(head: *mut sys::ConCommandBase, vars: Vec<*mut sys::ConVar>) -> *mut sys::ICvar {
let vtable = unsafe {
mock_vtable::<sys::ICvar__bindgen_vtable>(unexpected_call as *const (), |vtable| {
(&raw mut (*vtable).ICvar_FindVar).write(find_var);
(&raw mut (*vtable).ICvar_GetCommands).write(get_commands);
})
};
Box::into_raw(Box::new(MockCvar {
interface: sys::ICvar {
vtable_: Box::leak(vtable),
},
head,
vars,
}))
.cast()
}
pub fn mock_var(
name: &'static CStr,
default: &'static CStr,
value: &'static CStr,
flags: CommandFlags,
next: *mut sys::ConCommandBase,
) -> *mut sys::ConVar {
let var = Box::into_raw(Box::new(unsafe { std::mem::zeroed::<sys::ConVar>() }));
unsafe {
(*var)._base = mock_base(name, CommandBaseKind::Variable, flags, next);
(*var).m_pParent = var;
(*var).m_pszDefaultValue = default.as_ptr();
(*var).m_pszString = value.as_ptr().cast_mut();
}
var
}