mirror of
https://github.com/nushell/nushell
synced 2025-01-14 06:04:09 +00:00
Make custom value type handling more consistent (#12230)
[Context on Discord](https://discord.com/channels/601130461678272522/855947301380947968/1219425984990806207) # Description - Rename `CustomValue::value_string()` to `type_name()` to reflect its usage better. - Change print behavior to always call `to_base_value()` first, to give the custom value better control over the output. - Change `describe --detailed` to show the type name as the subtype, rather than trying to describe the base value. - Change custom `Type` to use `type_name()` rather than `typetag_name()` to make things like `PluginCustomValue` more transparent One question: should `describe --detailed` still include a description of the base value somewhere? I'm torn on it, it seems possibly useful for some things (maybe sqlite databases?), but having `describe -d` not include the custom type name anywhere felt weird. Another option would be to add another method to `CustomValue` for info to be displayed in `describe`, so that it can be more type-specific? # User-Facing Changes Everything above has implications for printing and `describe` on custom values # Tests + Formatting - 🟢 `toolkit fmt` - 🟢 `toolkit clippy` - 🟢 `toolkit test` - 🟢 `toolkit test stdlib`
This commit is contained in:
parent
931f522616
commit
6795ad7e33
19 changed files with 51 additions and 45 deletions
|
@ -20,7 +20,7 @@ impl CustomValue for NuDataFrame {
|
||||||
Value::custom_value(Box::new(cloned), span)
|
Value::custom_value(Box::new(cloned), span)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn value_string(&self) -> String {
|
fn type_name(&self) -> String {
|
||||||
self.typetag_name().to_string()
|
self.typetag_name().to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ impl CustomValue for NuExpression {
|
||||||
Value::custom_value(Box::new(cloned), span)
|
Value::custom_value(Box::new(cloned), span)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn value_string(&self) -> String {
|
fn type_name(&self) -> String {
|
||||||
self.typetag_name().to_string()
|
self.typetag_name().to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ impl CustomValue for NuLazyFrame {
|
||||||
Value::custom_value(Box::new(cloned), span)
|
Value::custom_value(Box::new(cloned), span)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn value_string(&self) -> String {
|
fn type_name(&self) -> String {
|
||||||
self.typetag_name().to_string()
|
self.typetag_name().to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ impl CustomValue for NuLazyGroupBy {
|
||||||
Value::custom_value(Box::new(cloned), span)
|
Value::custom_value(Box::new(cloned), span)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn value_string(&self) -> String {
|
fn type_name(&self) -> String {
|
||||||
self.typetag_name().to_string()
|
self.typetag_name().to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ impl CustomValue for NuWhen {
|
||||||
Value::custom_value(Box::new(cloned), span)
|
Value::custom_value(Box::new(cloned), span)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn value_string(&self) -> String {
|
fn type_name(&self) -> String {
|
||||||
self.typetag_name().to_string()
|
self.typetag_name().to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -235,7 +235,7 @@ fn run(
|
||||||
if options.no_collect {
|
if options.no_collect {
|
||||||
Value::string("any", head)
|
Value::string("any", head)
|
||||||
} else {
|
} else {
|
||||||
describe_value(input.into_value(head), head, engine_state, call, options)?
|
describe_value(input.into_value(head), head, engine_state, options)?
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"metadata" => metadata_to_value(metadata, head),
|
"metadata" => metadata_to_value(metadata, head),
|
||||||
|
@ -246,10 +246,7 @@ fn run(
|
||||||
Value::string("stream", head)
|
Value::string("stream", head)
|
||||||
} else {
|
} else {
|
||||||
let value = input.into_value(head);
|
let value = input.into_value(head);
|
||||||
let base_description = match value {
|
let base_description = value.get_type().to_string();
|
||||||
Value::CustomValue { val, .. } => val.value_string(),
|
|
||||||
_ => value.get_type().to_string(),
|
|
||||||
};
|
|
||||||
|
|
||||||
Value::string(format!("{} (stream)", base_description), head)
|
Value::string(format!("{} (stream)", base_description), head)
|
||||||
}
|
}
|
||||||
|
@ -257,12 +254,9 @@ fn run(
|
||||||
_ => {
|
_ => {
|
||||||
let value = input.into_value(head);
|
let value = input.into_value(head);
|
||||||
if !options.detailed {
|
if !options.detailed {
|
||||||
match value {
|
Value::string(value.get_type().to_string(), head)
|
||||||
Value::CustomValue { val, .. } => Value::string(val.value_string(), head),
|
|
||||||
_ => Value::string(value.get_type().to_string(), head),
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
describe_value(value, head, engine_state, call, options)?
|
describe_value(value, head, engine_state, options)?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -286,14 +280,13 @@ fn describe_value(
|
||||||
value: Value,
|
value: Value,
|
||||||
head: nu_protocol::Span,
|
head: nu_protocol::Span,
|
||||||
engine_state: Option<&EngineState>,
|
engine_state: Option<&EngineState>,
|
||||||
call: &Call,
|
|
||||||
options: Options,
|
options: Options,
|
||||||
) -> Result<Value, ShellError> {
|
) -> Result<Value, ShellError> {
|
||||||
Ok(match value {
|
Ok(match value {
|
||||||
Value::CustomValue { val, internal_span } => Value::record(
|
Value::CustomValue { val, .. } => Value::record(
|
||||||
record!(
|
record!(
|
||||||
"type" => Value::string("custom", head),
|
"type" => Value::string("custom", head),
|
||||||
"subtype" => run(engine_state,call, val.to_base_value(internal_span)?.into_pipeline_data(), options)?.into_value(head),
|
"subtype" => Value::string(val.type_name(), head),
|
||||||
),
|
),
|
||||||
head,
|
head,
|
||||||
),
|
),
|
||||||
|
@ -318,7 +311,6 @@ fn describe_value(
|
||||||
std::mem::take(v),
|
std::mem::take(v),
|
||||||
head,
|
head,
|
||||||
engine_state,
|
engine_state,
|
||||||
call,
|
|
||||||
options,
|
options,
|
||||||
)?);
|
)?);
|
||||||
}
|
}
|
||||||
|
@ -338,7 +330,7 @@ fn describe_value(
|
||||||
"length" => Value::int(vals.len() as i64, head),
|
"length" => Value::int(vals.len() as i64, head),
|
||||||
"values" => Value::list(vals.into_iter().map(|v|
|
"values" => Value::list(vals.into_iter().map(|v|
|
||||||
Ok(compact_primitive_description(
|
Ok(compact_primitive_description(
|
||||||
describe_value(v, head, engine_state, call, options)?
|
describe_value(v, head, engine_state, options)?
|
||||||
))
|
))
|
||||||
)
|
)
|
||||||
.collect::<Result<Vec<Value>, ShellError>>()?, head),
|
.collect::<Result<Vec<Value>, ShellError>>()?, head),
|
||||||
|
@ -406,7 +398,7 @@ fn describe_value(
|
||||||
if options.collect_lazyrecords {
|
if options.collect_lazyrecords {
|
||||||
let collected = val.collect()?;
|
let collected = val.collect()?;
|
||||||
if let Value::Record { mut val, .. } =
|
if let Value::Record { mut val, .. } =
|
||||||
describe_value(collected, head, engine_state, call, options)?
|
describe_value(collected, head, engine_state, options)?
|
||||||
{
|
{
|
||||||
record.push("length", Value::int(val.len() as i64, head));
|
record.push("length", Value::int(val.len() as i64, head));
|
||||||
for (_k, v) in val.iter_mut() {
|
for (_k, v) in val.iter_mut() {
|
||||||
|
@ -414,7 +406,6 @@ fn describe_value(
|
||||||
std::mem::take(v),
|
std::mem::take(v),
|
||||||
head,
|
head,
|
||||||
engine_state,
|
engine_state,
|
||||||
call,
|
|
||||||
options,
|
options,
|
||||||
)?);
|
)?);
|
||||||
}
|
}
|
||||||
|
|
|
@ -353,7 +353,7 @@ impl CustomValue for SQLiteDatabase {
|
||||||
Value::custom_value(Box::new(cloned), span)
|
Value::custom_value(Box::new(cloned), span)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn value_string(&self) -> String {
|
fn type_name(&self) -> String {
|
||||||
self.typetag_name().to_string()
|
self.typetag_name().to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -294,6 +294,11 @@ pub fn debug_string_without_formatting(value: &Value) -> String {
|
||||||
Value::Error { error, .. } => format!("{error:?}"),
|
Value::Error { error, .. } => format!("{error:?}"),
|
||||||
Value::Binary { val, .. } => format!("{val:?}"),
|
Value::Binary { val, .. } => format!("{val:?}"),
|
||||||
Value::CellPath { val, .. } => val.to_string(),
|
Value::CellPath { val, .. } => val.to_string(),
|
||||||
Value::CustomValue { val, .. } => val.value_string(),
|
// If we fail to collapse the custom value, just print <{type_name}> - failure is not
|
||||||
|
// that critical here
|
||||||
|
Value::CustomValue { val, .. } => val
|
||||||
|
.to_base_value(value.span())
|
||||||
|
.map(|val| debug_string_without_formatting(&val))
|
||||||
|
.unwrap_or_else(|_| format!("<{}>", val.type_name())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ use super::inspect_table;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::Call,
|
ast::Call,
|
||||||
engine::{Command, EngineState, Stack},
|
engine::{Command, EngineState, Stack},
|
||||||
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Type, Value,
|
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Type,
|
||||||
};
|
};
|
||||||
use terminal_size::{terminal_size, Height, Width};
|
use terminal_size::{terminal_size, Height, Width};
|
||||||
|
|
||||||
|
@ -40,10 +40,7 @@ impl Command for Inspect {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
let original_input = input_val.clone();
|
let original_input = input_val.clone();
|
||||||
let description = match input_val {
|
let description = input_val.get_type().to_string();
|
||||||
Value::CustomValue { ref val, .. } => val.value_string(),
|
|
||||||
_ => input_val.get_type().to_string(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let (cols, _rows) = match terminal_size() {
|
let (cols, _rows) = match terminal_size() {
|
||||||
Some((w, h)) => (Width(w.0), Height(h.0)),
|
Some((w, h)) => (Width(w.0), Height(h.0)),
|
||||||
|
|
|
@ -110,6 +110,7 @@ impl Iterator for ListStreamIterator {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn local_into_string(value: Value, separator: &str, config: &Config) -> String {
|
fn local_into_string(value: Value, separator: &str, config: &Config) -> String {
|
||||||
|
let span = value.span();
|
||||||
match value {
|
match value {
|
||||||
Value::Bool { val, .. } => val.to_string(),
|
Value::Bool { val, .. } => val.to_string(),
|
||||||
Value::Int { val, .. } => val.to_string(),
|
Value::Int { val, .. } => val.to_string(),
|
||||||
|
@ -148,7 +149,12 @@ fn local_into_string(value: Value, separator: &str, config: &Config) -> String {
|
||||||
Value::Error { error, .. } => format!("{error:?}"),
|
Value::Error { error, .. } => format!("{error:?}"),
|
||||||
Value::Binary { val, .. } => format!("{val:?}"),
|
Value::Binary { val, .. } => format!("{val:?}"),
|
||||||
Value::CellPath { val, .. } => val.to_string(),
|
Value::CellPath { val, .. } => val.to_string(),
|
||||||
Value::CustomValue { val, .. } => val.value_string(),
|
// If we fail to collapse the custom value, just print <{type_name}> - failure is not
|
||||||
|
// that critical here
|
||||||
|
Value::CustomValue { val, .. } => val
|
||||||
|
.to_base_value(span)
|
||||||
|
.map(|val| local_into_string(val, separator, config))
|
||||||
|
.unwrap_or_else(|_| format!("<{}>", val.type_name())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1138,7 +1138,7 @@ impl CustomValue for CantSerialize {
|
||||||
Value::custom_value(Box::new(self.clone()), span)
|
Value::custom_value(Box::new(self.clone()), span)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn value_string(&self) -> String {
|
fn type_name(&self) -> String {
|
||||||
"CantSerialize".into()
|
"CantSerialize".into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -55,7 +55,7 @@ impl CustomValue for PluginCustomValue {
|
||||||
Value::custom_value(Box::new(self.clone()), span)
|
Value::custom_value(Box::new(self.clone()), span)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn value_string(&self) -> String {
|
fn type_name(&self) -> String {
|
||||||
self.name().to_owned()
|
self.name().to_owned()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -212,7 +212,7 @@ impl PluginCustomValue {
|
||||||
custom_value: &dyn CustomValue,
|
custom_value: &dyn CustomValue,
|
||||||
span: Span,
|
span: Span,
|
||||||
) -> Result<PluginCustomValue, ShellError> {
|
) -> Result<PluginCustomValue, ShellError> {
|
||||||
let name = custom_value.value_string();
|
let name = custom_value.type_name();
|
||||||
let notify_on_drop = custom_value.notify_plugin_on_drop();
|
let notify_on_drop = custom_value.notify_plugin_on_drop();
|
||||||
bincode::serialize(custom_value)
|
bincode::serialize(custom_value)
|
||||||
.map(|data| PluginCustomValue::new(name, data, notify_on_drop, None))
|
.map(|data| PluginCustomValue::new(name, data, notify_on_drop, None))
|
||||||
|
@ -297,7 +297,7 @@ impl PluginCustomValue {
|
||||||
} else {
|
} else {
|
||||||
// Only PluginCustomValues can be sent
|
// Only PluginCustomValues can be sent
|
||||||
Err(ShellError::CustomValueIncorrectForPlugin {
|
Err(ShellError::CustomValueIncorrectForPlugin {
|
||||||
name: val.value_string(),
|
name: val.type_name(),
|
||||||
span,
|
span,
|
||||||
dest_plugin: source.name().to_owned(),
|
dest_plugin: source.name().to_owned(),
|
||||||
src_plugin: None,
|
src_plugin: None,
|
||||||
|
|
|
@ -19,7 +19,7 @@ fn serialize_deserialize() -> Result<(), ShellError> {
|
||||||
let original_value = TestCustomValue(32);
|
let original_value = TestCustomValue(32);
|
||||||
let span = Span::test_data();
|
let span = Span::test_data();
|
||||||
let serialized = PluginCustomValue::serialize_from_custom_value(&original_value, span)?;
|
let serialized = PluginCustomValue::serialize_from_custom_value(&original_value, span)?;
|
||||||
assert_eq!(original_value.value_string(), serialized.name());
|
assert_eq!(original_value.type_name(), serialized.name());
|
||||||
assert!(serialized.source.is_none());
|
assert!(serialized.source.is_none());
|
||||||
let deserialized = serialized.deserialize_to_custom_value(span)?;
|
let deserialized = serialized.deserialize_to_custom_value(span)?;
|
||||||
let downcasted = deserialized
|
let downcasted = deserialized
|
||||||
|
|
|
@ -14,7 +14,7 @@ impl CustomValue for TestCustomValue {
|
||||||
Value::custom_value(Box::new(self.clone()), span)
|
Value::custom_value(Box::new(self.clone()), span)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn value_string(&self) -> String {
|
fn type_name(&self) -> String {
|
||||||
"TestCustomValue".into()
|
"TestCustomValue".into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,8 +13,10 @@ pub trait CustomValue: fmt::Debug + Send + Sync {
|
||||||
|
|
||||||
//fn category(&self) -> Category;
|
//fn category(&self) -> Category;
|
||||||
|
|
||||||
/// Define string representation of the custom value
|
/// The friendly type name to show for the custom value, e.g. in `describe` and in error
|
||||||
fn value_string(&self) -> String;
|
/// messages. This does not have to be the same as the name of the struct or enum, but
|
||||||
|
/// conventionally often is.
|
||||||
|
fn type_name(&self) -> String;
|
||||||
|
|
||||||
/// Converts the custom value to a base nushell value.
|
/// Converts the custom value to a base nushell value.
|
||||||
///
|
///
|
||||||
|
@ -34,7 +36,7 @@ pub trait CustomValue: fmt::Debug + Send + Sync {
|
||||||
) -> Result<Value, ShellError> {
|
) -> Result<Value, ShellError> {
|
||||||
let _ = (self_span, index);
|
let _ = (self_span, index);
|
||||||
Err(ShellError::IncompatiblePathAccess {
|
Err(ShellError::IncompatiblePathAccess {
|
||||||
type_name: self.value_string(),
|
type_name: self.type_name(),
|
||||||
span: path_span,
|
span: path_span,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -48,7 +50,7 @@ pub trait CustomValue: fmt::Debug + Send + Sync {
|
||||||
) -> Result<Value, ShellError> {
|
) -> Result<Value, ShellError> {
|
||||||
let _ = (self_span, column_name);
|
let _ = (self_span, column_name);
|
||||||
Err(ShellError::IncompatiblePathAccess {
|
Err(ShellError::IncompatiblePathAccess {
|
||||||
type_name: self.value_string(),
|
type_name: self.type_name(),
|
||||||
span: path_span,
|
span: path_span,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -831,7 +831,7 @@ impl Value {
|
||||||
Value::Error { .. } => Type::Error,
|
Value::Error { .. } => Type::Error,
|
||||||
Value::Binary { .. } => Type::Binary,
|
Value::Binary { .. } => Type::Binary,
|
||||||
Value::CellPath { .. } => Type::CellPath,
|
Value::CellPath { .. } => Type::CellPath,
|
||||||
Value::CustomValue { val, .. } => Type::Custom(val.typetag_name().into()),
|
Value::CustomValue { val, .. } => Type::Custom(val.type_name()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -947,7 +947,12 @@ impl Value {
|
||||||
Value::Error { error, .. } => format!("{error:?}"),
|
Value::Error { error, .. } => format!("{error:?}"),
|
||||||
Value::Binary { val, .. } => format!("{val:?}"),
|
Value::Binary { val, .. } => format!("{val:?}"),
|
||||||
Value::CellPath { val, .. } => val.to_string(),
|
Value::CellPath { val, .. } => val.to_string(),
|
||||||
Value::CustomValue { val, .. } => val.value_string(),
|
// If we fail to collapse the custom value, just print <{type_name}> - failure is not
|
||||||
|
// that critical here
|
||||||
|
Value::CustomValue { val, .. } => val
|
||||||
|
.to_base_value(span)
|
||||||
|
.map(|val| val.to_expanded_string(separator, config))
|
||||||
|
.unwrap_or_else(|_| format!("<{}>", val.type_name())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -50,7 +50,7 @@ impl CustomValue for CoolCustomValue {
|
||||||
Value::custom_value(Box::new(self.clone()), span)
|
Value::custom_value(Box::new(self.clone()), span)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn value_string(&self) -> String {
|
fn type_name(&self) -> String {
|
||||||
self.typetag_name().to_string()
|
self.typetag_name().to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,7 @@ impl CustomValue for DropCheckValue {
|
||||||
self.clone().into_value(span)
|
self.clone().into_value(span)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn value_string(&self) -> String {
|
fn type_name(&self) -> String {
|
||||||
"DropCheckValue".into()
|
"DropCheckValue".into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,7 +45,7 @@ impl CustomValue for SecondCustomValue {
|
||||||
Value::custom_value(Box::new(self.clone()), span)
|
Value::custom_value(Box::new(self.clone()), span)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn value_string(&self) -> String {
|
fn type_name(&self) -> String {
|
||||||
self.typetag_name().to_string()
|
self.typetag_name().to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue