Skip to main content

sql_fun/
lib.rs

1#![deny(missing_docs)]
2//! procedual macro `sql-fun`
3//!
4mod connector;
5mod errors;
6mod generate_sql_execute;
7mod generate_sql_query_one;
8mod generate_sql_query_opt;
9mod generate_sqy_query_many;
10mod rust_ast;
11mod sql_ast;
12mod tokio_postgres_generator;
13
14use self::{
15    connector::Connector,
16    errors::SqlFunError,
17    generate_sql_execute::generate_sql_execute,
18    generate_sql_query_one::generate_sql_query_one,
19    generate_sql_query_opt::generate_sql_query_opt,
20    generate_sqy_query_many::generate_sql_query_many,
21    rust_ast::{
22        get_arg_name, get_generic_type_bounds, get_last_parameter_name, get_last_parameter_type,
23        get_last_parameter_type_from_fn_trait, get_result_types,
24    },
25    tokio_postgres_generator::{
26        TokioPostgresCodeGenerator, TokioPostgresCodeGeneratorBuilderError,
27    },
28};
29
30use proc_macro2::TokenStream;
31
32use syn::{Ident, ItemFn, LitStr, Path, Type, parse_macro_input};
33
34///
35/// fill table/view fields to struct
36///  
37#[proc_macro_attribute]
38pub fn from_table(
39    table_name: proc_macro::TokenStream,
40    item: proc_macro::TokenStream,
41) -> proc_macro::TokenStream {
42    let table_name = parse_macro_input!(table_name as LitStr).value();
43
44    match generate_table_struct(table_name, item.into()) {
45        Ok(ts) => ts.into(),
46        Err(err) => err.to_compile_error(),
47    }
48}
49
50fn generate_table_struct(
51    _table_name: String,
52    item: TokenStream,
53) -> Result<TokenStream, SqlFunError> {
54    Ok(item)
55}
56
57#[proc_macro_attribute]
58#[doc = include_str!("attr_sql_query_one.md")]
59pub fn sql_query_one(
60    sql_attr: proc_macro::TokenStream,
61    item: proc_macro::TokenStream,
62) -> proc_macro::TokenStream {
63    let sql_str = parse_macro_input!(sql_attr as LitStr).value();
64
65    match generate_sql_query_one(sql_str, item.into()) {
66        Ok(ts) => ts.into(),
67        Err(err) => err.to_compile_error(),
68    }
69}
70
71#[proc_macro_attribute]
72#[doc = include_str!("attr_sql_query_opt.md")]
73pub fn sql_query_opt(
74    sql_attr: proc_macro::TokenStream,
75    item: proc_macro::TokenStream,
76) -> proc_macro::TokenStream {
77    let sql_str = parse_macro_input!(sql_attr as LitStr).value();
78
79    match generate_sql_query_opt(sql_str, item.into()) {
80        Ok(ts) => ts.into(),
81        Err(err) => err.to_compile_error(),
82    }
83}
84
85#[proc_macro_attribute]
86#[doc = include_str!("attr_sql_query_many.md")]
87pub fn sql_query_many(
88    sql_attr: proc_macro::TokenStream,
89    item: proc_macro::TokenStream,
90) -> proc_macro::TokenStream {
91    let sql_str = parse_macro_input!(sql_attr as LitStr).value();
92
93    match generate_sql_query_many(sql_str, item.into()) {
94        Ok(ts) => ts.into(),
95        Err(err) => err.to_compile_error(),
96    }
97}
98
99/// This macro attribute is used to specify the SQL statement to be executed
100///
101/// ```no_run
102/// use tokio_postgres::{Client as PgClient, Error as PgError};
103///
104/// #[sql_fun::sql_statement("create table users(id serial primary key, name text);")]
105/// async fn create_table_users(client: &PgClient) -> Result<u64, PgError> {}
106/// ```
107///
108/// please set connector in Cargo.toml
109///
110/// ```toml
111/// [package.metadata.database]
112/// connector = "tokio-postgres"
113/// ```
114///
115/// sql_fun supports only the `tokio-postgres` for database connector(currently).
116///
117#[proc_macro_attribute]
118pub fn sql_statement(
119    sql_attr: proc_macro::TokenStream,
120    item: proc_macro::TokenStream,
121) -> proc_macro::TokenStream {
122    let sql_str = parse_macro_input!(sql_attr as LitStr).value();
123
124    match generate_sql_execute(sql_str, item.into()) {
125        Ok(ts) => ts.into(),
126        Err(err) => err.to_compile_error(),
127    }
128}
129
130fn get_result_type_args(item_fn: &ItemFn) -> Result<(Path, Path), SqlFunError> {
131    let Some((value_type, error_type)) = get_result_types(item_fn) else {
132        return Err(SqlFunError::custom("function must returns Result<T,E>"));
133    };
134    let Type::Path(value_path) = value_type else {
135        return Err(SqlFunError::custom(
136            "must T is concrete type on Result<T,E> ",
137        ));
138    };
139    let Type::Path(error_path) = error_type else {
140        return Err(SqlFunError::custom(
141            "must E is concrete type on Result<T,E>",
142        ));
143    };
144    Ok((value_path.path.clone(), error_path.path.clone()))
145}
146
147fn handler_type_name(function_ast: &ItemFn) -> Result<String, SqlFunError> {
148    let ident = get_last_parameter_type(function_ast)
149        .ok_or_else(|| SqlFunError::custom("function must have a handler argument"))?;
150    Ok(ident.to_string())
151}
152
153fn handler_arg_name(function_ast: &ItemFn) -> Result<Ident, SqlFunError> {
154    get_last_parameter_name(function_ast)
155        .ok_or_else(|| SqlFunError::custom("function must have a handler argument"))
156}
157
158fn collector_arg_name(function_ast: &ItemFn) -> Result<Ident, SqlFunError> {
159    let index = function_ast.sig.inputs.len() - 2;
160    function_ast
161        .sig
162        .inputs
163        .iter()
164        .nth(index)
165        .and_then(get_arg_name)
166        .ok_or_else(|| SqlFunError::custom("invalid collector arg"))
167}