98 lines
3.2 KiB
Rust
Raw Normal View History

2025-06-27 02:33:44 +01:00
use std::sync::{Arc, mpsc};
use nu_plugin::Plugin;
use nu_plugin_core::{InterfaceManager, PluginRead, PluginWrite};
use nu_plugin_engine::{PluginInterfaceManager, PluginSource};
use nu_plugin_protocol::{PluginInput, PluginOutput};
use nu_protocol::{PluginIdentity, ShellError, shell_error::io::IoError};
use crate::fake_persistent_plugin::FakePersistentPlugin;
struct FakePluginRead<T>(mpsc::Receiver<T>);
struct FakePluginWrite<T>(mpsc::Sender<T>);
impl<T> PluginRead<T> for FakePluginRead<T> {
fn read(&mut self) -> Result<Option<T>, ShellError> {
Ok(self.0.recv().ok())
}
}
impl<T: Clone + Send> PluginWrite<T> for FakePluginWrite<T> {
fn write(&self, data: &T) -> Result<(), ShellError> {
self.0
.send(data.clone())
.map_err(|e| ShellError::GenericError {
error: "Error sending data".to_string(),
msg: e.to_string(),
span: None,
help: None,
inner: vec![],
})
}
fn flush(&self) -> Result<(), ShellError> {
Ok(())
}
}
fn fake_plugin_channel<T: Clone + Send>() -> (FakePluginRead<T>, FakePluginWrite<T>) {
let (tx, rx) = mpsc::channel();
(FakePluginRead(rx), FakePluginWrite(tx))
}
/// Spawn a plugin on another thread and return the registration
pub(crate) fn spawn_fake_plugin(
name: &str,
plugin: Arc<impl Plugin + Send + 'static>,
) -> Result<Arc<FakePersistentPlugin>, ShellError> {
let (input_read, input_write) = fake_plugin_channel::<PluginInput>();
let (output_read, output_write) = fake_plugin_channel::<PluginOutput>();
let identity = PluginIdentity::new_fake(name);
let reg_plugin = Arc::new(FakePersistentPlugin::new(identity.clone()));
let source = Arc::new(PluginSource::new(reg_plugin.clone()));
// The fake plugin has no process ID, and we also don't set the garbage collector
let mut manager = PluginInterfaceManager::new(source, None, input_write);
// Set up the persistent plugin with the interface before continuing
let interface = manager.get_interface();
interface.hello()?;
reg_plugin.initialize(interface);
// Start the interface reader on another thread
std::thread::Builder::new()
.name(format!("fake plugin interface reader ({name})"))
.spawn(move || manager.consume_all(output_read).expect("Plugin read error"))
.map_err(|err| {
IoError::new_internal(
err,
format!("Could not spawn fake plugin interface reader ({name})"),
nu_protocol::location!(),
)
})?;
// Start the plugin on another thread
let name_string = name.to_owned();
std::thread::Builder::new()
.name(format!("fake plugin runner ({name})"))
.spawn(move || {
nu_plugin::serve_plugin_io(
&*plugin,
&name_string,
move || input_read,
move || output_write,
)
.expect("Plugin runner error")
})
.map_err(|err| {
IoError::new_internal(
err,
format!("Could not spawn fake plugin runner ({name})"),
nu_protocol::location!(),
)
})?;
Ok(reg_plugin)
}