forked from extern/nushell
Core nu plugin load capability. (#2544)
We introduce the `plugin` nu sub command (`nu plugin`) with basic plugin loading support. We can choose to load plugins from a directory. Originally introduced to make integration tests faster (by not loading any plugins on startup at all) but `nu plugin --load some_path ; test_pipeline_that_uses_plugins_just_loaded` does not see it. Therefore, a `nu_with_plugins!` macro for tests was introduced on top of nu`s `--skip-plugins` switch executable which is set to true when running the integration tests that use the `nu!` macro now..
This commit is contained in:
committed by
GitHub
parent
e05e6b42fe
commit
0178b53289
@ -200,6 +200,28 @@ pub(crate) async fn run_internal_command(
|
||||
)]);
|
||||
InputStream::from_stream(futures::stream::iter(vec![]))
|
||||
}
|
||||
CommandAction::AddPlugins(path) => {
|
||||
match crate::plugin::scan(vec![std::path::PathBuf::from(path)]) {
|
||||
Ok(plugins) => {
|
||||
context.add_commands(
|
||||
plugins
|
||||
.into_iter()
|
||||
.filter(|p| {
|
||||
!context.is_command_registered(p.name())
|
||||
})
|
||||
.collect(),
|
||||
);
|
||||
|
||||
InputStream::from_stream(futures::stream::iter(vec![]))
|
||||
}
|
||||
Err(reason) => {
|
||||
context.error(reason.clone());
|
||||
InputStream::one(
|
||||
UntaggedValue::Error(reason).into_untagged_value(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
CommandAction::PreviousShell => {
|
||||
context.shell_manager.prev();
|
||||
InputStream::from_stream(futures::stream::iter(vec![]))
|
||||
|
3
crates/nu-cli/src/commands/nu/mod.rs
Normal file
3
crates/nu-cli/src/commands/nu/mod.rs
Normal file
@ -0,0 +1,3 @@
|
||||
mod plugin;
|
||||
|
||||
pub use plugin::SubCommand as NuPlugin;
|
124
crates/nu-cli/src/commands/nu/plugin.rs
Normal file
124
crates/nu-cli/src/commands/nu/plugin.rs
Normal file
@ -0,0 +1,124 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::context::CommandRegistry;
|
||||
use crate::path::canonicalize;
|
||||
use crate::prelude::*;
|
||||
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{CommandAction, ReturnSuccess, Signature, SyntaxShape, UntaggedValue};
|
||||
use nu_source::Tagged;
|
||||
|
||||
#[cfg(unix)]
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
|
||||
pub struct SubCommand;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct Arguments {
|
||||
#[serde(rename = "load")]
|
||||
pub load_path: Option<Tagged<PathBuf>>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"nu plugin"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("nu plugin").named(
|
||||
"load",
|
||||
SyntaxShape::Path,
|
||||
"a path to load the plugins from",
|
||||
Some('l'),
|
||||
)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Nu Plugin"
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Load all plugins in the current directory",
|
||||
example: "nu plugin --load .",
|
||||
result: Some(vec![]),
|
||||
}]
|
||||
}
|
||||
|
||||
async fn run(
|
||||
&self,
|
||||
args: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
let shell_manager = args.shell_manager.clone();
|
||||
let (Arguments { load_path }, _) = args.process(®istry).await?;
|
||||
|
||||
if let Some(Tagged {
|
||||
item: load_path,
|
||||
tag,
|
||||
}) = load_path
|
||||
{
|
||||
let path = canonicalize(shell_manager.path(), load_path).map_err(|_| {
|
||||
ShellError::labeled_error(
|
||||
"Cannot load plugins from directory",
|
||||
"directory not found",
|
||||
&tag,
|
||||
)
|
||||
})?;
|
||||
|
||||
if !path.is_dir() {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Cannot load plugins from directory",
|
||||
"is not a directory",
|
||||
&tag,
|
||||
));
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
{
|
||||
let has_exec = path
|
||||
.metadata()
|
||||
.map(|m| umask::Mode::from(m.permissions().mode()).has(umask::USER_READ))
|
||||
.map_err(|e| {
|
||||
ShellError::labeled_error(
|
||||
"Cannot load plugins from directory",
|
||||
format!("cannot stat ({})", e),
|
||||
&tag,
|
||||
)
|
||||
})?;
|
||||
|
||||
if !has_exec {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Cannot load plugins from directory",
|
||||
"permission denied",
|
||||
&tag,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(vec![ReturnSuccess::action(CommandAction::AddPlugins(
|
||||
path.to_string_lossy().to_string(),
|
||||
))]
|
||||
.into());
|
||||
}
|
||||
|
||||
Ok(OutputStream::one(ReturnSuccess::value(
|
||||
UntaggedValue::string(crate::commands::help::get_help(&SubCommand, ®istry))
|
||||
.into_value(Tag::unknown()),
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::SubCommand;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(SubCommand {})
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user