mirror of
https://github.com/nushell/nushell.git
synced 2025-08-09 22:07:57 +02:00
Improve signature infrastructure
The `config` command uses different kinds of named arguments, which illustrates how it works.
This commit is contained in:
@ -85,6 +85,13 @@ impl Expression {
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
crate fn is_flag(&self, value: &str) -> bool {
|
||||
match self {
|
||||
Expression::Flag(Flag::Longhand(f)) if value == f => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, new)]
|
||||
@ -151,17 +158,22 @@ impl Path {
|
||||
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq)]
|
||||
pub enum Variable {
|
||||
It,
|
||||
True,
|
||||
False,
|
||||
Other(String),
|
||||
}
|
||||
|
||||
impl Variable {
|
||||
crate fn from_str(input: &str) -> Expression {
|
||||
match input {
|
||||
"it" => Expression::VariableReference(Variable::It),
|
||||
"true" => Expression::Leaf(Leaf::Boolean(true)),
|
||||
"false" => Expression::Leaf(Leaf::Boolean(false)),
|
||||
other => Expression::VariableReference(Variable::Other(other.to_string())),
|
||||
}
|
||||
}
|
||||
|
||||
fn print(&self) -> String {
|
||||
match self {
|
||||
Variable::It => format!("$it"),
|
||||
Variable::True => format!("$true"),
|
||||
Variable::False => format!("$false"),
|
||||
Variable::Other(s) => format!("${}", s),
|
||||
}
|
||||
}
|
||||
@ -171,18 +183,6 @@ impl Variable {
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Variable {
|
||||
type Err = ();
|
||||
fn from_str(input: &str) -> Result<Self, <Self as std::str::FromStr>::Err> {
|
||||
Ok(match input {
|
||||
"it" => Variable::It,
|
||||
"true" => Variable::True,
|
||||
"false" => Variable::False,
|
||||
other => Variable::Other(other.to_string()),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq)]
|
||||
pub struct BarePath {
|
||||
head: String,
|
||||
|
@ -65,8 +65,8 @@ Expr: Expression = {
|
||||
<PathHead>
|
||||
}
|
||||
|
||||
Variable: Variable = {
|
||||
"$" <"variable"> => Variable::from_str(<>.as_slice()).unwrap(),
|
||||
Variable: Expression = {
|
||||
"$" <"variable"> => Variable::from_str(<>.as_slice()),
|
||||
}
|
||||
|
||||
Member: String = {
|
||||
|
@ -1,5 +1,5 @@
|
||||
// auto-generated: "lalrpop 0.17.0"
|
||||
// sha256: efaf89a1d956869b47a3f5daff048341c19934c68ad5e1ed9fe8e5c4222d2
|
||||
// sha256: 327a2eaaded6615e365add5d44719ae0dd3217f5b0fc3ba130f052328c2bd439
|
||||
#![allow(unused)]
|
||||
use std::str::FromStr;
|
||||
use crate::parser::ast::*;
|
||||
@ -41,7 +41,6 @@ mod __parse__Pipeline {
|
||||
Variant10(i64),
|
||||
Variant11(Operator),
|
||||
Variant12(Pipeline),
|
||||
Variant13(Variable),
|
||||
}
|
||||
const __ACTION: &'static [i8] = &[
|
||||
// State 0
|
||||
@ -1364,17 +1363,6 @@ mod __parse__Pipeline {
|
||||
_ => panic!("symbol type mismatch")
|
||||
}
|
||||
}
|
||||
fn __pop_Variant13<
|
||||
'input,
|
||||
>(
|
||||
__symbols: &mut ::std::vec::Vec<(usize,__Symbol<'input>,usize)>
|
||||
) -> (usize, Variable, usize)
|
||||
{
|
||||
match __symbols.pop().unwrap() {
|
||||
(__l, __Symbol::Variant13(__v), __r) => (__l, __v, __r),
|
||||
_ => panic!("symbol type mismatch")
|
||||
}
|
||||
}
|
||||
fn __pop_Variant10<
|
||||
'input,
|
||||
>(
|
||||
@ -2022,7 +2010,7 @@ mod __parse__Pipeline {
|
||||
) -> (usize, usize)
|
||||
{
|
||||
// Leaf = Variable => ActionFn(9);
|
||||
let __sym0 = __pop_Variant13(__symbols);
|
||||
let __sym0 = __pop_Variant6(__symbols);
|
||||
let __start = __sym0.0.clone();
|
||||
let __end = __sym0.2.clone();
|
||||
let __nt = super::__action9::<>(__sym0);
|
||||
@ -2393,7 +2381,7 @@ mod __parse__Pipeline {
|
||||
let __start = __sym0.0.clone();
|
||||
let __end = __sym1.2.clone();
|
||||
let __nt = super::__action25::<>(__sym0, __sym1);
|
||||
__symbols.push((__start, __Symbol::Variant13(__nt), __end));
|
||||
__symbols.push((__start, __Symbol::Variant6(__nt), __end));
|
||||
(2, 25)
|
||||
}
|
||||
pub(crate) fn __reduce52<
|
||||
@ -2522,7 +2510,7 @@ fn __action8<
|
||||
fn __action9<
|
||||
'input,
|
||||
>(
|
||||
(_, __0, _): (usize, Variable, usize),
|
||||
(_, __0, _): (usize, Expression, usize),
|
||||
) -> Expression
|
||||
{
|
||||
Expression::VariableReference(__0)
|
||||
@ -2679,9 +2667,9 @@ fn __action25<
|
||||
>(
|
||||
(_, _, _): (usize, SpannedToken<'input>, usize),
|
||||
(_, __0, _): (usize, SpannedToken<'input>, usize),
|
||||
) -> Variable
|
||||
) -> Expression
|
||||
{
|
||||
Variable::from_str(__0.as_slice()).unwrap()
|
||||
Variable::from_str(__0.as_slice())
|
||||
}
|
||||
|
||||
fn __action26<
|
||||
|
@ -5,10 +5,21 @@ use indexmap::IndexMap;
|
||||
#[allow(unused)]
|
||||
#[derive(Debug)]
|
||||
pub enum NamedType {
|
||||
Switch(String),
|
||||
Single(String),
|
||||
Array(String),
|
||||
Block(String),
|
||||
Switch,
|
||||
Mandatory(NamedValue),
|
||||
Optional(NamedValue),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum NamedValue {
|
||||
Single,
|
||||
Tuple,
|
||||
|
||||
#[allow(unused)]
|
||||
Block,
|
||||
|
||||
#[allow(unused)]
|
||||
Array,
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
@ -62,13 +73,55 @@ pub struct CommandConfig {
|
||||
crate named: IndexMap<String, NamedType>,
|
||||
}
|
||||
|
||||
pub struct Args {
|
||||
pub positional: Vec<Value>,
|
||||
pub named: IndexMap<String, Value>,
|
||||
}
|
||||
|
||||
impl CommandConfig {
|
||||
crate fn evaluate_args(
|
||||
&self,
|
||||
mut args: impl Iterator<Item = &'expr ast::Expression>,
|
||||
args: impl Iterator<Item = &'expr ast::Expression>,
|
||||
scope: &Scope,
|
||||
) -> Result<Vec<Value>, ShellError> {
|
||||
let mut results: Vec<Value> = vec![];
|
||||
) -> Result<Args, ShellError> {
|
||||
let mut positional: Vec<Value> = vec![];
|
||||
let mut named: IndexMap<String, Value> = IndexMap::default();
|
||||
|
||||
let mut args: Vec<ast::Expression> = args.cloned().collect();
|
||||
|
||||
for (key, ty) in self.named.iter() {
|
||||
let index = args.iter().position(|a| a.is_flag(&key));
|
||||
|
||||
match (index, ty) {
|
||||
(Some(i), NamedType::Switch) => {
|
||||
args.remove(i);
|
||||
named.insert(key.clone(), Value::boolean(true));
|
||||
}
|
||||
|
||||
(None, NamedType::Switch) => {}
|
||||
|
||||
(Some(i), NamedType::Optional(v)) => {
|
||||
args.remove(i);
|
||||
named.insert(key.clone(), extract_named(&mut args, i, v)?);
|
||||
}
|
||||
|
||||
(None, NamedType::Optional(_)) => {}
|
||||
|
||||
(Some(i), NamedType::Mandatory(v)) => {
|
||||
args.remove(i);
|
||||
named.insert(key.clone(), extract_named(&mut args, i, v)?);
|
||||
}
|
||||
|
||||
(None, NamedType::Mandatory(_)) => {
|
||||
return Err(ShellError::string(&format!(
|
||||
"Expected mandatory argument {}, but it was missing",
|
||||
key
|
||||
)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut args = args.into_iter();
|
||||
|
||||
for param in &self.mandatory_positional {
|
||||
let arg = args.next();
|
||||
@ -84,21 +137,25 @@ impl CommandConfig {
|
||||
Some(arg) => param.evaluate(arg.clone(), scope)?,
|
||||
};
|
||||
|
||||
results.push(value);
|
||||
positional.push(value);
|
||||
}
|
||||
|
||||
if self.rest_positional {
|
||||
let rest: Result<Vec<Value>, _> =
|
||||
args.map(|i| evaluate_expr(i, &Scope::empty())).collect();
|
||||
results.extend(rest?);
|
||||
args.map(|i| evaluate_expr(&i, &Scope::empty())).collect();
|
||||
positional.extend(rest?);
|
||||
} else {
|
||||
match args.next() {
|
||||
None => {}
|
||||
Some(_) => return Err(ShellError::string("Too many arguments")),
|
||||
let rest: Vec<ast::Expression> = args.collect();
|
||||
|
||||
if rest.len() > 0 {
|
||||
return Err(ShellError::string(&format!(
|
||||
"Too many arguments, extras: {:?}",
|
||||
rest
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(results)
|
||||
Ok(Args { positional, named })
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
@ -107,6 +164,49 @@ impl CommandConfig {
|
||||
}
|
||||
}
|
||||
|
||||
fn extract_named(
|
||||
v: &mut Vec<ast::Expression>,
|
||||
position: usize,
|
||||
ty: &NamedValue,
|
||||
) -> Result<Value, ShellError> {
|
||||
match ty {
|
||||
NamedValue::Single => {
|
||||
let expr = v.remove(position);
|
||||
expect_simple_expr(expr)
|
||||
}
|
||||
|
||||
NamedValue::Tuple => {
|
||||
let expr = v.remove(position);
|
||||
let next = v.remove(position);
|
||||
|
||||
let list = vec![expect_simple_expr(expr)?, expect_simple_expr(next)?];
|
||||
Ok(Value::List(list))
|
||||
}
|
||||
|
||||
other => Err(ShellError::string(&format!(
|
||||
"Unimplemented named argument {:?}",
|
||||
other
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
fn expect_simple_expr(expr: ast::Expression) -> Result<Value, ShellError> {
|
||||
match expr {
|
||||
ast::Expression::Leaf(l) => Ok(match l {
|
||||
ast::Leaf::Bare(s) => Value::string(s.to_string()),
|
||||
ast::Leaf::String(s) => Value::string(s),
|
||||
ast::Leaf::Boolean(b) => Value::boolean(b),
|
||||
ast::Leaf::Int(i) => Value::int(i),
|
||||
}),
|
||||
|
||||
// TODO: Diagnostic
|
||||
other => Err(ShellError::string(&format!(
|
||||
"Expected a value, found {}",
|
||||
other.print()
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
pub trait CommandRegistry {
|
||||
fn get(&self, name: &str) -> CommandConfig;
|
||||
}
|
||||
|
Reference in New Issue
Block a user