use crate::{AnyObject, Array, Hash, Proc};
#[derive(Debug)]
pub struct ScannedArgs {
pub required: Vec<AnyObject>,
pub optional: Vec<Option<AnyObject>>,
pub splat: Option<Array>,
pub post: Vec<AnyObject>,
pub keywords: Option<Hash>,
pub block: Option<Proc>,
}
#[derive(Debug)]
pub struct KeywordArgs {
pub required: Vec<AnyObject>,
pub optional: Vec<Option<AnyObject>>,
pub rest: Option<Hash>,
}
#[derive(Debug, Default, PartialEq)]
pub(crate) struct ScanArgsFormat {
pub required: usize,
pub optional: usize,
pub splat: bool,
pub post: usize,
pub keywords: bool,
pub block: bool,
}
impl ScanArgsFormat {
pub fn parse(format: &str) -> Result<Self, String> {
let bytes = format.as_bytes();
let mut spec = ScanArgsFormat::default();
let mut i = 0;
let digit = |i: usize| -> Option<usize> {
bytes
.get(i)
.filter(|b| b.is_ascii_digit())
.map(|b| (b - b'0') as usize)
};
if let Some(required) = digit(i) {
spec.required = required;
i += 1;
if let Some(optional) = digit(i) {
spec.optional = optional;
i += 1;
}
}
if bytes.get(i) == Some(&b'*') {
spec.splat = true;
i += 1;
}
if let Some(post) = digit(i) {
spec.post = post;
i += 1;
}
if bytes.get(i) == Some(&b':') {
spec.keywords = true;
i += 1;
}
if bytes.get(i) == Some(&b'&') {
spec.block = true;
i += 1;
}
if i != bytes.len() {
return Err(format!("bad scan arg format: {}", format));
}
Ok(spec)
}
pub fn variables(&self) -> usize {
self.required
+ self.optional
+ self.splat as usize
+ self.post
+ self.keywords as usize
+ self.block as usize
}
}
#[cfg(test)]
mod tests {
use super::ScanArgsFormat;
#[test]
fn test_scan_args_format_parse() {
let spec = ScanArgsFormat::parse("21*1:&").unwrap();
assert_eq!(
spec,
ScanArgsFormat {
required: 2,
optional: 1,
splat: true,
post: 1,
keywords: true,
block: true,
}
);
assert_eq!(spec.variables(), 7);
assert_eq!(
ScanArgsFormat::parse("").unwrap(),
ScanArgsFormat::default()
);
assert_eq!(ScanArgsFormat::parse("*").unwrap().variables(), 1);
assert_eq!(ScanArgsFormat::parse("*1").unwrap().post, 1);
assert_eq!(ScanArgsFormat::parse("1&").unwrap().variables(), 2);
assert_eq!(ScanArgsFormat::parse("123").unwrap().post, 3);
for bad in &["x", "1x", "&:", "**", "1234", "1:*", "1 ", "-1"] {
assert!(
ScanArgsFormat::parse(bad).is_err(),
"{} should be rejected",
bad
);
}
}
}