Slim down cli plugin logic.

This commit is contained in:
Andrés N. Robalino
2020-08-27 02:57:40 -05:00
parent 303a9defd3
commit 4724b3c570
10 changed files with 308 additions and 241 deletions

View File

@ -1,8 +1,5 @@
use crate::commands::classified::block::run_block;
use crate::commands::classified::maybe_text_codec::{MaybeTextCodec, StringOrBinary};
use crate::commands::plugin::JsonRpc;
use crate::commands::plugin::{PluginCommand, PluginSink};
use crate::commands::whole_stream_command;
use crate::context::Context;
use crate::git::current_branch;
use crate::path::canonicalize;
@ -12,117 +9,31 @@ use crate::EnvironmentSyncer;
use futures_codec::FramedRead;
use nu_errors::{ProximateShellError, ShellDiagnostic, ShellError};
use nu_protocol::hir::{ClassifiedCommand, Expression, InternalCommand, Literal, NamedArguments};
use nu_protocol::{Primitive, ReturnSuccess, Signature, UntaggedValue, Value};
#[allow(unused)]
use nu_source::Tagged;
use nu_protocol::{Primitive, ReturnSuccess, UntaggedValue, Value};
use log::{debug, trace};
use rustyline::config::{ColorMode, CompletionType, Config};
use rustyline::error::ReadlineError;
use rustyline::{self, config::Configurer, At, Cmd, Editor, KeyPress, Movement, Word};
use std::error::Error;
use std::io::{BufRead, BufReader, Write};
use std::iter::Iterator;
use std::path::{Path, PathBuf};
use std::process::{Child, Command, Stdio};
use std::sync::atomic::Ordering;
use rayon::prelude::*;
fn register_plugins(context: &mut Context) -> Result<(), ShellError> {
if let Ok(plugins) = crate::plugin::scan() {
context.add_commands(
plugins
.into_iter()
.filter(|p| !context.is_command_registered(p.name()))
.collect(),
);
}
fn load_plugin(path: &std::path::Path, context: &mut Context) -> Result<(), ShellError> {
let ext = path.extension();
let ps1_file = match ext {
Some(ext) => ext == "ps1",
None => false,
};
let mut child: Child = if ps1_file {
Command::new("pwsh")
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.args(&[
"-NoLogo",
"-NoProfile",
"-ExecutionPolicy",
"Bypass",
"-File",
&path.to_string_lossy(),
])
.spawn()
.expect("Failed to spawn PowerShell process")
} else {
Command::new(path)
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.spawn()
.expect("Failed to spawn child process")
};
let stdin = child.stdin.as_mut().expect("Failed to open stdin");
let stdout = child.stdout.as_mut().expect("Failed to open stdout");
let mut reader = BufReader::new(stdout);
let request = JsonRpc::new("config", Vec::<Value>::new());
let request_raw = serde_json::to_string(&request)?;
trace!(target: "nu::load", "plugin infrastructure config -> path {:#?}, request {:?}", &path, &request_raw);
stdin.write_all(format!("{}\n", request_raw).as_bytes())?;
let path = dunce::canonicalize(path)?;
let mut input = String::new();
let result = match reader.read_line(&mut input) {
Ok(count) => {
trace!(target: "nu::load", "plugin infrastructure -> config response for {:#?}", &path);
trace!(target: "nu::load", "plugin infrastructure -> processing response ({} bytes)", count);
trace!(target: "nu::load", "plugin infrastructure -> response: {}", input);
let response = serde_json::from_str::<JsonRpc<Result<Signature, ShellError>>>(&input);
match response {
Ok(jrpc) => match jrpc.params {
Ok(params) => {
let fname = path.to_string_lossy();
trace!(target: "nu::load", "plugin infrastructure -> processing {:?}", params);
let name = params.name.clone();
let fname = fname.to_string();
if context.get_command(&name).is_some() {
trace!(target: "nu::load", "plugin infrastructure -> {:?} already loaded.", &name);
} else if params.is_filter {
context.add_commands(vec![whole_stream_command(PluginCommand::new(
name, fname, params,
))]);
} else {
context.add_commands(vec![whole_stream_command(PluginSink::new(
name, fname, params,
))]);
}
Ok(())
}
Err(e) => Err(e),
},
Err(e) => {
trace!(target: "nu::load", "plugin infrastructure -> incompatible {:?}", input);
Err(ShellError::untagged_runtime_error(format!(
"Error: {:?}",
e
)))
}
}
}
Err(e) => Err(ShellError::untagged_runtime_error(format!(
"Error: {:?}",
e
))),
};
let _ = child.wait();
result
Ok(())
}
fn search_paths() -> Vec<std::path::PathBuf> {
pub fn search_paths() -> Vec<std::path::PathBuf> {
use std::env;
let mut search_paths = Vec::new();
@ -153,78 +64,6 @@ fn search_paths() -> Vec<std::path::PathBuf> {
search_paths
}
pub fn load_plugins(context: &mut Context) -> Result<(), ShellError> {
let opts = glob::MatchOptions {
case_sensitive: false,
require_literal_separator: false,
require_literal_leading_dot: false,
};
for path in search_paths() {
let mut pattern = path.to_path_buf();
pattern.push(std::path::Path::new("nu_plugin_[a-z0-9][a-z0-9]*"));
let plugs: Vec<_> = glob::glob_with(&pattern.to_string_lossy(), opts)?
.filter_map(|x| x.ok())
.collect();
let _failures: Vec<_> = plugs
.par_iter()
.map(|path| {
let bin_name = {
if let Some(name) = path.file_name() {
name.to_str().unwrap_or("")
} else {
""
}
};
// allow plugins with extensions on all platforms
let is_valid_name = {
bin_name
.chars()
.all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '.')
};
let is_executable = {
#[cfg(windows)]
{
bin_name.ends_with(".exe")
|| bin_name.ends_with(".bat")
|| bin_name.ends_with(".cmd")
|| bin_name.ends_with(".py")
|| bin_name.ends_with(".ps1")
}
#[cfg(not(windows))]
{
!bin_name.contains('.')
|| (bin_name.ends_with('.')
|| bin_name.ends_with(".py")
|| bin_name.ends_with(".rb")
|| bin_name.ends_with(".sh")
|| bin_name.ends_with(".bash")
|| bin_name.ends_with(".zsh")
|| bin_name.ends_with(".pl")
|| bin_name.ends_with(".awk")
|| bin_name.ends_with(".ps1"))
}
};
if is_valid_name && is_executable {
trace!(target: "nu::load", "plugin infrastructure -> Trying {:?}", path.display());
// we are ok if this plugin load fails
let _ = load_plugin(&path, &mut context.clone());
}
})
.collect();
}
Ok(())
}
pub struct History;
impl History {
@ -489,7 +328,7 @@ pub async fn run_vec_of_pipelines(
let mut syncer = crate::EnvironmentSyncer::new();
let mut context = create_default_context(&mut syncer, false)?;
let _ = crate::load_plugins(&mut context);
let _ = register_plugins(&mut context);
#[cfg(feature = "ctrlc")]
{
@ -755,7 +594,7 @@ pub async fn cli(
mut syncer: EnvironmentSyncer,
mut context: Context,
) -> Result<(), Box<dyn Error>> {
let _ = load_plugins(&mut context);
let _ = register_plugins(&mut context);
let (mut rl, config) = set_rustyline_configuration();