Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit1e944c4

Browse files
authored
Merge pull request#931 from matt-duch/derive-generic
Derive Generic FromSql/ToSql
2 parents14e11bd +3827b2e commit1e944c4

File tree

5 files changed

+179
-10
lines changed

5 files changed

+179
-10
lines changed

‎postgres-derive-test/src/composites.rs‎

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
usecrate::test_type;
1+
usecrate::{test_type, test_type_asymmetric};
22
use postgres::{Client,NoTls};
33
use postgres_types::{FromSql,ToSql,WrongType};
44
use std::error::Error;
@@ -238,3 +238,68 @@ fn raw_ident_field() {
238238

239239
test_type(&mut conn,"inventory_item",&[(item,"ROW('foo')")]);
240240
}
241+
242+
#[test]
243+
fngenerics(){
244+
#[derive(FromSql,Debug,PartialEq)]
245+
structInventoryItem<T:Clone,U>
246+
where
247+
U:Clone,
248+
{
249+
name:String,
250+
supplier_id:T,
251+
price:Option<U>,
252+
}
253+
254+
// doesn't make sense to implement derived FromSql on a type with borrows
255+
#[derive(ToSql,Debug,PartialEq)]
256+
#[postgres(name ="InventoryItem")]
257+
structInventoryItemRef<'a,T:'a +Clone,U>
258+
where
259+
U:'a +Clone,
260+
{
261+
name:&'astr,
262+
supplier_id:&'aT,
263+
price:Option<&'aU>,
264+
}
265+
266+
constNAME:&str ="foobar";
267+
constSUPPLIER_ID:i32 =100;
268+
constPRICE:f64 =15.50;
269+
270+
letmut conn =Client::connect("user=postgres host=localhost port=5433",NoTls).unwrap();
271+
conn.batch_execute(
272+
"CREATE TYPE pg_temp.\"InventoryItem\" AS (
273+
name TEXT,
274+
supplier_id INT,
275+
price DOUBLE PRECISION
276+
);",
277+
)
278+
.unwrap();
279+
280+
let item =InventoryItemRef{
281+
name:NAME,
282+
supplier_id:&SUPPLIER_ID,
283+
price:Some(&PRICE),
284+
};
285+
286+
let item_null =InventoryItemRef{
287+
name:NAME,
288+
supplier_id:&SUPPLIER_ID,
289+
price:None,
290+
};
291+
292+
test_type_asymmetric(
293+
&mut conn,
294+
"\"InventoryItem\"",
295+
&[
296+
(item,"ROW('foobar', 100, 15.50)"),
297+
(item_null,"ROW('foobar', 100, NULL)"),
298+
],
299+
|t:&InventoryItemRef<i32,f64>,f:&InventoryItem<i32,f64>|{
300+
t.name == f.name.as_str()
301+
&& t.supplier_id ==&f.supplier_id
302+
&& t.price == f.price.as_ref()
303+
},
304+
);
305+
}

‎postgres-derive-test/src/lib.rs‎

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,30 @@ where
2727
}
2828
}
2929

30+
pubfntest_type_asymmetric<T,F,S,C>(
31+
conn:&mutClient,
32+
sql_type:&str,
33+
checks:&[(T,S)],
34+
cmp:C,
35+
)where
36+
T:ToSql +Sync,
37+
F:FromSqlOwned,
38+
S: fmt::Display,
39+
C:Fn(&T,&F) ->bool,
40+
{
41+
for&(ref val,ref repr)in checks.iter(){
42+
let stmt = conn
43+
.prepare(&*format!("SELECT {}::{}",*repr, sql_type))
44+
.unwrap();
45+
let result:F = conn.query_one(&stmt,&[]).unwrap().get(0);
46+
assert!(cmp(val,&result));
47+
48+
let stmt = conn.prepare(&*format!("SELECT $1::{}", sql_type)).unwrap();
49+
let result:F = conn.query_one(&stmt,&[val]).unwrap().get(0);
50+
assert!(cmp(val,&result));
51+
}
52+
}
53+
3054
#[test]
3155
fncompile_fail(){
3256
trybuild::TestCases::new().compile_fail("src/compile-fail/*.rs");

‎postgres-derive/src/composites.rs‎

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
use syn::{Error,Ident,Type};
1+
use proc_macro2::Span;
2+
use syn::{
3+
punctuated::Punctuated,Error,GenericParam,Generics,Ident,Path,PathSegment,Type,
4+
TypeParamBound,
5+
};
26

37
usecrate::overrides::Overrides;
48

@@ -26,3 +30,23 @@ impl Field {
2630
})
2731
}
2832
}
33+
34+
pub(crate)fnappend_generic_bound(mutgenerics:Generics,bound:&TypeParamBound) ->Generics{
35+
for paramin&mut generics.params{
36+
ifletGenericParam::Type(param) = param{
37+
param.bounds.push(bound.to_owned())
38+
}
39+
}
40+
generics
41+
}
42+
43+
pub(crate)fnnew_derive_path(last:PathSegment) ->Path{
44+
letmut path =Path{
45+
leading_colon:None,
46+
segments:Punctuated::new(),
47+
};
48+
path.segments
49+
.push(Ident::new("postgres_types",Span::call_site()).into());
50+
path.segments.push(last);
51+
path
52+
}

‎postgres-derive/src/fromsql.rs‎

