nushell/crates/nu-protocol/src/signature.rs

401 lines
10 KiB
Rust
Raw Normal View History

2021-09-03 00:58:15 +02:00
use crate::ast::Call;
2021-09-02 20:21:37 +02:00
use crate::engine::Command;
2021-10-25 08:31:39 +02:00
use crate::engine::EngineState;
use crate::engine::Stack;
2021-09-02 10:25:22 +02:00
use crate::BlockId;
2021-10-25 06:01:02 +02:00
use crate::PipelineData;
2021-09-02 10:25:22 +02:00
use crate::SyntaxShape;
2021-09-02 03:29:43 +02:00
use crate::VarId;
2021-07-02 00:40:08 +02:00
2021-09-04 09:45:49 +02:00
#[derive(Debug, Clone, PartialEq, Eq)]
2021-07-02 00:40:08 +02:00
pub struct Flag {
pub long: String,
pub short: Option<char>,
pub arg: Option<SyntaxShape>,
pub required: bool,
pub desc: String,
2021-07-23 23:19:30 +02:00
// For custom commands
pub var_id: Option<VarId>,
2021-07-02 00:40:08 +02:00
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PositionalArg {
pub name: String,
pub desc: String,
pub shape: SyntaxShape,
2021-07-23 23:19:30 +02:00
// For custom commands
pub var_id: Option<VarId>,
2021-07-02 00:40:08 +02:00
}
#[derive(Clone, Debug)]
pub struct Signature {
pub name: String,
pub usage: String,
pub extra_usage: String,
pub required_positional: Vec<PositionalArg>,
pub optional_positional: Vec<PositionalArg>,
pub rest_positional: Option<PositionalArg>,
pub named: Vec<Flag>,
pub is_filter: bool,
2021-10-09 18:10:46 +02:00
pub creates_scope: bool,
2021-07-02 00:40:08 +02:00
}
impl PartialEq for Signature {
fn eq(&self, other: &Self) -> bool {
self.name == other.name
&& self.usage == other.usage
&& self.required_positional == other.required_positional
&& self.optional_positional == other.optional_positional
&& self.rest_positional == other.rest_positional
&& self.is_filter == other.is_filter
}
}
impl Eq for Signature {}
impl Signature {
pub fn new(name: impl Into<String>) -> Signature {
2021-10-13 19:53:27 +02:00
// default help flag
let flag = Flag {
long: "help".into(),
short: Some('h'),
arg: None,
desc: "Display this help message".into(),
required: false,
var_id: None,
};
2021-07-02 00:40:08 +02:00
Signature {
name: name.into(),
usage: String::new(),
extra_usage: String::new(),
required_positional: vec![],
optional_positional: vec![],
rest_positional: None,
2021-10-13 19:53:27 +02:00
named: vec![flag],
2021-07-02 00:40:08 +02:00
is_filter: false,
2021-10-09 18:10:46 +02:00
creates_scope: false,
2021-07-02 00:40:08 +02:00
}
}
pub fn build(name: impl Into<String>) -> Signature {
Signature::new(name.into())
}
/// Add a description to the signature
pub fn desc(mut self, usage: impl Into<String>) -> Signature {
self.usage = usage.into();
self
}
/// Add a required positional argument to the signature
pub fn required(
mut self,
name: impl Into<String>,
shape: impl Into<SyntaxShape>,
desc: impl Into<String>,
) -> Signature {
self.required_positional.push(PositionalArg {
name: name.into(),
desc: desc.into(),
shape: shape.into(),
2021-07-23 23:19:30 +02:00
var_id: None,
2021-07-02 00:40:08 +02:00
});
self
}
/// Add a required positional argument to the signature
pub fn optional(
mut self,
name: impl Into<String>,
shape: impl Into<SyntaxShape>,
desc: impl Into<String>,
) -> Signature {
self.optional_positional.push(PositionalArg {
name: name.into(),
desc: desc.into(),
shape: shape.into(),
2021-07-23 23:19:30 +02:00
var_id: None,
2021-07-02 00:40:08 +02:00
});
self
}
2021-09-07 05:37:02 +02:00
pub fn rest(
mut self,
name: &str,
shape: impl Into<SyntaxShape>,
desc: impl Into<String>,
) -> Signature {
2021-07-30 05:26:06 +02:00
self.rest_positional = Some(PositionalArg {
2021-09-07 05:37:02 +02:00
name: name.into(),
2021-07-30 05:26:06 +02:00
desc: desc.into(),
shape: shape.into(),
var_id: None,
});
self
}
2021-07-02 00:40:08 +02:00
/// Add an optional named flag argument to the signature
pub fn named(
mut self,
name: impl Into<String>,
shape: impl Into<SyntaxShape>,
desc: impl Into<String>,
short: Option<char>,
) -> Signature {
2021-09-04 09:45:49 +02:00
let (name, s) = self.check_names(name, short);
2021-07-02 00:40:08 +02:00
self.named.push(Flag {
2021-09-04 10:19:07 +02:00
long: name,
2021-07-02 00:40:08 +02:00
short: s,
arg: Some(shape.into()),
required: false,
desc: desc.into(),
2021-07-23 23:19:30 +02:00
var_id: None,
2021-07-02 00:40:08 +02:00
});
self
}
/// Add a required named flag argument to the signature
pub fn required_named(
mut self,
name: impl Into<String>,
shape: impl Into<SyntaxShape>,
desc: impl Into<String>,
short: Option<char>,
) -> Signature {
2021-09-04 09:45:49 +02:00
let (name, s) = self.check_names(name, short);
2021-07-02 00:40:08 +02:00
self.named.push(Flag {
2021-09-04 10:19:07 +02:00
long: name,
2021-07-02 00:40:08 +02:00
short: s,
arg: Some(shape.into()),
required: true,
desc: desc.into(),
2021-07-23 23:19:30 +02:00
var_id: None,
2021-07-02 00:40:08 +02:00
});
self
}
/// Add a switch to the signature
pub fn switch(
mut self,
name: impl Into<String>,
desc: impl Into<String>,
short: Option<char>,
) -> Signature {
2021-09-04 09:45:49 +02:00
let (name, s) = self.check_names(name, short);
2021-07-02 00:40:08 +02:00
self.named.push(Flag {
2021-09-04 10:19:07 +02:00
long: name,
2021-07-02 00:40:08 +02:00
short: s,
arg: None,
required: false,
desc: desc.into(),
2021-07-23 23:19:30 +02:00
var_id: None,
2021-07-02 00:40:08 +02:00
});
2021-09-04 09:45:49 +02:00
2021-07-02 00:40:08 +02:00
self
}
2021-10-09 18:10:46 +02:00
/// Sets that signature will create a scope as it parses
pub fn creates_scope(mut self) -> Signature {
self.creates_scope = true;
self
}
2021-07-02 00:40:08 +02:00
/// Get list of the short-hand flags
pub fn get_shorts(&self) -> Vec<char> {
2021-09-04 09:45:49 +02:00
self.named.iter().filter_map(|f| f.short).collect()
}
/// Get list of the long-hand flags
2021-09-04 10:10:31 +02:00
pub fn get_names(&self) -> Vec<&str> {
self.named.iter().map(|f| f.long.as_str()).collect()
2021-09-04 09:45:49 +02:00
}
/// Checks if short or long are already present
/// Panics if one of them is found
fn check_names(&self, name: impl Into<String>, short: Option<char>) -> (String, Option<char>) {
let s = short.map(|c| {
debug_assert!(
!self.get_shorts().contains(&c),
"There may be duplicate short flags, such as -h"
);
c
});
let name = {
2021-09-04 10:10:31 +02:00
let name: String = name.into();
2021-09-04 09:45:49 +02:00
debug_assert!(
2021-09-04 10:10:31 +02:00
!self.get_names().contains(&name.as_str()),
2021-09-04 09:45:49 +02:00
"There may be duplicate name flags, such as --help"
);
name
};
(name, s)
2021-07-02 00:40:08 +02:00
}
pub fn get_positional(&self, position: usize) -> Option<PositionalArg> {
if position < self.required_positional.len() {
self.required_positional.get(position).cloned()
} else if position < (self.required_positional.len() + self.optional_positional.len()) {
self.optional_positional
.get(position - self.required_positional.len())
.cloned()
} else {
self.rest_positional.clone()
}
}
2021-07-08 00:55:46 +02:00
pub fn num_positionals(&self) -> usize {
2021-07-24 07:57:17 +02:00
let mut total = self.required_positional.len() + self.optional_positional.len();
for positional in &self.required_positional {
2021-07-30 00:56:51 +02:00
if let SyntaxShape::Keyword(..) = positional.shape {
// Keywords have a required argument, so account for that
total += 1;
2021-07-24 07:57:17 +02:00
}
}
for positional in &self.optional_positional {
2021-07-30 00:56:51 +02:00
if let SyntaxShape::Keyword(..) = positional.shape {
// Keywords have a required argument, so account for that
total += 1;
2021-07-24 07:57:17 +02:00
}
}
total
}
pub fn num_positionals_after(&self, idx: usize) -> usize {
let mut total = 0;
2021-09-04 09:59:38 +02:00
for (curr, positional) in self.required_positional.iter().enumerate() {
2021-07-24 07:57:17 +02:00
match positional.shape {
SyntaxShape::Keyword(..) => {
// Keywords have a required argument, so account for that
if curr > idx {
total += 2;
}
}
_ => {
if curr > idx {
total += 1;
}
}
}
}
total
2021-07-08 00:55:46 +02:00
}
2021-07-02 00:40:08 +02:00
/// Find the matching long flag
pub fn get_long_flag(&self, name: &str) -> Option<Flag> {
for flag in &self.named {
if flag.long == name {
return Some(flag.clone());
}
}
None
}
/// Find the matching long flag
pub fn get_short_flag(&self, short: char) -> Option<Flag> {
for flag in &self.named {
if let Some(short_flag) = &flag.short {
if *short_flag == short {
return Some(flag.clone());
}
}
}
None
}
2021-09-02 10:25:22 +02:00
/// Set the filter flag for the signature
pub fn filter(mut self) -> Signature {
self.is_filter = true;
self
}
/// Create a placeholder implementation of Command as a way to predeclare a definition's
/// signature so other definitions can see it. This placeholder is later replaced with the
/// full definition in a second pass of the parser.
pub fn predeclare(self) -> Box<dyn Command> {
Box::new(Predeclaration { signature: self })
}
/// Combines a signature and a block into a runnable block
pub fn into_block_command(self, block_id: BlockId) -> Box<dyn Command> {
Box::new(BlockCommand {
signature: self,
block_id,
})
}
2021-07-02 00:40:08 +02:00
}
2021-10-25 06:01:02 +02:00
#[derive(Clone)]
2021-09-02 10:25:22 +02:00
struct Predeclaration {
signature: Signature,
}
impl Command for Predeclaration {
fn name(&self) -> &str {
&self.signature.name
}
fn signature(&self) -> Signature {
self.signature.clone()
}
fn usage(&self) -> &str {
&self.signature.usage
2021-07-30 00:56:51 +02:00
}
2021-09-02 20:21:37 +02:00
2021-09-03 00:58:15 +02:00
fn run(
&self,
2021-10-25 08:31:39 +02:00
_engine_state: &EngineState,
_stack: &mut Stack,
2021-09-03 00:58:15 +02:00
_call: &Call,
2021-10-25 06:01:02 +02:00
_input: PipelineData,
) -> Result<PipelineData, crate::ShellError> {
2021-09-02 20:21:37 +02:00
panic!("Internal error: can't run a predeclaration without a body")
2021-07-30 00:56:51 +02:00
}
}
2021-10-25 06:01:02 +02:00
#[derive(Clone)]
2021-09-02 10:25:22 +02:00
struct BlockCommand {
signature: Signature,
block_id: BlockId,
}
impl Command for BlockCommand {
fn name(&self) -> &str {
&self.signature.name
}
fn signature(&self) -> Signature {
2021-09-06 04:20:02 +02:00
self.signature.clone()
2021-09-02 10:25:22 +02:00
}
fn usage(&self) -> &str {
&self.signature.usage
}
2021-09-02 20:21:37 +02:00
2021-09-03 00:58:15 +02:00
fn run(
&self,
2021-10-25 08:31:39 +02:00
_engine_state: &EngineState,
_stack: &mut Stack,
2021-09-03 00:58:15 +02:00
_call: &Call,
2021-10-25 06:01:02 +02:00
_input: PipelineData,
) -> Result<crate::PipelineData, crate::ShellError> {
2021-09-02 20:21:37 +02:00
panic!("Internal error: can't run custom command with 'run', use block_id");
}
2021-09-06 01:16:27 +02:00
fn get_block_id(&self) -> Option<BlockId> {
2021-09-02 20:21:37 +02:00
Some(self.block_id)
}
}