nushell/crates/nu-plugin-engine/src/process.rs
Devyn Cairns 0c4d5330ee
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`
2024-04-27 12:08:12 -05:00

91 lines
2.9 KiB
Rust

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(())
}
}