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

Commit 02654cd

Browse files
committed
Enum support in macros 1.1
1 parent 01b642b commit 02654cd

File tree

8 files changed

+181
-59
lines changed

8 files changed

+181
-59
lines changed

postgres-derive-internals/src/accepts.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ pub fn enum_body(variants: &[Variant]) -> String {
88
write!(body, "
99
match *type_.kind() {{
1010
::postgres::types::Kind::Enum(ref variants) => {{
11-
if variants.len != {} {{
11+
if variants.len() != {} {{
1212
return false;
1313
}}
1414
Lines changed: 22 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,18 @@
11
use std::fmt::Write;
2-
use syn::{self, Body, VariantData, Ident};
3-
use quote::{Tokens, ToTokens};
2+
use syn::{Body, Ident, MacroInput};
43

54
use accepts;
65
use enums::Variant;
76
use overrides::Overrides;
87

9-
pub fn expand_derive_fromsql(source: &str) -> Result<String, String> {
10-
let mut input = try!(syn::parse_macro_input(source));
11-
let overrides = try!(Overrides::extract(&mut input.attrs));
8+
pub fn expand_derive_fromsql(input: &MacroInput) -> Result<String, String> {
9+
let overrides = try!(Overrides::extract(&input.attrs));
1210

1311
let name = overrides.name.unwrap_or_else(|| input.ident.to_string());
1412

1513
let (accepts_body, to_sql_body) = match input.body {
16-
Body::Enum(ref mut variants) => {
17-
let variants: Vec<Variant> = try!(variants.iter_mut().map(Variant::parse).collect());
14+
Body::Enum(ref variants) => {
15+
let variants: Vec<Variant> = try!(variants.iter().map(Variant::parse).collect());
1816
(accepts::enum_body(&variants), enum_body(&input.ident, &variants))
1917
}
2018
_ => {
@@ -23,18 +21,15 @@ pub fn expand_derive_fromsql(source: &str) -> Result<String, String> {
2321
}
2422
};
2523

26-
let mut tokens = Tokens::new();
27-
input.to_tokens(&mut tokens);
28-
2924
let out = format!("
30-
{}
31-
32-
impl ::postgres::types::ToSql for {} {{
33-
fn to_sql(&self,
34-
_: &::postgres::types::Type,
35-
buf: &mut ::std::vec::Vec<u8>,
36-
_: &::postgres::types::SessionInfo)
37-
-> ::postgres::Result<::postgres::types::IsNull> {{{}
25+
impl ::postgres::types::FromSql for {} {{
26+
fn from_sql(_: &::postgres::types::Type,
27+
buf: &[u8],
28+
_: &::postgres::types::SessionInfo)
29+
-> ::std::result::Result<{},
30+
::std::boxed::Box<::std::error::Error +
31+
::std::marker::Sync +
32+
::std::marker::Send>> {{{}
3833
}}
3934
4035
fn accepts(type_: &::postgres::types::Type) -> bool {{
@@ -43,40 +38,27 @@ impl ::postgres::types::ToSql for {} {{
4338
}}
4439
{}
4540
}}
46-
47-
to_sql_checked!();
48-
}}", tokens, input.ident, to_sql_body, name, accepts_body);
41+
}}", input.ident, input.ident, to_sql_body, name, accepts_body);
4942

5043
Ok(out)
5144
}
5245

5346
fn enum_body(ident: &Ident, variants: &[Variant]) -> String {
5447
let mut out = "
55-
let s = match *self {".to_owned();
48+
match buf {".to_owned();
5649

5750
for variant in variants {
5851
write!(out, "
59-
{}::{} => \"{}\",", ident, variant.ident, variant.name).unwrap();
52+
b\"{}\" => Ok({}::{}),", variant.name, ident, variant.ident).unwrap();
6053
}
6154

6255
out.push_str("
63-
};
64-
65-
buf.extend_from_slice(s.as_bytes());
66-
Ok(::postgres::types::IsNull::No)");
56+
s => {
57+
::std::result::Result::Err(
58+
::std::convert::Into::into(format!(\"invalid variant `{}`\",
59+
::std::string::String::from_utf8_lossy(s))))
60+
}
61+
}");
6762

6863
out
6964
}
70-
71-
#[test]
72-
fn foo() {
73-
let code = "
74-
#[postgres(name = \"foo\")]
75-
enum Foo {
76-
#[postgres(name = \"bar\")]
77-
Bar,
78-
Baz
79-
}
80-
";
81-
panic!(expand_derive_tosql(code).unwrap());
82-
}

postgres-derive-internals/src/lib.rs

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
extern crate syn;
22
extern crate quote;
33

4-
use syn::{MacroInput, MetaItem};
4+
use syn::{MacroInput, MetaItem, Body, VariantData};
5+
use quote::{Tokens, ToTokens};
56

6-
pub use fromsql::expand_derive_fromsql;
7-
pub use tosql::expand_derive_tosql;
7+
use overrides::Overrides;
8+
use fromsql::expand_derive_fromsql;
9+
use tosql::expand_derive_tosql;
810

911
mod accepts;
1012
mod enums;
@@ -26,6 +28,13 @@ pub fn expand_derive(source: &str) -> Result<String, String> {
2628
} else {
2729
"".to_owned()
2830
};
31+
32+
strip_overrides(&mut input);
33+
34+
let mut tokens = Tokens::new();
35+
input.to_tokens(&mut tokens);
36+
37+
Ok(format!("{}{}{}", tokens, tosql, fromsql))
2938
}
3039

3140
fn strip_derives(input: &mut MacroInput) -> (bool, bool) {
@@ -70,5 +79,20 @@ fn strip_derives(input: &mut MacroInput) -> (bool, bool) {
7079
}
7180

7281
fn strip_overrides(input: &mut MacroInput) {
82+
Overrides::strip(&mut input.attrs);
7383

84+
match input.body {
85+
Body::Enum(ref mut variants) => {
86+
for variant in variants {
87+
Overrides::strip(&mut variant.attrs);
88+
}
89+
}
90+
Body::Struct(VariantData::Struct(ref mut fields)) |
91+
Body::Struct(VariantData::Tuple(ref mut fields)) => {
92+
for field in fields {
93+
Overrides::strip(&mut field.attrs);
94+
}
95+
}
96+
Body::Struct(VariantData::Unit) => {}
97+
}
7498
}

postgres-derive-internals/src/overrides.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ impl Overrides {
1010
name: None,
1111
};
1212

13-
for attr in attrs.drain(..) {
13+
for attr in attrs {
1414
if attr.value.name() != "postgres" {
1515
continue;
1616
}

postgres-derive-internals/src/tosql.rs

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,18 @@
11
use std::fmt::Write;
2-
use syn::{self, Body, VariantData, Ident};
3-
use quote::{Tokens, ToTokens};
2+
use syn::{Body, Ident, MacroInput};
43

54
use accepts;
65
use enums::Variant;
76
use overrides::Overrides;
87

9-
pub fn expand_derive_tosql(source: &str) -> Result<String, String> {
10-
let mut input = try!(syn::parse_macro_input(source));
11-
let overrides = try!(Overrides::extract(&mut input.attrs));
8+
pub fn expand_derive_tosql(input: &MacroInput) -> Result<String, String> {
9+
let overrides = try!(Overrides::extract(&input.attrs));
1210

1311
let name = overrides.name.unwrap_or_else(|| input.ident.to_string());
1412

1513
let (accepts_body, to_sql_body) = match input.body {
16-
Body::Enum(ref mut variants) => {
17-
let variants: Vec<Variant> = try!(variants.iter_mut().map(Variant::parse).collect());
14+
Body::Enum(ref variants) => {
15+
let variants: Vec<Variant> = try!(variants.iter().map(Variant::parse).collect());
1816
(accepts::enum_body(&variants), enum_body(&input.ident, &variants))
1917
}
2018
_ => {
@@ -23,18 +21,16 @@ pub fn expand_derive_tosql(source: &str) -> Result<String, String> {
2321
}
2422
};
2523

26-
let mut tokens = Tokens::new();
27-
input.to_tokens(&mut tokens);
28-
2924
let out = format!("
30-
{}
31-
3225
impl ::postgres::types::ToSql for {} {{
3326
fn to_sql(&self,
3427
_: &::postgres::types::Type,
3528
buf: &mut ::std::vec::Vec<u8>,
3629
_: &::postgres::types::SessionInfo)
37-
-> ::postgres::Result<::postgres::types::IsNull> {{{}
30+
-> ::std::result::Result<::postgres::types::IsNull,
31+
::std::boxed::Box<::std::error::Error +
32+
::std::marker::Sync +
33+
::std::marker::Send>> {{{}
3834
}}
3935
4036
fn accepts(type_: &::postgres::types::Type) -> bool {{
@@ -45,7 +41,7 @@ impl ::postgres::types::ToSql for {} {{
4541
}}
4642
4743
to_sql_checked!();
48-
}}", tokens, input.ident, to_sql_body, name, accepts_body);
44+
}}", input.ident, to_sql_body, name, accepts_body);
4945

5046
Ok(out)
5147
}

postgres-derive/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,6 @@ test = false
99

1010
[dependencies]
1111
postgres-derive-internals = { path = "../postgres-derive-internals" }
12+
13+
[dev-dependencies]
14+
postgres = { git = "https://github.com/sfackler/rust-postgres" }

postgres-derive/tests/enums.rs

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
#![feature(rustc_macro)]
2+
3+
#[macro_use]
4+
extern crate postgres_derive;
5+
#[macro_use]
6+
extern crate postgres;
7+
8+
use postgres::{Connection, TlsMode};
9+
use postgres::error::Error;
10+
use postgres::types::WrongType;
11+
12+
mod util;
13+
14+
#[test]
15+
fn defaults() {
16+
#[derive(Debug, ToSql, FromSql, PartialEq)]
17+
enum Foo {
18+
Bar,
19+
Baz
20+
}
21+
22+
let conn = Connection::connect("postgres://postgres@localhost", TlsMode::None).unwrap();
23+
conn.execute("CREATE TYPE pg_temp.\"Foo\" AS ENUM ('Bar', 'Baz')", &[]).unwrap();
24+
25+
util::test_type(&conn, "\"Foo\"", &[(Foo::Bar, "'Bar'"), (Foo::Baz, "'Baz'")]);
26+
}
27+
28+
#[test]
29+
fn name_overrides() {
30+
#[derive(Debug, ToSql, FromSql, PartialEq)]
31+
#[postgres(name = "mood")]
32+
enum Mood {
33+
#[postgres(name = "sad")]
34+
Sad,
35+
#[postgres(name = "ok")]
36+
Ok,
37+
#[postgres(name = "happy")]
38+
Happy,
39+
}
40+
41+
let conn = Connection::connect("postgres://postgres@localhost", TlsMode::None).unwrap();
42+
conn.execute("CREATE TYPE pg_temp.mood AS ENUM ('sad', 'ok', 'happy')", &[]).unwrap();
43+
44+
util::test_type(&conn,
45+
"mood",
46+
&[(Mood::Sad, "'sad'"), (Mood::Ok, "'ok'"), (Mood::Happy, "'happy'")]);
47+
}
48+
49+
#[test]
50+
fn wrong_name() {
51+
#[derive(Debug, ToSql, FromSql, PartialEq)]
52+
enum Foo {
53+
Bar,
54+
Baz
55+
}
56+
57+
let conn = Connection::connect("postgres://postgres@localhost", TlsMode::None).unwrap();
58+
conn.execute("CREATE TYPE pg_temp.foo AS ENUM ('Bar', 'Baz')", &[]).unwrap();
59+
60+
match conn.execute("SELECT $1::foo", &[&Foo::Bar]) {
61+
Err(Error::Conversion(ref r)) if r.is::<WrongType>() => {}
62+
v => panic!("unexpected response {:?}", v),
63+
}
64+
}
65+
66+
#[test]
67+
fn extra_variant() {
68+
#[derive(Debug, ToSql, FromSql, PartialEq)]
69+
#[postgres(name = "foo")]
70+
enum Foo {
71+
Bar,
72+
Baz,
73+
Buz,
74+
}
75+
76+
let conn = Connection::connect("postgres://postgres@localhost", TlsMode::None).unwrap();
77+
conn.execute("CREATE TYPE pg_temp.foo AS ENUM ('Bar', 'Baz')", &[]).unwrap();
78+
79+
match conn.execute("SELECT $1::foo", &[&Foo::Bar]) {
80+
Err(Error::Conversion(ref r)) if r.is::<WrongType>() => {}
81+
v => panic!("unexpected response {:?}", v),
82+
}
83+
}
84+
85+
#[test]
86+
fn missing_variant() {
87+
#[derive(Debug, ToSql, FromSql, PartialEq)]
88+
#[postgres(name = "foo")]
89+
enum Foo {
90+
Bar,
91+
}
92+
93+
let conn = Connection::connect("postgres://postgres@localhost", TlsMode::None).unwrap();
94+
conn.execute("CREATE TYPE pg_temp.foo AS ENUM ('Bar', 'Baz')", &[]).unwrap();
95+
96+
match conn.execute("SELECT $1::foo", &[&Foo::Bar]) {
97+
Err(Error::Conversion(ref r)) if r.is::<WrongType>() => {}
98+
v => panic!("unexpected response {:?}", v),
99+
}
100+
}

postgres-derive/tests/util/mod.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
use postgres::types::{FromSql, ToSql};
2+
use postgres::Connection;
3+
use std::fmt;
4+
5+
pub fn test_type<T, S>(conn: &Connection, sql_type: &str, checks: &[(T, S)])
6+
where T: PartialEq + FromSql + ToSql, S: fmt::Display
7+
{
8+
for &(ref val, ref repr) in checks.iter() {
9+
let stmt = conn.prepare(&*format!("SELECT {}::{}", *repr, sql_type)).unwrap();
10+
let result = stmt.query(&[]).unwrap().iter().next().unwrap().get(0);
11+
assert_eq!(val, &result);
12+
13+
let stmt = conn.prepare(&*format!("SELECT $1::{}", sql_type)).unwrap();
14+
let result = stmt.query(&[val]).unwrap().iter().next().unwrap().get(0);
15+
assert_eq!(val, &result);
16+
}
17+
}

0 commit comments

Comments
 (0)