include-sql 0.2.2

A Yesql inspired macro for using SQL in Rust
Documentation
use proc_macro2::{TokenStream, Span, Group, Delimiter, Literal, Ident, Punct, Spacing};
use quote::{ToTokens, TokenStreamExt};
use crate::sql::{IncludedSql, Stmt, StmtItem};

impl ToTokens for IncludedSql {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        tokens.append(Ident::new("impl_sql", Span::call_site()));
        tokens.append(Punct::new('!', Spacing::Alone));

        let mut macro_args = TokenStream::new();
        let mut name = crate::conv::to_camel_case(&self.file_name);
        name.push_str("Sql");
        macro_args.append(Ident::new(&name, Span::call_site()));
        macro_args.append(Punct::new('=', Spacing::Alone));

        macro_args.append_separated(&self.stmt_list, Punct::new(',', Spacing::Alone));

        tokens.append(Group::new(Delimiter::Brace, macro_args));
    }
}

impl ToTokens for Stmt {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        let mut stmt_tokens = TokenStream::new();

        let last = self.into.len() - 1;
        for (i, ch) in self.into.chars().enumerate() {
            let spacing = if i < last { Spacing::Joint } else { Spacing::Alone };
            stmt_tokens.append(Punct::new(ch, spacing));
        }

        stmt_tokens.append(Ident::new(&self.name, Span::call_site()));

        let mut stmt_params = TokenStream::new();
        for param in self.unique_binds() {
            match param {
                StmtItem::Bind(_) => {
                    stmt_params.append(Punct::new(':', Spacing::Alone));
                },
                StmtItem::List(_) => {
                    stmt_params.append(Punct::new('#', Spacing::Alone));
                },
                _ => {},
            }
            match param {
                StmtItem::Bind(name) | StmtItem::List(name) => {
                    stmt_params.append(Ident::new(name, Span::call_site()));

                    if let Some(param_type) = self.params.get(name) {
                        if let Ok(param_type) = syn::parse_str::<syn::Type>(param_type) {
                            let mut type_tokens = TokenStream::new();
                            param_type.to_tokens(&mut type_tokens);
                            stmt_params.append(Group::new(Delimiter::Parenthesis, type_tokens));
                        } else {
                            stmt_params.append(Ident::new("_", Span::call_site()));
                        }
                    } else {
                        stmt_params.append(Ident::new("_", Span::call_site()));
                    }
                }
                _ => {},
            }
        }
        stmt_tokens.append(Group::new(Delimiter::Parenthesis, stmt_params));

        if let Some(doc_comment) = self.docs.as_ref() {
            stmt_tokens.append(Literal::string(doc_comment));
        } else {
            stmt_tokens.append(Literal::string(""));
        }
        stmt_tokens.append(Punct::new('$', Spacing::Alone));
        stmt_tokens.append_all(&self.items);

        tokens.append(Group::new(Delimiter::Brace, stmt_tokens));
    }
}

impl ToTokens for StmtItem {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        match self {
            Self::Text( text ) => {
                tokens.append(Literal::string(text));
            },
            Self::Bind( name ) => {
                tokens.append(Punct::new(':', Spacing::Alone));
                tokens.append(Ident::new(name, Span::call_site()));
            },
            Self::List( name ) => {
                tokens.append(Punct::new('#', Spacing::Alone));
                tokens.append(Ident::new(name, Span::call_site()));
            }
        }
    }
}