mirror of
https://github.com/nushell/nushell.git
synced 2025-08-20 10:38:04 +02:00
Split the plugin crate (#12563)
# 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`
This commit is contained in:
90
crates/nu-plugin-engine/src/process.rs
Normal file
90
crates/nu-plugin-engine/src/process.rs
Normal file
@@ -0,0 +1,90 @@
|
||||
use std::sync::{atomic::AtomicU32, Arc, Mutex, MutexGuard};
|
||||
|
||||
use nu_protocol::{ShellError, Span};
|
||||
use nu_system::ForegroundGuard;
|
||||
|
||||
/// Provides a utility interface for a plugin interface to manage the process the plugin is running
|
||||
/// in.
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct PluginProcess {
|
||||
pid: u32,
|
||||
mutable: Mutex<MutablePart>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct MutablePart {
|
||||
foreground_guard: Option<ForegroundGuard>,
|
||||
}
|
||||
|
||||
impl PluginProcess {
|
||||
/// Manage a plugin process.
|
||||
pub(crate) fn new(pid: u32) -> PluginProcess {
|
||||
PluginProcess {
|
||||
pid,
|
||||
mutable: Mutex::new(MutablePart {
|
||||
foreground_guard: None,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
/// The process ID of the plugin.
|
||||
pub(crate) fn pid(&self) -> u32 {
|
||||
self.pid
|
||||
}
|
||||
|
||||
fn lock_mutable(&self) -> Result<MutexGuard<MutablePart>, ShellError> {
|
||||
self.mutable.lock().map_err(|_| ShellError::NushellFailed {
|
||||
msg: "the PluginProcess mutable lock has been poisoned".into(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Move the plugin process to the foreground. See [`ForegroundGuard::new`].
|
||||
///
|
||||
/// This produces an error if the plugin process was already in the foreground.
|
||||
///
|
||||
/// Returns `Some()` on Unix with the process group ID if the plugin process will need to join
|
||||
/// another process group to be part of the foreground.
|
||||
pub(crate) fn enter_foreground(
|
||||
&self,
|
||||
span: Span,
|
||||
pipeline_state: &Arc<(AtomicU32, AtomicU32)>,
|
||||
) -> Result<Option<u32>, ShellError> {
|
||||
let pid = self.pid;
|
||||
let mut mutable = self.lock_mutable()?;
|
||||
if mutable.foreground_guard.is_none() {
|
||||
let guard = ForegroundGuard::new(pid, pipeline_state).map_err(|err| {
|
||||
ShellError::GenericError {
|
||||
error: "Failed to enter foreground".into(),
|
||||
msg: err.to_string(),
|
||||
span: Some(span),
|
||||
help: None,
|
||||
inner: vec![],
|
||||
}
|
||||
})?;
|
||||
let pgrp = guard.pgrp();
|
||||
mutable.foreground_guard = Some(guard);
|
||||
Ok(pgrp)
|
||||
} else {
|
||||
Err(ShellError::GenericError {
|
||||
error: "Can't enter foreground".into(),
|
||||
msg: "this plugin is already running in the foreground".into(),
|
||||
span: Some(span),
|
||||
help: Some(
|
||||
"you may be trying to run the command in parallel, or this may be a bug in \
|
||||
the plugin"
|
||||
.into(),
|
||||
),
|
||||
inner: vec![],
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Move the plugin process out of the foreground. See [`ForegroundGuard::reset`].
|
||||
///
|
||||
/// This is a no-op if the plugin process was already in the background.
|
||||
pub(crate) fn exit_foreground(&self) -> Result<(), ShellError> {
|
||||
let mut mutable = self.lock_mutable()?;
|
||||
drop(mutable.foreground_guard.take());
|
||||
Ok(())
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user