mirror of
https://github.com/nushell/nushell.git
synced 2024-11-29 03:44:19 +01:00
0c4d5330ee
# Description This breaks `nu-plugin` up into four crates: - `nu-plugin-protocol`: just the type definitions for the protocol, no I/O. If someone wanted to wire up something more bare metal, maybe for async I/O, they could use this. - `nu-plugin-core`: the shared stuff between engine/plugin. Less stable interface. - `nu-plugin-engine`: everything required for the engine to talk to plugins. Less stable interface. - `nu-plugin`: everything required for the plugin to talk to the engine, what plugin developers use. Should be the most stable interface. No changes are made to the interface exposed by `nu-plugin` - it should all still be there. Re-exports from `nu-plugin-protocol` or `nu-plugin-core` are used as required. Plugins shouldn't ever have to use those crates directly. This should be somewhat faster to compile as `nu-plugin-engine` and `nu-plugin` can compile in parallel, and the engine doesn't need `nu-plugin` and plugins don't need `nu-plugin-engine` (except for test support), so that should reduce what needs to be compiled too. The only significant change here other than splitting stuff up was to break the `source` out of `PluginCustomValue` and create a new `PluginCustomValueWithSource` type that contains that instead. One bonus of that is we get rid of the option and it's now more type-safe, but it also means that the logic for that stuff (actually running the plugin for custom value ops) can live entirely within the `nu-plugin-engine` crate. # User-Facing Changes - New crates. - Added `local-socket` feature for `nu` to try to make it possible to compile without that support if needed. # Tests + Formatting - 🟢 `toolkit fmt` - 🟢 `toolkit clippy` - 🟢 `toolkit test` - 🟢 `toolkit test stdlib`
80 lines
2.6 KiB
Rust
80 lines
2.6 KiB
Rust
use std::sync::{mpsc, Arc};
|
|
|
|
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};
|
|
|
|
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(|err| ShellError::IOError {
|
|
msg: err.to_string(),
|
|
})
|
|
}
|
|
|
|
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"))?;
|
|
|
|
// 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")
|
|
})?;
|
|
|
|
Ok(reg_plugin)
|
|
}
|