use std::slice;
use starlark_syntax::internal_error;
use starlark_syntax::other_error;
use crate::values::UnpackValue;
use crate::values::Value;
pub struct ParametersParser<'v, 'a> {
slots: slice::Iter<'a, Option<Value<'v>>>,
names: slice::Iter<'a, String>,
}
impl<'v, 'a> ParametersParser<'v, 'a> {
pub(crate) fn new(slots: &'a [Option<Value<'v>>], names: &'a [String]) -> Self {
assert_eq!(slots.len(), names.len());
ParametersParser {
slots: slots.iter(),
names: names.iter(),
}
}
#[inline]
fn get_next(&mut self) -> anyhow::Result<(Option<Value<'v>>, &'a str)> {
let Some(v) = self.slots.next() else {
return Err(
internal_error!("Requesting more parameters than were specified").into_anyhow(),
);
};
let name = unsafe { self.names.next().unwrap_unchecked() };
Ok((*v, name))
}
pub fn next_opt<T: UnpackValue<'v>>(&mut self) -> anyhow::Result<Option<T>> {
match self.get_next()? {
(None, _) => Ok(None),
(Some(v), name) => Ok(Some(T::unpack_named_param(v, name)?)),
}
}
pub fn next<T: UnpackValue<'v>>(&mut self) -> anyhow::Result<T> {
let (v, name) = self.get_next()?;
let Some(v) = v else {
return Err(other_error!(
"Requested non-optional param {name} which was declared optional in signature"
)
.into_anyhow());
};
T::unpack_named_param(v, name)
}
#[inline]
pub(crate) fn is_eof(&self) -> bool {
self.slots.len() == 0
}
}
#[cfg(test)]
mod tests {
use std::collections::HashMap;
use crate::assert::Assert;
use crate::docs::DocParam;
use crate::docs::DocParams;
use crate::docs::DocString;
use crate::docs::DocStringKind;
use crate::eval::compiler::def::FrozenDef;
use crate::eval::runtime::params::display::PARAM_FMT_OPTIONAL;
use crate::eval::runtime::params::spec::ParametersSpec;
use crate::eval::ParametersSpecParam;
use crate::typing::Ty;
use crate::values::FrozenValue;
#[test]
fn test_documentation() -> anyhow::Result<()> {
let p = ParametersSpec::<FrozenValue>::new_parts(
"f",
[],
[],
true,
[
("a", ParametersSpecParam::Optional),
("b", ParametersSpecParam::Optional),
],
false,
);
let expected = DocParams {
args: Some(DocParam {
name: "args".to_owned(),
docs: None,
typ: Ty::any(),
default_value: None,
}),
named_only: vec![
DocParam {
name: "a".to_owned(),
docs: None,
typ: Ty::int(),
default_value: Some(PARAM_FMT_OPTIONAL.to_owned()),
},
DocParam {
name: "b".to_owned(),
docs: DocString::from_docstring(DocStringKind::Rust, "param b docs"),
typ: Ty::any(),
default_value: Some(PARAM_FMT_OPTIONAL.to_owned()),
},
],
pos_only: Vec::new(),
pos_or_named: Vec::new(),
kwargs: None,
};
let types = vec![Ty::any(), Ty::int(), Ty::any()];
let mut docs = HashMap::new();
docs.insert("a".to_owned(), None);
docs.insert(
"b".to_owned(),
DocString::from_docstring(DocStringKind::Rust, "param b docs"),
);
let params = p.documentation(types, docs);
assert_eq!(expected, params);
Ok(())
}
#[test]
fn test_parameters_str() {
fn test(sig: &str) {
let a = Assert::new();
let f = a
.pass_module(&format!("def f({sig}): pass"))
.get("f")
.unwrap();
assert_eq!(sig, &f.value().parameters_spec().unwrap().parameters_str());
}
test("");
test("a, b, c, d, e, f, g, h, *args, **kwargs");
test("*, a");
test("x, *, a");
test("*args, a");
test("x, *args, a");
test("**kwargs");
test("a, **kwargs");
}
#[test]
fn test_can_fill_with_args() {
fn test(sig: &str, pos: usize, names: &[&str], expected: bool) {
let a = Assert::new();
let module = a.pass_module(&format!("def f({}): pass", sig));
let f = module.get("f").unwrap().downcast::<FrozenDef>().unwrap();
let parameters_spec = &f.parameters;
assert_eq!(expected, parameters_spec.can_fill_with_args(pos, names));
}
test("", 0, &[], true);
test("", 1, &[], false);
test("", 0, &["a"], false);
test("a", 1, &[], true);
test("a", 0, &["a"], true);
test("a", 1, &["a"], false);
test("a", 0, &["x"], false);
test("a, b = 1", 1, &[], true);
test("a, b = 1", 2, &[], true);
test("a, b = 1", 0, &["a"], true);
test("a, b = 1", 0, &["b"], false);
test("a, b = 1", 0, &["a", "b"], true);
test("*, a", 0, &[], false);
test("*, a", 1, &[], false);
test("*, a", 0, &["a"], true);
test("a, *args", 0, &[], false);
test("a, *args", 1, &[], true);
test("a, *args", 10, &[], true);
test("*args, b", 0, &[], false);
test("*args, b", 1, &[], false);
test("*args, b", 0, &["b"], true);
test("**kwargs", 0, &[], true);
test("**kwargs", 0, &["a"], true);
test("**kwargs", 1, &[], false);
}
}