wip
This commit is contained in:
parent
d5e00c0d5d
commit
0da531a319
|
@ -24,6 +24,3 @@ pub use parser::{
|
||||||
is_math_expression_like, parse, parse_block, parse_expression, parse_external_call,
|
is_math_expression_like, parse, parse_block, parse_expression, parse_external_call,
|
||||||
parse_unit_value, trim_quotes, trim_quotes_str, unescape_unquote_string, DURATION_UNIT_GROUPS,
|
parse_unit_value, trim_quotes, trim_quotes_str, unescape_unquote_string, DURATION_UNIT_GROUPS,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "plugin")]
|
|
||||||
pub use parse_keywords::parse_register;
|
|
||||||
|
|
|
@ -82,7 +82,6 @@ pub const UNALIASABLE_PARSER_KEYWORDS: &[&[u8]] = &[
|
||||||
b"source-env",
|
b"source-env",
|
||||||
b"source",
|
b"source",
|
||||||
b"where",
|
b"where",
|
||||||
b"register",
|
|
||||||
b"plugin use",
|
b"plugin use",
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -3627,244 +3626,6 @@ pub fn parse_where(working_set: &mut StateWorkingSet, lite_command: &LiteCommand
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `register` is deprecated and will be removed in 0.94. Use `plugin add` and `plugin use` instead.
|
|
||||||
#[cfg(feature = "plugin")]
|
|
||||||
pub fn parse_register(working_set: &mut StateWorkingSet, lite_command: &LiteCommand) -> Pipeline {
|
|
||||||
use nu_plugin_engine::PluginDeclaration;
|
|
||||||
use nu_protocol::{
|
|
||||||
engine::Stack, ErrSpan, ParseWarning, PluginIdentity, PluginRegistryItem, PluginSignature,
|
|
||||||
RegisteredPlugin,
|
|
||||||
};
|
|
||||||
|
|
||||||
let spans = &lite_command.parts;
|
|
||||||
|
|
||||||
#[allow(deprecated)]
|
|
||||||
let cwd = working_set.get_cwd();
|
|
||||||
|
|
||||||
// Checking that the function is used with the correct name
|
|
||||||
// Maybe this is not necessary but it is a sanity check
|
|
||||||
if working_set.get_span_contents(spans[0]) != b"register" {
|
|
||||||
working_set.error(ParseError::UnknownState(
|
|
||||||
"internal error: Wrong call name for 'register' function".into(),
|
|
||||||
Span::concat(spans),
|
|
||||||
));
|
|
||||||
return garbage_pipeline(working_set, spans);
|
|
||||||
}
|
|
||||||
if let Some(redirection) = lite_command.redirection.as_ref() {
|
|
||||||
working_set.error(redirecting_builtin_error("register", redirection));
|
|
||||||
return garbage_pipeline(working_set, spans);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parsing the spans and checking that they match the register signature
|
|
||||||
// Using a parsed call makes more sense than checking for how many spans are in the call
|
|
||||||
// Also, by creating a call, it can be checked if it matches the declaration signature
|
|
||||||
let (call, call_span) = match working_set.find_decl(b"register") {
|
|
||||||
None => {
|
|
||||||
working_set.error(ParseError::UnknownState(
|
|
||||||
"internal error: Register declaration not found".into(),
|
|
||||||
Span::concat(spans),
|
|
||||||
));
|
|
||||||
return garbage_pipeline(working_set, spans);
|
|
||||||
}
|
|
||||||
Some(decl_id) => {
|
|
||||||
let ParsedInternalCall { call, output } =
|
|
||||||
parse_internal_call(working_set, spans[0], &spans[1..], decl_id);
|
|
||||||
let decl = working_set.get_decl(decl_id);
|
|
||||||
|
|
||||||
let call_span = Span::concat(spans);
|
|
||||||
|
|
||||||
let starting_error_count = working_set.parse_errors.len();
|
|
||||||
check_call(working_set, call_span, &decl.signature(), &call);
|
|
||||||
|
|
||||||
let Ok(is_help) = has_flag_const(working_set, &call, "help") else {
|
|
||||||
return garbage_pipeline(working_set, spans);
|
|
||||||
};
|
|
||||||
|
|
||||||
if starting_error_count != working_set.parse_errors.len() || is_help {
|
|
||||||
return Pipeline::from_vec(vec![Expression::new(
|
|
||||||
working_set,
|
|
||||||
Expr::Call(call),
|
|
||||||
call_span,
|
|
||||||
output,
|
|
||||||
)]);
|
|
||||||
}
|
|
||||||
|
|
||||||
(call, call_span)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Now that the call is parsed, add the deprecation warning
|
|
||||||
working_set
|
|
||||||
.parse_warnings
|
|
||||||
.push(ParseWarning::DeprecatedWarning {
|
|
||||||
old_command: "register".into(),
|
|
||||||
new_suggestion: "use `plugin add` and `plugin use`".into(),
|
|
||||||
span: call.head,
|
|
||||||
url: "https://www.nushell.sh/book/plugins.html".into(),
|
|
||||||
});
|
|
||||||
|
|
||||||
// Extracting the required arguments from the call and keeping them together in a tuple
|
|
||||||
let arguments = call
|
|
||||||
.positional_nth(0)
|
|
||||||
.map(|expr| {
|
|
||||||
let val =
|
|
||||||
eval_constant(working_set, expr).map_err(|err| err.wrap(working_set, call.head))?;
|
|
||||||
let filename = val
|
|
||||||
.coerce_into_string()
|
|
||||||
.map_err(|err| err.wrap(working_set, call.head))?;
|
|
||||||
|
|
||||||
let Some(path) = find_in_dirs(&filename, working_set, &cwd, Some(PLUGIN_DIRS_VAR))
|
|
||||||
else {
|
|
||||||
return Err(ParseError::RegisteredFileNotFound(filename, expr.span));
|
|
||||||
};
|
|
||||||
|
|
||||||
if path.exists() && path.is_file() {
|
|
||||||
Ok((path, expr.span))
|
|
||||||
} else {
|
|
||||||
Err(ParseError::RegisteredFileNotFound(filename, expr.span))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.expect("required positional has being checked");
|
|
||||||
|
|
||||||
// Signature is an optional value from the call and will be used to decide if
|
|
||||||
// the plugin is called to get the signatures or to use the given signature
|
|
||||||
let signature = call.positional_nth(1).map(|expr| {
|
|
||||||
let signature = working_set.get_span_contents(expr.span);
|
|
||||||
serde_json::from_slice::<PluginSignature>(signature).map_err(|e| {
|
|
||||||
ParseError::LabeledError(
|
|
||||||
"Signature deserialization error".into(),
|
|
||||||
format!("unable to deserialize signature: {e}"),
|
|
||||||
spans[0],
|
|
||||||
)
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
// Shell is another optional value used as base to call shell to plugins
|
|
||||||
let shell = call.get_flag_expr("shell").map(|expr| {
|
|
||||||
let shell_expr = working_set.get_span_contents(expr.span);
|
|
||||||
|
|
||||||
String::from_utf8(shell_expr.to_vec())
|
|
||||||
.map_err(|_| ParseError::NonUtf8(expr.span))
|
|
||||||
.and_then(|name| {
|
|
||||||
canonicalize_with(&name, cwd)
|
|
||||||
.map_err(|_| ParseError::RegisteredFileNotFound(name, expr.span))
|
|
||||||
})
|
|
||||||
.and_then(|path| {
|
|
||||||
if path.exists() & path.is_file() {
|
|
||||||
Ok(path)
|
|
||||||
} else {
|
|
||||||
Err(ParseError::RegisteredFileNotFound(
|
|
||||||
format!("{path:?}"),
|
|
||||||
expr.span,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
let shell = match shell {
|
|
||||||
None => None,
|
|
||||||
Some(path) => match path {
|
|
||||||
Ok(path) => Some(path),
|
|
||||||
Err(err) => {
|
|
||||||
working_set.error(err);
|
|
||||||
return Pipeline::from_vec(vec![Expression::new(
|
|
||||||
working_set,
|
|
||||||
Expr::Call(call),
|
|
||||||
call_span,
|
|
||||||
Type::Any,
|
|
||||||
)]);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
// We need the current environment variables for `python` based plugins
|
|
||||||
// Or we'll likely have a problem when a plugin is implemented in a virtual Python environment.
|
|
||||||
let get_envs = || {
|
|
||||||
let stack = Stack::new().capture();
|
|
||||||
nu_engine::env::env_to_strings(working_set.permanent_state, &stack)
|
|
||||||
};
|
|
||||||
|
|
||||||
let error = arguments.and_then(|(path, path_span)| {
|
|
||||||
let path = path.path_buf();
|
|
||||||
|
|
||||||
// Create the plugin identity. This validates that the plugin name starts with `nu_plugin_`
|
|
||||||
let identity = PluginIdentity::new(path, shell).err_span(path_span)?;
|
|
||||||
|
|
||||||
let plugin = nu_plugin_engine::add_plugin_to_working_set(working_set, &identity)
|
|
||||||
.map_err(|err| err.wrap(working_set, call.head))?;
|
|
||||||
|
|
||||||
let signatures = signature.map_or_else(
|
|
||||||
|| {
|
|
||||||
// It's important that the plugin is restarted if we're going to get signatures
|
|
||||||
//
|
|
||||||
// The user would expect that `register` would always run the binary to get new
|
|
||||||
// signatures, in case it was replaced with an updated binary
|
|
||||||
plugin.reset().map_err(|err| {
|
|
||||||
ParseError::LabeledError(
|
|
||||||
"Failed to restart plugin to get new signatures".into(),
|
|
||||||
err.to_string(),
|
|
||||||
spans[0],
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let metadata_and_signatures = plugin
|
|
||||||
.clone()
|
|
||||||
.get(get_envs)
|
|
||||||
.and_then(|p| {
|
|
||||||
let meta = p.get_metadata()?;
|
|
||||||
let sigs = p.get_signature()?;
|
|
||||||
Ok((meta, sigs))
|
|
||||||
})
|
|
||||||
.map_err(|err| {
|
|
||||||
log::warn!("Error getting metadata and signatures: {err:?}");
|
|
||||||
ParseError::LabeledError(
|
|
||||||
"Error getting metadata and signatures".into(),
|
|
||||||
err.to_string(),
|
|
||||||
spans[0],
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
match metadata_and_signatures {
|
|
||||||
Ok((meta, sigs)) => {
|
|
||||||
// Set the metadata on the plugin
|
|
||||||
plugin.set_metadata(Some(meta.clone()));
|
|
||||||
// Add the loaded plugin to the delta
|
|
||||||
working_set.update_plugin_registry(PluginRegistryItem::new(
|
|
||||||
&identity,
|
|
||||||
meta,
|
|
||||||
sigs.clone(),
|
|
||||||
));
|
|
||||||
Ok(sigs)
|
|
||||||
}
|
|
||||||
Err(err) => Err(err),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|sig| sig.map(|sig| vec![sig]),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
for signature in signatures {
|
|
||||||
// create plugin command declaration (need struct impl Command)
|
|
||||||
// store declaration in working set
|
|
||||||
let plugin_decl = PluginDeclaration::new(plugin.clone(), signature);
|
|
||||||
|
|
||||||
working_set.add_decl(Box::new(plugin_decl));
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
});
|
|
||||||
|
|
||||||
if let Err(err) = error {
|
|
||||||
working_set.error(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
Pipeline::from_vec(vec![Expression::new(
|
|
||||||
working_set,
|
|
||||||
Expr::Call(call),
|
|
||||||
call_span,
|
|
||||||
Type::Nothing,
|
|
||||||
)])
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "plugin")]
|
#[cfg(feature = "plugin")]
|
||||||
pub fn parse_plugin_use(working_set: &mut StateWorkingSet, call: Box<Call>) -> Pipeline {
|
pub fn parse_plugin_use(working_set: &mut StateWorkingSet, call: Box<Call>) -> Pipeline {
|
||||||
use nu_protocol::{FromValue, PluginRegistryFile};
|
use nu_protocol::{FromValue, PluginRegistryFile};
|
||||||
|
|
|
@ -5419,8 +5419,6 @@ pub fn parse_builtin_commands(
|
||||||
b"export" => parse_export_in_block(working_set, lite_command),
|
b"export" => parse_export_in_block(working_set, lite_command),
|
||||||
b"hide" => parse_hide(working_set, lite_command),
|
b"hide" => parse_hide(working_set, lite_command),
|
||||||
b"where" => parse_where(working_set, lite_command),
|
b"where" => parse_where(working_set, lite_command),
|
||||||
#[cfg(feature = "plugin")]
|
|
||||||
b"register" => parse_register(working_set, lite_command),
|
|
||||||
// Only "plugin use" is a keyword
|
// Only "plugin use" is a keyword
|
||||||
#[cfg(feature = "plugin")]
|
#[cfg(feature = "plugin")]
|
||||||
b"plugin"
|
b"plugin"
|
||||||
|
|
Loading…
Reference in New Issue
Block a user