2024-03-12 10:37:08 +01:00
|
|
|
use nu_protocol::{ast, CustomValue, ShellError, Span, Value};
|
2022-07-25 18:32:56 +02:00
|
|
|
use serde::{Deserialize, Serialize};
|
Add `command_prelude` module (#12291)
# Description
When implementing a `Command`, one must also import all the types
present in the function signatures for `Command`. This makes it so that
we often import the same set of types in each command implementation
file. E.g., something like this:
```rust
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
record, Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData,
ShellError, Signature, Span, Type, Value,
};
```
This PR adds the `nu_engine::command_prelude` module which contains the
necessary and commonly used types to implement a `Command`:
```rust
// command_prelude.rs
pub use crate::CallExt;
pub use nu_protocol::{
ast::{Call, CellPath},
engine::{Command, EngineState, Stack},
record, Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, IntoSpanned,
PipelineData, Record, ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value,
};
```
This should reduce the boilerplate needed to implement a command and
also gives us a place to track the breadth of the `Command` API. I tried
to be conservative with what went into the prelude modules, since it
might be hard/annoying to remove items from the prelude in the future.
Let me know if something should be included or excluded.
2024-03-26 22:17:30 +01:00
|
|
|
use std::cmp::Ordering;
|
2022-07-25 18:32:56 +02:00
|
|
|
|
2024-03-12 10:37:08 +01:00
|
|
|
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
|
2022-07-25 18:32:56 +02:00
|
|
|
pub struct CoolCustomValue {
|
|
|
|
pub(crate) cool: String,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl CoolCustomValue {
|
|
|
|
pub fn new(content: &str) -> Self {
|
|
|
|
Self {
|
|
|
|
cool: content.to_owned(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn into_value(self, span: Span) -> Value {
|
Rename `Value::CustomValue` to `Value::Custom` (#12309)
# Description
The second `Value` is redundant and will consume five extra bytes on
each transmission of a custom value to/from a plugin.
# User-Facing Changes
This is a breaking change to the plugin protocol.
The [example in the protocol
reference](https://www.nushell.sh/contributor-book/plugin_protocol_reference.html#value)
becomes
```json
{
"Custom": {
"val": {
"type": "PluginCustomValue",
"name": "database",
"data": [36, 190, 127, 40, 12, 3, 46, 83],
"notify_on_drop": true
},
"span": {
"start": 320,
"end": 340
}
}
}
```
instead of
```json
{
"CustomValue": {
...
}
}
```
# After Submitting
Update plugin protocol reference
2024-03-27 22:10:56 +01:00
|
|
|
Value::custom(Box::new(self), span)
|
2022-07-25 18:32:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn try_from_value(value: &Value) -> Result<Self, ShellError> {
|
2023-09-03 16:27:29 +02:00
|
|
|
let span = value.span();
|
2022-07-25 18:32:56 +02:00
|
|
|
match value {
|
Rename `Value::CustomValue` to `Value::Custom` (#12309)
# Description
The second `Value` is redundant and will consume five extra bytes on
each transmission of a custom value to/from a plugin.
# User-Facing Changes
This is a breaking change to the plugin protocol.
The [example in the protocol
reference](https://www.nushell.sh/contributor-book/plugin_protocol_reference.html#value)
becomes
```json
{
"Custom": {
"val": {
"type": "PluginCustomValue",
"name": "database",
"data": [36, 190, 127, 40, 12, 3, 46, 83],
"notify_on_drop": true
},
"span": {
"start": 320,
"end": 340
}
}
}
```
instead of
```json
{
"CustomValue": {
...
}
}
```
# After Submitting
Update plugin protocol reference
2024-03-27 22:10:56 +01:00
|
|
|
Value::Custom { val, .. } => {
|
2023-01-24 12:23:42 +01:00
|
|
|
if let Some(cool) = val.as_any().downcast_ref::<Self>() {
|
|
|
|
Ok(cool.clone())
|
|
|
|
} else {
|
2023-03-06 18:33:09 +01:00
|
|
|
Err(ShellError::CantConvert {
|
|
|
|
to_type: "cool".into(),
|
|
|
|
from_type: "non-cool".into(),
|
2023-09-03 16:27:29 +02:00
|
|
|
span,
|
2023-03-06 18:33:09 +01:00
|
|
|
help: None,
|
|
|
|
})
|
2023-01-24 12:23:42 +01:00
|
|
|
}
|
|
|
|
}
|
2023-03-06 18:33:09 +01:00
|
|
|
x => Err(ShellError::CantConvert {
|
|
|
|
to_type: "cool".into(),
|
|
|
|
from_type: x.get_type().to_string(),
|
2023-09-03 16:27:29 +02:00
|
|
|
span,
|
2023-03-06 18:33:09 +01:00
|
|
|
help: None,
|
|
|
|
}),
|
2022-07-25 18:32:56 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[typetag::serde]
|
|
|
|
impl CustomValue for CoolCustomValue {
|
2024-03-12 10:37:08 +01:00
|
|
|
fn clone_value(&self, span: Span) -> Value {
|
Rename `Value::CustomValue` to `Value::Custom` (#12309)
# Description
The second `Value` is redundant and will consume five extra bytes on
each transmission of a custom value to/from a plugin.
# User-Facing Changes
This is a breaking change to the plugin protocol.
The [example in the protocol
reference](https://www.nushell.sh/contributor-book/plugin_protocol_reference.html#value)
becomes
```json
{
"Custom": {
"val": {
"type": "PluginCustomValue",
"name": "database",
"data": [36, 190, 127, 40, 12, 3, 46, 83],
"notify_on_drop": true
},
"span": {
"start": 320,
"end": 340
}
}
}
```
instead of
```json
{
"CustomValue": {
...
}
}
```
# After Submitting
Update plugin protocol reference
2024-03-27 22:10:56 +01:00
|
|
|
Value::custom(Box::new(self.clone()), span)
|
2022-07-25 18:32:56 +02:00
|
|
|
}
|
|
|
|
|
2024-03-19 11:09:59 +01:00
|
|
|
fn type_name(&self) -> String {
|
2022-07-25 18:32:56 +02:00
|
|
|
self.typetag_name().to_string()
|
|
|
|
}
|
|
|
|
|
2024-03-12 10:37:08 +01:00
|
|
|
fn to_base_value(&self, span: Span) -> Result<Value, ShellError> {
|
2023-09-03 16:27:29 +02:00
|
|
|
Ok(Value::string(
|
|
|
|
format!("I used to be a custom value! My data was ({})", self.cool),
|
2022-07-25 18:32:56 +02:00
|
|
|
span,
|
2023-09-03 16:27:29 +02:00
|
|
|
))
|
2022-07-25 18:32:56 +02:00
|
|
|
}
|
|
|
|
|
2024-03-12 10:37:08 +01:00
|
|
|
fn follow_path_int(
|
|
|
|
&self,
|
|
|
|
_self_span: Span,
|
|
|
|
index: usize,
|
|
|
|
path_span: Span,
|
|
|
|
) -> Result<Value, ShellError> {
|
|
|
|
if index == 0 {
|
|
|
|
Ok(Value::string(&self.cool, path_span))
|
|
|
|
} else {
|
|
|
|
Err(ShellError::AccessBeyondEnd {
|
|
|
|
max_idx: 0,
|
|
|
|
span: path_span,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn follow_path_string(
|
|
|
|
&self,
|
|
|
|
self_span: Span,
|
|
|
|
column_name: String,
|
|
|
|
path_span: Span,
|
|
|
|
) -> Result<Value, ShellError> {
|
|
|
|
if column_name == "cool" {
|
|
|
|
Ok(Value::string(&self.cool, path_span))
|
|
|
|
} else {
|
|
|
|
Err(ShellError::CantFindColumn {
|
|
|
|
col_name: column_name,
|
Add derive macros for `FromValue` and `IntoValue` to ease the use of `Value`s in Rust code (#13031)
# Description
After discussing with @sholderbach the cumbersome usage of
`nu_protocol::Value` in Rust, I created a derive macro to simplify it.
I’ve added a new crate called `nu-derive-value`, which includes two
macros, `IntoValue` and `FromValue`. These are re-exported in
`nu-protocol` and should be encouraged to be used via that re-export.
The macros ensure that all types can easily convert from and into
`Value`. For example, as a plugin author, you can define your plugin
configuration using a Rust struct and easily convert it using
`FromValue`. This makes plugin configuration less of a hassle.
I introduced the `IntoValue` trait for a standardized approach to
converting values into `Value` (and a fallible variant `TryIntoValue`).
This trait could potentially replace existing `into_value` methods.
Along with this, I've implemented `FromValue` for several standard types
and refined other implementations to use blanket implementations where
applicable.
I made these design choices with input from @devyn.
There are more improvements possible, but this is a solid start and the
PR is already quite substantial.
# User-Facing Changes
For `nu-protocol` users, these changes simplify the handling of
`Value`s. There are no changes for end-users of nushell itself.
# Tests + Formatting
Documenting the macros itself is not really possible, as they cannot
really reference any other types since they are the root of the
dependency graph. The standard library has the same problem
([std::Debug](https://doc.rust-lang.org/stable/std/fmt/derive.Debug.html)).
However I documented the `FromValue` and `IntoValue` traits completely.
For testing, I made of use `proc-macro2` in the derive macro code. This
would allow testing the generated source code. Instead I just tested
that the derived functionality is correct. This is done in
`nu_protocol::value::test_derive`, as a consumer of `nu-derive-value`
needs to do the testing of the macro usage. I think that these tests
should provide a stable baseline so that users can be sure that the impl
works.
# After Submitting
With these macros available, we can probably use them in some examples
for plugins to showcase the use of them.
2024-06-18 01:05:11 +02:00
|
|
|
span: Some(path_span),
|
2024-03-12 10:37:08 +01:00
|
|
|
src_span: self_span,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn partial_cmp(&self, other: &Value) -> Option<Ordering> {
|
Rename `Value::CustomValue` to `Value::Custom` (#12309)
# Description
The second `Value` is redundant and will consume five extra bytes on
each transmission of a custom value to/from a plugin.
# User-Facing Changes
This is a breaking change to the plugin protocol.
The [example in the protocol
reference](https://www.nushell.sh/contributor-book/plugin_protocol_reference.html#value)
becomes
```json
{
"Custom": {
"val": {
"type": "PluginCustomValue",
"name": "database",
"data": [36, 190, 127, 40, 12, 3, 46, 83],
"notify_on_drop": true
},
"span": {
"start": 320,
"end": 340
}
}
}
```
instead of
```json
{
"CustomValue": {
...
}
}
```
# After Submitting
Update plugin protocol reference
2024-03-27 22:10:56 +01:00
|
|
|
if let Value::Custom { val, .. } = other {
|
2024-03-12 10:37:08 +01:00
|
|
|
val.as_any()
|
|
|
|
.downcast_ref()
|
|
|
|
.and_then(|other: &CoolCustomValue| PartialOrd::partial_cmp(self, other))
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn operation(
|
|
|
|
&self,
|
|
|
|
lhs_span: Span,
|
|
|
|
operator: ast::Operator,
|
|
|
|
op_span: Span,
|
|
|
|
right: &Value,
|
|
|
|
) -> Result<Value, ShellError> {
|
|
|
|
match operator {
|
|
|
|
// Append the string inside `cool`
|
|
|
|
ast::Operator::Math(ast::Math::Append) => {
|
|
|
|
if let Some(right) = right
|
|
|
|
.as_custom_value()
|
|
|
|
.ok()
|
|
|
|
.and_then(|c| c.as_any().downcast_ref::<CoolCustomValue>())
|
|
|
|
{
|
Rename `Value::CustomValue` to `Value::Custom` (#12309)
# Description
The second `Value` is redundant and will consume five extra bytes on
each transmission of a custom value to/from a plugin.
# User-Facing Changes
This is a breaking change to the plugin protocol.
The [example in the protocol
reference](https://www.nushell.sh/contributor-book/plugin_protocol_reference.html#value)
becomes
```json
{
"Custom": {
"val": {
"type": "PluginCustomValue",
"name": "database",
"data": [36, 190, 127, 40, 12, 3, 46, 83],
"notify_on_drop": true
},
"span": {
"start": 320,
"end": 340
}
}
}
```
instead of
```json
{
"CustomValue": {
...
}
}
```
# After Submitting
Update plugin protocol reference
2024-03-27 22:10:56 +01:00
|
|
|
Ok(Value::custom(
|
2024-03-12 10:37:08 +01:00
|
|
|
Box::new(CoolCustomValue {
|
|
|
|
cool: format!("{}{}", self.cool, right.cool),
|
|
|
|
}),
|
|
|
|
op_span,
|
|
|
|
))
|
|
|
|
} else {
|
|
|
|
Err(ShellError::OperatorMismatch {
|
|
|
|
op_span,
|
|
|
|
lhs_ty: self.typetag_name().into(),
|
|
|
|
lhs_span,
|
|
|
|
rhs_ty: right.get_type().to_string(),
|
|
|
|
rhs_span: right.span(),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => Err(ShellError::UnsupportedOperator {
|
|
|
|
operator,
|
|
|
|
span: op_span,
|
|
|
|
}),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-25 18:32:56 +02:00
|
|
|
fn as_any(&self) -> &dyn std::any::Any {
|
|
|
|
self
|
|
|
|
}
|
2024-04-04 09:13:25 +02:00
|
|
|
|
|
|
|
fn as_mut_any(&mut self) -> &mut dyn std::any::Any {
|
|
|
|
self
|
|
|
|
}
|
2022-07-25 18:32:56 +02:00
|
|
|
}
|