mirror of
https://github.com/nushell/nushell.git
synced 2025-08-09 02:35:00 +02:00
Plugin with evaluated call (#393)
* plugin trait * impl of trait * record and absolute path * plugin example crate * clippy error * correcting cargo * evaluated call for plugin
This commit is contained in:
@ -1,3 +1,5 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::{ast::Call, BlockId, Example, PipelineData, ShellError, Signature};
|
||||
|
||||
use super::{EngineState, Stack};
|
||||
@ -47,7 +49,7 @@ pub trait Command: Send + Sync + CommandClone {
|
||||
}
|
||||
|
||||
// Is a plugin command
|
||||
fn is_plugin(&self) -> Option<&str> {
|
||||
fn is_plugin(&self) -> Option<&PathBuf> {
|
||||
None
|
||||
}
|
||||
|
||||
|
@ -222,26 +222,27 @@ impl EngineState {
|
||||
if let Some(plugin_path) = &self.plugin_signatures {
|
||||
// Always creating the file which will erase previous signatures
|
||||
let mut plugin_file = std::fs::File::create(plugin_path.as_path())
|
||||
.map_err(|err| ShellError::PluginError(err.to_string()))?;
|
||||
.map_err(|err| ShellError::InternalError(err.to_string()))?;
|
||||
|
||||
// Plugin definitions with parsed signature
|
||||
for decl in self.plugin_decls() {
|
||||
// A successful plugin registration already includes the plugin filename
|
||||
// No need to check the None option
|
||||
let file_name = decl.is_plugin().expect("plugin should have file name");
|
||||
let path = decl.is_plugin().expect("plugin should have file name");
|
||||
let file_name = path.to_str().expect("path should be a str");
|
||||
|
||||
let line = serde_json::to_string_pretty(&decl.signature())
|
||||
.map(|signature| format!("register {} {}\n", file_name, signature))
|
||||
.map_err(|err| ShellError::PluginError(err.to_string()))?;
|
||||
.map_err(|err| ShellError::InternalError(err.to_string()))?;
|
||||
|
||||
plugin_file
|
||||
.write_all(line.as_bytes())
|
||||
.map_err(|err| ShellError::PluginError(err.to_string()))?;
|
||||
.map_err(|err| ShellError::InternalError(err.to_string()))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ShellError::PluginError("Plugin file not found".into()))
|
||||
Err(ShellError::InternalError("Plugin file not found".into()))
|
||||
}
|
||||
}
|
||||
|
||||
@ -510,11 +511,13 @@ pub struct StateDelta {
|
||||
pub(crate) file_contents: Vec<(Vec<u8>, usize, usize)>,
|
||||
vars: Vec<Type>, // indexed by VarId
|
||||
decls: Vec<Box<dyn Command>>, // indexed by DeclId
|
||||
#[cfg(feature = "plugin")]
|
||||
plugin_decls: Vec<Box<dyn Command>>, // indexed by DeclId
|
||||
blocks: Vec<Block>, // indexed by BlockId
|
||||
overlays: Vec<Overlay>, // indexed by OverlayId
|
||||
pub scope: Vec<ScopeFrame>,
|
||||
#[cfg(feature = "plugin")]
|
||||
pub plugin_signatures: Vec<(PathBuf, Option<Signature>)>,
|
||||
#[cfg(feature = "plugin")]
|
||||
plugin_decls: Vec<Box<dyn Command>>,
|
||||
}
|
||||
|
||||
impl StateDelta {
|
||||
@ -551,11 +554,13 @@ impl<'a> StateWorkingSet<'a> {
|
||||
file_contents: vec![],
|
||||
vars: vec![],
|
||||
decls: vec![],
|
||||
#[cfg(feature = "plugin")]
|
||||
plugin_decls: vec![],
|
||||
blocks: vec![],
|
||||
overlays: vec![],
|
||||
scope: vec![ScopeFrame::new()],
|
||||
#[cfg(feature = "plugin")]
|
||||
plugin_signatures: vec![],
|
||||
#[cfg(feature = "plugin")]
|
||||
plugin_decls: vec![],
|
||||
},
|
||||
permanent_state,
|
||||
}
|
||||
@ -624,8 +629,20 @@ impl<'a> StateWorkingSet<'a> {
|
||||
}
|
||||
|
||||
#[cfg(feature = "plugin")]
|
||||
pub fn add_plugin_decl(&mut self, decl: Box<dyn Command>) {
|
||||
self.delta.plugin_decls.push(decl);
|
||||
pub fn add_plugin_decls(&mut self, decls: Vec<Box<dyn Command>>) {
|
||||
for decl in decls {
|
||||
self.delta.plugin_decls.push(decl);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "plugin")]
|
||||
pub fn add_plugin_signature(&mut self, path: PathBuf, signature: Option<Signature>) {
|
||||
self.delta.plugin_signatures.push((path, signature));
|
||||
}
|
||||
|
||||
#[cfg(feature = "plugin")]
|
||||
pub fn get_signatures(&self) -> impl Iterator<Item = &(PathBuf, Option<Signature>)> {
|
||||
self.delta.plugin_signatures.iter()
|
||||
}
|
||||
|
||||
pub fn merge_predecl(&mut self, name: &[u8]) -> Option<DeclId> {
|
||||
|
@ -200,9 +200,6 @@ pub enum ShellError {
|
||||
#[error("No file to be copied")]
|
||||
NoFileToBeCopied(),
|
||||
|
||||
#[error("Plugin error")]
|
||||
PluginError(String),
|
||||
|
||||
#[error("Name not found")]
|
||||
#[diagnostic(code(nu::shell::name_not_found), url(docsrs))]
|
||||
DidYouMean(String, #[label("did you mean '{0}'?")] Span),
|
||||
|
339
crates/nu-protocol/src/value/from_value.rs
Normal file
339
crates/nu-protocol/src/value/from_value.rs
Normal file
@ -0,0 +1,339 @@
|
||||
// use std::path::PathBuf;
|
||||
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
|
||||
use chrono::{DateTime, FixedOffset};
|
||||
// use nu_path::expand_path;
|
||||
use crate::ast::{CellPath, PathMember};
|
||||
use crate::ShellError;
|
||||
use crate::{Range, Spanned, Value};
|
||||
|
||||
pub trait FromValue: Sized {
|
||||
fn from_value(v: &Value) -> Result<Self, ShellError>;
|
||||
}
|
||||
|
||||
impl FromValue for Value {
|
||||
fn from_value(v: &Value) -> Result<Self, ShellError> {
|
||||
Ok(v.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl FromValue for Spanned<i64> {
|
||||
fn from_value(v: &Value) -> Result<Self, ShellError> {
|
||||
match v {
|
||||
Value::Int { val, span } => Ok(Spanned {
|
||||
item: *val,
|
||||
span: *span,
|
||||
}),
|
||||
Value::Filesize { val, span } => Ok(Spanned {
|
||||
item: *val as i64,
|
||||
span: *span,
|
||||
}),
|
||||
Value::Duration { val, span } => Ok(Spanned {
|
||||
item: *val as i64,
|
||||
span: *span,
|
||||
}),
|
||||
|
||||
v => Err(ShellError::CantConvert(
|
||||
"integer".into(),
|
||||
v.get_type().to_string(),
|
||||
v.span()?,
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromValue for i64 {
|
||||
fn from_value(v: &Value) -> Result<Self, ShellError> {
|
||||
match v {
|
||||
Value::Int { val, .. } => Ok(*val),
|
||||
Value::Filesize { val, .. } => Ok(*val as i64),
|
||||
Value::Duration { val, .. } => Ok(*val as i64),
|
||||
|
||||
v => Err(ShellError::CantConvert(
|
||||
"integer".into(),
|
||||
v.get_type().to_string(),
|
||||
v.span()?,
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromValue for Spanned<f64> {
|
||||
fn from_value(v: &Value) -> Result<Self, ShellError> {
|
||||
match v {
|
||||
Value::Int { val, span } => Ok(Spanned {
|
||||
item: *val as f64,
|
||||
span: *span,
|
||||
}),
|
||||
Value::Float { val, span } => Ok(Spanned {
|
||||
item: *val,
|
||||
span: *span,
|
||||
}),
|
||||
|
||||
v => Err(ShellError::CantConvert(
|
||||
"float".into(),
|
||||
v.get_type().to_string(),
|
||||
v.span()?,
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromValue for f64 {
|
||||
fn from_value(v: &Value) -> Result<Self, ShellError> {
|
||||
match v {
|
||||
Value::Float { val, .. } => Ok(*val),
|
||||
Value::Int { val, .. } => Ok(*val as f64),
|
||||
v => Err(ShellError::CantConvert(
|
||||
"float".into(),
|
||||
v.get_type().to_string(),
|
||||
v.span()?,
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromValue for Spanned<usize> {
|
||||
fn from_value(v: &Value) -> Result<Self, ShellError> {
|
||||
match v {
|
||||
Value::Int { val, span } => Ok(Spanned {
|
||||
item: *val as usize,
|
||||
span: *span,
|
||||
}),
|
||||
Value::Filesize { val, span } => Ok(Spanned {
|
||||
item: *val as usize,
|
||||
span: *span,
|
||||
}),
|
||||
Value::Duration { val, span } => Ok(Spanned {
|
||||
item: *val as usize,
|
||||
span: *span,
|
||||
}),
|
||||
|
||||
v => Err(ShellError::CantConvert(
|
||||
"integer".into(),
|
||||
v.get_type().to_string(),
|
||||
v.span()?,
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromValue for usize {
|
||||
fn from_value(v: &Value) -> Result<Self, ShellError> {
|
||||
match v {
|
||||
Value::Int { val, .. } => Ok(*val as usize),
|
||||
Value::Filesize { val, .. } => Ok(*val as usize),
|
||||
Value::Duration { val, .. } => Ok(*val as usize),
|
||||
|
||||
v => Err(ShellError::CantConvert(
|
||||
"integer".into(),
|
||||
v.get_type().to_string(),
|
||||
v.span()?,
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromValue for String {
|
||||
fn from_value(v: &Value) -> Result<Self, ShellError> {
|
||||
// FIXME: we may want to fail a little nicer here
|
||||
match v {
|
||||
Value::CellPath { val, .. } => Ok(val.into_string()),
|
||||
Value::String { val, .. } => Ok(val.clone()),
|
||||
v => Err(ShellError::CantConvert(
|
||||
"string".into(),
|
||||
v.get_type().to_string(),
|
||||
v.span()?,
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromValue for Spanned<String> {
|
||||
fn from_value(v: &Value) -> Result<Self, ShellError> {
|
||||
Ok(Spanned {
|
||||
item: match v {
|
||||
Value::CellPath { val, .. } => val.into_string(),
|
||||
Value::String { val, .. } => val.clone(),
|
||||
v => {
|
||||
return Err(ShellError::CantConvert(
|
||||
"string".into(),
|
||||
v.get_type().to_string(),
|
||||
v.span()?,
|
||||
))
|
||||
}
|
||||
},
|
||||
span: v.span()?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl FromValue for Vec<String> {
|
||||
fn from_value(v: &Value) -> Result<Self, ShellError> {
|
||||
// FIXME: we may want to fail a little nicer here
|
||||
match v {
|
||||
Value::List { vals, .. } => vals
|
||||
.iter()
|
||||
.map(|val| match val {
|
||||
Value::String { val, .. } => Ok(val.clone()),
|
||||
c => Err(ShellError::CantConvert(
|
||||
"string".into(),
|
||||
c.get_type().to_string(),
|
||||
c.span()?,
|
||||
)),
|
||||
})
|
||||
.collect::<Result<Vec<String>, ShellError>>(),
|
||||
v => Err(ShellError::CantConvert(
|
||||
"string".into(),
|
||||
v.get_type().to_string(),
|
||||
v.span()?,
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromValue for CellPath {
|
||||
fn from_value(v: &Value) -> Result<Self, ShellError> {
|
||||
let span = v.span()?;
|
||||
match v {
|
||||
Value::CellPath { val, .. } => Ok(val.clone()),
|
||||
Value::String { val, .. } => Ok(CellPath {
|
||||
members: vec![PathMember::String {
|
||||
val: val.clone(),
|
||||
span,
|
||||
}],
|
||||
}),
|
||||
Value::Int { val, .. } => Ok(CellPath {
|
||||
members: vec![PathMember::Int {
|
||||
val: *val as usize,
|
||||
span,
|
||||
}],
|
||||
}),
|
||||
x => Err(ShellError::CantConvert(
|
||||
"cell path".into(),
|
||||
x.get_type().to_string(),
|
||||
span,
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromValue for bool {
|
||||
fn from_value(v: &Value) -> Result<Self, ShellError> {
|
||||
match v {
|
||||
Value::Bool { val, .. } => Ok(*val),
|
||||
v => Err(ShellError::CantConvert(
|
||||
"bool".into(),
|
||||
v.get_type().to_string(),
|
||||
v.span()?,
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromValue for Spanned<bool> {
|
||||
fn from_value(v: &Value) -> Result<Self, ShellError> {
|
||||
match v {
|
||||
Value::Bool { val, span } => Ok(Spanned {
|
||||
item: *val,
|
||||
span: *span,
|
||||
}),
|
||||
v => Err(ShellError::CantConvert(
|
||||
"bool".into(),
|
||||
v.get_type().to_string(),
|
||||
v.span()?,
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromValue for DateTime<FixedOffset> {
|
||||
fn from_value(v: &Value) -> Result<Self, ShellError> {
|
||||
match v {
|
||||
Value::Date { val, .. } => Ok(*val),
|
||||
v => Err(ShellError::CantConvert(
|
||||
"date".into(),
|
||||
v.get_type().to_string(),
|
||||
v.span()?,
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromValue for Spanned<DateTime<FixedOffset>> {
|
||||
fn from_value(v: &Value) -> Result<Self, ShellError> {
|
||||
match v {
|
||||
Value::Date { val, span } => Ok(Spanned {
|
||||
item: *val,
|
||||
span: *span,
|
||||
}),
|
||||
v => Err(ShellError::CantConvert(
|
||||
"date".into(),
|
||||
v.get_type().to_string(),
|
||||
v.span()?,
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromValue for Range {
|
||||
fn from_value(v: &Value) -> Result<Self, ShellError> {
|
||||
match v {
|
||||
Value::Range { val, .. } => Ok((**val).clone()),
|
||||
v => Err(ShellError::CantConvert(
|
||||
"range".into(),
|
||||
v.get_type().to_string(),
|
||||
v.span()?,
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromValue for Spanned<Range> {
|
||||
fn from_value(v: &Value) -> Result<Self, ShellError> {
|
||||
match v {
|
||||
Value::Range { val, span } => Ok(Spanned {
|
||||
item: (**val).clone(),
|
||||
span: *span,
|
||||
}),
|
||||
v => Err(ShellError::CantConvert(
|
||||
"range".into(),
|
||||
v.get_type().to_string(),
|
||||
v.span()?,
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromValue for Vec<u8> {
|
||||
fn from_value(v: &Value) -> Result<Self, ShellError> {
|
||||
match v {
|
||||
Value::Binary { val, .. } => Ok(val.clone()),
|
||||
Value::String { val, .. } => Ok(val.bytes().collect()),
|
||||
v => Err(ShellError::CantConvert(
|
||||
"binary data".into(),
|
||||
v.get_type().to_string(),
|
||||
v.span()?,
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromValue for Spanned<PathBuf> {
|
||||
fn from_value(v: &Value) -> Result<Self, ShellError> {
|
||||
match v {
|
||||
Value::String { val, span } => Ok(Spanned {
|
||||
item: PathBuf::from_str(val)
|
||||
.map_err(|err| ShellError::FileNotFoundCustom(err.to_string(), *span))?,
|
||||
span: *span,
|
||||
}),
|
||||
v => Err(ShellError::CantConvert(
|
||||
"range".into(),
|
||||
v.get_type().to_string(),
|
||||
v.span()?,
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
@ -1,11 +1,13 @@
|
||||
mod custom_value;
|
||||
mod from;
|
||||
mod from_value;
|
||||
mod range;
|
||||
mod stream;
|
||||
mod unit;
|
||||
|
||||
use chrono::{DateTime, FixedOffset};
|
||||
use chrono_humanize::HumanTime;
|
||||
pub use from_value::FromValue;
|
||||
use indexmap::map::IndexMap;
|
||||
pub use range::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
Reference in New Issue
Block a user