use proc_macro::TokenStream;
use quote::quote;
use syn::{
Ident, Token,
parse::{Parse, ParseStream},
};
use crate::query::SqlQuery;
mod condition;
mod limit;
mod orderby;
mod query;
#[proc_macro]
pub fn qb_sql(input: TokenStream) -> TokenStream {
let query = syn::parse_macro_input!(input as SqlQuery);
let expanded = query.expand();
TokenStream::from(expanded)
}
struct OptionField(Vec<Ident>);
impl OptionField {
fn to_camel_case_string(&self) -> String {
let camel_parts: Vec<String> = self
.0
.iter()
.map(|ident| snake_to_camel_case(&ident.to_string()))
.collect();
camel_parts.join(".")
}
}
impl Parse for OptionField {
fn parse(input: ParseStream) -> syn::Result<Self> {
let mut idents = Vec::new();
idents.push(input.parse::<Ident>()?);
while input.peek(Token![.]) {
input.parse::<Token![.]>()?;
idents.push(input.parse::<Ident>()?);
}
Ok(OptionField(idents))
}
}
fn snake_to_camel_case(s: &str) -> String {
s.split('_')
.map(|word| {
let mut c = word.chars();
match c.next() {
None => String::new(),
Some(f) => f.to_uppercase().collect::<String>() + c.as_str(),
}
})
.collect()
}
impl std::fmt::Display for OptionField {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.to_camel_case_string())
}
}
impl quote::ToTokens for OptionField {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
if self.0.is_empty() {
return;
}
let fields = &self.0;
let first = &fields[0];
let mut combined = quote! { #first.clone()._qb_wrap() };
for i in 1..fields.len() {
let item = &fields[i];
combined = quote! { #combined._qb_access(|v| v.#item.clone()._qb_wrap()) };
}
tokens.extend(combined);
}
}
mod kw {
syn::custom_keyword!(select);
syn::custom_keyword!(from);
syn::custom_keyword!(and);
syn::custom_keyword!(order);
syn::custom_keyword!(by);
syn::custom_keyword!(limit);
syn::custom_keyword!(offset);
syn::custom_keyword!(asc);
syn::custom_keyword!(desc);
syn::custom_keyword!(like);
}