1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
use quote2::{proc_macro2::TokenStream, quote, Quote};
use syn::*;
/// Implements `From<&Row>` trait for a struct, allowing direct conversion from a database row to the struct.
///
/// ## Example
///
/// ```rust
/// use tokio_postgres_utils::FromRow;
///
/// #[derive(FromRow)]
/// struct User {
/// id: i32,
/// name: String,
/// }
/// ```
///
/// Expand into:
///
/// ```
/// impl From<&Row> for User {
/// fn from(row: &Row) -> Self {
/// Self {
/// id: row.get("id"),
/// name: row.get("name"),
/// }
/// }
/// }
/// ```
#[proc_macro_derive(FromRow)]
pub fn from_row(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let name = input.ident;
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
let body = quote(|tokens| match input.data {
Data::Struct(data) => match &data.fields {
Fields::Named(fields) => {
let body = quote(|tokens| {
for field in &fields.named {
if let Some(name) = &field.ident {
let raw_str = name.to_string();
quote!(tokens, {
#name: r.get(#raw_str),
});
}
}
});
quote!(tokens, {
{ #body }
});
}
Fields::Unnamed(fields) => {
let body = quote(|tokens| {
for (i, _) in fields.unnamed.iter().enumerate() {
let idx = Index::from(i);
quote!(tokens, {
r.get(#idx),
});
}
});
quote!(tokens, {
(#body)
});
}
Fields::Unit => {}
},
Data::Enum(_) | Data::Union(_) => unimplemented!(),
});
let mut tokens = TokenStream::new();
quote!(tokens, {
impl #impl_generics ::std::convert::From<&tokio_postgres::Row> for #name #ty_generics #where_clause {
#[inline]
fn from(r: &tokio_postgres::Row) -> Self {
Self #body
}
}
});
tokens.into()
}
/// Implements the `TryFrom<&Row>` trait for a struct
#[proc_macro_derive(TryFromRow)]
pub fn try_from_row(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let name = input.ident;
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
let body = quote(|tokens| match input.data {
Data::Struct(data) => match &data.fields {
Fields::Named(fields) => {
let body = quote(|tokens| {
for field in &fields.named {
if let Some(name) = &field.ident {
let raw_str = name.to_string();
quote!(tokens, {
#name: r.try_get(#raw_str)?,
});
}
}
});
quote!(tokens, {
{ #body }
});
}
Fields::Unnamed(fields) => {
let body = quote(|tokens| {
for (i, _) in fields.unnamed.iter().enumerate() {
let idx = Index::from(i);
quote!(tokens, {
r.try_get(#idx)?,
});
}
});
quote!(tokens, {
(#body)
});
}
Fields::Unit => {}
},
Data::Enum(_) | Data::Union(_) => unimplemented!(),
});
let mut tokens = TokenStream::new();
quote!(tokens, {
impl #impl_generics ::std::convert::TryFrom<&tokio_postgres::Row> for #name #ty_generics #where_clause {
type Error = tokio_postgres::Error;
#[inline]
fn try_from(r: &tokio_postgres::Row) -> ::std::result::Result<Self, Self::Error> {
Ok(Self #body)
}
}
});
tokens.into()
}