use proc_macro2::{TokenStream as TokenStream2, TokenTree};
use quote::quote;
use syn::{parse::Parse, Expr, ExprPath, ExprUnary, Ident, Token, Type, UnOp};
use super::super::ast::*;
impl Parse for LinqInput {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
if input.peek(Ident) {
let cursor = input.cursor();
if let Some((ident, _)) = cursor.ident() {
match ident.to_string().as_str() {
"filter" => {
let value = parse_value_filter(input)?;
return Ok(LinqInput::Value(value));
}
"index" => {
let value = parse_value_index_or_key(input, ValueKind::Index)?;
return Ok(LinqInput::Value(value));
}
"key" => {
let value = parse_value_index_or_key(input, ValueKind::Key)?;
return Ok(LinqInput::Value(value));
}
_ => {}
}
}
}
let query = parse_query(input)?;
Ok(LinqInput::Query(query))
}
}
enum ValueKind {
Index,
Key,
}
fn parse_value_filter(input: syn::parse::ParseStream) -> syn::Result<ValueInput> {
let keyword: Ident = input.parse()?;
debug_assert_eq!(keyword, "filter");
let _open: Token![|] = input.parse()?;
let param: Ident = input.parse()?;
let _colon: Token![:] = input.parse()?;
let entity: Type = input.parse()?;
let _close: Token![|] = input.parse()?;
let body: Expr = input.parse()?;
Ok(ValueInput::Filter {
entity,
param,
body,
})
}
fn parse_value_index_or_key(
input: syn::parse::ParseStream,
kind: ValueKind,
) -> syn::Result<ValueInput> {
let _keyword: Ident = input.parse()?;
let _open: Token![|] = input.parse()?;
let _param: Ident = input.parse()?;
let _colon: Token![:] = input.parse()?;
let entity: Type = input.parse()?;
let _close: Token![|] = input.parse()?;
let fields = parse_field_or_tuple(input)?;
match kind {
ValueKind::Index => Ok(ValueInput::Index { entity, fields }),
ValueKind::Key => Ok(ValueInput::Key { entity, fields }),
}
}
pub(super) fn parse_field_or_tuple(input: syn::parse::ParseStream) -> syn::Result<Vec<Expr>> {
if input.peek(syn::token::Paren) {
let content;
syn::parenthesized!(content in input);
let mut fields = Vec::new();
while !content.is_empty() {
let expr: Expr = content.parse()?;
fields.push(expr);
if !content.is_empty() {
let _comma: Token![,] = content.parse()?;
}
}
Ok(fields)
} else {
let expr: Expr = input.parse()?;
Ok(vec![expr])
}
}
fn parse_query(input: syn::parse::ParseStream) -> syn::Result<QueryInput> {
if input.peek(Token![|]) {
let (entity, where_param, where_body) = parse_typed_closure(input)?;
let order = parse_optional_order(input)?;
let clauses = parse_optional_clauses(input)?;
return Ok(QueryInput {
source: None,
entity,
where_param: Some(where_param),
where_body: Some(where_body),
order,
clauses,
});
}
let first: Expr = input.parse()?;
if input.peek(Token![,]) {
let _comma: Token![,] = input.parse()?;
if input.peek(Token![|]) {
if is_source_expr(&first) {
let source_entity = source_entity_type(&first).ok();
let (entity, where_param, where_body) = match source_entity {
Some(ref se) => parse_closure_with_inference(input, se)?,
None => parse_typed_closure(input)?,
};
let order = parse_optional_order(input)?;
let clauses = parse_optional_clauses(input)?;
return Ok(QueryInput {
source: Some(first),
entity,
where_param: Some(where_param),
where_body: Some(where_body),
order,
clauses,
});
}
let entity = expr_as_entity_type(&first)?;
let (where_param, where_body) = parse_untyped_closure(input)?;
let order = parse_optional_order(input)?;
let clauses = parse_optional_clauses(input)?;
return Ok(QueryInput {
source: None,
entity,
where_param: Some(where_param),
where_body: Some(where_body),
order,
clauses,
});
}
let entity = expr_as_entity_type(&first)?;
let where_body: Expr = input.parse()?;
let order = parse_optional_order(input)?;
let clauses = parse_optional_clauses(input)?;
return Ok(QueryInput {
source: None,
entity,
where_param: None,
where_body: Some(where_body),
order,
clauses,
});
}
if input.peek(Token![;]) {
let entity = source_entity_type(&first)?;
let clauses = parse_optional_clauses(input)?;
return Ok(QueryInput {
source: Some(first),
entity,
where_param: None,
where_body: None,
order: None,
clauses,
});
}
let entity = expr_as_entity_type(&first)?;
let where_body = parse_where_rest(input)?;
let order = parse_optional_order(input)?;
let clauses = parse_optional_clauses(input)?;
Ok(QueryInput {
source: None,
entity,
where_param: None,
where_body: Some(where_body),
order,
clauses,
})
}
fn is_source_expr(expr: &Expr) -> bool {
match expr {
Expr::Path(ExprPath { path, .. }) => path.segments.len() != 1,
_ => true,
}
}
fn source_entity_type(expr: &Expr) -> syn::Result<Type> {
let mut current = expr;
loop {
match current {
Expr::MethodCall(call) => {
if let Some(ty) = call
.turbofish
.as_ref()
.and_then(|tf| tf.args.first())
.and_then(|arg| match arg {
syn::GenericArgument::Type(ty) => Some(ty.clone()),
_ => None,
})
{
return Ok(ty);
}
current = &call.receiver;
}
Expr::Call(call) => {
current = &call.func;
}
_ => break,
}
}
expr_as_entity_type(expr)
}
fn expr_as_entity_type(expr: &Expr) -> syn::Result<Type> {
match expr {
Expr::Path(path) => syn::parse2(quote! { #path }),
_ => Err(syn::Error::new_spanned(
expr,
"expected entity type, e.g. `Blog`",
)),
}
}
fn parse_typed_closure(input: syn::parse::ParseStream) -> syn::Result<(Type, Ident, Expr)> {
let _open: Token![|] = input.parse()?;
let param: Ident = input.parse()?;
let _colon: Token![:] = input.parse()?;
let entity: Type = input.parse()?;
let _close: Token![|] = input.parse()?;
let body = parse_expr_until_fat_arrow_or_semi(input)?;
Ok((entity, param, body))
}
fn parse_closure_with_inference(
input: syn::parse::ParseStream,
fallback: &Type,
) -> syn::Result<(Type, Ident, Expr)> {
let _open: Token![|] = input.parse()?;
let param: Ident = input.parse()?;
if input.peek(Token![:]) {
let _colon: Token![:] = input.parse()?;
let entity: Type = input.parse()?;
let _close: Token![|] = input.parse()?;
let body = parse_expr_until_fat_arrow_or_semi(input)?;
Ok((entity, param, body))
} else {
let _close: Token![|] = input.parse()?;
let body = parse_expr_until_fat_arrow_or_semi(input)?;
Ok((fallback.clone(), param, body))
}
}
fn parse_untyped_closure(input: syn::parse::ParseStream) -> syn::Result<(Ident, Expr)> {
let _open: Token![|] = input.parse()?;
let param: Ident = input.parse()?;
let _close: Token![|] = input.parse()?;
let body = parse_expr_until_fat_arrow_or_semi(input)?;
Ok((param, body))
}
fn parse_where_rest(input: syn::parse::ParseStream) -> syn::Result<Expr> {
parse_expr_until_fat_arrow_or_semi(input)
}
fn parse_optional_order(input: syn::parse::ParseStream) -> syn::Result<Option<LinqOrder>> {
if !input.peek(Token![=>]) {
return Ok(None);
}
let _arrow: Token![=>] = input.parse()?;
let order_body: Expr = input.parse()?;
let (field_expr, descending) = parse_order_expr(&order_body)?;
Ok(Some(LinqOrder {
body: field_expr,
descending,
}))
}
fn parse_order_expr(expr: &Expr) -> syn::Result<(Expr, bool)> {
if let Expr::Unary(ExprUnary {
op: UnOp::Neg(_),
expr: inner,
..
}) = expr
{
return Ok((*inner.clone(), true));
}
Ok((expr.clone(), false))
}
fn parse_optional_clauses(input: syn::parse::ParseStream) -> syn::Result<Vec<LinqClause>> {
let mut clauses = Vec::new();
while input.peek(Token![;]) {
let _semi: Token![;] = input.parse()?;
if input.is_empty() {
break; }
let clause_tokens = collect_until_semi(input)?;
if clause_tokens.is_empty() {
break;
}
let clause: LinqClause = syn::parse2(clause_tokens)?;
clauses.push(clause);
}
Ok(clauses)
}
fn collect_until_semi(input: syn::parse::ParseStream) -> syn::Result<TokenStream2> {
let mut tokens = TokenStream2::new();
while !input.is_empty() && !input.peek(Token![;]) {
let tt: TokenTree = input.parse()?;
tokens.extend(std::iter::once(tt));
}
Ok(tokens)
}
pub(super) fn parse_expr_until_fat_arrow_or_semi(
input: syn::parse::ParseStream,
) -> syn::Result<Expr> {
let mut tokens = TokenStream2::new();
while !input.is_empty() {
if input.peek(Token![=>]) || input.peek(Token![;]) {
break;
}
let tt: TokenTree = input.parse()?;
tokens.extend(std::iter::once(tt));
}
if tokens.is_empty() {
return Ok(syn::parse_quote!(true));
}
syn::parse2(tokens)
}