use anyhow::Result;
use indexmap::IndexMap;
use super::{RustValue, Value};
#[derive(Debug, Clone)]
pub struct Args {
positional: Vec<Value>,
keywords: IndexMap<String, Value>,
function_name: Option<String>,
positional_index: usize,
consumed_keywords: Vec<String>,
}
impl Args {
pub fn new(positional: Vec<Value>, keywords: IndexMap<String, Value>) -> Self {
Self {
positional,
keywords,
function_name: None,
positional_index: 0,
consumed_keywords: Vec::new(),
}
}
pub fn positional(positional: Vec<Value>) -> Self {
Self::new(positional, IndexMap::new())
}
pub fn set_function_name(&mut self, name: &str) {
self.function_name = Some(name.to_string());
}
pub fn expect(&mut self, name: &str) -> Result<Value> {
let function_name = self.function_name.as_deref().unwrap_or("function");
if self.positional_index >= self.positional.len() {
return Err(anyhow::anyhow!(
"{}() missing required argument '{}'",
function_name,
name
));
}
let value = self.positional[self.positional_index].clone();
self.positional_index += 1;
Ok(value)
}
pub fn optional(&mut self, _name: &str, default: Value) -> Value {
if self.positional_index >= self.positional.len() {
default
} else {
let value = self.positional[self.positional_index].clone();
self.positional_index += 1;
value
}
}
pub fn keyword(&mut self, name: &str) -> Result<Value> {
let function_name = self.function_name.as_deref().unwrap_or("function");
if let Some(value) = self.keywords.get(name) {
self.consumed_keywords.push(name.to_string());
Ok(value.clone())
} else {
Err(anyhow::anyhow!(
"{}() missing required keyword argument '{}'",
function_name,
name
))
}
}
pub fn keyword_optional(&mut self, name: &str, default: Value) -> Value {
if let Some(value) = self.keywords.get(name) {
self.consumed_keywords.push(name.to_string());
value.clone()
} else {
default
}
}
pub fn rest(&mut self) -> Vec<Value> {
let remaining = self.positional[self.positional_index..].to_vec();
self.positional_index = self.positional.len();
remaining
}
pub fn complete(&self) -> Result<()> {
let function_name = self.function_name.as_deref().unwrap_or("function");
if self.positional_index < self.positional.len() {
let remaining = self.positional.len() - self.positional_index;
return Err(anyhow::anyhow!(
"{}() got {} unexpected positional argument{}",
function_name,
remaining,
if remaining == 1 { "" } else { "s" }
));
}
let unconsumed_keywords: Vec<&String> = self
.keywords
.keys()
.filter(|k| !self.consumed_keywords.contains(k))
.collect();
if !unconsumed_keywords.is_empty() {
return Err(anyhow::anyhow!(
"{}() got unexpected keyword argument{}: {}",
function_name,
if unconsumed_keywords.len() == 1 {
""
} else {
"s"
},
unconsumed_keywords
.into_iter()
.map(|s| format!("'{s}'"))
.collect::<Vec<_>>()
.join(", ")
));
}
Ok(())
}
pub fn len(&self) -> usize {
self.positional.len() + self.keywords.len()
}
pub fn is_empty(&self) -> bool {
self.positional.is_empty() && self.keywords.is_empty()
}
}
impl From<Vec<Value>> for Args {
fn from(positional: Vec<Value>) -> Self {
Self::positional(positional)
}
}
impl Args {
pub fn no_args(&mut self, function_name: &str) -> Result<()> {
self.set_function_name(function_name);
self.complete()
}
pub fn single_f64(&mut self, function_name: &str, arg_name: &str) -> Result<f64> {
self.set_function_name(function_name);
let value = self.expect(arg_name)?;
self.complete()?;
value.expect_f64(function_name, arg_name)
}
pub fn single_i64(&mut self, function_name: &str, arg_name: &str) -> Result<i64> {
self.set_function_name(function_name);
let value = self.expect(arg_name)?;
self.complete()?;
value.expect_i64(function_name, arg_name)
}
pub fn single_bool(&mut self, function_name: &str, arg_name: &str) -> Result<bool> {
self.set_function_name(function_name);
let value = self.expect(arg_name)?;
self.complete()?;
value.expect_bool(function_name, arg_name)
}
pub fn single_string(&mut self, function_name: &str, arg_name: &str) -> Result<String> {
self.set_function_name(function_name);
let value = self.expect(arg_name)?;
self.complete()?;
Ok(value.expect_string(function_name, arg_name)?.to_string())
}
pub fn single_rust_value<T: RustValue + 'static>(
&mut self,
function_name: &str,
arg_name: &str,
expected_type: &str,
) -> Result<Value> {
self.set_function_name(function_name);
let value = self.expect(arg_name)?;
self.complete()?;
value.expect_rust_value::<T>(function_name, expected_type)?;
Ok(value)
}
pub fn two_f64(
&mut self,
function_name: &str,
arg1_name: &str,
arg2_name: &str,
) -> Result<(f64, f64)> {
self.set_function_name(function_name);
let value1 = self.expect(arg1_name)?;
let value2 = self.expect(arg2_name)?;
self.complete()?;
let arg1 = value1.expect_f64(function_name, arg1_name)?;
let arg2 = value2.expect_f64(function_name, arg2_name)?;
Ok((arg1, arg2))
}
}