kosame_dsl 0.3.0

Macro-based Rust ORM focused on developer ergonomics
Documentation
use proc_macro2::TokenStream;
use quote::{ToTokens, quote};
use syn::{
    Token,
    parse::{Parse, ParseStream},
    punctuated::Punctuated,
};

use crate::{
    clause::{Clause, peek_clause},
    expr::ExprRoot,
    keyword,
    parse_option::ParseOption,
    pretty::{PrettyPrint, Printer},
    visit::Visit,
};

pub struct GroupBy {
    pub group_keyword: keyword::group,
    pub by_keyword: keyword::by,
    pub items: Punctuated<GroupByItem, Token![,]>,
}

impl ParseOption for GroupBy {
    fn peek(input: ParseStream) -> bool {
        input.peek(keyword::group) && input.peek2(keyword::by)
    }
}

pub fn visit_group_by<'a>(visit: &mut (impl Visit<'a> + ?Sized), group_by: &'a GroupBy) {
    for item in &group_by.items {
        visit.visit_expr_root(&item.expr);
    }
}

impl Parse for GroupBy {
    fn parse(input: ParseStream) -> syn::Result<Self> {
        Ok(Self {
            group_keyword: input.call(keyword::group::parse_autocomplete)?,
            by_keyword: input.call(keyword::by::parse_autocomplete)?,
            items: {
                let mut punctuated = Punctuated::new();
                while !input.is_empty() {
                    if peek_clause(input) {
                        break;
                    }
                    punctuated.push(input.parse()?);
                    if !input.peek(Token![,]) {
                        break;
                    }
                    punctuated.push_punct(input.parse()?);
                }
                if punctuated.is_empty() {
                    return Err(syn::Error::new(
                        input.span(),
                        "group by clause cannot be empty",
                    ));
                }
                punctuated
            },
        })
    }
}

impl ToTokens for GroupBy {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        let items = self.items.iter();
        quote! { ::kosame::repr::clause::GroupBy::new(&[#(#items),*]) }.to_tokens(tokens);
    }
}

impl PrettyPrint for GroupBy {
    fn pretty_print(&self, printer: &mut Printer<'_>) {
        Clause::new(&[&self.group_keyword, &self.by_keyword], &self.items).pretty_print(printer);
    }
}

pub struct GroupByItem {
    pub expr: ExprRoot,
}

impl Parse for GroupByItem {
    fn parse(input: ParseStream) -> syn::Result<Self> {
        Ok(Self {
            expr: input.parse()?,
        })
    }
}

impl ToTokens for GroupByItem {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        let expr = &self.expr;
        quote! { ::kosame::repr::clause::GroupByItem::new(#expr) }.to_tokens(tokens);
    }
}

impl PrettyPrint for GroupByItem {
    fn pretty_print(&self, printer: &mut Printer<'_>) {
        self.expr.pretty_print(printer);
    }
}