use proc_macro2::{
Delimiter, Group, Ident, Literal, Spacing, Span, TokenStream as Tokens, TokenTree,
};
use nichlink::registry_core::declaration::FACE_FIELD_ORDER;
use crate::mirror::{Field, punct};
pub(crate) fn splice(tokens: Tokens, placeholder: &str, replacement: &Tokens) -> Tokens {
tokens
.into_iter()
.map(|token| match token {
TokenTree::Ident(ident) if ident == placeholder => {
TokenTree::Group(Group::new(Delimiter::None, replacement.clone()))
}
TokenTree::Group(group) => {
let mut replaced = Group::new(
group.delimiter(),
splice(group.stream(), placeholder, replacement),
);
replaced.set_span(group.span());
TokenTree::Group(replaced)
}
other => other,
})
.collect()
}
pub(crate) fn split_semicolons(tokens: Tokens) -> Vec<Tokens> {
let mut parts = vec![Tokens::new()];
for token in tokens {
let is_separator = matches!(
&token,
TokenTree::Punct(punct) if punct.as_char() == ';'
) && parts.len() < 3;
if is_separator {
parts.push(Tokens::new());
} else {
parts
.last_mut()
.expect("a part is always open")
.extend([token]);
}
}
parts
}
pub(crate) fn split_mirror_fields(input: Tokens) -> Result<Vec<Field>, Tokens> {
let mut fields: Vec<Field> = Vec::new();
for tokens in super::split_face_fields(input) {
let tokens = tokens.into_iter().collect::<Vec<_>>();
let Some(TokenTree::Ident(name)) = tokens.first().cloned() else {
return Err(error_at(
tokens.first().map_or_else(Span::call_site, TokenTree::span),
"expected a face field name".to_owned(),
));
};
let key = name.to_string();
if !FACE_FIELD_ORDER.contains(&key.as_str()) {
continue;
}
if fields.iter().any(|field| field.key == key) {
continue;
}
fields.push(Field {
key,
span: name.span(),
value_span: tokens.get(1).map_or_else(|| name.span(), TokenTree::span),
tokens: tokens.into_iter().collect(),
});
}
Ok(fields)
}
pub(crate) fn render(tokens: &[TokenTree]) -> String {
tokens
.iter()
.map(ToString::to_string)
.collect::<Vec<_>>()
.join(" ")
}
pub(crate) fn error_at(span: Span, message: String) -> Tokens {
let mut literal = Literal::string(&message);
literal.set_span(span);
Tokens::from_iter([
TokenTree::Ident(Ident::new("compile_error", span)),
punct('!', Spacing::Alone),
TokenTree::Group(Group::new(
Delimiter::Brace,
Tokens::from(TokenTree::Literal(literal)),
)),
])
}