@@ -2,55 +2,84 @@ use syn;
22use quote;
33
44use model:: { infer_association_name, Model } ;
5- use util:: str_value_of_meta_item;
5+ use util:: { str_value_of_meta_item, wrap_item_in_const } ;
66
77pub fn derive_associations ( input : syn:: DeriveInput ) -> quote:: Tokens {
8- let mut derived_associations = Vec :: new ( ) ;
98 let model = t ! ( Model :: from_item( & input, "Associations" ) ) ;
109
11- for attr in input. attrs . as_slice ( ) {
10+ let derived_associations = input. attrs . as_slice ( ) . iter ( ) . filter_map ( |attr| {
1211 if attr. name ( ) == "belongs_to" {
13- let options = t ! ( build_association_options( attr, "belongs_to" ) ) ;
14- derived_associations. push ( expand_belongs_to ( & model, options) )
12+ Some (
13+ build_association_options ( attr)
14+ . map ( |options| expand_belongs_to ( & model, options) )
15+ . unwrap_or_else ( |e| e) ,
16+ )
17+ } else {
18+ None
1519 }
16- }
20+ } ) ;
1721
18- quote ! ( #( #derived_associations) * )
22+ wrap_item_in_const (
23+ model. dummy_const_name ( "ASSOCIATIONS" ) ,
24+ quote ! ( #( #derived_associations) * ) ,
25+ )
1926}
2027
2128fn expand_belongs_to ( model : & Model , options : AssociationOptions ) -> quote:: Tokens {
2229 let parent_struct = options. name ;
2330 let struct_name = & model. name ;
31+ let fields = model. attrs . as_slice ( ) ;
2432
2533 let foreign_key_name = options
2634 . foreign_key_name
2735 . unwrap_or_else ( || to_foreign_key ( parent_struct. as_ref ( ) ) ) ;
36+ let foreign_key_name = & foreign_key_name;
2837 let child_table_name = model. table_name ( ) ;
29- let fields = model. attrs . as_slice ( ) ;
38+ let child_table_name = & child_table_name;
39+ let foreign_key_attr = fields
40+ . iter ( )
41+ . find ( |attr| attr. column_name ( ) . as_ref ( ) == foreign_key_name. as_ref ( ) )
42+ . unwrap ( ) ;
43+ let foreign_key_ty = & foreign_key_attr. ty ;
44+
45+ // we need to special case foreign keys on with an Option type
46+ // to allow self referencing joins
47+ let ( foreign_key, foreign_key_ty) = match * foreign_key_ty {
48+ syn:: Ty :: Path ( None , ref p) if p. segments [ 0 ] . ident == "Option" => {
49+ let segment = & p. segments [ 0 ] ;
50+ let t = match segment. parameters {
51+ syn:: PathParameters :: AngleBracketed ( ref p) => p. types [ 0 ] . clone ( ) ,
52+ syn:: PathParameters :: Parenthesized ( _) => unreachable ! ( ) ,
53+ } ;
54+ ( quote ! ( self . #foreign_key_name. as_ref( ) ) , t)
55+ }
56+ ref t => ( quote ! ( Some ( & self . #foreign_key_name) ) , t. clone ( ) ) ,
57+ } ;
3058
31- quote ! ( BelongsTo ! {
32- (
33- struct_name = #struct_name,
34- parent_struct = #parent_struct,
35- foreign_key_name = #foreign_key_name,
36- child_table_name = #child_table_name,
37- ) ,
38- fields = [ #( #fields) * ] ,
39- } )
59+ quote ! (
60+ impl diesel:: associations:: BelongsTo <#parent_struct> for #struct_name {
61+ type ForeignKey = #foreign_key_ty;
62+ type ForeignKeyColumn = #child_table_name:: #foreign_key_name;
63+
64+ fn foreign_key( & self ) -> Option <& Self :: ForeignKey > {
65+ #foreign_key
66+ }
67+
68+ fn foreign_key_column( ) -> Self :: ForeignKeyColumn {
69+ #child_table_name:: #foreign_key_name
70+ }
71+ }
72+ )
4073}
4174
4275struct AssociationOptions {
4376 name : syn:: Ident ,
4477 foreign_key_name : Option < syn:: Ident > ,
4578}
4679
47- fn build_association_options (
48- attr : & syn:: Attribute ,
49- association_kind : & str ,
50- ) -> Result < AssociationOptions , String > {
51- let usage_error = Err ( format ! (
52- "`#[{}]` must be in the form `#[{}(table_name, option=value)]`" ,
53- association_kind, association_kind
80+ fn build_association_options ( attr : & syn:: Attribute ) -> Result < AssociationOptions , quote:: Tokens > {
81+ let usage_error = Err ( quote ! (
82+ compile_error!( "`#[belongs_to]` must be in the form `#[belongs_to(table_name, foreign_key=\" column_name\" )]`" )
5483 ) ) ;
5584 match attr. value {
5685 syn:: MetaItem :: List ( _, ref options) if options. len ( ) >= 1 => {
@@ -67,11 +96,14 @@ fn build_association_options(
6796 . find ( |a| a. name ( ) == "foreign_key" )
6897 . map ( |a| str_value_of_meta_item ( a, "foreign_key" ) )
6998 . map ( syn:: Ident :: new) ;
70-
71- Ok ( AssociationOptions {
72- name : association_name,
73- foreign_key_name : foreign_key_name,
74- } )
99+ if options. len ( ) == 1 || ( options. len ( ) == 2 && foreign_key_name. is_some ( ) ) {
100+ Ok ( AssociationOptions {
101+ name : association_name,
102+ foreign_key_name : foreign_key_name,
103+ } )
104+ } else {
105+ usage_error
106+ }
75107 }
76108 _ => usage_error,
77109 }
0 commit comments