use crate::qemu_plugin_bool_parse;
use qemu_plugin_sys::{
QEMU_PLUGIN_VERSION, qemu_info_t, qemu_info_t__bindgen_ty_1,
qemu_info_t__bindgen_ty_2__bindgen_ty_1, qemu_plugin_id_t,
};
use std::{
collections::HashMap,
ffi::{CStr, c_char, c_int},
};
use crate::{error::Error, plugin::PLUGIN};
#[allow(non_upper_case_globals)]
#[unsafe(no_mangle)]
pub static qemu_plugin_version: c_int = QEMU_PLUGIN_VERSION as c_int;
pub const PLUGIN_INSTALL_SUCCESS: c_int = 0;
#[derive(Debug, Clone)]
pub enum Value {
Bool(bool),
Integer(i64),
String(String),
}
impl Value {
fn new(key: &str, value: &str) -> Result<Self, Error> {
if let Ok(maybe_bool) = qemu_plugin_bool_parse(key, value) {
Ok(Self::Bool(maybe_bool))
} else if let Ok(int) = value.parse::<i64>() {
Ok(Self::Integer(int))
} else {
Ok(Self::String(value.to_string()))
}
}
}
#[derive(Debug, Clone)]
pub struct Args {
pub raw: Vec<String>,
pub parsed: HashMap<String, Value>,
}
impl Args {
fn new(argc: c_int, value: *const *const c_char) -> Result<Self, Error> {
Ok(Self {
raw: (0..argc)
.map(|i| unsafe { CStr::from_ptr(*value.offset(i as isize)) })
.map(|cstr| cstr.to_string_lossy().into_owned())
.collect::<Vec<_>>(),
parsed: (0..argc)
.map(|i| unsafe { CStr::from_ptr(*value.offset(i as isize)) })
.map(|cstr| cstr.to_string_lossy().into_owned())
.map(|argument| {
let mut split = argument.splitn(2, '=');
let Some(key) = split.next() else {
return Err(Error::MissingArgKey { argument });
};
let Some(value) = split.next() else {
return Err(Error::MissingArgValue { argument });
};
Ok((key.to_string(), Value::new(key, value)?))
})
.collect::<Result<Vec<(_, _)>, Error>>()?
.into_iter()
.collect::<HashMap<_, _>>(),
})
}
}
#[derive(Debug, Clone)]
pub struct Version {
pub current: i64,
pub mininum: i64,
}
impl From<&qemu_info_t__bindgen_ty_1> for Version {
fn from(value: &qemu_info_t__bindgen_ty_1) -> Self {
Self {
current: value.cur as i64,
mininum: value.min as i64,
}
}
}
#[derive(Debug, Clone)]
pub struct System {
pub max_vcpus: i64,
pub smp_vcpus: i64,
}
impl From<&qemu_info_t__bindgen_ty_2__bindgen_ty_1> for System {
fn from(value: &qemu_info_t__bindgen_ty_2__bindgen_ty_1) -> Self {
Self {
max_vcpus: value.max_vcpus as i64,
smp_vcpus: value.smp_vcpus as i64,
}
}
}
#[derive(Debug, Clone)]
pub struct Info {
pub target_name: String,
pub version: Version,
pub system: Option<System>,
}
impl Info {
unsafe fn try_from(value: *const qemu_info_t) -> Result<Self, Error> {
let target_name = unsafe { CStr::from_ptr((*value).target_name) }
.to_str()
.map_err(Error::from)?
.to_string();
let version = Version::from(unsafe { &(*value).version });
let system_emulation = unsafe { (*value).system_emulation };
let system = if system_emulation {
Some(System::from(unsafe { &(*value).__bindgen_anon_1.system }))
} else {
None
};
Ok(Self {
target_name,
version,
system,
})
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn qemu_plugin_install(
id: qemu_plugin_id_t,
info: *const qemu_info_t,
argc: c_int,
argv: *const *const c_char,
) -> c_int {
let args = Args::new(argc, argv).expect("Failed to parse arguments");
let info = unsafe { Info::try_from(info) }.expect("Failed to convert qemu_info_t");
let Some(plugin) = PLUGIN.get() else {
panic!("Plugin not set");
};
let Ok(mut plugin) = plugin.lock() else {
panic!("Failed to lock plugin");
};
plugin
.register_default(id, &args, &info)
.expect("Failed to register plugin");
PLUGIN_INSTALL_SUCCESS
}