use proc_macro2::Span;
use syn::parse::{ParseStream, Result};
use syn::punctuated::Punctuated;
use syn::{Expr, Path, Token, token};
use crate::diag;
use crate::ir::{BodyItem, TeksiElement};
use super::parse_body;
pub(crate) fn parse_element(input: ParseStream) -> Result<TeksiElement> {
let type_path: Path = input.parse()?;
let head_span: Span = type_path
.segments
.first()
.map(|s| s.ident.span())
.unwrap_or_else(Span::call_site);
let has_explicit_ctor = type_path
.segments
.last()
.map(|s| {
let name = s.ident.to_string();
name.chars()
.next()
.is_some_and(|c| c.is_ascii_lowercase() || c == '_')
})
.unwrap_or(false);
let mut args_close: Option<Span> = None;
let args: Vec<Expr> = if input.peek(token::Paren) {
let content;
let paren = syn::parenthesized!(content in input);
args_close = Some(paren.span.close());
let punct: Punctuated<Expr, Token![,]> = Punctuated::parse_terminated(&content)?;
punct.into_iter().collect()
} else {
Vec::new()
};
let mut body_close: Option<Span> = None;
let body = if input.peek(token::Brace) {
let content;
let brace = syn::braced!(content in input);
body_close = Some(brace.span.close());
parse_body(&content)?
} else {
Vec::new()
};
let leaf_name = type_path.segments.last().map(|s| s.ident.to_string());
if let Some(name) = leaf_name.as_deref()
&& diag::is_category_b_widget(name)
&& let Some(child) = body.iter().find_map(|item| match item {
BodyItem::Child(c) => Some(c),
_ => None,
})
{
return Err(diag::category_b_bare_child(name, child.head_span));
}
Ok(TeksiElement {
type_path,
has_explicit_ctor,
args,
body,
head_span,
args_close,
body_close,
})
}