Lines changed: 46 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
1-
use proc_macro2::TokenStream;
1+
use proc_macro2::{Span,TokenStream};
22
use quote::{format_ident, quote};
33
use std::iter;
4-
use syn::{Data,DataStruct,DeriveInput,Error,Fields,Ident};
4+
use syn::{
5+
punctuated::Punctuated, token,AngleBracketedGenericArguments,Data,DataStruct,DeriveInput,
6+
Error,Fields,GenericArgument,GenericParam,Generics,Ident,Lifetime,LifetimeDef,
7+
PathArguments,PathSegment,
8+
};
9+
use syn::{TraitBound,TraitBoundModifier,TypeParamBound};
510

611
usecrate::accepts;
712
usecrate::composites::Field;
13+
usecrate::composites::{append_generic_bound, new_derive_path};
814
usecrate::enums::Variant;
915
usecrate::overrides::Overrides;
1016

@@ -86,10 +92,13 @@ pub fn expand_derive_fromsql(input: DeriveInput) -> Result<TokenStream, Error> {
8692
};
8793

8894
let ident =&input.ident;
95+
let(generics, lifetime) =build_generics(&input.generics);
96+
let(impl_generics, _, _) = generics.split_for_impl();
97+
let(_, ty_generics, where_clause) = input.generics.split_for_impl();
8998
let out =quote!{
90-
impl<'a> postgres_types::FromSql<'a>for #ident{
91-
fn from_sql(_type:&postgres_types::Type, buf:&'a[u8])
92-
-> std::result::Result<#ident,
99+
impl#impl_generics postgres_types::FromSql<#lifetime>for #ident#ty_generics #where_clause{
100+
fn from_sql(_type:&postgres_types::Type, buf:&#lifetime[u8])
101+
-> std::result::Result<#ident#ty_generics,
93102
std::boxed::Box<dyn std::error::Error +
94103
std::marker::Sync +
95104
std::marker::Send>>{
@@ -200,3 +209,35 @@ fn composite_body(ident: &Ident, fields: &[Field]) -> TokenStream {
200209
})
201210
}
202211
}
212+
213+
fnbuild_generics(source:&Generics) ->(Generics,Lifetime){
214+
// don't worry about lifetime name collisions, it doesn't make sense to derive FromSql on a struct with a lifetime
215+
let lifetime =Lifetime::new("'a",Span::call_site());
216+
217+
letmut out =append_generic_bound(source.to_owned(),&new_fromsql_bound(&lifetime));
218+
out.params.insert(
219+
0,
220+
GenericParam::Lifetime(LifetimeDef::new(lifetime.to_owned())),
221+
);
222+
223+
(out, lifetime)
224+
}
225+
226+
fnnew_fromsql_bound(lifetime:&Lifetime) ->TypeParamBound{
227+
letmut path_segment:PathSegment =Ident::new("FromSql",Span::call_site()).into();
228+
letmut seg_args =Punctuated::new();
229+
seg_args.push(GenericArgument::Lifetime(lifetime.to_owned()));
230+
path_segment.arguments =PathArguments::AngleBracketed(AngleBracketedGenericArguments{
231+
colon2_token:None,
232+
lt_token: token::Lt::default(),
233+
args: seg_args,
234+
gt_token: token::Gt::default(),
235+
});
236+
237+
TypeParamBound::Trait(TraitBound{
238+
lifetimes:None,
239+
modifier:TraitBoundModifier::None,
240+
paren_token:None,
241+
path:new_derive_path(path_segment),
242+
})
243+
}

‎postgres-derive/src/tosql.rs‎

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
1-
use proc_macro2::TokenStream;
1+
use proc_macro2::{Span,TokenStream};
22
use quote::quote;
33
use std::iter;
4-
use syn::{Data,DataStruct,DeriveInput,Error,Fields,Ident};
4+
use syn::{
5+
Data,DataStruct,DeriveInput,Error,Fields,Ident,TraitBound,TraitBoundModifier,
6+
TypeParamBound,
7+
};
58

69
usecrate::accepts;
710
usecrate::composites::Field;
11+
usecrate::composites::{append_generic_bound, new_derive_path};
812
usecrate::enums::Variant;
913
usecrate::overrides::Overrides;
1014

@@ -82,8 +86,10 @@ pub fn expand_derive_tosql(input: DeriveInput) -> Result<TokenStream, Error> {
8286
};
8387

8488
let ident =&input.ident;
89+
let generics =append_generic_bound(input.generics.to_owned(),&new_tosql_bound());
90+
let(impl_generics, ty_generics, where_clause) = generics.split_for_impl();
8591
let out =quote!{
86-
impl postgres_types::ToSqlfor #ident{
92+
impl#impl_generics postgres_types::ToSqlfor #ident#ty_generics #where_clause{
8793
fn to_sql(&self,
8894
_type:&postgres_types::Type,
8995
buf:&mut postgres_types::private::BytesMut)
@@ -181,3 +187,12 @@ fn composite_body(fields: &[Field]) -> TokenStream {
181187
std::result::Result::Ok(postgres_types::IsNull::No)
182188
}
183189
}
190+
191+
fnnew_tosql_bound() ->TypeParamBound{
192+
TypeParamBound::Trait(TraitBound{
193+
lifetimes:None,
194+
modifier:TraitBoundModifier::None,
195+
paren_token:None,
196+
path:new_derive_path(Ident::new("ToSql",Span::call_site()).into()),
197+
})
198+
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp