use proc_macro2::Span;
use syn::ext::IdentExt;
use syn::{DeriveInput, Result};
pub(super) struct JoinClause {
pub table: String,
pub on: String,
pub join_type: String,
}
struct JoinAttr {
table: String,
on: String,
join_type: String,
}
impl syn::parse::Parse for JoinAttr {
fn parse(input: syn::parse::ParseStream) -> Result<Self> {
let ident: syn::Ident = input.parse()?;
if ident != "join" {
return Err(syn::Error::new(ident.span(), "expected join"));
}
let content;
syn::parenthesized!(content in input);
let mut table: Option<String> = None;
let mut on: Option<String> = None;
let mut join_type = "inner".to_string();
loop {
if content.is_empty() {
break;
}
let key = syn::Ident::parse_any(&content)?;
let _: syn::Token![=] = content.parse()?;
let value: syn::LitStr = content.parse()?;
if key == "table" {
table = Some(value.value());
} else if key == "on" {
on = Some(value.value());
} else if key == "type" {
join_type = value.value();
}
if content.peek(syn::Token![,]) {
let _: syn::Token![,] = content.parse()?;
} else {
break;
}
}
let table = table
.ok_or_else(|| syn::Error::new(Span::call_site(), "join requires table = \"...\""))?;
let on =
on.ok_or_else(|| syn::Error::new(Span::call_site(), "join requires on = \"...\""))?;
Ok(JoinAttr {
table,
on,
join_type,
})
}
}
pub(super) fn get_join_clauses(input: &DeriveInput) -> Result<Vec<JoinClause>> {
let mut joins = Vec::new();
for attr in &input.attrs {
if attr.path().is_ident("orm") {
if let syn::Meta::List(meta_list) = &attr.meta {
let tokens = meta_list.tokens.clone();
if let Ok(parsed) = syn::parse2::<JoinAttr>(tokens) {
joins.push(JoinClause {
table: parsed.table,
on: parsed.on,
join_type: parsed.join_type,
});
}
}
}
}
Ok(joins)
}