diesel_derives-1.4.0/Cargo.toml.orig010066400017500001750000000014731342114537700156300ustar0000000000000000[package] name = "diesel_derives" version = "1.4.0" authors = ["Sean Griffin "] license = "MIT OR Apache-2.0" description = "You should not use this crate directly, it is internal to Diesel." documentation = "https://diesel.rs/guides/" homepage = "https://diesel.rs" repository = "https://github.com/diesel-rs/diesel/tree/master/diesel_derives" autotests = false [dependencies] syn = { version = "0.15.0", features = ["full", "fold"] } quote = "0.6.0" proc-macro2 = "0.4.0" [dev-dependencies] cfg-if = "0.1.0" #diesel = "1.4.0" dotenv = "0.10.0" [lib] proc-macro = true [[test]] name = "tests" [features] default = [] nightly = ["proc-macro2/nightly"] postgres = [] sqlite = [] mysql = [] [badges] travis-ci = { repository = "diesel-rs/diesel" } appveyor = { repository = "diesel-rs/diesel" } diesel_derives-1.4.0/Cargo.toml0000644000000025750000000000000120720ustar00# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g. crates.io) dependencies # # If you believe there's an error in this file please file an # issue against the rust-lang/cargo repository. If you're # editing this file be aware that the upstream Cargo.toml # will likely look very different (and much more reasonable) [package] name = "diesel_derives" version = "1.4.0" authors = ["Sean Griffin "] autotests = false description = "You should not use this crate directly, it is internal to Diesel." homepage = "https://diesel.rs" documentation = "https://diesel.rs/guides/" license = "MIT OR Apache-2.0" repository = "https://github.com/diesel-rs/diesel/tree/master/diesel_derives" [lib] proc-macro = true [[test]] name = "tests" [dependencies.proc-macro2] version = "0.4.0" [dependencies.quote] version = "0.6.0" [dependencies.syn] version = "0.15.0" features = ["full", "fold"] [dev-dependencies.cfg-if] version = "0.1.0" [dev-dependencies.dotenv] version = "0.10.0" [features] default = [] mysql = [] nightly = ["proc-macro2/nightly"] postgres = [] sqlite = [] [badges.appveyor] repository = "diesel-rs/diesel" [badges.travis-ci] repository = "diesel-rs/diesel" diesel_derives-1.4.0/src/as_changeset.rs010066400017500001750000000105431342112552100165050ustar0000000000000000use proc_macro2; use proc_macro2::Span; use syn; use diagnostic_shim::*; use field::*; use meta::*; use model::*; use util::*; pub fn derive(item: syn::DeriveInput) -> Result { let treat_none_as_null = MetaItem::with_name(&item.attrs, "changeset_options") .map(|meta| { meta.warn_if_other_options(&["treat_none_as_null"]); meta.required_nested_item("treat_none_as_null") .map(|m| m.expect_bool_value()) }) .unwrap_or(Ok(false))?; let model = Model::from_item(&item)?; let struct_name = &model.name; let table_name = model.table_name(); let (_, ty_generics, where_clause) = item.generics.split_for_impl(); let mut impl_generics = item.generics.clone(); impl_generics.params.push(parse_quote!('update)); let (impl_generics, _, _) = impl_generics.split_for_impl(); let fields_for_update = model .fields() .iter() .filter(|f| !model.primary_key_names.contains(&f.column_name())) .collect::>(); let ref_changeset_ty = fields_for_update.iter().map(|field| { field_changeset_ty( field, &table_name, treat_none_as_null, Some(quote!(&'update)), ) }); let ref_changeset_expr = fields_for_update .iter() .map(|field| field_changeset_expr(field, &table_name, treat_none_as_null, Some(quote!(&)))); let direct_changeset_ty = fields_for_update .iter() .map(|field| field_changeset_ty(field, &table_name, treat_none_as_null, None)); let direct_changeset_expr = fields_for_update .iter() .map(|field| field_changeset_expr(field, &table_name, treat_none_as_null, None)); if fields_for_update.is_empty() { Span::call_site() .error( "Deriving `AsChangeset` on a structure that only contains the primary key isn't supported." ) .help("If you want to change the primary key of a row, you should do so with `.set(table::id.eq(new_id))`.") .note("`#[derive(AsChangeset)]` never changes the primary key of a row.") .emit(); } Ok(wrap_in_dummy_mod( model.dummy_mod_name("as_changeset"), quote!( use diesel::query_builder::AsChangeset; use diesel::prelude::*; impl #impl_generics AsChangeset for &'update #struct_name #ty_generics #where_clause { type Target = #table_name::table; type Changeset = <(#(#ref_changeset_ty,)*) as AsChangeset>::Changeset; fn as_changeset(self) -> Self::Changeset { (#(#ref_changeset_expr,)*).as_changeset() } } impl #impl_generics AsChangeset for #struct_name #ty_generics #where_clause { type Target = #table_name::table; type Changeset = <(#(#direct_changeset_ty,)*) as AsChangeset>::Changeset; fn as_changeset(self) -> Self::Changeset { (#(#direct_changeset_expr,)*).as_changeset() } } ), )) } fn field_changeset_ty( field: &Field, table_name: &syn::Ident, treat_none_as_null: bool, lifetime: Option, ) -> syn::Type { let column_name = field.column_name(); if !treat_none_as_null && is_option_ty(&field.ty) { let field_ty = inner_of_option_ty(&field.ty); parse_quote!(std::option::Option>) } else { let field_ty = &field.ty; parse_quote!(diesel::dsl::Eq<#table_name::#column_name, #lifetime #field_ty>) } } fn field_changeset_expr( field: &Field, table_name: &syn::Ident, treat_none_as_null: bool, lifetime: Option, ) -> syn::Expr { let field_access = field.name.access(); let column_name = field.column_name(); if !treat_none_as_null && is_option_ty(&field.ty) { if lifetime.is_some() { parse_quote!(self#field_access.as_ref().map(|x| #table_name::#column_name.eq(x))) } else { parse_quote!(self#field_access.map(|x| #table_name::#column_name.eq(x))) } } else { parse_quote!(#table_name::#column_name.eq(#lifetime self#field_access)) } } diesel_derives-1.4.0/src/as_expression.rs010066400017500001750000000077461341474127700167740ustar0000000000000000use proc_macro2::{self, Ident, Span}; use syn; use meta::*; use util::*; pub fn derive(item: syn::DeriveInput) -> Result { let dummy_mod = format!("_impl_as_expression_for_{}", item.ident,).to_lowercase(); let flags = MetaItem::with_name(&item.attrs, "diesel").unwrap_or_else(|| MetaItem::empty("diesel")); let is_sized = !flags.has_flag("not_sized"); let sql_types = MetaItem::all_with_name(&item.attrs, "sql_type"); let any_sql_types = !sql_types.is_empty(); let sql_types = sql_types .into_iter() .filter_map(|attr| attr.ty_value().map_err(Diagnostic::emit).ok()); let (impl_generics, ..) = item.generics.split_for_impl(); let lifetimes = item.generics.lifetimes().collect::>(); let ty_params = item.generics.type_params().collect::>(); let struct_ty = ty_for_foreign_derive(&item, &flags)?; let tokens = sql_types.map(|sql_type| { let lifetimes = &lifetimes; let ty_params = &ty_params; let tokens = quote!( impl<'expr, #(#lifetimes,)* #(#ty_params,)*> AsExpression<#sql_type> for &'expr #struct_ty { type Expression = Bound<#sql_type, Self>; fn as_expression(self) -> Self::Expression { Bound::new(self) } } impl<'expr, #(#lifetimes,)* #(#ty_params,)*> AsExpression> for &'expr #struct_ty { type Expression = Bound, Self>; fn as_expression(self) -> Self::Expression { Bound::new(self) } } impl<'expr2, 'expr, #(#lifetimes,)* #(#ty_params,)*> AsExpression<#sql_type> for &'expr2 &'expr #struct_ty { type Expression = Bound<#sql_type, Self>; fn as_expression(self) -> Self::Expression { Bound::new(self) } } impl<'expr2, 'expr, #(#lifetimes,)* #(#ty_params,)*> AsExpression> for &'expr2 &'expr #struct_ty { type Expression = Bound, Self>; fn as_expression(self) -> Self::Expression { Bound::new(self) } } impl<#(#lifetimes,)* #(#ty_params,)* __DB> diesel::serialize::ToSql, __DB> for #struct_ty where __DB: diesel::backend::Backend, Self: ToSql<#sql_type, __DB>, { fn to_sql(&self, out: &mut Output) -> serialize::Result { ToSql::<#sql_type, __DB>::to_sql(self, out) } } ); if is_sized { quote!( #tokens impl#impl_generics AsExpression<#sql_type> for #struct_ty { type Expression = Bound<#sql_type, Self>; fn as_expression(self) -> Self::Expression { Bound::new(self) } } impl#impl_generics AsExpression> for #struct_ty { type Expression = Bound, Self>; fn as_expression(self) -> Self::Expression { Bound::new(self) } } ) } else { tokens } }); if any_sql_types { Ok(wrap_in_dummy_mod( Ident::new(&dummy_mod, Span::call_site()), quote! { use diesel::expression::AsExpression; use diesel::expression::bound::Bound; use diesel::sql_types::Nullable; use diesel::serialize::{self, ToSql, Output}; #(#tokens)* }, )) } else { Ok(quote!()) } } diesel_derives-1.4.0/src/associations.rs010066400017500001750000000133311342112552100165560ustar0000000000000000use proc_macro2; use proc_macro2::Span; use syn; use syn::fold::Fold; use syn::spanned::Spanned; use diagnostic_shim::*; use meta::*; use model::*; use util::*; pub fn derive(item: syn::DeriveInput) -> Result { let model = Model::from_item(&item)?; let tokens = MetaItem::all_with_name(&item.attrs, "belongs_to") .into_iter() .filter_map( |attr| match derive_belongs_to(&model, &item.generics, attr) { Ok(t) => Some(t), Err(e) => { e.emit(); None } }, ); Ok(wrap_in_dummy_mod( model.dummy_mod_name("associations"), quote!(#(#tokens)*), )) } fn derive_belongs_to( model: &Model, generics: &syn::Generics, meta: MetaItem, ) -> Result { let AssociationOptions { parent_struct, foreign_key, } = AssociationOptions::from_meta(meta)?; let (_, ty_generics, _) = generics.split_for_impl(); let foreign_key_field = model.find_column(&foreign_key)?; let struct_name = &model.name; let foreign_key_access = foreign_key_field.name.access(); let foreign_key_ty = inner_of_option_ty(&foreign_key_field.ty); let table_name = model.table_name(); let mut generics = generics.clone(); let parent_struct = ReplacePathLifetimes::new(|i, span| { let letter = char::from(b'b' + i as u8); let lifetime = syn::Lifetime::new(&format!("'__{}", letter), span); generics.params.push(parse_quote!(#lifetime)); lifetime }) .fold_type_path(parent_struct); // TODO: Remove this special casing as soon as we bump our minimal supported // rust version to >= 1.30.0 because this version will add // `impl<'a, T> From<&'a Option> for Option<&'a T>` to the std-lib let (foreign_key_expr, foreign_key_ty) = if is_option_ty(&foreign_key_field.ty) { ( quote!(self#foreign_key_access.as_ref()), quote!(#foreign_key_ty), ) } else { generics.params.push(parse_quote!(__FK)); { let where_clause = generics.where_clause.get_or_insert(parse_quote!(where)); where_clause .predicates .push(parse_quote!(__FK: std::hash::Hash + std::cmp::Eq)); where_clause.predicates.push( parse_quote!(for<'__a> &'__a #foreign_key_ty: std::convert::Into<::std::option::Option<&'__a __FK>>), ); where_clause.predicates.push( parse_quote!(for<'__a> &'__a #parent_struct: diesel::associations::Identifiable), ); } ( quote!(std::convert::Into::into(&self#foreign_key_access)), quote!(__FK), ) }; let (impl_generics, _, where_clause) = generics.split_for_impl(); Ok(quote! { impl #impl_generics diesel::associations::BelongsTo<#parent_struct> for #struct_name #ty_generics #where_clause { type ForeignKey = #foreign_key_ty; type ForeignKeyColumn = #table_name::#foreign_key; fn foreign_key(&self) -> std::option::Option<&Self::ForeignKey> { #foreign_key_expr } fn foreign_key_column() -> Self::ForeignKeyColumn { #table_name::#foreign_key } } }) } struct AssociationOptions { parent_struct: syn::TypePath, foreign_key: syn::Ident, } impl AssociationOptions { fn from_meta(meta: MetaItem) -> Result { let parent_struct = meta .nested()? .find(|m| m.word().is_ok() || m.name() == "parent") .ok_or_else(|| meta.span()) .and_then(|m| { m.word() .map(|i| parse_quote!(#i)) .or_else(|_| m.ty_value()) .map_err(|_| m.span()) }) .and_then(|ty| match ty { syn::Type::Path(ty_path) => Ok(ty_path), _ => Err(ty.span()), }) .map_err(|span| { span.error("Expected a struct name") .help("e.g. `#[belongs_to(User)]` or `#[belongs_to(parent = \"User<'_>\")]") })?; let foreign_key = { let parent_struct_name = parent_struct .path .segments .last() .expect("paths always have at least one segment") .into_value(); meta.nested_item("foreign_key")? .map(|i| i.ident_value()) .unwrap_or_else(|| Ok(infer_foreign_key(&parent_struct_name.ident)))? }; let unrecognized_options = meta.nested()?.skip(1).filter(|n| n.name() != "foreign_key"); for ignored in unrecognized_options { ignored .span() .warning(format!("Unrecognized option {}", ignored.name())) .emit(); } Ok(Self { parent_struct, foreign_key, }) } } fn infer_foreign_key(name: &syn::Ident) -> syn::Ident { let snake_case = camel_to_snake(&name.to_string()); syn::Ident::new(&format!("{}_id", snake_case), name.span()) } struct ReplacePathLifetimes { count: usize, f: F, } impl ReplacePathLifetimes { fn new(f: F) -> Self { Self { count: 0, f } } } impl Fold for ReplacePathLifetimes where F: FnMut(usize, Span) -> syn::Lifetime, { fn fold_lifetime(&mut self, mut lt: syn::Lifetime) -> syn::Lifetime { if lt.ident == "_" { lt = (self.f)(self.count, lt.span()); self.count += 1; } lt } } diesel_derives-1.4.0/src/diagnostic_shim.rs010066400017500001750000000036161337706336500172510ustar0000000000000000use proc_macro2::Span; pub trait EmitErrorExt { fn emit_error(self) -> Option; } impl EmitErrorExt for Result { fn emit_error(self) -> Option { self.map_err(Diagnostic::emit).ok() } } pub trait DiagnosticShim { fn error>(self, msg: T) -> Diagnostic; fn warning>(self, msg: T) -> Diagnostic; } #[cfg(feature = "nightly")] impl DiagnosticShim for Span { fn error>(self, msg: T) -> Diagnostic { self.unstable().error(msg) } fn warning>(self, msg: T) -> Diagnostic { self.unstable().warning(msg) } } #[cfg(not(feature = "nightly"))] impl DiagnosticShim for Span { fn error>(self, msg: T) -> Diagnostic { Diagnostic::error(msg) } fn warning>(self, msg: T) -> Diagnostic { Diagnostic::warning(msg) } } #[cfg(feature = "nightly")] pub use proc_macro::Diagnostic; #[cfg(not(feature = "nightly"))] pub struct Diagnostic { message: String, level: Level, } #[cfg(not(feature = "nightly"))] impl Diagnostic { fn error>(msg: T) -> Self { Diagnostic { message: msg.into(), level: Level::Error, } } fn warning>(msg: T) -> Self { Diagnostic { message: msg.into(), level: Level::Warning, } } pub fn help>(mut self, msg: T) -> Self { self.message.push_str("\n"); self.message.push_str(&msg.into()); self } pub fn note(self, msg: &str) -> Self { self.help(msg) } pub fn emit(self) { match self.level { Level::Error => panic!("{}", self.message), Level::Warning => println!("{}", self.message), } } } #[cfg(not(feature = "nightly"))] enum Level { Warning, Error, } diesel_derives-1.4.0/src/diesel_numeric_ops.rs010066400017500001750000000056151341474127700177530ustar0000000000000000use proc_macro2::{self, Ident, Span}; use syn; use util::*; pub fn derive(mut item: syn::DeriveInput) -> Result { let struct_name = &item.ident; { let where_clause = item .generics .where_clause .get_or_insert(parse_quote!(where)); where_clause.predicates.push(parse_quote!(Self: Expression)); where_clause.predicates.push_punct(Default::default()); } let (_, ty_generics, where_clause) = item.generics.split_for_impl(); let mut impl_generics = item.generics.clone(); impl_generics.params.push(parse_quote!(__Rhs)); let (impl_generics, _, _) = impl_generics.split_for_impl(); let dummy_name = format!("_impl_diesel_numeric_ops_for_{}", item.ident); Ok(wrap_in_dummy_mod( Ident::new(&dummy_name.to_lowercase(), Span::call_site()), quote! { use diesel::expression::{ops, Expression, AsExpression}; use diesel::sql_types::ops::{Add, Sub, Mul, Div}; impl #impl_generics ::std::ops::Add<__Rhs> for #struct_name #ty_generics #where_clause ::SqlType: Add, __Rhs: AsExpression<<::SqlType as Add>::Rhs>, { type Output = ops::Add; fn add(self, rhs: __Rhs) -> Self::Output { ops::Add::new(self, rhs.as_expression()) } } impl #impl_generics ::std::ops::Sub<__Rhs> for #struct_name #ty_generics #where_clause ::SqlType: Sub, __Rhs: AsExpression<<::SqlType as Sub>::Rhs>, { type Output = ops::Sub; fn sub(self, rhs: __Rhs) -> Self::Output { ops::Sub::new(self, rhs.as_expression()) } } impl #impl_generics ::std::ops::Mul<__Rhs> for #struct_name #ty_generics #where_clause ::SqlType: Mul, __Rhs: AsExpression<<::SqlType as Mul>::Rhs>, { type Output = ops::Mul; fn mul(self, rhs: __Rhs) -> Self::Output { ops::Mul::new(self, rhs.as_expression()) } } impl #impl_generics ::std::ops::Div<__Rhs> for #struct_name #ty_generics #where_clause ::SqlType: Div, __Rhs: AsExpression<<::SqlType as Div>::Rhs>, { type Output = ops::Div; fn div(self, rhs: __Rhs) -> Self::Output { ops::Div::new(self, rhs.as_expression()) } } }, )) } diesel_derives-1.4.0/src/field.rs010066400017500001750000000067101342112552100151450ustar0000000000000000use proc_macro2::{self, Ident, Span}; use quote::ToTokens; use std::borrow::Cow; use syn; use syn::spanned::Spanned; use meta::*; use util::*; pub struct Field { pub ty: syn::Type, pub name: FieldName, pub span: Span, pub sql_type: Option, column_name_from_attribute: Option, flags: MetaItem, } impl Field { pub fn from_struct_field(field: &syn::Field, index: usize) -> Self { let column_name_from_attribute = MetaItem::with_name(&field.attrs, "column_name").map(|m| m.expect_ident_value()); let name = match field.ident.clone() { Some(mut x) => { // https://github.com/rust-lang/rust/issues/47983#issuecomment-362817105 let span = x.span(); x.set_span(fix_span(span, Span::call_site())); FieldName::Named(x) } None => FieldName::Unnamed(syn::Index { index: index as u32, // https://github.com/rust-lang/rust/issues/47312 span: Span::call_site(), }), }; let sql_type = MetaItem::with_name(&field.attrs, "sql_type") .and_then(|m| m.ty_value().map_err(Diagnostic::emit).ok()); let flags = MetaItem::with_name(&field.attrs, "diesel") .unwrap_or_else(|| MetaItem::empty("diesel")); let span = field.span(); Self { ty: field.ty.clone(), column_name_from_attribute, name, sql_type, flags, span, } } pub fn column_name(&self) -> syn::Ident { self.column_name_from_attribute .clone() .unwrap_or_else(|| match self.name { FieldName::Named(ref x) => x.clone(), _ => { self.span .error( "All fields of tuple structs must be annotated with `#[column_name]`", ) .emit(); Ident::new("unknown_column", self.span) } }) } pub fn has_flag(&self, flag: &str) -> bool { self.flags.has_flag(flag) } pub fn ty_for_deserialize(&self) -> Result, Diagnostic> { if let Some(meta) = self.flags.nested_item("deserialize_as")? { meta.ty_value().map(Cow::Owned) } else { Ok(Cow::Borrowed(&self.ty)) } } } pub enum FieldName { Named(syn::Ident), Unnamed(syn::Index), } impl FieldName { pub fn assign(&self, expr: syn::Expr) -> syn::FieldValue { let span = self.span(); // Parens are to work around https://github.com/rust-lang/rust/issues/47311 let tokens = quote_spanned!(span=> #self: (#expr)); parse_quote!(#tokens) } pub fn access(&self) -> proc_macro2::TokenStream { let span = self.span(); // Span of the dot is important due to // https://github.com/rust-lang/rust/issues/47312 quote_spanned!(span=> .#self) } pub fn span(&self) -> Span { match *self { FieldName::Named(ref x) => x.span(), FieldName::Unnamed(ref x) => x.span, } } } impl ToTokens for FieldName { fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { match *self { FieldName::Named(ref x) => x.to_tokens(tokens), FieldName::Unnamed(ref x) => x.to_tokens(tokens), } } } diesel_derives-1.4.0/src/from_sql_row.rs010066400017500001750000000032671341474127700166150ustar0000000000000000use proc_macro2::*; use syn; use meta::*; use util::*; pub fn derive(mut item: syn::DeriveInput) -> Result { let flags = MetaItem::with_name(&item.attrs, "diesel").unwrap_or_else(|| MetaItem::empty("diesel")); let struct_ty = ty_for_foreign_derive(&item, &flags)?; item.generics.params.push(parse_quote!(__ST)); item.generics.params.push(parse_quote!(__DB)); { let where_clause = item .generics .where_clause .get_or_insert(parse_quote!(where)); where_clause .predicates .push(parse_quote!(__DB: diesel::backend::Backend)); where_clause .predicates .push(parse_quote!(Self: FromSql<__ST, __DB>)); } let (impl_generics, _, where_clause) = item.generics.split_for_impl(); let dummy_mod = format!("_impl_from_sql_row_for_{}", item.ident,).to_lowercase(); Ok(wrap_in_dummy_mod( Ident::new(&dummy_mod, Span::call_site()), quote! { use diesel::deserialize::{self, FromSql, FromSqlRow, Queryable}; impl #impl_generics FromSqlRow<__ST, __DB> for #struct_ty #where_clause { fn build_from_row>(row: &mut R) -> deserialize::Result { FromSql::<__ST, __DB>::from_sql(row.take()) } } impl #impl_generics Queryable<__ST, __DB> for #struct_ty #where_clause { type Row = Self; fn build(row: Self::Row) -> Self { row } } }, )) } diesel_derives-1.4.0/src/identifiable.rs010066400017500001750000000027111341474127700165140ustar0000000000000000use proc_macro2; use syn; use model::*; use util::*; pub fn derive(item: syn::DeriveInput) -> Result { let model = Model::from_item(&item)?; let struct_name = &model.name; let table_name = model.table_name(); let (impl_generics, ty_generics, where_clause) = item.generics.split_for_impl(); let mut ref_generics = item.generics.clone(); ref_generics.params.push(parse_quote!('ident)); let (ref_generics, ..) = ref_generics.split_for_impl(); let (field_ty, field_access): (Vec<_>, Vec<_>) = model .primary_key_names .iter() .filter_map(|pk| model.find_column(pk).emit_error()) .map(|f| (&f.ty, f.name.access())) .unzip(); Ok(wrap_in_dummy_mod( model.dummy_mod_name("identifiable"), quote! { use diesel::associations::{HasTable, Identifiable}; impl #impl_generics HasTable for #struct_name #ty_generics #where_clause { type Table = #table_name::table; fn table() -> Self::Table { #table_name::table } } impl #ref_generics Identifiable for &'ident #struct_name #ty_generics #where_clause { type Id = (#(&'ident #field_ty),*); fn id(self) -> Self::Id { (#(&self#field_access),*) } } }, )) } diesel_derives-1.4.0/src/insertable.rs010066400017500001750000000075701342112552100162170ustar0000000000000000use proc_macro2; use proc_macro2::Span; use syn; use field::*; use model::*; use util::*; pub fn derive(item: syn::DeriveInput) -> Result { let model = Model::from_item(&item)?; if model.fields().is_empty() { return Err(Span::call_site() .error("Cannot derive Insertable for unit structs") .help(format!( "Use `insert_into({}::table).default_values()` if you want `DEFAULT VALUES`", model.table_name() ))); } let table_name = &model.table_name(); let struct_name = &item.ident; let (_, ty_generics, where_clause) = item.generics.split_for_impl(); let mut impl_generics = item.generics.clone(); impl_generics.params.push(parse_quote!('insert)); let (impl_generics, ..) = impl_generics.split_for_impl(); let (direct_field_ty, direct_field_assign): (Vec<_>, Vec<_>) = model .fields() .iter() .map(|f| { ( (field_ty(f, table_name, None)), (field_expr(f, table_name, None)), ) }) .unzip(); let (ref_field_ty, ref_field_assign): (Vec<_>, Vec<_>) = model .fields() .iter() .map(|f| { ( (field_ty(f, table_name, Some(quote!(&'insert)))), (field_expr(f, table_name, Some(quote!(&)))), ) }) .unzip(); Ok(wrap_in_dummy_mod( model.dummy_mod_name("insertable"), quote! { use diesel::insertable::Insertable; use diesel::query_builder::UndecoratedInsertRecord; use diesel::prelude::*; impl #impl_generics Insertable<#table_name::table> for #struct_name #ty_generics #where_clause { type Values = <(#(#direct_field_ty,)*) as Insertable<#table_name::table>>::Values; fn values(self) -> Self::Values { (#(#direct_field_assign,)*).values() } } impl #impl_generics Insertable<#table_name::table> for &'insert #struct_name #ty_generics #where_clause { type Values = <(#(#ref_field_ty,)*) as Insertable<#table_name::table>>::Values; fn values(self) -> Self::Values { (#(#ref_field_assign,)*).values() } } impl #impl_generics UndecoratedInsertRecord<#table_name::table> for #struct_name #ty_generics #where_clause { } }, )) } fn field_ty( field: &Field, table_name: &syn::Ident, lifetime: Option, ) -> syn::Type { if field.has_flag("embed") { let field_ty = &field.ty; parse_quote!(#lifetime #field_ty) } else { let inner_ty = inner_of_option_ty(&field.ty); let column_name = field.column_name(); parse_quote!( std::option::Option> ) } } fn field_expr( field: &Field, table_name: &syn::Ident, lifetime: Option, ) -> syn::Expr { let field_access = field.name.access(); if field.has_flag("embed") { parse_quote!(#lifetime self#field_access) } else { let column_name = field.column_name(); let column: syn::Expr = parse_quote!(#table_name::#column_name); if is_option_ty(&field.ty) { if lifetime.is_some() { parse_quote!(self#field_access.as_ref().map(|x| #column.eq(x))) } else { parse_quote!(self#field_access.map(|x| #column.eq(x))) } } else { parse_quote!(std::option::Option::Some(#column.eq(#lifetime self#field_access))) } } } diesel_derives-1.4.0/src/lib.rs010066400017500001750000000064661342112552100146400ustar0000000000000000#![recursion_limit = "1024"] // Built-in Lints #![deny(warnings, missing_copy_implementations)] // Clippy lints #![allow( clippy::needless_pass_by_value, clippy::option_map_unwrap_or_else, clippy::option_map_unwrap_or )] #![warn( clippy::wrong_pub_self_convention, clippy::mut_mut, clippy::non_ascii_literal, clippy::similar_names, clippy::unicode_not_nfc, clippy::if_not_else, clippy::items_after_statements, clippy::used_underscore_binding )] #![cfg_attr(feature = "nightly", feature(proc_macro_diagnostic, proc_macro_span))] extern crate proc_macro; extern crate proc_macro2; #[macro_use] extern crate quote; #[macro_use] extern crate syn; use proc_macro::TokenStream; mod diagnostic_shim; mod field; mod meta; mod model; mod resolved_at_shim; mod util; mod as_changeset; mod as_expression; mod associations; mod diesel_numeric_ops; mod from_sql_row; mod identifiable; mod insertable; mod query_id; mod queryable; mod queryable_by_name; mod sql_type; use diagnostic_shim::*; #[proc_macro_derive( AsChangeset, attributes(table_name, primary_key, column_name, changeset_options) )] pub fn derive_as_changeset(input: TokenStream) -> TokenStream { expand_derive(input, as_changeset::derive) } #[proc_macro_derive(AsExpression, attributes(diesel, sql_type))] pub fn derive_as_expression(input: TokenStream) -> TokenStream { expand_derive(input, as_expression::derive) } #[proc_macro_derive(Associations, attributes(belongs_to, column_name, table_name))] pub fn derive_associations(input: TokenStream) -> TokenStream { expand_derive(input, associations::derive) } #[proc_macro_derive(DieselNumericOps)] pub fn derive_diesel_numeric_ops(input: TokenStream) -> TokenStream { expand_derive(input, diesel_numeric_ops::derive) } #[proc_macro_derive(FromSqlRow, attributes(diesel))] pub fn derive_from_sql_row(input: TokenStream) -> TokenStream { expand_derive(input, from_sql_row::derive) } #[proc_macro_derive(Identifiable, attributes(table_name, primary_key, column_name))] pub fn derive_identifiable(input: TokenStream) -> TokenStream { expand_derive(input, identifiable::derive) } #[proc_macro_derive(Insertable, attributes(table_name, column_name, diesel))] pub fn derive_insertable(input: TokenStream) -> TokenStream { expand_derive(input, insertable::derive) } #[proc_macro_derive(QueryId)] pub fn derive_query_id(input: TokenStream) -> TokenStream { expand_derive(input, query_id::derive) } #[proc_macro_derive(Queryable, attributes(column_name, diesel))] pub fn derive_queryable(input: TokenStream) -> TokenStream { expand_derive(input, queryable::derive) } #[proc_macro_derive(QueryableByName, attributes(table_name, column_name, sql_type, diesel))] pub fn derive_queryable_by_name(input: TokenStream) -> TokenStream { expand_derive(input, queryable_by_name::derive) } #[proc_macro_derive(SqlType, attributes(postgres, sqlite_type, mysql_type))] pub fn derive_sql_type(input: TokenStream) -> TokenStream { expand_derive(input, sql_type::derive) } fn expand_derive( input: TokenStream, f: fn(syn::DeriveInput) -> Result, ) -> TokenStream { let item = syn::parse(input).unwrap(); match f(item) { Ok(x) => x.into(), Err(e) => { e.emit(); "".parse().unwrap() } } } diesel_derives-1.4.0/src/meta.rs010066400017500001750000000160731342112552100150130ustar0000000000000000use proc_macro2::{Ident, Span}; use syn; use syn::fold::Fold; use syn::spanned::Spanned; use resolved_at_shim::*; use util::*; pub struct MetaItem { meta: syn::Meta, } impl MetaItem { pub fn all_with_name(attrs: &[syn::Attribute], name: &str) -> Vec { attrs .iter() .filter_map(|attr| { attr.interpret_meta() .map(|m| FixSpan(attr.pound_token.spans[0]).fold_meta(m)) }) .filter(|m| m.name() == name) .map(|meta| Self { meta }) .collect() } pub fn with_name(attrs: &[syn::Attribute], name: &str) -> Option { Self::all_with_name(attrs, name).pop() } pub fn empty(name: &str) -> Self { Self { meta: syn::Meta::List(syn::MetaList { ident: Ident::new(name, Span::call_site()), paren_token: Default::default(), nested: Default::default(), }), } } pub fn nested_item(&self, name: &str) -> Result, Diagnostic> { self.nested().map(|mut i| i.find(|n| n.name() == name)) } pub fn required_nested_item(&self, name: &str) -> Result { self.nested_item(name)?.ok_or_else(|| { self.span() .error(format!("Missing required option `{}`", name)) }) } pub fn expect_bool_value(&self) -> bool { match self.str_value().as_ref().map(|s| s.as_str()) { Ok("true") => true, Ok("false") => false, _ => { self.span() .error(format!( "`{0}` must be in the form `{0} = \"true\"`", self.name() )) .emit(); false } } } pub fn expect_ident_value(&self) -> syn::Ident { self.ident_value().unwrap_or_else(|e| { e.emit(); self.name() }) } pub fn ident_value(&self) -> Result { let maybe_attr = self.nested().ok().and_then(|mut n| n.nth(0)); let maybe_word = maybe_attr.as_ref().and_then(|m| m.word().ok()); match maybe_word { Some(x) => { self.span() .warning(format!( "The form `{0}(value)` is deprecated. Use `{0} = \"value\"` instead", self.name(), )) .emit(); Ok(x) } _ => Ok(syn::Ident::new( &self.str_value()?, self.value_span().resolved_at(Span::call_site()), )), } } pub fn expect_word(&self) -> syn::Ident { self.word().unwrap_or_else(|e| { e.emit(); self.name() }) } pub fn word(&self) -> Result { use syn::Meta::*; match self.meta { Word(ref x) => Ok(x.clone()), _ => { let meta = &self.meta; Err(self.span().error(format!( "Expected `{}` found `{}`", self.name(), quote!(#meta) ))) } } } pub fn nested(&self) -> Result { use syn::Meta::*; match self.meta { List(ref list) => Ok(Nested(list.nested.iter())), _ => Err(self .span() .error(format!("`{0}` must be in the form `{0}(...)`", self.name()))), } } pub fn name(&self) -> syn::Ident { self.meta.name() } pub fn has_flag(&self, flag: &str) -> bool { self.nested() .map(|mut n| { n.any(|m| match m.word() { Ok(word) => word == flag, Err(_) => false, }) }) .unwrap_or_else(|e| { e.emit(); false }) } pub fn ty_value(&self) -> Result { let str = self.lit_str_value()?; str.parse() .map_err(|_| str.span().error("Invalid Rust type")) } pub fn expect_str_value(&self) -> String { self.str_value().unwrap_or_else(|e| { e.emit(); self.name().to_string() }) } fn str_value(&self) -> Result { self.lit_str_value().map(syn::LitStr::value) } fn lit_str_value(&self) -> Result<&syn::LitStr, Diagnostic> { use syn::Lit::*; match *self.lit_value()? { Str(ref s) => Ok(s), _ => Err(self.span().error(format!( "`{0}` must be in the form `{0} = \"value\"`", self.name() ))), } } pub fn expect_int_value(&self) -> u64 { self.int_value().emit_error().unwrap_or(0) } pub fn int_value(&self) -> Result { use syn::Lit::*; let error = self.value_span().error("Expected a number"); match *self.lit_value()? { Str(ref s) => s.value().parse().map_err(|_| error), Int(ref i) => Ok(i.value()), _ => Err(error), } } fn lit_value(&self) -> Result<&syn::Lit, Diagnostic> { use syn::Meta::*; match self.meta { NameValue(ref name_value) => Ok(&name_value.lit), _ => Err(self.span().error(format!( "`{0}` must be in the form `{0} = \"value\"`", self.name() ))), } } pub fn warn_if_other_options(&self, options: &[&str]) { let nested = match self.nested() { Ok(x) => x, Err(_) => return, }; let unrecognized_options = nested.filter(|n| !options.iter().any(|&o| n.name() == o)); for ignored in unrecognized_options { ignored .span() .warning(format!("Option {} has no effect", ignored.name())) .emit(); } } fn value_span(&self) -> Span { use syn::Meta::*; match self.meta { Word(ref ident) => ident.span(), List(ref meta) => meta.nested.span(), NameValue(ref meta) => meta.lit.span(), } } pub fn span(&self) -> Span { self.meta.span() } } #[cfg_attr(rustfmt, rustfmt_skip)] // https://github.com/rust-lang-nursery/rustfmt/issues/2392 pub struct Nested<'a>(syn::punctuated::Iter<'a, syn::NestedMeta>); impl<'a> Iterator for Nested<'a> { type Item = MetaItem; fn next(&mut self) -> Option { use syn::NestedMeta::*; match self.0.next() { Some(&Meta(ref item)) => Some(MetaItem { meta: item.clone() }), Some(_) => self.next(), None => None, } } } /// If the given span is affected by /// , /// returns the span of the pound token struct FixSpan(Span); impl Fold for FixSpan { fn fold_span(&mut self, span: Span) -> Span { fix_span(span, self.0) } } diesel_derives-1.4.0/src/model.rs010066400017500001750000000064231341474127700152010ustar0000000000000000use proc_macro2::{Ident, Span}; use syn; use diagnostic_shim::*; use field::*; use meta::*; use resolved_at_shim::*; pub struct Model { pub name: syn::Ident, pub primary_key_names: Vec, table_name_from_attribute: Option, fields: Vec, } impl Model { pub fn from_item(item: &syn::DeriveInput) -> Result { let table_name_from_attribute = MetaItem::with_name(&item.attrs, "table_name").map(|m| m.expect_ident_value()); let primary_key_names = MetaItem::with_name(&item.attrs, "primary_key") .map(|m| Ok(m.nested()?.map(|m| m.expect_word()).collect())) .unwrap_or_else(|| Ok(vec![Ident::new("id", Span::call_site())]))?; let fields = fields_from_item_data(&item.data)?; Ok(Self { name: item.ident.clone(), table_name_from_attribute, primary_key_names, fields, }) } pub fn table_name(&self) -> syn::Ident { self.table_name_from_attribute.clone().unwrap_or_else(|| { syn::Ident::new( &infer_table_name(&self.name.to_string()), self.name.span().resolved_at(Span::call_site()), ) }) } pub fn dummy_mod_name(&self, trait_name: &str) -> syn::Ident { let name = format!("_impl_{}_for_{}", trait_name, self.name).to_lowercase(); Ident::new(&name, Span::call_site()) } pub fn fields(&self) -> &[Field] { &self.fields } pub fn find_column(&self, column_name: &syn::Ident) -> Result<&Field, Diagnostic> { self.fields() .iter() .find(|f| &f.column_name() == column_name) .ok_or_else(|| { column_name .span() .error(format!("No field with column name {}", column_name)) }) } pub fn has_table_name_attribute(&self) -> bool { self.table_name_from_attribute.is_some() } } pub fn camel_to_snake(name: &str) -> String { let mut result = String::with_capacity(name.len()); result.push_str(&name[..1].to_lowercase()); for character in name[1..].chars() { if character.is_uppercase() { result.push('_'); for lowercase in character.to_lowercase() { result.push(lowercase); } } else { result.push(character); } } result } fn infer_table_name(name: &str) -> String { let mut result = camel_to_snake(name); result.push('s'); result } fn fields_from_item_data(data: &syn::Data) -> Result, Diagnostic> { use syn::Data::*; let struct_data = match *data { Struct(ref d) => d, _ => return Err(Span::call_site().error("This derive can only be used on structs")), }; Ok(struct_data .fields .iter() .enumerate() .map(|(i, f)| Field::from_struct_field(f, i)) .collect()) } #[test] fn infer_table_name_pluralizes_and_downcases() { assert_eq!("foos", &infer_table_name("Foo")); assert_eq!("bars", &infer_table_name("Bar")); } #[test] fn infer_table_name_properly_handles_underscores() { assert_eq!("foo_bars", &infer_table_name("FooBar")); assert_eq!("foo_bar_bazs", &infer_table_name("FooBarBaz")); } diesel_derives-1.4.0/src/query_id.rs010066400017500001750000000025631341474127700157230ustar0000000000000000use proc_macro2; use proc_macro2::*; use syn; use util::*; pub fn derive(mut item: syn::DeriveInput) -> Result { for ty_param in item.generics.type_params_mut() { ty_param.bounds.push(parse_quote!(QueryId)); } let (impl_generics, ty_generics, where_clause) = item.generics.split_for_impl(); let struct_name = &item.ident; let lifetimes = item.generics.lifetimes(); let query_id_ty_params = item .generics .type_params() .map(|ty_param| &ty_param.ident) .map(|ty_param| quote!(<#ty_param as QueryId>::QueryId)); let has_static_query_id = item .generics .type_params() .map(|ty_param| &ty_param.ident) .map(|ty_param| quote!(<#ty_param as QueryId>::HAS_STATIC_QUERY_ID)); let dummy_mod = format!("_impl_query_id_for_{}", item.ident).to_lowercase(); Ok(wrap_in_dummy_mod( Ident::new(&dummy_mod, Span::call_site()), quote! { use diesel::query_builder::QueryId; #[allow(non_camel_case_types)] impl #impl_generics QueryId for #struct_name #ty_generics #where_clause { type QueryId = #struct_name<#(#lifetimes,)* #(#query_id_ty_params,)*>; const HAS_STATIC_QUERY_ID: bool = #(#has_static_query_id &&)* true; } }, )) } diesel_derives-1.4.0/src/queryable.rs010066400017500001750000000032171341474127700160700ustar0000000000000000use proc_macro2; use syn; use model::*; use util::*; pub fn derive(item: syn::DeriveInput) -> Result { let model = Model::from_item(&item)?; let struct_name = &item.ident; let field_ty = model .fields() .iter() .map(|f| f.ty_for_deserialize()) .collect::, _>>()?; let field_ty = &field_ty; let build_expr = model.fields().iter().enumerate().map(|(i, f)| { let i = syn::Index::from(i); f.name.assign(parse_quote!(row.#i.into())) }); let (_, ty_generics, _) = item.generics.split_for_impl(); let mut generics = item.generics.clone(); generics .params .push(parse_quote!(__DB: diesel::backend::Backend)); generics.params.push(parse_quote!(__ST)); { let where_clause = generics.where_clause.get_or_insert(parse_quote!(where)); where_clause .predicates .push(parse_quote!((#(#field_ty,)*): Queryable<__ST, __DB>)); } let (impl_generics, _, where_clause) = generics.split_for_impl(); Ok(wrap_in_dummy_mod( model.dummy_mod_name("queryable"), quote! { use diesel::Queryable; impl #impl_generics Queryable<__ST, __DB> for #struct_name #ty_generics #where_clause { type Row = <(#(#field_ty,)*) as Queryable<__ST, __DB>>::Row; fn build(row: Self::Row) -> Self { let row: (#(#field_ty,)*) = Queryable::build(row); Self { #(#build_expr,)* } } } }, )) } diesel_derives-1.4.0/src/queryable_by_name.rs010066400017500001750000000064311342112552100175450ustar0000000000000000use proc_macro2::{self, Ident, Span}; use syn; use field::*; use model::*; use util::*; pub fn derive(item: syn::DeriveInput) -> Result { let model = Model::from_item(&item)?; let struct_name = &item.ident; let field_expr = model .fields() .iter() .map(|f| field_expr(f, &model)) .collect::, _>>()?; let (_, ty_generics, ..) = item.generics.split_for_impl(); let mut generics = item.generics.clone(); generics .params .push(parse_quote!(__DB: diesel::backend::Backend)); for field in model.fields() { let where_clause = generics.where_clause.get_or_insert(parse_quote!(where)); let field_ty = field.ty_for_deserialize()?; if field.has_flag("embed") { where_clause .predicates .push(parse_quote!(#field_ty: QueryableByName<__DB>)); } else { let st = sql_type(field, &model); where_clause .predicates .push(parse_quote!(#field_ty: diesel::deserialize::FromSql<#st, __DB>)); } } let (impl_generics, _, where_clause) = generics.split_for_impl(); Ok(wrap_in_dummy_mod( model.dummy_mod_name("queryable_by_name"), quote! { use diesel::deserialize::{self, QueryableByName}; use diesel::row::NamedRow; impl #impl_generics QueryableByName<__DB> for #struct_name #ty_generics #where_clause { fn build<__R: NamedRow<__DB>>(row: &__R) -> deserialize::Result { std::result::Result::Ok(Self { #(#field_expr,)* }) } } }, )) } fn field_expr(field: &Field, model: &Model) -> Result { if field.has_flag("embed") { Ok(field .name .assign(parse_quote!(QueryableByName::build(row)?))) } else { let column_name = field.column_name(); let ty = field.ty_for_deserialize()?; let st = sql_type(field, model); Ok(field .name .assign(parse_quote!(row.get::<#st, #ty>(stringify!(#column_name))?.into()))) } } fn sql_type(field: &Field, model: &Model) -> syn::Type { let table_name = model.table_name(); let column_name = field.column_name(); match field.sql_type { Some(ref st) => st.clone(), None => { if model.has_table_name_attribute() { parse_quote!(diesel::dsl::SqlTypeOf<#table_name::#column_name>) } else { let field_name = match field.name { FieldName::Named(ref x) => x.clone(), _ => Ident::new("field", Span::call_site()), }; field .span .error(format!("Cannot determine the SQL type of {}", field_name)) .help( "Your struct must either be annotated with `#[table_name = \"foo\"]` \ or have all of its fields annotated with `#[sql_type = \"Integer\"]`", ) .emit(); parse_quote!(()) } } } } diesel_derives-1.4.0/src/resolved_at_shim.rs010066400017500001750000000006211337706336500174250ustar0000000000000000use proc_macro2::Span; pub trait ResolvedAtExt { fn resolved_at(self, span: Span) -> Span; } #[cfg(feature = "nightly")] impl ResolvedAtExt for Span { fn resolved_at(self, span: Span) -> Span { self.unstable().resolved_at(span.unstable()).into() } } #[cfg(not(feature = "nightly"))] impl ResolvedAtExt for Span { fn resolved_at(self, _: Span) -> Span { self } } diesel_derives-1.4.0/src/sql_type.rs010066400017500001750000000122311342112552100157150ustar0000000000000000use proc_macro2; use proc_macro2::*; use syn; use meta::*; use util::*; pub fn derive(item: syn::DeriveInput) -> Result { let struct_name = &item.ident; let (impl_generics, ty_generics, where_clause) = item.generics.split_for_impl(); let sqlite_tokens = sqlite_tokens(&item); let mysql_tokens = mysql_tokens(&item); let pg_tokens = pg_tokens(&item); let dummy_name = format!("_impl_sql_type_for_{}", item.ident); Ok(wrap_in_dummy_mod( Ident::new(&dummy_name.to_lowercase(), Span::call_site()), quote! { impl #impl_generics diesel::sql_types::NotNull for #struct_name #ty_generics #where_clause { } impl #impl_generics diesel::sql_types::SingleValue for #struct_name #ty_generics #where_clause { } #sqlite_tokens #mysql_tokens #pg_tokens }, )) } fn sqlite_tokens(item: &syn::DeriveInput) -> Option { MetaItem::with_name(&item.attrs, "sqlite_type") .map(|attr| attr.expect_ident_value()) .and_then(|ty| { if cfg!(not(feature = "sqlite")) { return None; } let struct_name = &item.ident; let (impl_generics, ty_generics, where_clause) = item.generics.split_for_impl(); Some(quote! { impl #impl_generics diesel::sql_types::HasSqlType<#struct_name #ty_generics> for diesel::sqlite::Sqlite #where_clause { fn metadata(_: &()) -> diesel::sqlite::SqliteType { diesel::sqlite::SqliteType::#ty } } }) }) } fn mysql_tokens(item: &syn::DeriveInput) -> Option { MetaItem::with_name(&item.attrs, "mysql_type") .map(|attr| attr.expect_ident_value()) .and_then(|ty| { if cfg!(not(feature = "mysql")) { return None; } let struct_name = &item.ident; let (impl_generics, ty_generics, where_clause) = item.generics.split_for_impl(); Some(quote! { impl #impl_generics diesel::sql_types::HasSqlType<#struct_name #ty_generics> for diesel::mysql::Mysql #where_clause { fn metadata(_: &()) -> diesel::mysql::MysqlType { diesel::mysql::MysqlType::#ty } } }) }) } fn pg_tokens(item: &syn::DeriveInput) -> Option { MetaItem::with_name(&item.attrs, "postgres") .map(|attr| { if let Some(x) = get_type_name(&attr)? { Ok(x) } else if let Some(x) = get_oids(&attr)? { Ok(x) } else { Err(attr .span() .error("Missing required options") .help("Valid options are `type_name` or `oid` and `array_oid`")) } }) .and_then(|res| res.map_err(|e| e.emit()).ok()) .and_then(|ty| { if cfg!(not(feature = "postgres")) { return None; } let struct_name = &item.ident; let (impl_generics, ty_generics, where_clause) = item.generics.split_for_impl(); let metadata_fn = match ty { PgType::Fixed { oid, array_oid } => quote!( fn metadata(_: &PgMetadataLookup) -> PgTypeMetadata { PgTypeMetadata { oid: #oid, array_oid: #array_oid, } } ), PgType::Lookup(type_name) => quote!( fn metadata(lookup: &PgMetadataLookup) -> PgTypeMetadata { lookup.lookup_type(#type_name) } ), }; Some(quote! { use diesel::pg::{PgMetadataLookup, PgTypeMetadata}; impl #impl_generics diesel::sql_types::HasSqlType<#struct_name #ty_generics> for diesel::pg::Pg #where_clause { #metadata_fn } }) }) } fn get_type_name(attr: &MetaItem) -> Result, Diagnostic> { Ok(attr.nested_item("type_name")?.map(|ty| { attr.warn_if_other_options(&["type_name"]); PgType::Lookup(ty.expect_str_value()) })) } fn get_oids(attr: &MetaItem) -> Result, Diagnostic> { if let Some(oid) = attr.nested_item("oid")? { attr.warn_if_other_options(&["oid", "array_oid"]); let array_oid = attr.required_nested_item("array_oid")?.expect_int_value(); let oid = oid.expect_int_value(); Ok(Some(PgType::Fixed { oid: oid as u32, array_oid: array_oid as u32, })) } else { Ok(None) } } enum PgType { Fixed { oid: u32, array_oid: u32 }, Lookup(String), } diesel_derives-1.4.0/src/util.rs010066400017500001750000000046261341474127700150610ustar0000000000000000pub use diagnostic_shim::{Diagnostic, DiagnosticShim, EmitErrorExt}; use meta::MetaItem; use proc_macro2::{Span, TokenStream}; use syn::{Data, DeriveInput, GenericArgument, Ident, Type}; pub fn wrap_in_dummy_mod(const_name: Ident, item: TokenStream) -> TokenStream { quote! { #[allow(non_snake_case, unused_extern_crates, unused_imports)] fn #const_name() { // https://github.com/rust-lang/rust/issues/47314 extern crate std; use diesel; #item } } } pub fn inner_of_option_ty(ty: &Type) -> &Type { option_ty_arg(ty).unwrap_or(ty) } pub fn is_option_ty(ty: &Type) -> bool { option_ty_arg(ty).is_some() } fn option_ty_arg(ty: &Type) -> Option<&Type> { use syn::PathArguments::AngleBracketed; match *ty { Type::Path(ref ty) => { let last_segment = ty.path.segments.iter().last().unwrap(); match last_segment.arguments { AngleBracketed(ref args) if last_segment.ident == "Option" => { match args.args.iter().last() { Some(&GenericArgument::Type(ref ty)) => Some(ty), _ => None, } } _ => None, } } _ => None, } } pub fn ty_for_foreign_derive(item: &DeriveInput, flags: &MetaItem) -> Result { if flags.has_flag("foreign_derive") { match item.data { Data::Struct(ref body) => match body.fields.iter().nth(0) { Some(field) => Ok(field.ty.clone()), None => Err(flags .span() .error("foreign_derive requires at least one field")), }, _ => Err(flags .span() .error("foreign_derive can only be used with structs")), } } else { let ident = &item.ident; let (_, ty_generics, ..) = item.generics.split_for_impl(); Ok(parse_quote!(#ident #ty_generics)) } } pub fn fix_span(maybe_bad_span: Span, mut fallback: Span) -> Span { let bad_span_debug = "#0 bytes(0..0)"; if format!("{:?}", fallback) == bad_span_debug { // On recent rust nightlies, even our fallback span is bad. fallback = Span::call_site(); } if format!("{:?}", maybe_bad_span) == bad_span_debug { fallback } else { maybe_bad_span } } diesel_derives-1.4.0/tests/as_changeset.rs010066400017500001750000000267351342112552100170720ustar0000000000000000use diesel::*; use helpers::*; use schema::*; #[test] fn named_ref_struct() { #[derive(AsChangeset)] struct User { name: String, hair_color: String, } let connection = connection_with_sean_and_tess_in_users_table(); update(users::table.find(1)) .set(&User { name: String::from("Jim"), hair_color: String::from("blue"), }) .execute(&connection) .unwrap(); let expected = vec![ (1, String::from("Jim"), Some(String::from("blue"))), (2, String::from("Tess"), Some(String::from("brown"))), ]; let actual = users::table.order(users::id).load(&connection); assert_eq!(Ok(expected), actual); } #[test] fn named_struct() { #[derive(AsChangeset)] struct User { name: String, hair_color: String, } let connection = connection_with_sean_and_tess_in_users_table(); update(users::table.find(1)) .set(User { name: String::from("Jim"), hair_color: String::from("blue"), }) .execute(&connection) .unwrap(); let expected = vec![ (1, String::from("Jim"), Some(String::from("blue"))), (2, String::from("Tess"), Some(String::from("brown"))), ]; let actual = users::table.order(users::id).load(&connection); assert_eq!(Ok(expected), actual); } #[test] fn with_explicit_table_name() { #[derive(AsChangeset)] #[table_name = "users"] struct UserForm { name: String, hair_color: String, } let connection = connection_with_sean_and_tess_in_users_table(); update(users::table.find(1)) .set(&UserForm { name: String::from("Jim"), hair_color: String::from("blue"), }) .execute(&connection) .unwrap(); let expected = vec![ (1, String::from("Jim"), Some(String::from("blue"))), (2, String::from("Tess"), Some(String::from("brown"))), ]; let actual = users::table.order(users::id).load(&connection); assert_eq!(Ok(expected), actual); } #[test] fn with_lifetime() { #[derive(AsChangeset)] #[table_name = "users"] struct UserForm<'a> { name: &'a str, hair_color: &'a str, } let connection = connection_with_sean_and_tess_in_users_table(); update(users::table.find(1)) .set(&UserForm { name: "Jim", hair_color: "blue", }) .execute(&connection) .unwrap(); let expected = vec![ (1, String::from("Jim"), Some(String::from("blue"))), (2, String::from("Tess"), Some(String::from("brown"))), ]; let actual = users::table.order(users::id).load(&connection); assert_eq!(Ok(expected), actual); } #[test] fn with_multiple_lifetimes() { #[derive(AsChangeset)] #[table_name = "users"] struct UserForm<'a, 'b> { name: &'a str, hair_color: &'b str, } let connection = connection_with_sean_and_tess_in_users_table(); update(users::table.find(1)) .set(&UserForm { name: "Jim", hair_color: "blue", }) .execute(&connection) .unwrap(); let expected = vec![ (1, String::from("Jim"), Some(String::from("blue"))), (2, String::from("Tess"), Some(String::from("brown"))), ]; let actual = users::table.order(users::id).load(&connection); assert_eq!(Ok(expected), actual); } #[test] fn with_lifetime_constraints() { #[derive(AsChangeset)] #[table_name = "users"] struct UserForm<'a, 'b: 'a> { name: &'a str, hair_color: &'b str, } let connection = connection_with_sean_and_tess_in_users_table(); update(users::table.find(1)) .set(&UserForm { name: "Jim", hair_color: "blue", }) .execute(&connection) .unwrap(); let expected = vec![ (1, String::from("Jim"), Some(String::from("blue"))), (2, String::from("Tess"), Some(String::from("brown"))), ]; let actual = users::table.order(users::id).load(&connection); assert_eq!(Ok(expected), actual); } #[test] fn with_explicit_column_names() { #[derive(AsChangeset)] #[table_name = "users"] struct UserForm<'a> { #[column_name = "name"] nombre: &'a str, #[column_name = "hair_color"] color_de_pelo: &'a str, } let connection = connection_with_sean_and_tess_in_users_table(); update(users::table.find(1)) .set(&UserForm { nombre: "Jim", color_de_pelo: "blue", }) .execute(&connection) .unwrap(); let expected = vec![ (1, String::from("Jim"), Some(String::from("blue"))), (2, String::from("Tess"), Some(String::from("brown"))), ]; let actual = users::table.order(users::id).load(&connection); assert_eq!(Ok(expected), actual); } #[test] fn tuple_struct() { #[derive(AsChangeset)] #[table_name = "users"] struct UserForm<'a>( #[column_name = "name"] &'a str, #[column_name = "hair_color"] &'a str, ); let connection = connection_with_sean_and_tess_in_users_table(); update(users::table.find(1)) .set(&UserForm("Jim", "blue")) .execute(&connection) .unwrap(); let expected = vec![ (1, String::from("Jim"), Some(String::from("blue"))), (2, String::from("Tess"), Some(String::from("brown"))), ]; let actual = users::table.order(users::id).load(&connection); assert_eq!(Ok(expected), actual); } #[test] fn struct_containing_single_field() { #[derive(AsChangeset)] #[table_name = "users"] struct UserForm<'a> { name: &'a str, } let connection = connection_with_sean_and_tess_in_users_table(); update(users::table.find(1)) .set(&UserForm { name: "Jim" }) .execute(&connection) .unwrap(); let expected = vec![ (1, String::from("Jim"), Some(String::from("black"))), (2, String::from("Tess"), Some(String::from("brown"))), ]; let actual = users::table.order(users::id).load(&connection); assert_eq!(Ok(expected), actual); } #[test] fn tuple_struct_containing_single_field() { #[derive(AsChangeset)] #[table_name = "users"] struct UserForm<'a>(#[column_name = "name"] &'a str); let connection = connection_with_sean_and_tess_in_users_table(); update(users::table.find(1)) .set(&UserForm("Jim")) .execute(&connection) .unwrap(); let expected = vec![ (1, String::from("Jim"), Some(String::from("black"))), (2, String::from("Tess"), Some(String::from("brown"))), ]; let actual = users::table.order(users::id).load(&connection); assert_eq!(Ok(expected), actual); } #[test] fn primary_key_is_not_updated() { #[derive(AsChangeset)] #[table_name = "users"] struct UserForm<'a> { #[allow(dead_code)] id: i32, name: &'a str, hair_color: &'a str, } let connection = connection_with_sean_and_tess_in_users_table(); update(users::table.find(1)) .set(&UserForm { id: 3, name: "Jim", hair_color: "blue", }) .execute(&connection) .unwrap(); let expected = vec![ (1, String::from("Jim"), Some(String::from("blue"))), (2, String::from("Tess"), Some(String::from("brown"))), ]; let actual = users::table.order(users::id).load(&connection); assert_eq!(Ok(expected), actual); } #[test] fn primary_key_is_based_on_column_name() { #[derive(AsChangeset)] #[table_name = "users"] struct UserForm<'a> { #[column_name = "id"] _id: i32, name: &'a str, hair_color: &'a str, } let connection = connection_with_sean_and_tess_in_users_table(); update(users::table.find(1)) .set(&UserForm { _id: 3, name: "Jim", hair_color: "blue", }) .execute(&connection) .unwrap(); let expected = vec![ (1, String::from("Jim"), Some(String::from("blue"))), (2, String::from("Tess"), Some(String::from("brown"))), ]; let actual = users::table.order(users::id).load(&connection); assert_eq!(Ok(expected), actual); } #[test] fn primary_key_is_not_updated_with_custom_pk() { #[derive(AsChangeset)] #[table_name = "users"] #[primary_key(name)] struct UserForm<'a> { #[allow(dead_code)] name: &'a str, hair_color: &'a str, } let connection = connection_with_sean_and_tess_in_users_table(); update(users::table.find(1)) .set(&UserForm { name: "Jim", hair_color: "blue", }) .execute(&connection) .unwrap(); let expected = vec![ (1, String::from("Sean"), Some(String::from("blue"))), (2, String::from("Tess"), Some(String::from("brown"))), ]; let actual = users::table.order(users::id).load(&connection); assert_eq!(Ok(expected), actual); } #[test] fn primary_key_is_not_updated_with_custom_composite_pk() { #[derive(AsChangeset)] #[table_name = "users"] #[primary_key(id, name)] #[allow(dead_code)] struct UserForm<'a> { id: i32, name: &'a str, hair_color: &'a str, } let connection = connection_with_sean_and_tess_in_users_table(); update(users::table.find(1)) .set(&UserForm { id: 3, name: "Jim", hair_color: "blue", }) .execute(&connection) .unwrap(); let expected = vec![ (1, String::from("Sean"), Some(String::from("blue"))), (2, String::from("Tess"), Some(String::from("brown"))), ]; let actual = users::table.order(users::id).load(&connection); assert_eq!(Ok(expected), actual); } #[test] fn option_fields_are_skipped() { #[derive(AsChangeset)] #[table_name = "users"] struct UserForm<'a> { name: &'a str, hair_color: Option<&'a str>, } let connection = connection_with_sean_and_tess_in_users_table(); update(users::table.find(1)) .set(&UserForm { name: "Jim", hair_color: Some("blue"), }) .execute(&connection) .unwrap(); update(users::table.find(2)) .set(&UserForm { name: "Ruby", hair_color: None, }) .execute(&connection) .unwrap(); let expected = vec![ (1, String::from("Jim"), Some(String::from("blue"))), (2, String::from("Ruby"), Some(String::from("brown"))), ]; let actual = users::table.order(users::id).load(&connection); assert_eq!(Ok(expected), actual); } #[test] fn option_fields_are_assigned_null_when_specified() { #[derive(AsChangeset)] #[table_name = "users"] #[changeset_options(treat_none_as_null = "true")] struct UserForm<'a> { name: &'a str, hair_color: Option<&'a str>, } let connection = connection_with_sean_and_tess_in_users_table(); update(users::table.find(1)) .set(&UserForm { name: "Jim", hair_color: Some("blue"), }) .execute(&connection) .unwrap(); update(users::table.find(2)) .set(&UserForm { name: "Ruby", hair_color: None, }) .execute(&connection) .unwrap(); let expected = vec![ (1, String::from("Jim"), Some(String::from("blue"))), (2, String::from("Ruby"), None), ]; let actual = users::table.order(users::id).load(&connection); assert_eq!(Ok(expected), actual); } diesel_derives-1.4.0/tests/associations.rs010066400017500001750000000145341337706336500171600ustar0000000000000000use diesel::*; use helpers::*; type Backend = ::Backend; #[test] fn simple_belongs_to() { table! { users { id -> Integer, name -> Text, } } table! { posts { id -> Integer, user_id -> Integer, title -> Text, } } allow_tables_to_appear_in_same_query!(users, posts); #[derive(Identifiable)] pub struct User { id: i32, } #[derive(Associations, Identifiable)] #[belongs_to(User)] pub struct Post { id: i32, user_id: i32, } joinable!(posts -> users(user_id)); let _can_join_tables = posts::table .inner_join(users::table) .select((users::id, users::name, posts::id)) .filter( posts::id .eq(1) .and(posts::user_id.eq(2)) .and(posts::title.eq("Bar")), ); let _can_reverse_join_tables = users::table .inner_join(posts::table) .select((posts::id, posts::user_id, posts::title)) .filter(users::id.eq(1).and(users::name.eq("Sean"))); let t = User { id: 42 }; let belong_to = Post::belonging_to(&t); let filter = posts::table.filter(posts::user_id.eq(42)); assert_eq!( debug_query::(&belong_to).to_string(), debug_query::(&filter).to_string() ); } #[test] fn custom_foreign_key() { table! { users { id -> Integer, name -> Text, } } table! { posts { id -> Integer, belongs_to_user -> Integer, title -> Text, } } allow_tables_to_appear_in_same_query!(users, posts); #[derive(Identifiable)] pub struct User { id: i32, } #[derive(Associations, Identifiable)] #[belongs_to(User, foreign_key = "belongs_to_user")] pub struct Post { id: i32, belongs_to_user: i32, } joinable!(posts -> users(belongs_to_user)); let _can_join_tables = posts::table .inner_join(users::table) .select((users::id, users::name)) .filter( posts::id .eq(1) .and(posts::belongs_to_user.eq(2)) .and(posts::title.eq("Bar")), ); let _can_reverse_join_tables = users::table .inner_join(posts::table) .select((posts::id, posts::belongs_to_user, posts::title)) .filter(users::id.eq(1).and(users::name.eq("Sean"))); let t = User { id: 42 }; let belong_to = Post::belonging_to(&t); let filter = posts::table.filter(posts::belongs_to_user.eq(42)); assert_eq!( debug_query::(&belong_to).to_string(), debug_query::(&filter).to_string() ); } #[test] fn self_referential() { table! { trees { id -> Integer, parent_id -> Nullable, } } #[derive(Associations, Identifiable)] #[belongs_to(Tree, foreign_key = "parent_id")] pub struct Tree { id: i32, parent_id: Option, } let t = Tree { id: 42, parent_id: None, }; let belong_to = Tree::belonging_to(&t); let filter = trees::table.filter(trees::parent_id.eq(42)); assert_eq!( debug_query::(&belong_to).to_string(), debug_query::(&filter).to_string() ); } #[test] fn multiple_associations() { table! { users { id -> Integer, } } table! { posts { id -> Integer, } } table! { comments { id -> Integer, user_id -> Integer, post_id -> Integer, } } #[derive(Identifiable)] struct User { id: i32, } #[derive(Identifiable)] struct Post { id: i32, } #[derive(Identifiable, Associations)] #[belongs_to(User)] #[belongs_to(Post)] struct Comment { id: i32, user_id: i32, post_id: i32, } let user = User { id: 1 }; let post = Post { id: 2 }; let query = Comment::belonging_to(&user); let expected = comments::table.filter(comments::user_id.eq(1)); assert_eq!( debug_query::(&query).to_string(), debug_query::(&expected).to_string() ); let query = Comment::belonging_to(&post); let expected = comments::table.filter(comments::post_id.eq(2)); assert_eq!( debug_query::(&query).to_string(), debug_query::(&expected).to_string() ); } #[test] fn foreign_key_field_with_column_rename() { table! { users { id -> Integer, } } table! { posts { id -> Integer, user_id -> Integer, } } #[derive(Identifiable, Clone, Copy)] pub struct User { id: i32, } #[derive(Associations, Identifiable, Clone, Copy, PartialEq, Debug)] #[belongs_to(User)] pub struct Post { id: i32, #[column_name = "user_id"] author_id: i32, } let user1 = User { id: 1 }; let user2 = User { id: 2 }; let post1 = Post { id: 1, author_id: 2, }; let post2 = Post { id: 2, author_id: 1, }; let query = Post::belonging_to(&user1); let expected = posts::table.filter(posts::user_id.eq(1)); assert_eq!( debug_query::(&query).to_string(), debug_query::(&expected).to_string() ); let users = vec![user1, user2]; let posts = vec![post1, post2].grouped_by(&users); assert_eq!(vec![vec![post2], vec![post1]], posts); } #[test] fn tuple_struct() { table! { users { id -> Integer, } } table! { posts { id -> Integer, user_id -> Integer, } } #[derive(Identifiable)] pub struct User { id: i32, } #[derive(Associations, Identifiable)] #[belongs_to(User)] pub struct Post(#[column_name = "id"] i32, #[column_name = "user_id"] i32); let user = User { id: 1 }; let query = Post::belonging_to(&user); let expected = posts::table.filter(posts::user_id.eq(1)); assert_eq!( debug_query::(&query).to_string(), debug_query::(&expected).to_string() ); } diesel_derives-1.4.0/tests/helpers.rs010066400017500001750000000057161342112552100161040ustar0000000000000000use diesel::prelude::*; use diesel::sql_query; cfg_if! { if #[cfg(feature = "sqlite")] { pub type TestConnection = SqliteConnection; pub fn connection() -> TestConnection { let conn = SqliteConnection::establish(":memory:").unwrap(); sql_query("CREATE TABLE users (\ id INTEGER PRIMARY KEY AUTOINCREMENT, \ name VARCHAR NOT NULL, \ hair_color VARCHAR DEFAULT 'Green')") .execute(&conn) .unwrap(); conn } } else if #[cfg(feature = "postgres")] { extern crate dotenv; pub type TestConnection = PgConnection; pub fn connection() -> TestConnection { let database_url = dotenv::var("PG_DATABASE_URL") .or_else(|_| dotenv::var("DATABASE_URL")) .expect("DATABASE_URL must be set in order to run tests"); let conn = PgConnection::establish(&database_url).unwrap(); conn.begin_test_transaction().unwrap(); sql_query("DROP TABLE IF EXISTS users CASCADE").execute(&conn).unwrap(); sql_query("CREATE TABLE users (\ id SERIAL PRIMARY KEY, \ name VARCHAR NOT NULL, \ hair_color VARCHAR DEFAULT 'Green')") .execute(&conn) .unwrap(); conn } } else if #[cfg(feature = "mysql")] { extern crate dotenv; pub type TestConnection = MysqlConnection; pub fn connection() -> TestConnection { let database_url = dotenv::var("MYSQL_UNIT_TEST_DATABASE_URL") .or_else(|_| dotenv::var("DATABASE_URL")) .expect("DATABASE_URL must be set in order to run tests"); let conn = MysqlConnection::establish(&database_url).unwrap(); sql_query("DROP TABLE IF EXISTS users CASCADE").execute(&conn).unwrap(); sql_query("CREATE TABLE users (\ id INTEGER PRIMARY KEY AUTO_INCREMENT, \ name TEXT NOT NULL, \ hair_color VARCHAR(255) DEFAULT 'Green')") .execute(&conn) .unwrap(); conn.begin_test_transaction().unwrap(); conn } } else { compile_error!( "At least one backend must be used to test this crate.\n \ Pass argument `--features \"\"` with one or more of the following backends, \ 'mysql', 'postgres', or 'sqlite'. \n\n \ ex. cargo test --features \"mysql postgres sqlite\"\n" ); } } pub fn connection_with_sean_and_tess_in_users_table() -> TestConnection { use schema::users::dsl::*; let connection = connection(); ::diesel::insert_into(users) .values(&vec![ (id.eq(1), name.eq("Sean"), hair_color.eq("black")), (id.eq(2), name.eq("Tess"), hair_color.eq("brown")), ]) .execute(&connection) .unwrap(); connection } diesel_derives-1.4.0/tests/identifiable.rs010066400017500001750000000055361337706336500171020ustar0000000000000000use diesel::associations::Identifiable; table! { foos { id -> Integer, } } table! { bars { id -> VarChar, } } #[test] fn derive_identifiable_on_simple_struct() { #[derive(Identifiable)] struct Foo { id: i32, #[allow(dead_code)] foo: i32, } let foo1 = Foo { id: 1, foo: 2 }; let foo2 = Foo { id: 2, foo: 3 }; assert_eq!(&1, foo1.id()); assert_eq!(&2, foo2.id()); } #[test] fn derive_identifiable_on_tuple_struct() { #[derive(Identifiable)] struct Foo( #[column_name = "id"] i32, #[allow(dead_code)] #[column_name = "lol"] i32, ); let foo1 = Foo(1, 2); let foo2 = Foo(2, 3); assert_eq!(&1, foo1.id()); assert_eq!(&2, foo2.id()); } #[test] fn derive_identifiable_when_id_is_not_first_field() { #[derive(Identifiable)] struct Foo { #[allow(dead_code)] foo: i32, id: i32, } let foo1 = Foo { id: 1, foo: 2 }; let foo2 = Foo { id: 2, foo: 3 }; assert_eq!(&1, foo1.id()); assert_eq!(&2, foo2.id()); } #[test] fn derive_identifiable_on_struct_with_non_integer_pk() { #[derive(Identifiable)] #[table_name = "bars"] struct Foo { id: &'static str, #[allow(dead_code)] foo: i32, } let foo1 = Foo { id: "hi", foo: 2 }; let foo2 = Foo { id: "there", foo: 3, }; assert_eq!(&"hi", foo1.id()); assert_eq!(&"there", foo2.id()); } #[test] fn derive_identifiable_on_struct_with_lifetime() { #[derive(Identifiable)] #[table_name = "bars"] struct Foo<'a> { id: &'a str, #[allow(dead_code)] foo: i32, } let foo1 = Foo { id: "hi", foo: 2 }; let foo2 = Foo { id: "there", foo: 3, }; assert_eq!(&"hi", foo1.id()); assert_eq!(&"there", foo2.id()); } #[test] fn derive_identifiable_with_non_standard_pk() { #[allow(dead_code)] #[derive(Identifiable)] #[table_name = "bars"] #[primary_key(foo_id)] struct Foo<'a> { id: i32, foo_id: &'a str, foo: i32, } let foo1 = Foo { id: 1, foo_id: "hi", foo: 2, }; let foo2 = Foo { id: 2, foo_id: "there", foo: 3, }; assert_eq!(&"hi", foo1.id()); assert_eq!(&"there", foo2.id()); } #[test] fn derive_identifiable_with_composite_pk() { #[allow(dead_code)] #[derive(Identifiable)] #[table_name = "bars"] #[primary_key(foo_id, bar_id)] struct Foo { id: i32, foo_id: i32, bar_id: i32, foo: i32, } let foo1 = Foo { id: 1, foo_id: 2, bar_id: 3, foo: 4, }; let foo2 = Foo { id: 5, foo_id: 6, bar_id: 7, foo: 8, }; assert_eq!((&2, &3), foo1.id()); assert_eq!((&6, &7), foo2.id()); } diesel_derives-1.4.0/tests/insertable.rs010066400017500001750000000163561337706336500166150ustar0000000000000000use diesel::*; use helpers::*; table! { users { id -> Integer, name -> VarChar, hair_color -> Nullable, } } #[test] fn simple_struct_definition() { #[derive(Insertable)] #[table_name = "users"] struct NewUser { name: String, hair_color: String, } let conn = connection(); let new_user = NewUser { name: "Sean".into(), hair_color: "Black".into(), }; insert_into(users::table) .values(new_user) .execute(&conn) .unwrap(); let saved = users::table .select((users::name, users::hair_color)) .load::<(String, Option)>(&conn); let expected = vec![("Sean".to_string(), Some("Black".to_string()))]; assert_eq!(Ok(expected), saved); } #[test] fn simple_reference_definition() { #[derive(Insertable)] #[table_name = "users"] struct NewUser { name: String, hair_color: String, } let conn = connection(); let new_user = NewUser { name: "Sean".into(), hair_color: "Black".into(), }; insert_into(users::table) .values(&new_user) .execute(&conn) .unwrap(); let saved = users::table .select((users::name, users::hair_color)) .load::<(String, Option)>(&conn); let expected = vec![("Sean".to_string(), Some("Black".to_string()))]; assert_eq!(Ok(expected), saved); } macro_rules! test_struct_definition { ($test_name:ident, $struct_def:item) => { #[test] fn $test_name() { #[derive(Insertable)] #[table_name = "users"] $struct_def let conn = connection(); let new_user = NewUser { name: "Sean".into(), hair_color: None }; insert_into(users::table).values(&new_user).execute(&conn).unwrap(); let saved = users::table.select((users::name, users::hair_color)) .load::<(String, Option)>(&conn); let expected = vec![("Sean".to_string(), Some("Green".to_string()))]; assert_eq!(Ok(expected), saved); } } } test_struct_definition! { struct_with_option_field, struct NewUser { name: String, hair_color: Option, } } test_struct_definition! { pub_struct_definition, pub struct NewUser { name: String, hair_color: Option, } } test_struct_definition! { struct_with_pub_field, pub struct NewUser { pub name: String, hair_color: Option, } } test_struct_definition! { struct_with_pub_option_field, pub struct NewUser { name: String, pub hair_color: Option, } } test_struct_definition! { named_struct_with_borrowed_body, struct NewUser<'a> { name: &'a str, hair_color: Option<&'a str>, } } #[test] fn named_struct_with_renamed_field() { #[derive(Insertable)] #[table_name = "users"] struct NewUser { #[column_name = "name"] my_name: String, hair_color: String, } let conn = connection(); let new_user = NewUser { my_name: "Sean".into(), hair_color: "Black".into(), }; insert_into(users::table) .values(&new_user) .execute(&conn) .unwrap(); let saved = users::table .select((users::name, users::hair_color)) .load::<(String, Option)>(&conn); let expected = vec![("Sean".to_string(), Some("Black".to_string()))]; assert_eq!(Ok(expected), saved); } #[test] fn named_struct_with_renamed_option_field() { #[derive(Insertable)] #[table_name = "users"] struct NewUser { #[column_name = "name"] my_name: String, #[column_name = "hair_color"] my_hair_color: Option, } let conn = connection(); let new_user = NewUser { my_name: "Sean".into(), my_hair_color: None, }; insert_into(users::table) .values(&new_user) .execute(&conn) .unwrap(); let saved = users::table .select((users::name, users::hair_color)) .load::<(String, Option)>(&conn); let expected = vec![("Sean".to_string(), Some("Green".to_string()))]; assert_eq!(Ok(expected), saved); } #[test] fn tuple_struct() { #[derive(Insertable)] #[table_name = "users"] struct NewUser<'a>( #[column_name = "name"] &'a str, #[column_name = "hair_color"] Option<&'a str>, ); let conn = connection(); let new_user = NewUser("Sean", None); insert_into(users::table) .values(&new_user) .execute(&conn) .unwrap(); let saved = users::table .select((users::name, users::hair_color)) .load::<(String, Option)>(&conn); let expected = vec![("Sean".to_string(), Some("Green".to_string()))]; assert_eq!(Ok(expected), saved); } #[test] fn named_struct_with_unusual_reference_type() { #[derive(Insertable)] #[table_name = "users"] struct NewUser<'a> { name: &'a String, hair_color: Option<&'a String>, } let conn = connection(); let sean = "Sean".to_string(); let black = "Black".to_string(); let new_user = NewUser { name: &sean, hair_color: Some(&black), }; insert_into(users::table) .values(&new_user) .execute(&conn) .unwrap(); let saved = users::table .select((users::name, users::hair_color)) .load(&conn); let expected = vec![(sean.clone(), Some(black.clone()))]; assert_eq!(Ok(expected), saved); } #[test] #[cfg(all(feature = "postgres", not(feature = "sqlite")))] fn insertable_with_slice_of_borrowed() { table! { posts { id -> Serial, tags -> Array, } } #[derive(Insertable)] #[table_name = "posts"] struct NewPost<'a> { tags: &'a [&'a str], } let conn = connection(); sql_query("DROP TABLE IF EXISTS posts CASCADE") .execute(&conn) .unwrap(); sql_query("CREATE TABLE posts (id SERIAL PRIMARY KEY, tags TEXT[] NOT NULL)") .execute(&conn) .unwrap(); let new_post = NewPost { tags: &["hi", "there"], }; insert_into(posts::table) .values(&new_post) .execute(&conn) .unwrap(); let saved = posts::table.select(posts::tags).load::>(&conn); let expected = vec![vec![String::from("hi"), String::from("there")]]; assert_eq!(Ok(expected), saved); } #[test] fn embedded_struct() { #[derive(Insertable)] #[table_name = "users"] struct NameAndHairColor<'a> { name: &'a str, hair_color: &'a str, } #[derive(Insertable)] struct User<'a> { id: i32, #[diesel(embed)] name_and_hair_color: NameAndHairColor<'a>, } let conn = connection(); let new_user = User { id: 1, name_and_hair_color: NameAndHairColor { name: "Sean", hair_color: "Black", }, }; insert_into(users::table) .values(&new_user) .execute(&conn) .unwrap(); let saved = users::table.load::<(i32, String, Option)>(&conn); let expected = vec![(1, "Sean".to_string(), Some("Black".to_string()))]; assert_eq!(Ok(expected), saved); } diesel_derives-1.4.0/tests/queryable.rs010066400017500001750000000020201337706336500164350ustar0000000000000000use diesel::dsl::sql; use diesel::sql_types::Integer; use diesel::*; use helpers::connection; #[test] fn named_struct_definition() { #[derive(Debug, Clone, Copy, PartialEq, Eq, Queryable)] struct MyStruct { foo: i32, bar: i32, } let conn = connection(); let data = select(sql::<(Integer, Integer)>("1, 2")).get_result(&conn); assert_eq!(Ok(MyStruct { foo: 1, bar: 2 }), data); } #[test] fn tuple_struct() { #[derive(Debug, Clone, Copy, PartialEq, Eq, Queryable)] struct MyStruct(#[column_name = "foo"] i32, #[column_name = "bar"] i32); let conn = connection(); let data = select(sql::<(Integer, Integer)>("1, 2")).get_result(&conn); assert_eq!(Ok(MyStruct(1, 2)), data); } #[test] fn tuple_struct_without_column_name_annotations() { #[derive(Debug, Clone, Copy, PartialEq, Eq, Queryable)] struct MyStruct(i32, i32); let conn = connection(); let data = select(sql::<(Integer, Integer)>("1, 2")).get_result(&conn); assert_eq!(Ok(MyStruct(1, 2)), data); } diesel_derives-1.4.0/tests/queryable_by_name.rs010066400017500001750000000055431337706336500201440ustar0000000000000000use diesel::*; use helpers::connection; #[cfg(feature = "mysql")] type IntSql = ::diesel::sql_types::BigInt; #[cfg(feature = "mysql")] type IntRust = i64; #[cfg(not(feature = "mysql"))] type IntSql = ::diesel::sql_types::Integer; #[cfg(not(feature = "mysql"))] type IntRust = i32; table! { use super::IntSql; my_structs (foo) { foo -> IntSql, bar -> IntSql, } } #[test] fn named_struct_definition() { #[derive(Debug, Clone, Copy, PartialEq, Eq, QueryableByName)] #[table_name = "my_structs"] struct MyStruct { foo: IntRust, bar: IntRust, } let conn = connection(); let data = sql_query("SELECT 1 AS foo, 2 AS bar").get_result(&conn); assert_eq!(Ok(MyStruct { foo: 1, bar: 2 }), data); } #[test] fn tuple_struct() { #[derive(Debug, Clone, Copy, PartialEq, Eq, QueryableByName)] #[table_name = "my_structs"] struct MyStruct( #[column_name = "foo"] IntRust, #[column_name = "bar"] IntRust, ); let conn = connection(); let data = sql_query("SELECT 1 AS foo, 2 AS bar").get_result(&conn); assert_eq!(Ok(MyStruct(1, 2)), data); } // FIXME: Test usage with renamed columns #[test] fn struct_with_no_table() { #[derive(Debug, Clone, Copy, PartialEq, Eq, QueryableByName)] struct MyStructNamedSoYouCantInferIt { #[sql_type = "IntSql"] foo: IntRust, #[sql_type = "IntSql"] bar: IntRust, } let conn = connection(); let data = sql_query("SELECT 1 AS foo, 2 AS bar").get_result(&conn); assert_eq!(Ok(MyStructNamedSoYouCantInferIt { foo: 1, bar: 2 }), data); } #[test] fn embedded_struct() { #[derive(Debug, Clone, Copy, PartialEq, Eq, QueryableByName)] #[table_name = "my_structs"] struct A { foo: IntRust, #[diesel(embed)] b: B, } #[derive(Debug, Clone, Copy, PartialEq, Eq, QueryableByName)] #[table_name = "my_structs"] struct B { bar: IntRust, } let conn = connection(); let data = sql_query("SELECT 1 AS foo, 2 AS bar").get_result(&conn); assert_eq!( Ok(A { foo: 1, b: B { bar: 2 }, }), data ); } #[test] fn embedded_option() { #[derive(Debug, Clone, Copy, PartialEq, Eq, QueryableByName)] #[table_name = "my_structs"] struct A { foo: IntRust, #[diesel(embed)] b: Option, } #[derive(Debug, Clone, Copy, PartialEq, Eq, QueryableByName)] #[table_name = "my_structs"] struct B { bar: IntRust, } let conn = connection(); let data = sql_query("SELECT 1 AS foo, 2 AS bar").get_result(&conn); assert_eq!( Ok(A { foo: 1, b: Some(B { bar: 2 }), }), data ); let data = sql_query("SELECT 1 AS foo, NULL AS bar").get_result(&conn); assert_eq!(Ok(A { foo: 1, b: None }), data); } diesel_derives-1.4.0/tests/schema.rs010066400017500001750000000001601337706336500157070ustar0000000000000000table! { users { id -> Integer, name -> Text, hair_color -> Nullable, } } diesel_derives-1.4.0/tests/tests.rs010066400017500001750000000003371337706336500156170ustar0000000000000000#![deny(warnings)] #[macro_use] extern crate cfg_if; #[macro_use] extern crate diesel; mod helpers; mod schema; mod as_changeset; mod associations; mod identifiable; mod insertable; mod queryable; mod queryable_by_name; diesel_derives-1.4.0/src/associations.rs.orig010066400017500001750000000102641334147254000175260ustar0000000000000000use proc_macro2; use syn; use diagnostic_shim::*; use meta::*; use model::*; use util::*; pub fn derive(item: syn::DeriveInput) -> Result { let model = Model::from_item(&item)?; let tokens = MetaItem::all_with_name(&item.attrs, "belongs_to") .into_iter() .filter_map( |attr| match derive_belongs_to(&model, &item.generics, attr) { Ok(t) => Some(t), Err(e) => { e.emit(); None } }, ); Ok(wrap_in_dummy_mod( model.dummy_mod_name("associations"), quote!(#(#tokens)*), )) } fn derive_belongs_to( model: &Model, generics: &syn::Generics, meta: MetaItem, ) -> Result { let AssociationOptions { parent_struct, foreign_key, } = AssociationOptions::from_meta(meta)?; let (_, ty_generics, _) = generics.split_for_impl(); let foreign_key_field = model.find_column(&foreign_key)?; let struct_name = &model.name; let foreign_key_access = foreign_key_field.name.access(); let foreign_key_ty = inner_of_option_ty(&foreign_key_field.ty); let table_name = model.table_name(); let mut generics = generics.clone(); <<<<<<< HEAD ======= // TODO: Remove this special casing as soon as we bump our minimal supported // rust version to >= 1.30.0 because this version will add // `impl<'a, T> From<&'a Option> for Option<&'a T>` to the std-lib >>>>>>> origin/feature/belongs_to_uses_into let foreign_key_expr = if is_option_ty(&foreign_key_field.ty) { quote!(self#foreign_key_access.as_ref().and_then(std::convert::Into::into)) } else { quote!(std::convert::Into::into(&self#foreign_key_access)) }; generics.params.push(parse_quote!(__FK)); { let where_clause = generics.where_clause.get_or_insert(parse_quote!(where)); where_clause .predicates .push(parse_quote!(__FK: std::hash::Hash + std::cmp::Eq)); where_clause.predicates.push( parse_quote!(for<'__a> &'__a #foreign_key_ty: std::convert::Into<::std::option::Option<&'__a __FK>>), ); where_clause.predicates.push( parse_quote!(for<'__a> &'__a #parent_struct: diesel::associations::Identifiable), ); } let (impl_generics, _, where_clause) = generics.split_for_impl(); Ok(quote! { impl #impl_generics diesel::associations::BelongsTo<#parent_struct> for #struct_name #ty_generics #where_clause { type ForeignKey = __FK; type ForeignKeyColumn = #table_name::#foreign_key; fn foreign_key(&self) -> std::option::Option<&Self::ForeignKey> { #foreign_key_expr } fn foreign_key_column() -> Self::ForeignKeyColumn { #table_name::#foreign_key } } }) } struct AssociationOptions { parent_struct: syn::Ident, foreign_key: syn::Ident, } impl AssociationOptions { fn from_meta(meta: MetaItem) -> Result { let parent_struct = meta.nested()? .nth(0) .ok_or_else(|| meta.span()) .and_then(|m| m.word().map_err(|_| m.span())) .map_err(|span| { span.error("Expected a struct name") .help("e.g. `#[belongs_to(User)]`") })?; let foreign_key = meta.nested_item("foreign_key") .ok() .map(|i| i.ident_value()) .unwrap_or_else(|| Ok(infer_foreign_key(&parent_struct)))?; let unrecognized_options = meta.nested()?.skip(1).filter(|n| n.name() != "foreign_key"); for ignored in unrecognized_options { ignored .span() .warning(format!("Unrecognized option {}", ignored.name())) .emit(); } Ok(Self { parent_struct, foreign_key, }) } } fn infer_foreign_key(name: &syn::Ident) -> syn::Ident { let snake_case = camel_to_snake(&name.to_string()); syn::Ident::new(&format!("{}_id", snake_case), name.span()) } diesel_derives-1.4.0/tests/insertable.rs~010066400017500001750000000214011323134374400167650ustar0000000000000000use diesel::prelude::*; table! { users { id -> Integer, name -> VarChar, hair_color -> Nullable, } } #[test] fn simple_struct_definition() { struct NewUser { name: String, hair_color: String, } impl_Insertable! { (users) struct NewUser { name: String, hair_color: String, } } let conn = connection(); let new_user = NewUser { name: "Sean".into(), hair_color: "Black".into(), }; ::insert_into(users::table) .values(&new_user) .execute(&conn) .unwrap(); let saved = users::table .select((users::name, users::hair_color)) .load::<(String, Option)>(&conn); let expected = vec![("Sean".to_string(), Some("Black".to_string()))]; assert_eq!(Ok(expected), saved); } macro_rules! test_struct_definition { ($test_name:ident, $($struct_def:tt)*) => { #[test] fn $test_name() { use prelude::*; __diesel_parse_as_item!($($struct_def)*); impl_Insertable! { (users) $($struct_def)* } let conn = connection(); let new_user = NewUser { name: "Sean".into(), hair_color: None }; ::insert_into(users::table).values(&new_user).execute(&conn).unwrap(); let saved = users::table.select((users::name, users::hair_color)) .load::<(String, Option)>(&conn); let expected = vec![("Sean".to_string(), Some("Green".to_string()))]; assert_eq!(Ok(expected), saved); } } } test_struct_definition! { struct_with_option_field, struct NewUser { name: String, hair_color: Option, } } test_struct_definition! { pub_struct_definition, pub struct NewUser { name: String, hair_color: Option, } } test_struct_definition! { struct_with_pub_field, pub struct NewUser { pub name: String, hair_color: Option, } } test_struct_definition! { struct_with_pub_option_field, pub struct NewUser { name: String, pub hair_color: Option, } } test_struct_definition! { named_struct_with_borrowed_body, struct NewUser<'a> { name: &'a str, hair_color: Option<&'a str>, } } test_struct_definition! { named_struct_without_trailing_comma, struct NewUser<'a> { name: &'a str, hair_color: Option<&'a str> } } #[test] fn named_struct_with_renamed_field() { struct NewUser { my_name: String, hair_color: String, } impl_Insertable! { (users) struct NewUser { #[column_name(name)] my_name: String, hair_color: String, } } let conn = connection(); let new_user = NewUser { my_name: "Sean".into(), hair_color: "Black".into(), }; ::insert_into(users::table) .values(&new_user) .execute(&conn) .unwrap(); let saved = users::table .select((users::name, users::hair_color)) .load::<(String, Option)>(&conn); let expected = vec![("Sean".to_string(), Some("Black".to_string()))]; assert_eq!(Ok(expected), saved); } #[test] fn named_struct_with_renamed_option_field() { struct NewUser { my_name: String, my_hair_color: Option, } impl_Insertable! { (users) struct NewUser { #[column_name(name)] my_name: String, #[column_name(hair_color)] my_hair_color: Option, } } let conn = connection(); let new_user = NewUser { my_name: "Sean".into(), my_hair_color: None, }; ::insert_into(users::table) .values(&new_user) .execute(&conn) .unwrap(); let saved = users::table .select((users::name, users::hair_color)) .load::<(String, Option)>(&conn); let expected = vec![("Sean".to_string(), Some("Green".to_string()))]; assert_eq!(Ok(expected), saved); } #[test] fn tuple_struct() { struct NewUser<'a>(&'a str, Option<&'a str>); impl_Insertable! { (users) struct NewUser<'a>( #[column_name(name)] pub &'a str, #[column_name(hair_color)] Option<&'a str>, ); } let conn = connection(); let new_user = NewUser("Sean", None); ::insert_into(users::table) .values(&new_user) .execute(&conn) .unwrap(); let saved = users::table .select((users::name, users::hair_color)) .load::<(String, Option)>(&conn); let expected = vec![("Sean".to_string(), Some("Green".to_string()))]; assert_eq!(Ok(expected), saved); } #[test] fn tuple_struct_without_trailing_comma() { struct NewUser<'a>(&'a str, Option<&'a str>); impl_Insertable! { (users) struct NewUser<'a>( #[column_name(name)] pub &'a str, #[column_name(hair_color)] Option<&'a str> ); } let conn = connection(); let new_user = NewUser("Sean", None); ::insert_into(users::table) .values(&new_user) .execute(&conn) .unwrap(); let saved = users::table .select((users::name, users::hair_color)) .load::<(String, Option)>(&conn); let expected = vec![("Sean".to_string(), Some("Green".to_string()))]; assert_eq!(Ok(expected), saved); } #[test] fn named_struct_with_unusual_reference_type() { struct NewUser<'a> { name: &'a String, hair_color: Option<&'a String>, } impl_Insertable! { (users) struct NewUser<'a> { name: &'a String, hair_color: Option<&'a String>, } } let conn = connection(); let sean = "Sean".to_string(); let black = "Black".to_string(); let new_user = NewUser { name: &sean, hair_color: Some(&black), }; ::insert_into(users::table) .values(&new_user) .execute(&conn) .unwrap(); let saved = users::table .select((users::name, users::hair_color)) .load(&conn); let expected = vec![(sean.clone(), Some(black.clone()))]; assert_eq!(Ok(expected), saved); } cfg_if! { if #[cfg(feature = "sqlite")] { fn connection() -> ::test_helpers::TestConnection { let conn = ::test_helpers::connection(); conn.execute("CREATE TABLE users (id INTEGER PRIMARY KEY AUTOINCREMENT, name VARCHAR NOT NULL, hair_color VARCHAR DEFAULT 'Green')").unwrap(); conn } } else if #[cfg(feature = "postgres")] { fn connection() -> ::test_helpers::TestConnection { let conn = ::test_helpers::connection(); conn.execute("DROP TABLE IF EXISTS users").unwrap(); conn.execute("CREATE TABLE users (id SERIAL PRIMARY KEY, name VARCHAR NOT NULL, hair_color VARCHAR DEFAULT 'Green')").unwrap(); conn } #[test] fn insertable_with_slice_of_borrowed() { table! { posts { id -> Serial, tags -> Array, } } struct NewPost<'a> { tags: &'a [&'a str], } impl_Insertable! { (posts) struct NewPost<'a> { tags: &'a [&'a str], } } let conn = ::test_helpers::connection(); conn.execute("DROP TABLE IF EXISTS posts").unwrap(); conn.execute("CREATE TABLE posts (id SERIAL PRIMARY KEY, tags TEXT[] NOT NULL)").unwrap(); let new_post = NewPost { tags: &["hi", "there"] }; ::insert_into(posts::table).values(&new_post).execute(&conn).unwrap(); let saved = posts::table.select(posts::tags).load::>(&conn); let expected = vec![vec![String::from("hi"), String::from("there")]]; assert_eq!(Ok(expected), saved); } } else if #[cfg(feature = "mysql")] { fn connection() -> ::test_helpers::TestConnection { let conn = ::test_helpers::connection_no_transaction(); conn.execute("DROP TABLE IF EXISTS users").unwrap(); conn.execute("CREATE TABLE users (id INTEGER PRIMARY KEY AUTO_INCREMENT, name TEXT NOT NULL, hair_color VARCHAR(255) DEFAULT 'Green')").unwrap(); conn.begin_test_transaction().unwrap(); conn } } else { compile_error!( "At least one backend must be used to test this crate.\n \ Pass argument `--features \"\"` with one or more of the following backends, \ 'mysql', 'postgres', or 'sqlite'. \n\n \ ex. cargo test --features \"mysql postgres sqlite\"\n" ); } }