use crate::ast::{ArrayPatternElement, BindingPattern};
use crate::location::Span;
#[derive(Debug, Clone)]
pub(crate) struct PatternBinding {
pub(crate) name: String,
pub(crate) span: Span,
}
pub(crate) fn collect_bindings_from_pattern(pattern: &BindingPattern) -> Vec<PatternBinding> {
let mut bindings = Vec::new();
collect_bindings_recursive(pattern, &mut bindings);
bindings
}
pub(crate) fn is_primitive_name(name: &str) -> bool {
matches!(
name,
"String" | "I32" | "I64" | "F32" | "F64" | "Boolean" | "Path" | "Regex" | "Never"
)
}
pub(crate) fn strip_array_type(ty: &str) -> Option<&str> {
let trimmed = ty.trim();
let inner = trimmed
.strip_prefix('[')
.and_then(|s| s.strip_suffix(']'))?;
if has_depth_zero_colon(inner) {
None
} else {
Some(inner.trim())
}
}
pub(crate) fn has_depth_zero_colon(s: &str) -> bool {
depth_zero_colon_index(s).is_some()
}
pub(crate) fn depth_zero_colon_index(s: &str) -> Option<usize> {
let mut depth: u32 = 0;
for (i, ch) in s.char_indices() {
match ch {
'(' | '[' | '<' => depth = depth.saturating_add(1),
')' | ']' | '>' => depth = depth.saturating_sub(1),
':' if depth == 0 => return Some(i),
_ => {}
}
}
None
}
pub(crate) fn parse_tuple_field_types(ty: &str) -> Vec<String> {
let trimmed = ty.trim();
if !trimmed.starts_with('(') || !trimmed.ends_with(')') {
return Vec::new();
}
let inner = &trimmed[1..trimmed.len().saturating_sub(1)];
let mut fields = Vec::new();
let mut depth: u32 = 0;
let mut start = 0;
for (i, ch) in inner.char_indices() {
match ch {
'(' | '[' | '<' => depth = depth.saturating_add(1),
')' | ']' | '>' => depth = depth.saturating_sub(1),
',' if depth == 0 => {
fields.push(inner[start..i].to_string());
start = i.saturating_add(1);
}
_ => {}
}
}
if start < inner.len() {
fields.push(inner[start..].to_string());
}
fields
.into_iter()
.map(|part| {
let p = part.trim();
p.split_once(':')
.map_or_else(|| p.to_string(), |(_, ty)| ty.trim().to_string())
})
.collect()
}
fn collect_bindings_recursive(pattern: &BindingPattern, bindings: &mut Vec<PatternBinding>) {
match pattern {
BindingPattern::Simple(ident) => {
bindings.push(PatternBinding {
name: ident.name.clone(),
span: ident.span,
});
}
BindingPattern::Array { elements, .. } => {
for element in elements {
match element {
ArrayPatternElement::Binding(inner) => {
collect_bindings_recursive(inner, bindings);
}
ArrayPatternElement::Rest(Some(ident)) => {
bindings.push(PatternBinding {
name: ident.name.clone(),
span: ident.span,
});
}
ArrayPatternElement::Rest(None) | ArrayPatternElement::Wildcard => {
}
}
}
}
BindingPattern::Struct { fields, .. } => {
for field in fields {
let binding_ident = field.alias.as_ref().unwrap_or(&field.name);
bindings.push(PatternBinding {
name: binding_ident.name.clone(),
span: binding_ident.span,
});
}
}
BindingPattern::Tuple { elements, .. } => {
for element in elements {
collect_bindings_recursive(element, bindings);
}
}
}
}