use syn::{
Token,
parse::{Parse, ParseStream},
};
use crate::parse::next_node_id;
use crate::pattern::field::FieldName;
use crate::pattern::{FieldAssertion, FieldOperation, Pattern};
#[derive(Debug, Clone)]
pub(crate) struct PatternTuple {
pub node_id: usize,
pub span: proc_macro2::Span,
pub elements: Vec<TupleElement>,
}
impl Parse for PatternTuple {
fn parse(input: ParseStream) -> syn::Result<Self> {
let span = input.span();
let content;
syn::parenthesized!(content in input);
let elements = TupleElement::parse_comma_separated(&content)?;
Ok(PatternTuple {
node_id: next_node_id(),
span,
elements,
})
}
}
#[derive(Debug, Clone)]
pub(crate) enum TupleElement {
Positional(Box<Pattern>),
Indexed(Box<FieldAssertion>),
}
impl TupleElement {
pub(crate) fn parse_comma_separated(input: ParseStream) -> syn::Result<Vec<Self>> {
let mut elements = Vec::new();
let mut position = 0;
while !input.is_empty() {
let fork = input.fork();
if fork.parse::<Pattern>().is_ok() && !fork.peek(Token![:]) {
let pattern = input.parse()?;
elements.push(TupleElement::Positional(Box::new(pattern)));
} else {
let operations: FieldOperation = input.parse()?;
let root_field = operations.root_field_name();
match root_field {
FieldName::Index(index) if index == position => {
let _: Token![:] = input.parse()?;
let pattern = input.parse()?;
elements.push(TupleElement::Indexed(Box::new(FieldAssertion {
operations,
pattern,
})));
}
FieldName::Index(index) => {
return Err(syn::Error::new(
input.span(),
format!("Index {} must match position {} in tuple", index, position),
));
}
FieldName::Ident(_) => {
return Err(syn::Error::new(
input.span(),
"Operations like * can only be used with indexed elements (e.g., *0:, *1:)",
));
}
}
}
position += 1;
if !input.is_empty() {
let _: Token![,] = input.parse()?;
}
}
Ok(elements)
}
}