use crate::op::{Attribute, BindingOp, DynamicName, SlotOp};
use vize_l0::camelize;
use super::constant_for_hoist;
#[must_use]
pub(super) fn binding_blocker(
binding: &BindingOp<'_>,
) -> Option<(&'static str, Option<&'static str>)> {
const fn bind_rule(rule: &'static str) -> Option<(&'static str, Option<&'static str>)> {
Some(("ui.bind", Some(rule)))
}
let BindingOp::Bind(bind) = binding else {
return Some((binding.mnemonic(), None));
};
let Some(DynamicName::Static(name)) = &bind.name else {
return bind_rule("dynamic-name");
};
let mut has_camel = false;
let mut has_prop = false;
let mut has_attr = false;
for modifier in bind.modifiers.iter() {
match *modifier {
"camel" => has_camel = true,
"prop" => has_prop = true,
"attr" => has_attr = true,
_ => return bind_rule("modifier"),
}
}
let key = if has_camel {
camelize(name)
} else if has_prop {
prefixed('.', name)
} else if has_attr {
prefixed('^', name)
} else {
vize_l0::ToCompactString::to_compact_string(name)
};
if matches!(key.as_str(), "ref" | "class") {
return bind_rule("reserved-key");
}
let Some(value) = &bind.value else {
return bind_rule("no-value");
};
if constant_for_hoist(value) {
None
} else {
bind_rule("non-constant")
}
}
fn prefixed(prefix: char, name: &str) -> vize_l0::String {
let mut out = vize_l0::String::with_capacity(1 + name.len());
out.push(prefix);
out.push_str(name);
out
}
#[must_use]
pub(super) fn props_all_hoistable(
attributes: &[Attribute<'_>],
bindings: &[BindingOp<'_>],
) -> bool {
props_blocker(attributes, bindings).is_none()
}
#[must_use]
pub(super) fn props_blocker(
attributes: &[Attribute<'_>],
bindings: &[BindingOp<'_>],
) -> Option<PropsBlocker> {
if attributes.iter().any(|attribute| attribute.name == "ref") {
return Some(PropsBlocker::RefAttribute);
}
bindings
.iter()
.find_map(binding_blocker)
.map(|(op, rule)| PropsBlocker::Binding { op, rule })
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) enum PropsBlocker {
RefAttribute,
Binding {
op: &'static str,
rule: Option<&'static str>,
},
}
#[must_use]
pub(super) fn slot_props_static(slot: &SlotOp<'_>) -> bool {
let name_static = match &slot.name {
DynamicName::Static(_) => true,
DynamicName::Dynamic(expr) => constant_for_hoist(expr),
};
name_static && props_all_hoistable(&slot.attributes, &slot.bindings)
}