use proc_macro2::{Delimiter, Ident, Spacing, TokenStream, TokenTree};
use crate::generic::empty;
use crate::parse::{parse_item, parse_primitive};
use crate::scan::{Cursor, contains_punct};
use crate::types::*;
pub(crate) fn parse_attribute(
tokens: &[TokenTree],
) -> Option<(TokenStream, &[TokenTree])> {
match tokens {
[TokenTree::Punct(hash), TokenTree::Group(group), rest @ ..]
if hash.as_char() == '#'
&& group.delimiter() == Delimiter::Bracket =>
{
Some((group.stream(), rest))
},
_ => None,
}
}
pub(crate) fn parse_function(
tokens: &[TokenTree],
trait_name: Option<&Ident>,
) -> Option<Ty> {
let [TokenTree::Ident(name), TokenTree::Group(args), rest @ ..] =
tokens
else {
return None;
};
if name != "fn" || args.delimiter() != Delimiter::Parenthesis {
return None;
}
let args_tokens: Vec<_> = args.stream().into_iter().collect();
let mut cursor = Cursor::new(&args_tokens);
let mut parameters = Vec::new();
while let Some(parameter) =
parse_item(&mut cursor, Op::Comma, trait_name)
{
parameters.push(parameter);
}
let return_type = match rest {
[
TokenTree::Punct(dash),
TokenTree::Punct(arrow),
return_tokens @ ..,
] if dash.as_char() == '-'
&& dash.spacing() == Spacing::Joint
&& arrow.as_char() == '>'
&& !return_tokens.is_empty() =>
{
Some(Box::new(parse_primitive(return_tokens, trait_name)))
},
_ => None,
};
Some(Ty::Fn(TyFn(parameters, return_type)))
}
pub(crate) fn parse_prefix(tokens: &[TokenTree]) -> Option<(TyPrefix, &[TokenTree])> {
match tokens {
[TokenTree::Punct(p), TokenTree::Ident(name), rest @ ..]
if p.as_char() == '&' && name == "mut" =>
{
Some((TyPrefix::RefMut, rest))
},
[TokenTree::Punct(p), rest @ ..] if p.as_char() == '&' => {
Some((TyPrefix::Ref, rest))
},
[TokenTree::Punct(p), TokenTree::Ident(name), rest @ ..]
if p.as_char() == '*' && name == "const" =>
{
Some((TyPrefix::PtrConst, rest))
},
[TokenTree::Punct(p), TokenTree::Ident(name), rest @ ..]
if p.as_char() == '*' && name == "mut" =>
{
Some((TyPrefix::PtrMut, rest))
},
[TokenTree::Ident(name), rest @ ..] if name == "self" => {
Some((TyPrefix::SelfType, rest))
},
[TokenTree::Ident(name), rest @ ..] if name == "fn" => {
Some((TyPrefix::Fn, rest))
},
[TokenTree::Ident(name), rest @ ..] if name == "unsafe" => {
Some((TyPrefix::Unsafe, rest))
},
_ => None,
}
}
pub(crate) fn parse_range(tokens: &[TokenTree]) -> Option<Ty> {
let [
TokenTree::Literal(start),
TokenTree::Punct(first_dot),
TokenTree::Punct(second_dot),
rest @ ..,
] = tokens
else {
return None;
};
if first_dot.as_char() != '.'
|| second_dot.as_char() != '.'
|| first_dot.spacing() != Spacing::Joint
{
return None;
}
let start = start.to_string().parse::<u8>().ok()?;
let (inclusive, end) = match rest {
[TokenTree::Literal(end)] => (false, end),
[TokenTree::Punct(eq), TokenTree::Literal(end)]
if eq.as_char() == '='
&& second_dot.spacing() == Spacing::Joint =>
{
(true, end)
},
_ => return None,
};
Some(Ty::Range(TyRange {
start,
end: end.to_string().parse().ok()?,
inclusive,
}))
}
pub(crate) fn parse_group(
group: &proc_macro2::Group,
trait_name: Option<&Ident>,
) -> Ty {
let contents: Vec<_> = group.stream().into_iter().collect();
match group.delimiter() {
Delimiter::Parenthesis => {
if contents.is_empty() || contains_punct(&contents, ',') {
Ty::Tuple(TyTuple(parse_list(
&contents,
Op::Comma,
trait_name,
)))
} else {
Ty::Group(TyGroup(Box::new(
parse_item(
&mut Cursor::new(&contents),
Op::Dash,
trait_name,
)
.unwrap_or_else(empty),
)))
}
},
Delimiter::Bracket => {
if contains_punct(&contents, ',') {
Ty::Array(TyArray(parse_list(
&contents,
Op::Comma,
trait_name,
)))
} else {
let mut cursor = Cursor::new(&contents);
let element =
parse_item(&mut cursor, Op::Semi, trait_name)
.unwrap_or_else(empty);
if cursor.is_punct(';') {
cursor.bump();
let length =
cursor.take_rest().iter().cloned().collect();
Ty::FixedArray(TyFixedArray(Box::new(element), length))
} else {
Ty::Slice(TySlice(Box::new(element)))
}
}
},
Delimiter::Brace => Ty::CodeBlock(TyCodeBlock(group.stream())),
_ => empty(),
}
}
pub(crate) fn parse_list(
tokens: &[TokenTree],
level: Op,
trait_name: Option<&Ident>,
) -> Vec<Ty> {
let mut cursor = Cursor::new(tokens);
let mut items = Vec::new();
while let Some(item) = parse_item(&mut cursor, level, trait_name) {
items.push(item);
}
items
}