forked from extern/nushell
Support for all custom value operations on plugin custom values (#12088)
# Description Adds support for the following operations on plugin custom values, in addition to `to_base_value` which was already present: - `follow_path_int()` - `follow_path_string()` - `partial_cmp()` - `operation()` - `Drop` (notification, if opted into with `CustomValue::notify_plugin_on_drop`) There are additionally customizable methods within the `Plugin` and `StreamingPlugin` traits for implementing these functions in a way that requires access to the plugin state, as a registered handle model such as might be used in a dataframes plugin would. `Value::append` was also changed to handle custom values correctly. # User-Facing Changes - Signature of `CustomValue::follow_path_string` and `CustomValue::follow_path_int` changed to give access to the span of the custom value itself, useful for some errors. - Plugins using custom values have to be recompiled because the engine will try to do custom value operations that aren't supported - Plugins can do more things 🎉 # Tests + Formatting Tests were added for all of the new custom values functionality. - 🟢 `toolkit fmt` - 🟢 `toolkit clippy` - 🟢 `toolkit test` - 🟢 `toolkit test stdlib` # After Submitting - [ ] Document protocol reference `CustomValueOp` variants: - [ ] `FollowPathInt` - [ ] `FollowPathString` - [ ] `PartialCmp` - [ ] `Operation` - [ ] `Dropped` - [ ] Document `notify_on_drop` optional field in `PluginCustomValue`
This commit is contained in:
@ -26,18 +26,30 @@ pub trait CustomValue: fmt::Debug + Send + Sync {
|
||||
fn as_any(&self) -> &dyn std::any::Any;
|
||||
|
||||
/// Follow cell path by numeric index (e.g. rows)
|
||||
fn follow_path_int(&self, _count: usize, span: Span) -> Result<Value, ShellError> {
|
||||
fn follow_path_int(
|
||||
&self,
|
||||
self_span: Span,
|
||||
index: usize,
|
||||
path_span: Span,
|
||||
) -> Result<Value, ShellError> {
|
||||
let _ = (self_span, index);
|
||||
Err(ShellError::IncompatiblePathAccess {
|
||||
type_name: self.value_string(),
|
||||
span,
|
||||
span: path_span,
|
||||
})
|
||||
}
|
||||
|
||||
/// Follow cell path by string key (e.g. columns)
|
||||
fn follow_path_string(&self, _column_name: String, span: Span) -> Result<Value, ShellError> {
|
||||
fn follow_path_string(
|
||||
&self,
|
||||
self_span: Span,
|
||||
column_name: String,
|
||||
path_span: Span,
|
||||
) -> Result<Value, ShellError> {
|
||||
let _ = (self_span, column_name);
|
||||
Err(ShellError::IncompatiblePathAccess {
|
||||
type_name: self.value_string(),
|
||||
span,
|
||||
span: path_span,
|
||||
})
|
||||
}
|
||||
|
||||
@ -54,11 +66,23 @@ pub trait CustomValue: fmt::Debug + Send + Sync {
|
||||
/// Default impl raises [`ShellError::UnsupportedOperator`].
|
||||
fn operation(
|
||||
&self,
|
||||
_lhs_span: Span,
|
||||
lhs_span: Span,
|
||||
operator: Operator,
|
||||
op: Span,
|
||||
_right: &Value,
|
||||
right: &Value,
|
||||
) -> Result<Value, ShellError> {
|
||||
let _ = (lhs_span, right);
|
||||
Err(ShellError::UnsupportedOperator { operator, span: op })
|
||||
}
|
||||
|
||||
/// For custom values in plugins: return `true` here if you would like to be notified when all
|
||||
/// copies of this custom value are dropped in the engine.
|
||||
///
|
||||
/// The notification will take place via
|
||||
/// [`.custom_value_dropped()`](crate::StreamingPlugin::custom_value_dropped) on the plugin.
|
||||
///
|
||||
/// The default is `false`.
|
||||
fn notify_plugin_on_drop(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
@ -1106,18 +1106,19 @@ impl Value {
|
||||
});
|
||||
}
|
||||
}
|
||||
Value::CustomValue { val, .. } => {
|
||||
current = match val.follow_path_int(*count, *origin_span) {
|
||||
Ok(val) => val,
|
||||
Err(err) => {
|
||||
if *optional {
|
||||
return Ok(Value::nothing(*origin_span));
|
||||
// short-circuit
|
||||
} else {
|
||||
return Err(err);
|
||||
Value::CustomValue { ref val, .. } => {
|
||||
current =
|
||||
match val.follow_path_int(current.span(), *count, *origin_span) {
|
||||
Ok(val) => val,
|
||||
Err(err) => {
|
||||
if *optional {
|
||||
return Ok(Value::nothing(*origin_span));
|
||||
// short-circuit
|
||||
} else {
|
||||
return Err(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
Value::Nothing { .. } if *optional => {
|
||||
return Ok(Value::nothing(*origin_span)); // short-circuit
|
||||
@ -1249,8 +1250,22 @@ impl Value {
|
||||
|
||||
current = Value::list(list, span);
|
||||
}
|
||||
Value::CustomValue { val, .. } => {
|
||||
current = val.follow_path_string(column_name.clone(), *origin_span)?;
|
||||
Value::CustomValue { ref val, .. } => {
|
||||
current = match val.follow_path_string(
|
||||
current.span(),
|
||||
column_name.clone(),
|
||||
*origin_span,
|
||||
) {
|
||||
Ok(val) => val,
|
||||
Err(err) => {
|
||||
if *optional {
|
||||
return Ok(Value::nothing(*origin_span));
|
||||
// short-circuit
|
||||
} else {
|
||||
return Err(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Value::Nothing { .. } if *optional => {
|
||||
return Ok(Value::nothing(*origin_span)); // short-circuit
|
||||
@ -2652,6 +2667,9 @@ impl Value {
|
||||
val.extend(rhs);
|
||||
Ok(Value::binary(val, span))
|
||||
}
|
||||
(Value::CustomValue { val: lhs, .. }, rhs) => {
|
||||
lhs.operation(self.span(), Operator::Math(Math::Append), op, rhs)
|
||||
}
|
||||
_ => Err(ShellError::OperatorMismatch {
|
||||
op_span: op,
|
||||
lhs_ty: self.get_type().to_string(),
|
||||
|
Reference in New Issue
Block a user