mirror of
https://github.com/nushell/nushell.git
synced 2024-11-22 16:33:37 +01:00
input and output types (#5750)
* input and output types * added description * type from stored variable * string in custom value * more tests with non custom
This commit is contained in:
parent
9d10007085
commit
d5b99ae316
@ -4,6 +4,7 @@ use log::trace;
|
||||
use miette::{IntoDiagnostic, Result};
|
||||
use nu_engine::convert_env_values;
|
||||
use nu_parser::parse;
|
||||
use nu_protocol::Type;
|
||||
use nu_protocol::{
|
||||
ast::Call,
|
||||
engine::{EngineState, Stack, StateWorkingSet},
|
||||
@ -34,7 +35,7 @@ pub fn evaluate_file(
|
||||
|
||||
let _ = parse(&mut working_set, Some(&path), &file, false, &[]);
|
||||
|
||||
if working_set.find_decl(b"main").is_some() {
|
||||
if working_set.find_decl(b"main", &Type::Any).is_some() {
|
||||
let args = format!("main {}", args.join(" "));
|
||||
|
||||
if !eval_source(
|
||||
|
@ -91,7 +91,7 @@ impl CustomValue for ExprDb {
|
||||
Value::Bool { val, .. } => Ok(Expr::Value(sqlparser::ast::Value::Boolean(*val))),
|
||||
_ => Err(ShellError::OperatorMismatch {
|
||||
op_span: op,
|
||||
lhs_ty: Type::Custom,
|
||||
lhs_ty: Type::Custom(self.typetag_name().into()),
|
||||
lhs_span,
|
||||
rhs_ty: right.get_type(),
|
||||
rhs_span: right.span()?,
|
||||
|
@ -126,9 +126,9 @@ fn with_operator(
|
||||
.into_value(lhs_span)),
|
||||
_ => Err(ShellError::OperatorMismatch {
|
||||
op_span,
|
||||
lhs_ty: Type::Custom,
|
||||
lhs_ty: Type::Custom(left.typetag_name().into()),
|
||||
lhs_span,
|
||||
rhs_ty: Type::Custom,
|
||||
rhs_ty: Type::Custom(right.typetag_name().into()),
|
||||
rhs_span,
|
||||
}),
|
||||
}
|
||||
|
@ -829,7 +829,7 @@ pub fn create_scope(
|
||||
})
|
||||
}
|
||||
|
||||
for (command_name, decl_id) in commands_map {
|
||||
for ((command_name, _), decl_id) in commands_map {
|
||||
if visibility.is_decl_id_visible(decl_id) {
|
||||
let mut cols = vec![];
|
||||
let mut vals = vec![];
|
||||
|
@ -115,7 +115,7 @@ pub fn parse_for(
|
||||
// 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"for") {
|
||||
let (call, call_span) = match working_set.find_decl(b"for", &Type::Any) {
|
||||
None => {
|
||||
return (
|
||||
garbage(spans[0]),
|
||||
@ -284,7 +284,7 @@ pub fn parse_def(
|
||||
// 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(&def_call) {
|
||||
let (call, call_span) = match working_set.find_decl(&def_call, &Type::Any) {
|
||||
None => {
|
||||
return (
|
||||
garbage_pipeline(spans),
|
||||
@ -427,7 +427,7 @@ pub fn parse_extern(
|
||||
// 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(&extern_call) {
|
||||
let (call, call_span) = match working_set.find_decl(&extern_call, &Type::Any) {
|
||||
None => {
|
||||
return (
|
||||
garbage_pipeline(spans),
|
||||
@ -520,7 +520,7 @@ pub fn parse_alias(
|
||||
return (Pipeline::from_vec(vec![garbage(*span)]), Some(err));
|
||||
}
|
||||
|
||||
if let Some(decl_id) = working_set.find_decl(b"alias") {
|
||||
if let Some(decl_id) = working_set.find_decl(b"alias", &Type::Any) {
|
||||
let (call, _) = parse_internal_call(
|
||||
working_set,
|
||||
spans[0],
|
||||
@ -622,7 +622,7 @@ pub fn parse_export(
|
||||
);
|
||||
};
|
||||
|
||||
let export_decl_id = if let Some(id) = working_set.find_decl(b"export") {
|
||||
let export_decl_id = if let Some(id) = working_set.find_decl(b"export", &Type::Any) {
|
||||
id
|
||||
} else {
|
||||
return (
|
||||
@ -655,18 +655,19 @@ pub fn parse_export(
|
||||
parse_def(working_set, &lite_command, expand_aliases_denylist);
|
||||
error = error.or(err);
|
||||
|
||||
let export_def_decl_id = if let Some(id) = working_set.find_decl(b"export def") {
|
||||
id
|
||||
} else {
|
||||
return (
|
||||
garbage_pipeline(spans),
|
||||
None,
|
||||
Some(ParseError::InternalError(
|
||||
"missing 'export def' command".into(),
|
||||
export_span,
|
||||
)),
|
||||
);
|
||||
};
|
||||
let export_def_decl_id =
|
||||
if let Some(id) = working_set.find_decl(b"export def", &Type::Any) {
|
||||
id
|
||||
} else {
|
||||
return (
|
||||
garbage_pipeline(spans),
|
||||
None,
|
||||
Some(ParseError::InternalError(
|
||||
"missing 'export def' command".into(),
|
||||
export_span,
|
||||
)),
|
||||
);
|
||||
};
|
||||
|
||||
// Trying to warp the 'def' call into the 'export def' in a very clumsy way
|
||||
if let Some(Expression {
|
||||
@ -690,7 +691,7 @@ pub fn parse_export(
|
||||
if error.is_none() {
|
||||
let decl_name = working_set.get_span_contents(spans[2]);
|
||||
let decl_name = trim_quotes(decl_name);
|
||||
if let Some(decl_id) = working_set.find_decl(decl_name) {
|
||||
if let Some(decl_id) = working_set.find_decl(decl_name, &Type::Any) {
|
||||
Some(Exportable::Decl(decl_id))
|
||||
} else {
|
||||
error = error.or_else(|| {
|
||||
@ -714,19 +715,19 @@ pub fn parse_export(
|
||||
parse_def(working_set, &lite_command, expand_aliases_denylist);
|
||||
error = error.or(err);
|
||||
|
||||
let export_def_decl_id = if let Some(id) = working_set.find_decl(b"export def-env")
|
||||
{
|
||||
id
|
||||
} else {
|
||||
return (
|
||||
garbage_pipeline(spans),
|
||||
None,
|
||||
Some(ParseError::InternalError(
|
||||
"missing 'export def-env' command".into(),
|
||||
export_span,
|
||||
)),
|
||||
);
|
||||
};
|
||||
let export_def_decl_id =
|
||||
if let Some(id) = working_set.find_decl(b"export def-env", &Type::Any) {
|
||||
id
|
||||
} else {
|
||||
return (
|
||||
garbage_pipeline(spans),
|
||||
None,
|
||||
Some(ParseError::InternalError(
|
||||
"missing 'export def-env' command".into(),
|
||||
export_span,
|
||||
)),
|
||||
);
|
||||
};
|
||||
|
||||
// Trying to warp the 'def' call into the 'export def' in a very clumsy way
|
||||
if let Some(Expression {
|
||||
@ -750,7 +751,7 @@ pub fn parse_export(
|
||||
if error.is_none() {
|
||||
let decl_name = working_set.get_span_contents(spans[2]);
|
||||
let decl_name = trim_quotes(decl_name);
|
||||
if let Some(decl_id) = working_set.find_decl(decl_name) {
|
||||
if let Some(decl_id) = working_set.find_decl(decl_name, &Type::Any) {
|
||||
Some(Exportable::Decl(decl_id))
|
||||
} else {
|
||||
error = error.or_else(|| {
|
||||
@ -774,18 +775,19 @@ pub fn parse_export(
|
||||
parse_extern(working_set, &lite_command, expand_aliases_denylist);
|
||||
error = error.or(err);
|
||||
|
||||
let export_def_decl_id = if let Some(id) = working_set.find_decl(b"export extern") {
|
||||
id
|
||||
} else {
|
||||
return (
|
||||
garbage_pipeline(spans),
|
||||
None,
|
||||
Some(ParseError::InternalError(
|
||||
"missing 'export extern' command".into(),
|
||||
export_span,
|
||||
)),
|
||||
);
|
||||
};
|
||||
let export_def_decl_id =
|
||||
if let Some(id) = working_set.find_decl(b"export extern", &Type::Any) {
|
||||
id
|
||||
} else {
|
||||
return (
|
||||
garbage_pipeline(spans),
|
||||
None,
|
||||
Some(ParseError::InternalError(
|
||||
"missing 'export extern' command".into(),
|
||||
export_span,
|
||||
)),
|
||||
);
|
||||
};
|
||||
|
||||
// Trying to warp the 'def' call into the 'export def' in a very clumsy way
|
||||
if let Some(Expression {
|
||||
@ -809,7 +811,7 @@ pub fn parse_export(
|
||||
if error.is_none() {
|
||||
let decl_name = working_set.get_span_contents(spans[2]);
|
||||
let decl_name = trim_quotes(decl_name);
|
||||
if let Some(decl_id) = working_set.find_decl(decl_name) {
|
||||
if let Some(decl_id) = working_set.find_decl(decl_name, &Type::Any) {
|
||||
Some(Exportable::Decl(decl_id))
|
||||
} else {
|
||||
error = error.or_else(|| {
|
||||
@ -833,19 +835,19 @@ pub fn parse_export(
|
||||
parse_alias(working_set, &lite_command.parts, expand_aliases_denylist);
|
||||
error = error.or(err);
|
||||
|
||||
let export_alias_decl_id = if let Some(id) = working_set.find_decl(b"export alias")
|
||||
{
|
||||
id
|
||||
} else {
|
||||
return (
|
||||
garbage_pipeline(spans),
|
||||
None,
|
||||
Some(ParseError::InternalError(
|
||||
"missing 'export alias' command".into(),
|
||||
export_span,
|
||||
)),
|
||||
);
|
||||
};
|
||||
let export_alias_decl_id =
|
||||
if let Some(id) = working_set.find_decl(b"export alias", &Type::Any) {
|
||||
id
|
||||
} else {
|
||||
return (
|
||||
garbage_pipeline(spans),
|
||||
None,
|
||||
Some(ParseError::InternalError(
|
||||
"missing 'export alias' command".into(),
|
||||
export_span,
|
||||
)),
|
||||
);
|
||||
};
|
||||
|
||||
// Trying to warp the 'alias' call into the 'export alias' in a very clumsy way
|
||||
if let Some(Expression {
|
||||
@ -885,7 +887,7 @@ pub fn parse_export(
|
||||
}
|
||||
}
|
||||
b"env" => {
|
||||
if let Some(id) = working_set.find_decl(b"export env") {
|
||||
if let Some(id) = working_set.find_decl(b"export env", &Type::Any) {
|
||||
call.decl_id = id;
|
||||
} else {
|
||||
return (
|
||||
@ -1190,7 +1192,7 @@ pub fn parse_module(
|
||||
};
|
||||
|
||||
let module_decl_id = working_set
|
||||
.find_decl(b"module")
|
||||
.find_decl(b"module", &Type::Any)
|
||||
.expect("internal error: missing module command");
|
||||
|
||||
let call = Box::new(Call {
|
||||
@ -1239,7 +1241,7 @@ pub fn parse_use(
|
||||
);
|
||||
}
|
||||
|
||||
let (call, call_span, use_decl_id) = match working_set.find_decl(b"use") {
|
||||
let (call, call_span, use_decl_id) = match working_set.find_decl(b"use", &Type::Any) {
|
||||
Some(decl_id) => {
|
||||
let (call, mut err) = parse_internal_call(
|
||||
working_set,
|
||||
@ -1472,7 +1474,7 @@ pub fn parse_hide(
|
||||
);
|
||||
}
|
||||
|
||||
let (call, call_span, hide_decl_id) = match working_set.find_decl(b"hide") {
|
||||
let (call, call_span, hide_decl_id) = match working_set.find_decl(b"hide", &Type::Any) {
|
||||
Some(decl_id) => {
|
||||
let (call, mut err) = parse_internal_call(
|
||||
working_set,
|
||||
@ -1542,33 +1544,34 @@ pub fn parse_hide(
|
||||
error = error.or(err);
|
||||
}
|
||||
|
||||
let (is_module, module) =
|
||||
if let Some(module_id) = working_set.find_module(&import_pattern.head.name) {
|
||||
(true, working_set.get_module(module_id).clone())
|
||||
} else if import_pattern.members.is_empty() {
|
||||
// The pattern head can be:
|
||||
if let Some(id) = working_set.find_alias(&import_pattern.head.name) {
|
||||
// an alias,
|
||||
let mut module = Module::new();
|
||||
module.add_alias(&import_pattern.head.name, id);
|
||||
let (is_module, module) = if let Some(module_id) =
|
||||
working_set.find_module(&import_pattern.head.name)
|
||||
{
|
||||
(true, working_set.get_module(module_id).clone())
|
||||
} else if import_pattern.members.is_empty() {
|
||||
// The pattern head can be:
|
||||
if let Some(id) = working_set.find_alias(&import_pattern.head.name) {
|
||||
// an alias,
|
||||
let mut module = Module::new();
|
||||
module.add_alias(&import_pattern.head.name, id);
|
||||
|
||||
(false, module)
|
||||
} else if let Some(id) = working_set.find_decl(&import_pattern.head.name) {
|
||||
// a custom command,
|
||||
let mut module = Module::new();
|
||||
module.add_decl(&import_pattern.head.name, id);
|
||||
(false, module)
|
||||
} else if let Some(id) = working_set.find_decl(&import_pattern.head.name, &Type::Any) {
|
||||
// a custom command,
|
||||
let mut module = Module::new();
|
||||
module.add_decl(&import_pattern.head.name, id);
|
||||
|
||||
(false, module)
|
||||
} else {
|
||||
// , or it could be an env var (handled by the engine)
|
||||
(false, Module::new())
|
||||
}
|
||||
(false, module)
|
||||
} else {
|
||||
return (
|
||||
garbage_pipeline(spans),
|
||||
Some(ParseError::ModuleNotFound(spans[1])),
|
||||
);
|
||||
};
|
||||
// , or it could be an env var (handled by the engine)
|
||||
(false, Module::new())
|
||||
}
|
||||
} else {
|
||||
return (
|
||||
garbage_pipeline(spans),
|
||||
Some(ParseError::ModuleNotFound(spans[1])),
|
||||
);
|
||||
};
|
||||
|
||||
// This kind of inverts the import pattern matching found in parse_use()
|
||||
let (aliases_to_hide, decls_to_hide) = if import_pattern.members.is_empty() {
|
||||
@ -1696,7 +1699,7 @@ pub fn parse_overlay(
|
||||
}
|
||||
b"list" => {
|
||||
// TODO: Abstract this code blob, it's repeated all over the place:
|
||||
let call = match working_set.find_decl(b"overlay list") {
|
||||
let call = match working_set.find_decl(b"overlay list", &Type::Any) {
|
||||
Some(decl_id) => {
|
||||
let (call, mut err) = parse_internal_call(
|
||||
working_set,
|
||||
@ -1755,7 +1758,7 @@ pub fn parse_overlay(
|
||||
}
|
||||
}
|
||||
|
||||
let call = match working_set.find_decl(b"overlay") {
|
||||
let call = match working_set.find_decl(b"overlay", &Type::Any) {
|
||||
Some(decl_id) => {
|
||||
let (call, mut err) = parse_internal_call(
|
||||
working_set,
|
||||
@ -1820,7 +1823,7 @@ pub fn parse_overlay_new(
|
||||
);
|
||||
}
|
||||
|
||||
let (call, call_span) = match working_set.find_decl(b"overlay new") {
|
||||
let (call, call_span) = match working_set.find_decl(b"overlay new", &Type::Any) {
|
||||
Some(decl_id) => {
|
||||
let (call, mut err) = parse_internal_call(
|
||||
working_set,
|
||||
@ -1911,7 +1914,7 @@ pub fn parse_overlay_add(
|
||||
}
|
||||
|
||||
// TODO: Allow full import pattern as argument (requires custom naming of module/overlay)
|
||||
let (call, call_span) = match working_set.find_decl(b"overlay add") {
|
||||
let (call, call_span) = match working_set.find_decl(b"overlay add", &Type::Any) {
|
||||
Some(decl_id) => {
|
||||
let (call, mut err) = parse_internal_call(
|
||||
working_set,
|
||||
@ -2098,7 +2101,7 @@ pub fn parse_overlay_remove(
|
||||
);
|
||||
}
|
||||
|
||||
let call = match working_set.find_decl(b"overlay remove") {
|
||||
let call = match working_set.find_decl(b"overlay remove", &Type::Any) {
|
||||
Some(decl_id) => {
|
||||
let (call, mut err) = parse_internal_call(
|
||||
working_set,
|
||||
@ -2209,7 +2212,7 @@ pub fn parse_let(
|
||||
return (Pipeline::from_vec(vec![garbage(*span)]), Some(err));
|
||||
}
|
||||
|
||||
if let Some(decl_id) = working_set.find_decl(b"let") {
|
||||
if let Some(decl_id) = working_set.find_decl(b"let", &Type::Any) {
|
||||
let cmd = working_set.get_decl(decl_id);
|
||||
let call_signature = cmd.signature().call_signature();
|
||||
|
||||
@ -2313,7 +2316,7 @@ pub fn parse_source(
|
||||
let name = working_set.get_span_contents(spans[0]);
|
||||
|
||||
if name == b"source" {
|
||||
if let Some(decl_id) = working_set.find_decl(b"source") {
|
||||
if let Some(decl_id) = working_set.find_decl(b"source", &Type::Any) {
|
||||
let cwd = working_set.get_cwd();
|
||||
// Is this the right call to be using here?
|
||||
// Some of the others (`parse_let`) use it, some of them (`parse_hide`) don't.
|
||||
@ -2445,7 +2448,7 @@ pub fn parse_register(
|
||||
// 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") {
|
||||
let (call, call_span) = match working_set.find_decl(b"register", &Type::Any) {
|
||||
None => {
|
||||
return (
|
||||
garbage_pipeline(spans),
|
||||
|
@ -739,7 +739,10 @@ pub fn parse_internal_call(
|
||||
call.decl_id = decl_id;
|
||||
call.head = command_span;
|
||||
|
||||
let signature = working_set.get_decl(decl_id).signature();
|
||||
let decl = working_set.get_decl(decl_id);
|
||||
let signature = decl.signature();
|
||||
let output = decl.output_type();
|
||||
working_set.found_outputs.push(output);
|
||||
|
||||
if signature.creates_scope {
|
||||
working_set.enter_scope();
|
||||
@ -1009,7 +1012,8 @@ pub fn parse_call(
|
||||
pos += 1;
|
||||
}
|
||||
|
||||
let mut maybe_decl_id = working_set.find_decl(&name);
|
||||
let input = working_set.found_outputs.last().unwrap_or(&Type::Any);
|
||||
let mut maybe_decl_id = working_set.find_decl(&name, input);
|
||||
|
||||
while maybe_decl_id.is_none() {
|
||||
// Find the longest command match
|
||||
@ -1031,7 +1035,7 @@ pub fn parse_call(
|
||||
name.extend(name_part);
|
||||
}
|
||||
}
|
||||
maybe_decl_id = working_set.find_decl(&name);
|
||||
maybe_decl_id = working_set.find_decl(&name, input);
|
||||
}
|
||||
|
||||
if let Some(decl_id) = maybe_decl_id {
|
||||
@ -2648,7 +2652,7 @@ pub fn parse_shape_name(
|
||||
);
|
||||
let command_name = trim_quotes(split[1].as_bytes());
|
||||
|
||||
let decl_id = working_set.find_decl(command_name);
|
||||
let decl_id = working_set.find_decl(command_name, &Type::Any);
|
||||
|
||||
if let Some(decl_id) = decl_id {
|
||||
return (SyntaxShape::Custom(Box::new(shape), decl_id), err);
|
||||
@ -4511,7 +4515,7 @@ pub fn parse_expression(
|
||||
}
|
||||
};
|
||||
|
||||
let with_env = working_set.find_decl(b"with-env");
|
||||
let with_env = working_set.find_decl(b"with-env", &Type::Any);
|
||||
|
||||
if !shorthand.is_empty() {
|
||||
if let Some(decl_id) = with_env {
|
||||
@ -4607,7 +4611,7 @@ pub fn parse_builtin_commands(
|
||||
b"overlay" => parse_overlay(working_set, &lite_command.parts, expand_aliases_denylist),
|
||||
b"source" => parse_source(working_set, &lite_command.parts, expand_aliases_denylist),
|
||||
b"export" => {
|
||||
if let Some(decl_id) = working_set.find_decl(b"alias") {
|
||||
if let Some(decl_id) = working_set.find_decl(b"alias", &Type::Any) {
|
||||
let (call, _) = parse_internal_call(
|
||||
working_set,
|
||||
lite_command.parts[0],
|
||||
@ -4818,8 +4822,9 @@ pub fn parse_block(
|
||||
);
|
||||
|
||||
if idx == 0 {
|
||||
if let Some(let_decl_id) = working_set.find_decl(b"let") {
|
||||
if let Some(let_env_decl_id) = working_set.find_decl(b"let-env") {
|
||||
if let Some(let_decl_id) = working_set.find_decl(b"let", &Type::Any) {
|
||||
if let Some(let_env_decl_id) = working_set.find_decl(b"let-env", &Type::Any)
|
||||
{
|
||||
for expr in pipeline.expressions.iter_mut() {
|
||||
if let Expression {
|
||||
expr: Expr::Call(call),
|
||||
@ -5136,7 +5141,7 @@ pub fn discover_captures_in_expr(
|
||||
fn wrap_expr_with_collect(working_set: &mut StateWorkingSet, expr: &Expression) -> Expression {
|
||||
let span = expr.span;
|
||||
|
||||
if let Some(decl_id) = working_set.find_decl(b"collect") {
|
||||
if let Some(decl_id) = working_set.find_decl(b"collect", &Type::Any) {
|
||||
let mut output = vec![];
|
||||
|
||||
let var_id = working_set.next_var_id();
|
||||
|
@ -662,3 +662,291 @@ mod range {
|
||||
assert!(err.is_some());
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod input_types {
|
||||
use super::*;
|
||||
use nu_protocol::{Category, Type};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct LsTest;
|
||||
|
||||
impl Command for LsTest {
|
||||
fn name(&self) -> &str {
|
||||
"ls"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Mock ls command"
|
||||
}
|
||||
|
||||
fn signature(&self) -> nu_protocol::Signature {
|
||||
Signature::build(self.name()).category(Category::Default)
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
_engine_state: &EngineState,
|
||||
_stack: &mut Stack,
|
||||
_call: &nu_protocol::ast::Call,
|
||||
_input: nu_protocol::PipelineData,
|
||||
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct GroupByList;
|
||||
|
||||
impl Command for GroupByList {
|
||||
fn name(&self) -> &str {
|
||||
"group-by"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Mock group-by command"
|
||||
}
|
||||
|
||||
fn signature(&self) -> nu_protocol::Signature {
|
||||
Signature::build(self.name())
|
||||
.required("column", SyntaxShape::String, "column name")
|
||||
.category(Category::Default)
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
_engine_state: &EngineState,
|
||||
_stack: &mut Stack,
|
||||
_call: &nu_protocol::ast::Call,
|
||||
_input: nu_protocol::PipelineData,
|
||||
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ToCustom;
|
||||
|
||||
impl Command for ToCustom {
|
||||
fn name(&self) -> &str {
|
||||
"to-custom"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Mock converter command"
|
||||
}
|
||||
|
||||
fn signature(&self) -> nu_protocol::Signature {
|
||||
Signature::build(self.name()).category(Category::Custom("custom".into()))
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
_engine_state: &EngineState,
|
||||
_stack: &mut Stack,
|
||||
_call: &nu_protocol::ast::Call,
|
||||
_input: nu_protocol::PipelineData,
|
||||
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn input_type(&self) -> nu_protocol::Type {
|
||||
Type::Any
|
||||
}
|
||||
|
||||
fn output_type(&self) -> nu_protocol::Type {
|
||||
Type::Custom("custom".into())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct GroupByCustom;
|
||||
|
||||
impl Command for GroupByCustom {
|
||||
fn name(&self) -> &str {
|
||||
"group-by"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Mock custom group-by command"
|
||||
}
|
||||
|
||||
fn signature(&self) -> nu_protocol::Signature {
|
||||
Signature::build(self.name())
|
||||
.required("column", SyntaxShape::String, "column name")
|
||||
.required("other", SyntaxShape::String, "other value")
|
||||
.category(Category::Custom("custom".into()))
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
_engine_state: &EngineState,
|
||||
_stack: &mut Stack,
|
||||
_call: &nu_protocol::ast::Call,
|
||||
_input: nu_protocol::PipelineData,
|
||||
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn input_type(&self) -> nu_protocol::Type {
|
||||
Type::Custom("custom".into())
|
||||
}
|
||||
|
||||
fn output_type(&self) -> nu_protocol::Type {
|
||||
Type::Custom("custom".into())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct AggCustom;
|
||||
|
||||
impl Command for AggCustom {
|
||||
fn name(&self) -> &str {
|
||||
"agg"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Mock custom agg command"
|
||||
}
|
||||
|
||||
fn signature(&self) -> nu_protocol::Signature {
|
||||
Signature::build(self.name())
|
||||
.required("operation", SyntaxShape::String, "operation")
|
||||
.category(Category::Custom("custom".into()))
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
_engine_state: &EngineState,
|
||||
_stack: &mut Stack,
|
||||
_call: &nu_protocol::ast::Call,
|
||||
_input: nu_protocol::PipelineData,
|
||||
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn input_type(&self) -> nu_protocol::Type {
|
||||
Type::Custom("custom".into())
|
||||
}
|
||||
}
|
||||
|
||||
fn add_declations(engine_state: &mut EngineState) {
|
||||
let delta = {
|
||||
let mut working_set = StateWorkingSet::new(&engine_state);
|
||||
working_set.add_decl(Box::new(Let));
|
||||
working_set.add_decl(Box::new(AggCustom));
|
||||
working_set.add_decl(Box::new(GroupByCustom));
|
||||
working_set.add_decl(Box::new(GroupByList));
|
||||
working_set.add_decl(Box::new(LsTest));
|
||||
working_set.add_decl(Box::new(ToCustom));
|
||||
working_set.add_decl(Box::new(Let));
|
||||
|
||||
working_set.render()
|
||||
};
|
||||
|
||||
let cwd = std::env::current_dir().expect("Could not get current working directory.");
|
||||
let _ = engine_state.merge_delta(delta, None, &cwd);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn call_types_test() {
|
||||
let mut engine_state = EngineState::new();
|
||||
add_declations(&mut engine_state);
|
||||
|
||||
let mut working_set = StateWorkingSet::new(&engine_state);
|
||||
let input = r#"ls | to-custom | group-by name other"#;
|
||||
|
||||
let (block, err) = parse(&mut working_set, None, input.as_bytes(), true, &[]);
|
||||
|
||||
assert!(err.is_none());
|
||||
assert!(block.len() == 1);
|
||||
|
||||
let expressions = &block[0];
|
||||
assert!(expressions.len() == 3);
|
||||
|
||||
match &expressions[0].expr {
|
||||
Expr::Call(call) => {
|
||||
let expected_id = working_set.find_decl(b"ls", &Type::Any).unwrap();
|
||||
assert_eq!(call.decl_id, expected_id)
|
||||
}
|
||||
_ => panic!("Expected expression Call not found"),
|
||||
}
|
||||
|
||||
match &expressions[1].expr {
|
||||
Expr::Call(call) => {
|
||||
let expected_id = working_set.find_decl(b"to-custom", &Type::Any).unwrap();
|
||||
assert_eq!(call.decl_id, expected_id)
|
||||
}
|
||||
_ => panic!("Expected expression Call not found"),
|
||||
}
|
||||
|
||||
match &expressions[2].expr {
|
||||
Expr::Call(call) => {
|
||||
let expected_id = working_set
|
||||
.find_decl(b"group-by", &Type::Custom("custom".into()))
|
||||
.unwrap();
|
||||
assert_eq!(call.decl_id, expected_id)
|
||||
}
|
||||
_ => panic!("Expected expression Call not found"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn storing_variable_test() {
|
||||
let mut engine_state = EngineState::new();
|
||||
add_declations(&mut engine_state);
|
||||
|
||||
let mut working_set = StateWorkingSet::new(&engine_state);
|
||||
let input =
|
||||
r#"let a = (ls | to-custom | group-by name other); let b = (1+3); $a | agg sum"#;
|
||||
|
||||
let (block, err) = parse(&mut working_set, None, input.as_bytes(), true, &[]);
|
||||
|
||||
assert!(err.is_none());
|
||||
assert!(block.len() == 3);
|
||||
|
||||
let expressions = &block[2];
|
||||
match &expressions[1].expr {
|
||||
Expr::Call(call) => {
|
||||
let expected_id = working_set
|
||||
.find_decl(b"agg", &Type::Custom("custom".into()))
|
||||
.unwrap();
|
||||
assert_eq!(call.decl_id, expected_id)
|
||||
}
|
||||
_ => panic!("Expected expression Call not found"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn call_non_custom_types_test() {
|
||||
let mut engine_state = EngineState::new();
|
||||
add_declations(&mut engine_state);
|
||||
|
||||
let mut working_set = StateWorkingSet::new(&engine_state);
|
||||
let input = r#"ls | group-by name"#;
|
||||
|
||||
let (block, err) = parse(&mut working_set, None, input.as_bytes(), true, &[]);
|
||||
|
||||
assert!(err.is_none());
|
||||
assert!(block.len() == 1);
|
||||
|
||||
let expressions = &block[0];
|
||||
assert!(expressions.len() == 2);
|
||||
|
||||
match &expressions[0].expr {
|
||||
Expr::Call(call) => {
|
||||
let expected_id = working_set.find_decl(b"ls", &Type::Any).unwrap();
|
||||
assert_eq!(call.decl_id, expected_id)
|
||||
}
|
||||
_ => panic!("Expected expression Call not found"),
|
||||
}
|
||||
|
||||
match &expressions[1].expr {
|
||||
Expr::Call(call) => {
|
||||
let expected_id = working_set.find_decl(b"group-by", &Type::Any).unwrap();
|
||||
assert_eq!(call.decl_id, expected_id)
|
||||
}
|
||||
_ => panic!("Expected expression Call not found"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::{ast::Call, BlockId, Example, PipelineData, ShellError, Signature};
|
||||
use crate::{ast::Call, BlockId, Example, PipelineData, ShellError, Signature, Type};
|
||||
|
||||
use super::{EngineState, Stack};
|
||||
|
||||
@ -66,6 +66,20 @@ pub trait Command: Send + Sync + CommandClone {
|
||||
fn search_terms(&self) -> Vec<&str> {
|
||||
vec![]
|
||||
}
|
||||
|
||||
// Command input type. The Type is used during parsing to find the
|
||||
// correct internal command with similar names. The input type is
|
||||
// obtained from the previous expression found in the pipeline
|
||||
fn input_type(&self) -> Type {
|
||||
Type::Any
|
||||
}
|
||||
|
||||
// Command output type. The output type is the value from the command
|
||||
// It is used during parsing to find the next command in case there
|
||||
// are commands with similar names
|
||||
fn output_type(&self) -> Type {
|
||||
Type::Any
|
||||
}
|
||||
}
|
||||
|
||||
pub trait CommandClone {
|
||||
|
@ -464,9 +464,9 @@ impl EngineState {
|
||||
for overlay_frame in self.active_overlays(removed_overlays).iter().rev() {
|
||||
visibility.append(&overlay_frame.visibility);
|
||||
|
||||
if let Some(decl_id) = overlay_frame.decls.get(name) {
|
||||
if visibility.is_decl_id_visible(decl_id) {
|
||||
return Some(*decl_id);
|
||||
if let Some(decl_id) = overlay_frame.get_decl(name, &Type::Any) {
|
||||
if visibility.is_decl_id_visible(&decl_id) {
|
||||
return Some(decl_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -533,9 +533,9 @@ impl EngineState {
|
||||
|
||||
for overlay_frame in self.active_overlays(&[]).iter().rev() {
|
||||
for decl in &overlay_frame.decls {
|
||||
if overlay_frame.visibility.is_decl_id_visible(decl.1) && predicate(decl.0) {
|
||||
if overlay_frame.visibility.is_decl_id_visible(decl.1) && predicate(&decl.0 .0) {
|
||||
let command = self.get_decl(*decl.1);
|
||||
output.push((decl.0.clone(), Some(command.usage().to_string())));
|
||||
output.push((decl.0 .0.clone(), Some(command.usage().to_string())));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -614,7 +614,8 @@ impl EngineState {
|
||||
decls_map.extend(new_decls);
|
||||
}
|
||||
|
||||
let mut decls: Vec<(Vec<u8>, DeclId)> = decls_map.into_iter().collect();
|
||||
let mut decls: Vec<(Vec<u8>, DeclId)> =
|
||||
decls_map.into_iter().map(|(v, k)| (v.0, k)).collect();
|
||||
|
||||
decls.sort_by(|a, b| a.0.cmp(&b.0));
|
||||
decls.into_iter().map(|(_, id)| id)
|
||||
@ -743,6 +744,9 @@ pub struct StateWorkingSet<'a> {
|
||||
pub permanent_state: &'a EngineState,
|
||||
pub delta: StateDelta,
|
||||
pub external_commands: Vec<Vec<u8>>,
|
||||
// Internal commands output that the next expression in the pipe will use to select a declaration
|
||||
// that matches the name in the found output
|
||||
pub found_outputs: Vec<Type>,
|
||||
}
|
||||
|
||||
/// A delta (or change set) between the current global state and a possible future global state. Deltas
|
||||
@ -867,6 +871,7 @@ impl<'a> StateWorkingSet<'a> {
|
||||
delta: StateDelta::new(permanent_state),
|
||||
permanent_state,
|
||||
external_commands: vec![],
|
||||
found_outputs: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
@ -918,11 +923,13 @@ impl<'a> StateWorkingSet<'a> {
|
||||
|
||||
pub fn add_decl(&mut self, decl: Box<dyn Command>) -> DeclId {
|
||||
let name = decl.name().as_bytes().to_vec();
|
||||
let input_type = decl.input_type();
|
||||
|
||||
self.delta.decls.push(decl);
|
||||
let decl_id = self.num_decls() - 1;
|
||||
|
||||
self.last_overlay_mut().decls.insert(name, decl_id);
|
||||
self.last_overlay_mut()
|
||||
.insert_decl(name, input_type, decl_id);
|
||||
|
||||
decl_id
|
||||
}
|
||||
@ -931,7 +938,7 @@ impl<'a> StateWorkingSet<'a> {
|
||||
let overlay_frame = self.last_overlay_mut();
|
||||
|
||||
for (name, decl_id) in decls {
|
||||
overlay_frame.decls.insert(name, decl_id);
|
||||
overlay_frame.insert_decl(name, Type::Any, decl_id);
|
||||
overlay_frame.visibility.use_decl_id(&decl_id);
|
||||
}
|
||||
}
|
||||
@ -968,7 +975,7 @@ impl<'a> StateWorkingSet<'a> {
|
||||
let overlay_frame = self.last_overlay_mut();
|
||||
|
||||
if let Some(decl_id) = overlay_frame.predecls.remove(name) {
|
||||
overlay_frame.decls.insert(name.into(), decl_id);
|
||||
overlay_frame.insert_decl(name.into(), Type::Any, decl_id);
|
||||
|
||||
return Some(decl_id);
|
||||
}
|
||||
@ -998,11 +1005,11 @@ impl<'a> StateWorkingSet<'a> {
|
||||
|
||||
visibility.append(&overlay_frame.visibility);
|
||||
|
||||
if let Some(decl_id) = overlay_frame.decls.get(name) {
|
||||
if visibility.is_decl_id_visible(decl_id) {
|
||||
if let Some(decl_id) = overlay_frame.get_decl(name, &Type::Any) {
|
||||
if visibility.is_decl_id_visible(&decl_id) {
|
||||
// Hide decl only if it's not already hidden
|
||||
overlay_frame.visibility.hide_decl_id(decl_id);
|
||||
return Some(*decl_id);
|
||||
overlay_frame.visibility.hide_decl_id(&decl_id);
|
||||
return Some(decl_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1018,11 +1025,11 @@ impl<'a> StateWorkingSet<'a> {
|
||||
{
|
||||
visibility.append(&overlay_frame.visibility);
|
||||
|
||||
if let Some(decl_id) = overlay_frame.decls.get(name) {
|
||||
if visibility.is_decl_id_visible(decl_id) {
|
||||
if let Some(decl_id) = overlay_frame.get_decl(name, &Type::Any) {
|
||||
if visibility.is_decl_id_visible(&decl_id) {
|
||||
// Hide decl only if it's not already hidden
|
||||
self.last_overlay_mut().visibility.hide_decl_id(decl_id);
|
||||
return Some(*decl_id);
|
||||
self.last_overlay_mut().visibility.hide_decl_id(&decl_id);
|
||||
return Some(decl_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1254,7 +1261,7 @@ impl<'a> StateWorkingSet<'a> {
|
||||
None
|
||||
}
|
||||
|
||||
pub fn find_decl(&self, name: &[u8]) -> Option<DeclId> {
|
||||
pub fn find_decl(&self, name: &[u8], input: &Type) -> Option<DeclId> {
|
||||
let mut removed_overlays = vec![];
|
||||
|
||||
let mut visibility: Visibility = Visibility::new();
|
||||
@ -1280,9 +1287,9 @@ impl<'a> StateWorkingSet<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(decl_id) = overlay_frame.decls.get(name) {
|
||||
if visibility.is_decl_id_visible(decl_id) {
|
||||
return Some(*decl_id);
|
||||
if let Some(decl_id) = overlay_frame.get_decl(name, input) {
|
||||
if visibility.is_decl_id_visible(&decl_id) {
|
||||
return Some(decl_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1297,9 +1304,9 @@ impl<'a> StateWorkingSet<'a> {
|
||||
{
|
||||
visibility.append(&overlay_frame.visibility);
|
||||
|
||||
if let Some(decl_id) = overlay_frame.decls.get(name) {
|
||||
if visibility.is_decl_id_visible(decl_id) {
|
||||
return Some(*decl_id);
|
||||
if let Some(decl_id) = overlay_frame.get_decl(name, input) {
|
||||
if visibility.is_decl_id_visible(&decl_id) {
|
||||
return Some(decl_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1384,7 +1391,7 @@ impl<'a> StateWorkingSet<'a> {
|
||||
.rev()
|
||||
{
|
||||
for decl in &overlay_frame.decls {
|
||||
if decl.0.starts_with(name) {
|
||||
if decl.0 .0.starts_with(name) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -1398,7 +1405,7 @@ impl<'a> StateWorkingSet<'a> {
|
||||
.rev()
|
||||
{
|
||||
for decl in &overlay_frame.decls {
|
||||
if decl.0.starts_with(name) {
|
||||
if decl.0 .0.starts_with(name) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -1562,9 +1569,10 @@ impl<'a> StateWorkingSet<'a> {
|
||||
let overlay_frame = scope_frame.get_overlay(*overlay_id);
|
||||
|
||||
for decl in &overlay_frame.decls {
|
||||
if overlay_frame.visibility.is_decl_id_visible(decl.1) && predicate(decl.0) {
|
||||
if overlay_frame.visibility.is_decl_id_visible(decl.1) && predicate(&decl.0 .0)
|
||||
{
|
||||
let command = self.get_decl(*decl.1);
|
||||
output.push((decl.0.clone(), Some(command.usage().to_string())));
|
||||
output.push((decl.0 .0.clone(), Some(command.usage().to_string())));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1718,8 +1726,8 @@ impl<'a> StateWorkingSet<'a> {
|
||||
if let Some(overlay_id) = self.permanent_state.find_overlay(name) {
|
||||
let overlay_frame = self.permanent_state.get_overlay(overlay_id);
|
||||
|
||||
for (decl_name, decl_id) in &overlay_frame.decls {
|
||||
result.insert(decl_name.to_owned(), *decl_id);
|
||||
for (decl_key, decl_id) in &overlay_frame.decls {
|
||||
result.insert(decl_key.0.to_owned(), *decl_id);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1727,8 +1735,8 @@ impl<'a> StateWorkingSet<'a> {
|
||||
if let Some(overlay_id) = scope_frame.find_overlay(name) {
|
||||
let overlay_frame = scope_frame.get_overlay(overlay_id);
|
||||
|
||||
for (decl_name, decl_id) in &overlay_frame.decls {
|
||||
result.insert(decl_name.to_owned(), *decl_id);
|
||||
for (decl_key, decl_id) in &overlay_frame.decls {
|
||||
result.insert(decl_key.0.to_owned(), *decl_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
use crate::{AliasId, DeclId, ModuleId, OverlayId, Type, VarId};
|
||||
use std::borrow::Borrow;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::{AliasId, DeclId, ModuleId, OverlayId, VarId};
|
||||
use std::hash::{Hash, Hasher};
|
||||
|
||||
pub static DEFAULT_OVERLAY_NAME: &str = "zero";
|
||||
|
||||
@ -199,7 +200,7 @@ impl ScopeFrame {
|
||||
pub struct OverlayFrame {
|
||||
pub vars: HashMap<Vec<u8>, VarId>,
|
||||
pub predecls: HashMap<Vec<u8>, DeclId>, // temporary storage for predeclarations
|
||||
pub decls: HashMap<Vec<u8>, DeclId>,
|
||||
pub decls: HashMap<(Vec<u8>, Type), DeclId>,
|
||||
pub aliases: HashMap<Vec<u8>, AliasId>,
|
||||
pub modules: HashMap<Vec<u8>, ModuleId>,
|
||||
pub visibility: Visibility,
|
||||
@ -218,4 +219,58 @@ impl OverlayFrame {
|
||||
origin,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert_decl(&mut self, name: Vec<u8>, input: Type, decl_id: DeclId) -> Option<DeclId> {
|
||||
self.decls.insert((name, input), decl_id)
|
||||
}
|
||||
|
||||
pub fn get_decl(&self, name: &[u8], input: &Type) -> Option<DeclId> {
|
||||
self.decls.get(&(name, input) as &dyn DeclKey).cloned()
|
||||
}
|
||||
}
|
||||
|
||||
trait DeclKey {
|
||||
fn name(&self) -> &[u8];
|
||||
fn input(&self) -> &Type;
|
||||
}
|
||||
|
||||
impl Hash for dyn DeclKey + '_ {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.name().hash(state);
|
||||
self.input().hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for dyn DeclKey + '_ {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.name() == other.name() && self.input() == other.input()
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for dyn DeclKey + '_ {}
|
||||
|
||||
impl<'a> DeclKey for (&'a [u8], &Type) {
|
||||
fn name(&self) -> &[u8] {
|
||||
self.0
|
||||
}
|
||||
|
||||
fn input(&self) -> &Type {
|
||||
self.1
|
||||
}
|
||||
}
|
||||
|
||||
impl DeclKey for (Vec<u8>, Type) {
|
||||
fn name(&self) -> &[u8] {
|
||||
&self.0
|
||||
}
|
||||
|
||||
fn input(&self) -> &Type {
|
||||
&self.1
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Borrow<dyn DeclKey + 'a> for (Vec<u8>, Type) {
|
||||
fn borrow(&self) -> &(dyn DeclKey + 'a) {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ use std::fmt::Display;
|
||||
|
||||
use crate::SyntaxShape;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Hash)]
|
||||
pub enum Type {
|
||||
Int,
|
||||
Float,
|
||||
@ -25,7 +25,7 @@ pub enum Type {
|
||||
Any,
|
||||
Error,
|
||||
Binary,
|
||||
Custom,
|
||||
Custom(String),
|
||||
Signature,
|
||||
}
|
||||
|
||||
@ -51,7 +51,7 @@ impl Type {
|
||||
Type::Any => SyntaxShape::Any,
|
||||
Type::Error => SyntaxShape::Any,
|
||||
Type::Binary => SyntaxShape::Binary,
|
||||
Type::Custom => SyntaxShape::Any,
|
||||
Type::Custom(_) => SyntaxShape::Any,
|
||||
Type::Signature => SyntaxShape::Signature,
|
||||
}
|
||||
}
|
||||
@ -95,7 +95,7 @@ impl Display for Type {
|
||||
Type::Any => write!(f, "any"),
|
||||
Type::Error => write!(f, "error"),
|
||||
Type::Binary => write!(f, "binary"),
|
||||
Type::Custom => write!(f, "custom"),
|
||||
Type::Custom(custom) => write!(f, "custom<{}>", custom),
|
||||
Type::Signature => write!(f, "signature"),
|
||||
}
|
||||
}
|
||||
|
@ -417,7 +417,7 @@ impl Value {
|
||||
Value::Error { .. } => Type::Error,
|
||||
Value::Binary { .. } => Type::Binary,
|
||||
Value::CellPath { .. } => Type::CellPath,
|
||||
Value::CustomValue { .. } => Type::Custom,
|
||||
Value::CustomValue { val, .. } => Type::Custom(val.typetag_name().into()),
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user