kosame_dsl 0.3.0

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

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

pub struct Set {
    set_keyword: keyword::set,
    items: Punctuated<SetItem, Token![,]>,
}

impl Set {
    pub fn peek(input: ParseStream) -> bool {
        input.peek(keyword::set)
    }
}

pub fn visit_set<'a>(visit: &mut (impl Visit<'a> + ?Sized), set: &'a Set) {
    for item in &set.items {
        visit.visit_set_item(item);
    }
}

impl Parse for Set {
    fn parse(input: ParseStream) -> syn::Result<Self> {
        Ok(Self {
            set_keyword: input.parse()?,
            items: {
                let mut items = Punctuated::<SetItem, _>::new();
                while !input.is_empty() {
                    if peek_clause(input) {
                        break;
                    }

                    items.push(input.parse()?);

                    if !input.peek(Token![,]) {
                        break;
                    }
                    items.push_punct(input.parse()?);
                }

                items
            },
        })
    }
}

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

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

pub enum SetItem {
    Default {
        column: Ident,
        eq_token: Token![=],
        default_keyword: keyword::default,
    },
    Expr {
        column: Ident,
        eq_token: Token![=],
        expr: ExprRoot,
    },
}

pub fn visit_set_item<'a>(visit: &mut (impl Visit<'a> + ?Sized), set_item: &'a SetItem) {
    match set_item {
        SetItem::Default { .. } => {}
        SetItem::Expr { expr, .. } => {
            visit.visit_expr_root(expr);
        }
    }
}

impl Parse for SetItem {
    fn parse(input: ParseStream) -> syn::Result<Self> {
        let column: Ident = input.parse()?;
        let eq_token = input.parse()?;

        if input.peek(keyword::default) {
            Ok(Self::Default {
                column,
                eq_token,
                default_keyword: input.parse()?,
            })
        } else {
            Ok(Self::Expr {
                column,
                eq_token,
                expr: input.parse()?,
            })
        }
    }
}

impl ToTokens for SetItem {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        match self {
            Self::Default { column, .. } => {
                let column = column.to_string();
                quote! {
                    ::kosame::repr::clause::SetItem::Default { column: #column }
                }
            }
            Self::Expr { column, expr, .. } => {
                let column = column.to_string();
                quote! {
                    ::kosame::repr::clause::SetItem::Expr { column: #column, expr: #expr }
                }
            }
        }
        .to_tokens(tokens);
    }
}

impl PrettyPrint for SetItem {
    fn pretty_print(&self, printer: &mut Printer<'_>) {
        match self {
            Self::Default {
                column,
                eq_token,
                default_keyword,
            } => {
                column.pretty_print(printer);
                " ".pretty_print(printer);
                eq_token.pretty_print(printer);
                " ".pretty_print(printer);
                default_keyword.pretty_print(printer);
            }
            Self::Expr {
                column,
                eq_token,
                expr,
            } => {
                column.pretty_print(printer);
                " ".pretty_print(printer);
                eq_token.pretty_print(printer);
                " ".pretty_print(printer);

                printer.scan_indent(1);
                expr.pretty_print(printer);
                printer.scan_indent(-1);
            }
        }
    }
}