-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
WIP Macros 1.1 port #453
WIP Macros 1.1 port #453
Changes from 8 commits
ef79f64
a76262f
177c2f6
1fd8262
15cf2ac
85925e7
2609d33
f56a29c
3900f9e
0111cf1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
[package] | ||
name = "diesel_codegen_new" | ||
version = "0.8.0" | ||
authors = ["Sean Griffin <sean@seantheprogrammer.com>"] | ||
|
||
[dependencies] | ||
syn = "0.4.0" | ||
quote = "0.1.1" | ||
|
||
[lib] | ||
rustc-macro = true |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
use syn; | ||
use quote; | ||
|
||
use model::Model; | ||
use util::attr_with_name; | ||
|
||
pub fn derive_as_changeset(item: syn::Item) -> quote::Tokens { | ||
let treat_none_as_null = format!("{}", treat_none_as_null(&item.attrs)); | ||
let model = t!(Model::from_item(&item, "AsChangeset")); | ||
|
||
let struct_name = &model.name; | ||
let table_name = model.table_name(); | ||
let struct_ty = &model.ty; | ||
let mut lifetimes = item.generics.lifetimes; | ||
let attrs = model.attrs.into_iter() | ||
.filter(|a| a.column_name != Some(syn::Ident::new("id"))) | ||
.collect::<Vec<_>>(); | ||
|
||
if lifetimes.is_empty() { | ||
lifetimes.push(syn::LifetimeDef { | ||
lifetime: syn::Lifetime { | ||
ident: syn::Ident::new("'a"), | ||
}, | ||
bounds: Vec::new(), | ||
}); | ||
} | ||
|
||
quote!(AsChangeset! { | ||
( | ||
struct_name = #struct_name, | ||
table_name = #table_name, | ||
treat_none_as_null = #treat_none_as_null, | ||
struct_ty = #struct_ty, | ||
lifetimes = (#(lifetimes),*), | ||
), | ||
fields = [#(attrs)*], | ||
}) | ||
} | ||
|
||
fn treat_none_as_null(attrs: &[syn::Attribute]) -> bool { | ||
let options_attr = match attr_with_name(attrs, "changeset_options") { | ||
Some(attr) => attr, | ||
None => return false, | ||
}; | ||
|
||
let usage_err = || panic!(r#"`#[changeset_options]` must be in the form \ | ||
`#[changeset_options(treat_none_as_null = "true")]`"#); | ||
|
||
match options_attr.value { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I filed dtolnay/syn#25 to make attribute parsing less annoying. |
||
syn::MetaItem::List(_, ref values) => { | ||
if values.len() != 1 { | ||
usage_err(); | ||
} | ||
match values[0] { | ||
syn::MetaItem::NameValue(ref name, ref value) | ||
if name.as_ref() == "treat_none_as_null" => value == "true", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I added an |
||
_ => usage_err(), | ||
} | ||
} | ||
_ => usage_err(), | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
use syn::{Ident, Ty, Path, PathSegment}; | ||
|
||
pub fn ty_ident(ident: Ident) -> Ty { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you think it makes sense to add these upstream as constructors? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it makes sense for syn to only provide the AST definitions and parsing, and having a separate crate for the AST builder. I definitely think that abstraction needs to exist. I'm not confident enough in the usage patterns that have emerged to really find the right abstractions yet though. |
||
ty_path(path_ident(ident)) | ||
} | ||
|
||
pub fn ty_path(path: Path) -> Ty { | ||
Ty::Path(None, path) | ||
} | ||
|
||
pub fn path_ident(ident: Ident) -> Path { | ||
Path { | ||
global: false, | ||
segments: vec![PathSegment::ident(ident)], | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
use quote; | ||
use syn; | ||
|
||
use util::{ident_value_of_attr_with_name, is_option_ty}; | ||
|
||
pub struct Attr { | ||
pub column_name: Option<syn::Ident>, | ||
pub field_name: Option<syn::Ident>, | ||
pub ty: syn::Ty, | ||
} | ||
|
||
impl Attr { | ||
pub fn from_struct_field(field: &syn::Field) -> Self { | ||
let field_name = field.ident.clone(); | ||
let column_name = ident_value_of_attr_with_name(&field.attrs, "column_name") | ||
.map(Clone::clone) | ||
.or_else(|| field_name.clone()); | ||
let ty = field.ty.clone(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wow, there's a lot of cloning going on in these lines. I don't have any concrete advice to reduce that right now, just seems like a bad smell. |
||
|
||
Attr { | ||
column_name: column_name, | ||
field_name: field_name, | ||
ty: ty, | ||
} | ||
} | ||
|
||
fn field_kind(&self) -> &str { | ||
if is_option_ty(&self.ty) { | ||
"option" | ||
} else if self.column_name.is_none() && self.field_name.is_none() { | ||
"bare" | ||
} else { | ||
"regular" | ||
} | ||
} | ||
} | ||
|
||
impl quote::ToTokens for Attr { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Memo to self: This impl is used by |
||
fn to_tokens(&self, tokens: &mut quote::Tokens) { | ||
tokens.append("{"); | ||
if let Some(ref name) = self.field_name { | ||
tokens.append("field_name: "); | ||
name.to_tokens(tokens); | ||
tokens.append(", "); | ||
} | ||
if let Some(ref name) = self.column_name { | ||
tokens.append("column_name: "); | ||
name.to_tokens(tokens); | ||
tokens.append(", "); | ||
} | ||
tokens.append("field_ty: "); | ||
self.ty.to_tokens(tokens); | ||
tokens.append(", "); | ||
tokens.append("field_kind: "); | ||
tokens.append(self.field_kind()); | ||
tokens.append(", "); | ||
tokens.append("}"); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
use quote::Tokens; | ||
use syn; | ||
|
||
use model::Model; | ||
|
||
pub fn derive_identifiable(item: syn::Item) -> Tokens { | ||
let model = t!(Model::from_item(&item, "Queryable")); | ||
let table_name = model.table_name(); | ||
let struct_ty = &model.ty; | ||
let fields = model.attrs; | ||
if !fields.iter().any(|f| f.field_name == Some(syn::Ident::new("id"))) { | ||
panic!("Could not find a field named `id` on `{}`", &model.name); | ||
} | ||
|
||
quote!(Identifiable! { | ||
( | ||
table_name = #table_name, | ||
struct_ty = #struct_ty, | ||
), | ||
fields = [#(fields)*], | ||
}) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
use syn; | ||
use quote; | ||
|
||
use model::Model; | ||
|
||
pub fn derive_insertable(item: syn::Item) -> quote::Tokens { | ||
let model = t!(Model::from_item(&item, "Insertable")); | ||
|
||
if !model.has_table_name_annotation() { | ||
panic!(r#"`#[derive(Insertable)]` requires the struct to be annotated \ | ||
with `#[table_name="something"]`"#); | ||
} | ||
|
||
if !model.generics.ty_params.is_empty() { | ||
panic!("`#[derive(Insertable)]` does not support generic types"); | ||
} | ||
|
||
let struct_name = &model.name; | ||
let struct_ty = &model.ty; | ||
let table_name = &model.table_name(); | ||
let lifetimes = model.generics.lifetimes; | ||
let fields = model.attrs; | ||
|
||
quote!(Insertable! { | ||
( | ||
struct_name = #struct_name, | ||
table_name = #table_name, | ||
struct_ty = #struct_ty, | ||
lifetimes = (#(lifetimes),*), | ||
), | ||
fields = [#(fields)*], | ||
}) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added
syn::LifetimeDef::new("'a")
in syn 0.8.5 to simplify this a bit.