panda/plugins/guest_plugin_manager/
guest_plugin.rs

1use std::{ffi::CString, io::Write, os::raw::c_char, path::Path, ptr};
2
3use super::GUEST_PLUGIN_MANAGER;
4
5/// A raw Channel ID
6pub type ChannelId = u32;
7
8/// A callback for recieving writes to a channel performed by the guest
9pub type ChannelCB = extern "C" fn(ChannelId, *const u8, usize);
10
11/// A guest plugin to be loaded by the guest plugin manager
12#[repr(C)]
13pub struct GuestPlugin {
14    pub plugin_name: *const c_char,
15    pub guest_binary_path: *const c_char,
16    pub msg_receive_cb: ChannelCB,
17}
18
19/// An [`io::Write`](Write) type for writing to a guest plugin channel
20#[repr(transparent)]
21pub struct Channel(ChannelId);
22
23impl Channel {
24    /// Write data to a single packet without going through the io::Write trait
25    pub fn write_packet(&mut self, buf: &[u8]) {
26        GUEST_PLUGIN_MANAGER.channel_write(self.0, buf.as_ptr(), buf.len());
27    }
28
29    /// Creates a new anonymous channel provided a callback for handling writes
30    pub fn new(callback: ChannelCB) -> Self {
31        Channel(GUEST_PLUGIN_MANAGER.allocate_channel(callback))
32    }
33
34    /// Get the raw channel ID of this channel
35    pub fn id(&self) -> ChannelId {
36        self.0
37    }
38}
39
40impl Write for Channel {
41    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
42        self.write_packet(buf);
43
44        Ok(buf.len())
45    }
46
47    fn flush(&mut self) -> std::io::Result<()> {
48        Ok(())
49    }
50}
51
52/// Load a guest plugin given the guest plugin's name and a callback for when a message
53/// is recieved from this plugin.
54///
55/// Returns a channel with the same name as the plugin for use when communicating with
56/// the guest plugin.
57pub fn load_guest_plugin(name: impl Into<String>, msg_received: ChannelCB) -> Channel {
58    Channel(GUEST_PLUGIN_MANAGER.add_guest_plugin(GuestPlugin::new(name.into(), msg_received)))
59}
60
61impl GuestPlugin {
62    /// Initiailizes a `GuestPlugin` to be passed to `add_guest_plugin` by name, finding
63    /// the path of the plugin by name lookup.
64    pub fn new(plugin_name: String, msg_receive_cb: ChannelCB) -> Self {
65        let plugin_name = CString::new(plugin_name).unwrap().into_raw();
66
67        GuestPlugin {
68            plugin_name,
69            guest_binary_path: ptr::null(),
70            msg_receive_cb,
71        }
72    }
73
74    /// Initiailizes a `GuestPlugin` to be passed to `add_guest_plugin` by name, using
75    /// a set path rather than lookup by name.
76    pub fn new_with_path(
77        plugin_name: String,
78        guest_binary_path: &Path,
79        msg_receive_cb: ChannelCB,
80    ) -> Self {
81        let plugin_name = CString::new(plugin_name).unwrap().into_raw();
82        let guest_binary_path = CString::new(guest_binary_path.to_string_lossy().into_owned())
83            .unwrap()
84            .into_raw();
85
86        GuestPlugin {
87            plugin_name,
88            guest_binary_path,
89            msg_receive_cb,
90        }
91    }
92}