pub const ABI_VERSION: u32 = 1;
pub const PMPX_OK: u32 = 0;
pub const PMPX_ERR_UNSUPPORTED_VERB: u32 = 1;
pub const PMPX_ERR_INVALID_ARGS: u32 = 2;
pub const PMPX_ERR_INTERNAL: u32 = 3;
pub const VERB_INSTALL: u32 = 0;
pub const VERB_REMOVE: u32 = 1;
pub const VERB_RUN: u32 = 2;
pub const VERB_BUILD: u32 = 3;
pub const VERB_TEST: u32 = 4;
pub const VERB_UPDATE: u32 = 5;
pub const VERB_EXEC: u32 = 6;
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct PmpxStr {
pub ptr: *const u8,
pub len: usize,
}
unsafe impl Sync for PmpxStr {}
impl PmpxStr {
pub const EMPTY: PmpxStr = PmpxStr {
ptr: std::ptr::null(),
len: 0,
};
pub const fn from_static(s: &'static str) -> Self {
Self {
ptr: s.as_ptr(),
len: s.len(),
}
}
pub const fn is_empty(&self) -> bool {
self.len == 0
}
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct PmpxCommand {
pub program: PmpxStr,
pub args: *const PmpxStr,
pub args_len: usize,
pub cwd: PmpxStr,
}
unsafe impl Sync for PmpxCommand {}
#[repr(C)]
pub struct PmpxPluginV1 {
pub abi_version: u32,
pub rustc_version: PmpxStr,
pub target: PmpxStr,
pub name: unsafe extern "C" fn() -> PmpxStr,
pub family: unsafe extern "C" fn() -> PmpxStr,
pub command: unsafe extern "C" fn(
project_root: PmpxStr,
matched: *const PmpxStr,
matched_len: usize,
verb: u32,
args: *const PmpxStr,
args_len: usize,
out: *mut PmpxCommand,
) -> u32,
pub free_str: unsafe extern "C" fn(PmpxStr),
pub free_command: unsafe extern "C" fn(*mut PmpxCommand),
}
unsafe impl Sync for PmpxPluginV1 {}
pub const ENTRY_SYMBOL: &str = "pmpx_plugin_entry_v1";
pub const fn build_rustc() -> PmpxStr {
PmpxStr::from_static(env!("PMPX_BUILD_RUSTC"))
}
pub const fn build_target() -> PmpxStr {
PmpxStr::from_static(env!("PMPX_BUILD_TARGET"))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Verb;
#[test]
fn verb_numbers_match_the_public_enum() {
assert_eq!(Verb::Install.to_abi(), VERB_INSTALL);
assert_eq!(Verb::Remove.to_abi(), VERB_REMOVE);
assert_eq!(Verb::Run.to_abi(), VERB_RUN);
assert_eq!(Verb::Build.to_abi(), VERB_BUILD);
assert_eq!(Verb::Test.to_abi(), VERB_TEST);
assert_eq!(Verb::Update.to_abi(), VERB_UPDATE);
assert_eq!(Verb::Exec.to_abi(), VERB_EXEC);
}
#[test]
fn verb_round_trips() {
for v in Verb::ALL {
assert_eq!(Verb::from_abi(v.to_abi()), Some(*v));
}
assert_eq!(Verb::from_abi(99), None);
}
#[test]
fn build_info_is_populated() {
let rustc = build_rustc();
let target = build_target();
assert!(rustc.len > 0);
assert!(target.len > 0);
let rustc = unsafe { std::slice::from_raw_parts(rustc.ptr, rustc.len) };
let target = unsafe { std::slice::from_raw_parts(target.ptr, target.len) };
assert!(
std::str::from_utf8(rustc).unwrap().contains("rustc"),
"rustc_version should look like `rustc 1.x.y (...)`"
);
assert!(std::str::from_utf8(target).unwrap().contains('-'));
}
}