use crate::codegen::shared::is_promoted_optional;
use crate::core::ir::ParamDef;
pub(in crate::backends::pyo3) struct PySignatureParam<'a> {
pub param: &'a ParamDef,
pub defaulted: bool,
}
pub(in crate::backends::pyo3) fn python_signature_params<'a>(
params: &'a [ParamDef],
facade_defaultable: impl Fn(&ParamDef) -> bool,
) -> Vec<PySignatureParam<'a>> {
let mut defaulted: Vec<bool> = params
.iter()
.enumerate()
.map(|(idx, param)| param.optional || is_promoted_optional(params, idx))
.collect();
let mut suffix_all_defaulted = true;
for idx in (0..params.len()).rev() {
if !defaulted[idx] && suffix_all_defaulted && facade_defaultable(¶ms[idx]) {
defaulted[idx] = true;
}
suffix_all_defaulted &= defaulted[idx];
}
params
.iter()
.zip(defaulted)
.map(|(param, defaulted)| PySignatureParam { param, defaulted })
.collect()
}
pub(in crate::backends::pyo3) fn leaf_named_type(param: &ParamDef) -> Option<&str> {
use crate::core::ir::TypeRef;
match ¶m.ty {
TypeRef::Named(name) => Some(name.as_str()),
TypeRef::Optional(inner) => match inner.as_ref() {
TypeRef::Named(name) => Some(name.as_str()),
_ => None,
},
_ => None,
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::core::ir::TypeRef;
fn param(name: &str, optional: bool) -> ParamDef {
ParamDef {
name: name.to_string(),
ty: TypeRef::String,
optional,
..Default::default()
}
}
fn shape(params: &[ParamDef], defaultable: &[&str]) -> Vec<(String, bool)> {
python_signature_params(params, |p| defaultable.contains(&p.name.as_str()))
.into_iter()
.map(|entry| (entry.param.name.clone(), entry.defaulted))
.collect()
}
#[test]
fn a_defaultable_param_before_a_required_one_stays_required() {
let params = vec![param("options", false), param("source", false)];
assert_eq!(
shape(¶ms, &["options"]),
vec![("options".to_string(), false), ("source".to_string(), false)]
);
}
#[test]
fn a_trailing_defaultable_param_is_defaulted() {
let params = vec![param("source", false), param("options", false)];
assert_eq!(
shape(¶ms, &["options"]),
vec![("source".to_string(), false), ("options".to_string(), true)]
);
}
#[test]
fn an_optional_param_promotes_every_later_param_to_defaulted() {
let params = vec![param("source", true), param("limit", false)];
assert_eq!(
shape(¶ms, &[]),
vec![("source".to_string(), true), ("limit".to_string(), true)]
);
}
#[test]
fn adjacent_defaultable_params_are_granted_defaults_back_to_front() {
let params = vec![param("source", false), param("config", false), param("options", false)];
assert_eq!(
shape(¶ms, &["config", "options"]),
vec![
("source".to_string(), false),
("config".to_string(), true),
("options".to_string(), true),
]
);
}
#[test]
fn leaf_named_type_looks_through_one_option_layer() {
let mut wrapped = param("options", true);
wrapped.ty = TypeRef::Optional(Box::new(TypeRef::Named("WidgetOptions".to_string())));
assert_eq!(leaf_named_type(&wrapped), Some("WidgetOptions"));
assert_eq!(leaf_named_type(¶m("source", false)), None);
}
}