Skip to content
This repository was archived by the owner on Oct 10, 2019. It is now read-only.

Commit d0f50fe

Browse files
committed
Support composites
1 parent f1b0ad3 commit d0f50fe

File tree

6 files changed

+421
-6
lines changed

6 files changed

+421
-6
lines changed

postgres-derive-internals/src/accepts.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
use std::fmt::Write;
2+
use quote::{Tokens, ToTokens};
23

34
use enums::Variant;
5+
use composites::Field;
46

57
pub fn enum_body(name: &str, variants: &[Variant]) -> String {
68
let mut body = String::new();
@@ -34,3 +36,39 @@ pub fn enum_body(name: &str, variants: &[Variant]) -> String {
3436

3537
body
3638
}
39+
40+
pub fn composite_body(name: &str, trait_: &str, fields: &[Field]) -> String {
41+
let mut body = String::new();
42+
43+
write!(body, "
44+
if type_.name() != \"{}\" {{
45+
return false;
46+
}}
47+
48+
match *type_.kind() {{
49+
::postgres::types::Kind::Composite(ref fields) => {{
50+
if fields.len() != {} {{
51+
return false;
52+
}}
53+
54+
fields.iter().all(|f| {{
55+
match f.name() {{", name, fields.len()).unwrap();
56+
57+
for field in fields {
58+
let mut tokens = Tokens::new();
59+
field.type_.to_tokens(&mut tokens);
60+
write!(body, "
61+
\"{}\" => <{} as ::postgres::types::{}>::accepts(f.type_()),",
62+
field.name, tokens, trait_).unwrap();
63+
}
64+
65+
write!(body, "\
66+
_ => false,\
67+
}}\
68+
}})\
69+
}}\
70+
_ => false,\
71+
}}").unwrap();
72+
73+
body
74+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
use syn::{self, Ident, Ty};
2+
3+
use overrides::Overrides;
4+
5+
pub struct Field {
6+
pub name: String,
7+
pub ident: Ident,
8+
pub type_: Ty,
9+
}
10+
11+
impl Field {
12+
pub fn parse(raw: &syn::Field) -> Result<Field, String> {
13+
let overrides = try!(Overrides::extract(&raw.attrs));
14+
15+
let ident = raw.ident.as_ref().unwrap().clone();
16+
Ok(Field {
17+
name: overrides.name.unwrap_or_else(|| ident.to_string()),
18+
ident: ident,
19+
type_: raw.ty.clone(),
20+
})
21+
}
22+
}

postgres-derive-internals/src/fromsql.rs

Lines changed: 101 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
use std::fmt::Write;
2-
use syn::{Body, Ident, MacroInput, VariantData, Field};
2+
use syn::{self, Body, Ident, MacroInput, VariantData};
33
use quote::{Tokens, ToTokens};
44

55
use accepts;
6+
use composites::Field;
67
use enums::Variant;
78
use overrides::Overrides;
89

@@ -20,6 +21,11 @@ pub fn expand_derive_fromsql(input: &MacroInput) -> Result<String, String> {
2021
let field = &fields[0];
2122
(domain_accepts_body(field), domain_body(&input.ident, field))
2223
}
24+
Body::Struct(VariantData::Struct(ref fields)) => {
25+
let fields: Vec<Field> = try!(fields.iter().map(Field::parse).collect());
26+
(accepts::composite_body(&name, "FromSql", &fields),
27+
composite_body(&input.ident, &fields))
28+
}
2329
_ => {
2430
return Err("#[derive(ToSql)] may only be applied to structs, single field tuple \
2531
structs, and enums".to_owned())
@@ -64,16 +70,108 @@ fn enum_body(ident: &Ident, variants: &[Variant]) -> String {
6470
out
6571
}
6672

67-
fn domain_accepts_body(field: &Field) -> String {
73+
fn domain_accepts_body(field: &syn::Field) -> String {
6874
let mut tokens = Tokens::new();
6975
field.ty.to_tokens(&mut tokens);
7076
format!("
7177
<{} as ::postgres::types::FromSql>::accepts(type_)", tokens)
7278
}
7379

74-
fn domain_body(ident: &Ident, field: &Field) -> String {
80+
fn domain_body(ident: &Ident, field: &syn::Field) -> String {
7581
let mut tokens = Tokens::new();
7682
field.ty.to_tokens(&mut tokens);
7783
format!("\
7884
<{} as ::postgres::types::FromSql>::from_sql(_type, buf, _info).map({})", tokens, ident)
7985
}
86+
87+
fn composite_body(ident: &Ident, fields: &[Field]) -> String {
88+
let mut out = "
89+
fn read_be_i32(buf: &mut &[u8]) -> ::std::io::Result<i32> {
90+
let mut bytes = [0; 4];
91+
try!(::std::io::Read::read_exact(buf, &mut bytes));
92+
let num = ((bytes[0] as i32) << 24) |
93+
((bytes[1] as i32) << 16) |
94+
((bytes[2] as i32) << 8) |
95+
(bytes[3] as i32);
96+
::std::result::Result::Ok(num)
97+
}
98+
99+
fn read_value<T>(type_: &::postgres::types::Type,
100+
buf: &mut &[u8],
101+
info: &::postgres::types::SessionInfo)
102+
-> ::std::result::Result<T,
103+
::std::boxed::Box<::std::error::Error +
104+
::std::marker::Sync +
105+
::std::marker::Send>>
106+
where T: ::postgres::types::FromSql
107+
{
108+
let len = try!(read_be_i32(buf));
109+
let value = if len < 0 {
110+
::std::option::Option::None
111+
} else {
112+
if len as usize > buf.len() {
113+
return ::std::result::Result::Err(
114+
::std::convert::Into::into(\"invalid buffer size\"));
115+
}
116+
let (head, tail) = buf.split_at(len as usize);
117+
*buf = tail;
118+
::std::option::Option::Some(&head[..])
119+
};
120+
::postgres::types::FromSql::from_sql_nullable(type_, value, info)
121+
}
122+
123+
let fields = match *_type.kind() {
124+
::postgres::types::Kind::Composite(ref fields) => fields,
125+
_ => unreachable!(),
126+
};
127+
128+
let mut buf = buf;
129+
let num_fields = try!(read_be_i32(&mut buf));
130+
if num_fields as usize != fields.len() {
131+
return ::std::result::Result::Err(
132+
::std::convert::Into::into(format!(\"invalid field count: {} vs {}\", num_fields,
133+
fields.len())));
134+
}
135+
".to_owned();
136+
137+
for field in fields {
138+
write!(out, "
139+
let mut __{} = ::std::option::Option::None;", field.ident).unwrap();
140+
}
141+
142+
write!(out, "
143+
for field in fields {{
144+
let oid = try!(read_be_i32(&mut buf)) as u32;
145+
if oid != field.type_().oid() {{
146+
return ::std::result::Result::Err(
147+
::std::convert::Into::into(\"unexpected OID\"));
148+
}}\
149+
\
150+
match field.name() {{").unwrap();
151+
152+
for field in fields {
153+
write!(out, "
154+
\"{}\" => {{
155+
__{} = ::std::option::Option::Some(
156+
try!(read_value(field.type_(), &mut buf, _info)));
157+
}}",
158+
field.name, field.ident).unwrap();
159+
}
160+
161+
write!(out, "
162+
_ => unreachable!(),
163+
}}
164+
}}
165+
166+
::std::result::Result::Ok({} {{", ident).unwrap();
167+
168+
for field in fields {
169+
write!(out, "
170+
{}: __{}.unwrap(),", field.ident, field.ident).unwrap();
171+
}
172+
173+
write!(out, "
174+
}})").unwrap();
175+
176+
out
177+
}

postgres-derive-internals/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use fromsql::expand_derive_fromsql;
99
use tosql::expand_derive_tosql;
1010

1111
mod accepts;
12+
mod composites;
1213
mod enums;
1314
mod fromsql;
1415
mod overrides;

postgres-derive-internals/src/tosql.rs

Lines changed: 62 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
use std::fmt::Write;
2-
use syn::{Body, Ident, MacroInput, VariantData, Field};
2+
use syn::{self, Body, Ident, MacroInput, VariantData};
33
use quote::{Tokens, ToTokens};
44

55
use accepts;
6+
use composites::Field;
67
use enums::Variant;
78
use overrides::Overrides;
89

@@ -20,6 +21,11 @@ pub fn expand_derive_tosql(input: &MacroInput) -> Result<String, String> {
2021
let field = &fields[0];
2122
(domain_accepts_body(&name, &field), domain_body())
2223
}
24+
Body::Struct(VariantData::Struct(ref fields)) => {
25+
let fields: Vec<Field> = try!(fields.iter().map(Field::parse).collect());
26+
(accepts::composite_body(&name, "ToSql", &fields),
27+
composite_body(&fields))
28+
}
2329
_ => {
2430
return Err("#[derive(ToSql)] may only be applied to structs, single field tuple \
2531
structs, and enums".to_owned())
@@ -60,12 +66,12 @@ fn enum_body(ident: &Ident, variants: &[Variant]) -> String {
6066
};
6167
6268
buf.extend_from_slice(s.as_bytes());
63-
Ok(::postgres::types::IsNull::No)");
69+
::std::result::Result::Ok(::postgres::types::IsNull::No)");
6470

6571
out
6672
}
6773

68-
fn domain_accepts_body(name: &str, field: &Field) -> String {
74+
fn domain_accepts_body(name: &str, field: &syn::Field) -> String {
6975
let mut tokens = Tokens::new();
7076
field.ty.to_tokens(&mut tokens);
7177

@@ -91,3 +97,56 @@ fn domain_body() -> String {
9197
9298
::postgres::types::ToSql::to_sql(&self.0, type_, buf, _info)".to_owned()
9399
}
100+
101+
fn composite_body(fields: &[Field]) -> String {
102+
let mut out = "
103+
fn write_be_i32<W>(buf: &mut W, n: i32) -> ::std::io::Result<()>
104+
where W: ::std::io::Write
105+
{
106+
let be = [(n >> 24) as u8, (n >> 16) as u8, (n >> 8) as u8, n as u8];
107+
buf.write_all(&be)
108+
}
109+
110+
let fields = match *_type.kind() {
111+
::postgres::types::Kind::Composite(ref fields) => fields,
112+
_ => unreachable!(),
113+
};
114+
115+
try!(write_be_i32(buf, fields.len() as i32));
116+
117+
for field in fields {
118+
try!(write_be_i32(buf, field.type_().oid() as i32));
119+
120+
let base = buf.len();
121+
try!(write_be_i32(buf, 0));
122+
let r = match field.name() {".to_owned();
123+
124+
for field in fields {
125+
write!(out, "
126+
\"{}\" => ::postgres::types::ToSql::to_sql(&self.{}, field.type_(), buf, _info),",
127+
field.name, field.ident).unwrap();
128+
}
129+
130+
write!(out, "\
131+
_ => unreachable!(),\
132+
}};\
133+
\
134+
let count = match try!(r) {{\
135+
::postgres::types::IsNull::Yes => -1,\
136+
::postgres::types::IsNull::No => {{\
137+
let len = buf.len() - base - 4;\
138+
if len > i32::max_value() as usize {{\
139+
return ::std::result::Result::Err(\
140+
::std::convert::Into::into(\"value too large to transmit\"));\
141+
}}\
142+
len as i32\
143+
}}\
144+
}};\
145+
\
146+
try!(write_be_i32(&mut &mut buf[base..base + 4], count));\
147+
}}\
148+
\
149+
::std::result::Result::Ok(::postgres::types::IsNull::No)").unwrap();
150+
151+
out
152+
}

0 commit comments

Comments
 (0)