Add #[postgres(transparent)]
This commit is contained in:
parent
630f179892
commit
4561d44661
35
postgres-derive-test/src/compile-fail/invalid-transparent.rs
Normal file
35
postgres-derive-test/src/compile-fail/invalid-transparent.rs
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
use postgres_types::{FromSql, ToSql};
|
||||||
|
|
||||||
|
#[derive(ToSql, Debug)]
|
||||||
|
#[postgres(transparent)]
|
||||||
|
struct ToSqlTransparentStruct {
|
||||||
|
a: i32
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(FromSql, Debug)]
|
||||||
|
#[postgres(transparent)]
|
||||||
|
struct FromSqlTransparentStruct {
|
||||||
|
a: i32
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(ToSql, Debug)]
|
||||||
|
#[postgres(transparent)]
|
||||||
|
enum ToSqlTransparentEnum {
|
||||||
|
Foo
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(FromSql, Debug)]
|
||||||
|
#[postgres(transparent)]
|
||||||
|
enum FromSqlTransparentEnum {
|
||||||
|
Foo
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(ToSql, Debug)]
|
||||||
|
#[postgres(transparent)]
|
||||||
|
struct ToSqlTransparentTwoFieldTupleStruct(i32, i32);
|
||||||
|
|
||||||
|
#[derive(FromSql, Debug)]
|
||||||
|
#[postgres(transparent)]
|
||||||
|
struct FromSqlTransparentTwoFieldTupleStruct(i32, i32);
|
||||||
|
|
||||||
|
fn main() {}
|
@ -0,0 +1,49 @@
|
|||||||
|
error: #[postgres(transparent)] may only be applied to single field tuple structs
|
||||||
|
--> src/compile-fail/invalid-transparent.rs:4:1
|
||||||
|
|
|
||||||
|
4 | / #[postgres(transparent)]
|
||||||
|
5 | | struct ToSqlTransparentStruct {
|
||||||
|
6 | | a: i32
|
||||||
|
7 | | }
|
||||||
|
| |_^
|
||||||
|
|
||||||
|
error: #[postgres(transparent)] may only be applied to single field tuple structs
|
||||||
|
--> src/compile-fail/invalid-transparent.rs:10:1
|
||||||
|
|
|
||||||
|
10 | / #[postgres(transparent)]
|
||||||
|
11 | | struct FromSqlTransparentStruct {
|
||||||
|
12 | | a: i32
|
||||||
|
13 | | }
|
||||||
|
| |_^
|
||||||
|
|
||||||
|
error: #[postgres(transparent)] may only be applied to single field tuple structs
|
||||||
|
--> src/compile-fail/invalid-transparent.rs:16:1
|
||||||
|
|
|
||||||
|
16 | / #[postgres(transparent)]
|
||||||
|
17 | | enum ToSqlTransparentEnum {
|
||||||
|
18 | | Foo
|
||||||
|
19 | | }
|
||||||
|
| |_^
|
||||||
|
|
||||||
|
error: #[postgres(transparent)] may only be applied to single field tuple structs
|
||||||
|
--> src/compile-fail/invalid-transparent.rs:22:1
|
||||||
|
|
|
||||||
|
22 | / #[postgres(transparent)]
|
||||||
|
23 | | enum FromSqlTransparentEnum {
|
||||||
|
24 | | Foo
|
||||||
|
25 | | }
|
||||||
|
| |_^
|
||||||
|
|
||||||
|
error: #[postgres(transparent)] may only be applied to single field tuple structs
|
||||||
|
--> src/compile-fail/invalid-transparent.rs:28:1
|
||||||
|
|
|
||||||
|
28 | / #[postgres(transparent)]
|
||||||
|
29 | | struct ToSqlTransparentTwoFieldTupleStruct(i32, i32);
|
||||||
|
| |_____________________________________________________^
|
||||||
|
|
||||||
|
error: #[postgres(transparent)] may only be applied to single field tuple structs
|
||||||
|
--> src/compile-fail/invalid-transparent.rs:32:1
|
||||||
|
|
|
||||||
|
32 | / #[postgres(transparent)]
|
||||||
|
33 | | struct FromSqlTransparentTwoFieldTupleStruct(i32, i32);
|
||||||
|
| |_______________________________________________________^
|
@ -7,6 +7,7 @@ use std::fmt;
|
|||||||
mod composites;
|
mod composites;
|
||||||
mod domains;
|
mod domains;
|
||||||
mod enums;
|
mod enums;
|
||||||
|
mod transparent;
|
||||||
|
|
||||||
pub fn test_type<T, S>(conn: &mut Client, sql_type: &str, checks: &[(T, S)])
|
pub fn test_type<T, S>(conn: &mut Client, sql_type: &str, checks: &[(T, S)])
|
||||||
where
|
where
|
||||||
|
18
postgres-derive-test/src/transparent.rs
Normal file
18
postgres-derive-test/src/transparent.rs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
use postgres::{Client, NoTls};
|
||||||
|
use postgres_types::{FromSql, ToSql};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn round_trip() {
|
||||||
|
#[derive(FromSql, ToSql, Debug, PartialEq)]
|
||||||
|
#[postgres(transparent)]
|
||||||
|
struct UserId(i32);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
Client::connect("user=postgres host=localhost port=5433", NoTls)
|
||||||
|
.unwrap()
|
||||||
|
.query_one("SELECT $1::integer", &[&UserId(123)])
|
||||||
|
.unwrap()
|
||||||
|
.get::<_, UserId>(0),
|
||||||
|
UserId(123)
|
||||||
|
);
|
||||||
|
}
|
@ -6,6 +6,14 @@ use syn::Ident;
|
|||||||
use crate::composites::Field;
|
use crate::composites::Field;
|
||||||
use crate::enums::Variant;
|
use crate::enums::Variant;
|
||||||
|
|
||||||
|
pub fn transparent_body(field: &syn::Field) -> TokenStream {
|
||||||
|
let ty = &field.ty;
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
<#ty as ::postgres_types::ToSql>::accepts(type_)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn domain_body(name: &str, field: &syn::Field) -> TokenStream {
|
pub fn domain_body(name: &str, field: &syn::Field) -> TokenStream {
|
||||||
let ty = &field.ty;
|
let ty = &field.ty;
|
||||||
|
|
||||||
|
@ -11,9 +11,36 @@ use crate::overrides::Overrides;
|
|||||||
pub fn expand_derive_fromsql(input: DeriveInput) -> Result<TokenStream, Error> {
|
pub fn expand_derive_fromsql(input: DeriveInput) -> Result<TokenStream, Error> {
|
||||||
let overrides = Overrides::extract(&input.attrs)?;
|
let overrides = Overrides::extract(&input.attrs)?;
|
||||||
|
|
||||||
|
if overrides.name.is_some() && overrides.transparent {
|
||||||
|
return Err(Error::new_spanned(
|
||||||
|
&input,
|
||||||
|
"#[postgres(transparent)] is not allowed with #[postgres(name = \"...\")]",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
let name = overrides.name.unwrap_or_else(|| input.ident.to_string());
|
let name = overrides.name.unwrap_or_else(|| input.ident.to_string());
|
||||||
|
|
||||||
let (accepts_body, to_sql_body) = match input.data {
|
let (accepts_body, to_sql_body) = if overrides.transparent {
|
||||||
|
match input.data {
|
||||||
|
Data::Struct(DataStruct {
|
||||||
|
fields: Fields::Unnamed(ref fields),
|
||||||
|
..
|
||||||
|
}) if fields.unnamed.len() == 1 => {
|
||||||
|
let field = fields.unnamed.first().unwrap();
|
||||||
|
(
|
||||||
|
accepts::transparent_body(field),
|
||||||
|
transparent_body(&input.ident, field),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
return Err(Error::new_spanned(
|
||||||
|
input,
|
||||||
|
"#[postgres(transparent)] may only be applied to single field tuple structs",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
match input.data {
|
||||||
Data::Enum(ref data) => {
|
Data::Enum(ref data) => {
|
||||||
let variants = data
|
let variants = data
|
||||||
.variants
|
.variants
|
||||||
@ -55,6 +82,7 @@ pub fn expand_derive_fromsql(input: DeriveInput) -> Result<TokenStream, Error> {
|
|||||||
"#[derive(FromSql)] may only be applied to structs, single field tuple structs, and enums",
|
"#[derive(FromSql)] may only be applied to structs, single field tuple structs, and enums",
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let ident = &input.ident;
|
let ident = &input.ident;
|
||||||
@ -77,6 +105,13 @@ pub fn expand_derive_fromsql(input: DeriveInput) -> Result<TokenStream, Error> {
|
|||||||
Ok(out)
|
Ok(out)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn transparent_body(ident: &Ident, field: &syn::Field) -> TokenStream {
|
||||||
|
let ty = &field.ty;
|
||||||
|
quote! {
|
||||||
|
<#ty as postgres_types::FromSql>::from_sql(_type, buf).map(#ident)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn enum_body(ident: &Ident, variants: &[Variant]) -> TokenStream {
|
fn enum_body(ident: &Ident, variants: &[Variant]) -> TokenStream {
|
||||||
let variant_names = variants.iter().map(|v| &v.name);
|
let variant_names = variants.iter().map(|v| &v.name);
|
||||||
let idents = iter::repeat(ident);
|
let idents = iter::repeat(ident);
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
extern crate proc_macro;
|
extern crate proc_macro;
|
||||||
|
|
||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
|
use syn::parse_macro_input;
|
||||||
|
|
||||||
mod accepts;
|
mod accepts;
|
||||||
mod composites;
|
mod composites;
|
||||||
@ -14,7 +15,8 @@ mod tosql;
|
|||||||
|
|
||||||
#[proc_macro_derive(ToSql, attributes(postgres))]
|
#[proc_macro_derive(ToSql, attributes(postgres))]
|
||||||
pub fn derive_tosql(input: TokenStream) -> TokenStream {
|
pub fn derive_tosql(input: TokenStream) -> TokenStream {
|
||||||
let input = syn::parse(input).unwrap();
|
let input = parse_macro_input!(input);
|
||||||
|
|
||||||
tosql::expand_derive_tosql(input)
|
tosql::expand_derive_tosql(input)
|
||||||
.unwrap_or_else(|e| e.to_compile_error())
|
.unwrap_or_else(|e| e.to_compile_error())
|
||||||
.into()
|
.into()
|
||||||
@ -22,7 +24,8 @@ pub fn derive_tosql(input: TokenStream) -> TokenStream {
|
|||||||
|
|
||||||
#[proc_macro_derive(FromSql, attributes(postgres))]
|
#[proc_macro_derive(FromSql, attributes(postgres))]
|
||||||
pub fn derive_fromsql(input: TokenStream) -> TokenStream {
|
pub fn derive_fromsql(input: TokenStream) -> TokenStream {
|
||||||
let input = syn::parse(input).unwrap();
|
let input = parse_macro_input!(input);
|
||||||
|
|
||||||
fromsql::expand_derive_fromsql(input)
|
fromsql::expand_derive_fromsql(input)
|
||||||
.unwrap_or_else(|e| e.to_compile_error())
|
.unwrap_or_else(|e| e.to_compile_error())
|
||||||
.into()
|
.into()
|
||||||
|
@ -2,17 +2,18 @@ use syn::{Attribute, Error, Lit, Meta, NestedMeta};
|
|||||||
|
|
||||||
pub struct Overrides {
|
pub struct Overrides {
|
||||||
pub name: Option<String>,
|
pub name: Option<String>,
|
||||||
|
pub transparent: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Overrides {
|
impl Overrides {
|
||||||
pub fn extract(attrs: &[Attribute]) -> Result<Overrides, Error> {
|
pub fn extract(attrs: &[Attribute]) -> Result<Overrides, Error> {
|
||||||
let mut overrides = Overrides { name: None };
|
let mut overrides = Overrides {
|
||||||
|
name: None,
|
||||||
|
transparent: false,
|
||||||
|
};
|
||||||
|
|
||||||
for attr in attrs {
|
for attr in attrs {
|
||||||
let attr = match attr.parse_meta() {
|
let attr = attr.parse_meta()?;
|
||||||
Ok(meta) => meta,
|
|
||||||
Err(_) => continue,
|
|
||||||
};
|
|
||||||
|
|
||||||
if !attr.path().is_ident("postgres") {
|
if !attr.path().is_ident("postgres") {
|
||||||
continue;
|
continue;
|
||||||
@ -39,7 +40,14 @@ impl Overrides {
|
|||||||
|
|
||||||
overrides.name = Some(value);
|
overrides.name = Some(value);
|
||||||
}
|
}
|
||||||
bad => return Err(Error::new_spanned(bad, "expected a name-value meta item")),
|
NestedMeta::Meta(Meta::Path(ref path)) => {
|
||||||
|
if !path.is_ident("transparent") {
|
||||||
|
return Err(Error::new_spanned(path, "unknown override"));
|
||||||
|
}
|
||||||
|
|
||||||
|
overrides.transparent = true;
|
||||||
|
}
|
||||||
|
bad => return Err(Error::new_spanned(bad, "unknown attribute")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,46 +11,73 @@ use crate::overrides::Overrides;
|
|||||||
pub fn expand_derive_tosql(input: DeriveInput) -> Result<TokenStream, Error> {
|
pub fn expand_derive_tosql(input: DeriveInput) -> Result<TokenStream, Error> {
|
||||||
let overrides = Overrides::extract(&input.attrs)?;
|
let overrides = Overrides::extract(&input.attrs)?;
|
||||||
|
|
||||||
|
if overrides.name.is_some() && overrides.transparent {
|
||||||
|
return Err(Error::new_spanned(
|
||||||
|
&input,
|
||||||
|
"#[postgres(transparent)] is not allowed with #[postgres(name = \"...\")]",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
let name = overrides.name.unwrap_or_else(|| input.ident.to_string());
|
let name = overrides.name.unwrap_or_else(|| input.ident.to_string());
|
||||||
|
|
||||||
let (accepts_body, to_sql_body) = match input.data {
|
let (accepts_body, to_sql_body) = if overrides.transparent {
|
||||||
Data::Enum(ref data) => {
|
match input.data {
|
||||||
let variants = data
|
Data::Struct(DataStruct {
|
||||||
.variants
|
fields: Fields::Unnamed(ref fields),
|
||||||
.iter()
|
..
|
||||||
.map(Variant::parse)
|
}) if fields.unnamed.len() == 1 => {
|
||||||
.collect::<Result<Vec<_>, _>>()?;
|
let field = fields.unnamed.first().unwrap();
|
||||||
(
|
|
||||||
accepts::enum_body(&name, &variants),
|
(accepts::transparent_body(field), transparent_body())
|
||||||
enum_body(&input.ident, &variants),
|
}
|
||||||
)
|
_ => {
|
||||||
|
return Err(Error::new_spanned(
|
||||||
|
input,
|
||||||
|
"#[postgres(transparent)] may only be applied to single field tuple structs",
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Data::Struct(DataStruct {
|
} else {
|
||||||
fields: Fields::Unnamed(ref fields),
|
match input.data {
|
||||||
..
|
Data::Enum(ref data) => {
|
||||||
}) if fields.unnamed.len() == 1 => {
|
let variants = data
|
||||||
let field = fields.unnamed.first().unwrap();
|
.variants
|
||||||
(accepts::domain_body(&name, field), domain_body())
|
.iter()
|
||||||
}
|
.map(Variant::parse)
|
||||||
Data::Struct(DataStruct {
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
fields: Fields::Named(ref fields),
|
(
|
||||||
..
|
accepts::enum_body(&name, &variants),
|
||||||
}) => {
|
enum_body(&input.ident, &variants),
|
||||||
let fields = fields
|
)
|
||||||
.named
|
}
|
||||||
.iter()
|
Data::Struct(DataStruct {
|
||||||
.map(Field::parse)
|
fields: Fields::Unnamed(ref fields),
|
||||||
.collect::<Result<Vec<_>, _>>()?;
|
..
|
||||||
(
|
}) if fields.unnamed.len() == 1 => {
|
||||||
accepts::composite_body(&name, "ToSql", &fields),
|
let field = fields.unnamed.first().unwrap();
|
||||||
composite_body(&fields),
|
|
||||||
)
|
(accepts::domain_body(&name, field), domain_body())
|
||||||
}
|
}
|
||||||
_ => {
|
Data::Struct(DataStruct {
|
||||||
return Err(Error::new_spanned(
|
fields: Fields::Named(ref fields),
|
||||||
input,
|
..
|
||||||
"#[derive(ToSql)] may only be applied to structs, single field tuple structs, and enums",
|
}) => {
|
||||||
));
|
let fields = fields
|
||||||
|
.named
|
||||||
|
.iter()
|
||||||
|
.map(Field::parse)
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
(
|
||||||
|
accepts::composite_body(&name, "ToSql", &fields),
|
||||||
|
composite_body(&fields),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
return Err(Error::new_spanned(
|
||||||
|
input,
|
||||||
|
"#[derive(ToSql)] may only be applied to structs, single field tuple structs, and enums",
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -78,6 +105,12 @@ pub fn expand_derive_tosql(input: DeriveInput) -> Result<TokenStream, Error> {
|
|||||||
Ok(out)
|
Ok(out)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn transparent_body() -> TokenStream {
|
||||||
|
quote! {
|
||||||
|
postgres_types::ToSql::to_sql(&self.0, _type, buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn enum_body(ident: &Ident, variants: &[Variant]) -> TokenStream {
|
fn enum_body(ident: &Ident, variants: &[Variant]) -> TokenStream {
|
||||||
let idents = iter::repeat(ident);
|
let idents = iter::repeat(ident);
|
||||||
let variant_idents = variants.iter().map(|v| &v.ident);
|
let variant_idents = variants.iter().map(|v| &v.ident);
|
||||||
|
@ -55,6 +55,21 @@
|
|||||||
//! struct SessionId(Vec<u8>);
|
//! struct SessionId(Vec<u8>);
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
|
//! ## Newtypes
|
||||||
|
//!
|
||||||
|
//! The `#[postgres(transparent)]` attribute can be used on a single-field tuple struct to create a
|
||||||
|
//! Rust-only wrapper type that will use the [`ToSql`] & [`FromSql`] implementation of the inner
|
||||||
|
//! value :
|
||||||
|
//! ```rust
|
||||||
|
//! # #[cfg(feature = "derive")]
|
||||||
|
//! use postgres_types::{ToSql, FromSql};
|
||||||
|
//!
|
||||||
|
//! # #[cfg(feature = "derive")]
|
||||||
|
//! #[derive(Debug, ToSql, FromSql)]
|
||||||
|
//! #[postgres(transparent)]
|
||||||
|
//! struct UserId(i32);
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
//! ## Composites
|
//! ## Composites
|
||||||
//!
|
//!
|
||||||
//! Postgres composite types correspond to structs in Rust:
|
//! Postgres composite types correspond to structs in Rust:
|
||||||
|
Loading…
Reference in New Issue
Block a user