98 lines
3.2 KiB
Rust
98 lines
3.2 KiB
Rust
![]() |
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)
|
||
|
}
|