]> git.lizzy.rs Git - rust.git/blob - src/libsyntax/ext/deriving/encodable.rs
Associated types support for deriving::generic::TraitDef
[rust.git] / src / libsyntax / ext / deriving / encodable.rs
1 // Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 //! The compiler code necessary to implement the `#[derive(Encodable)]`
12 //! (and `Decodable`, in decodable.rs) extension.  The idea here is that
13 //! type-defining items may be tagged with `#[derive(Encodable, Decodable)]`.
14 //!
15 //! For example, a type like:
16 //!
17 //! ```ignore
18 //! #[derive(Encodable, Decodable)]
19 //! struct Node { id: usize }
20 //! ```
21 //!
22 //! would generate two implementations like:
23 //!
24 //! ```ignore
25 //! impl<S: Encoder<E>, E> Encodable<S, E> for Node {
26 //!     fn encode(&self, s: &mut S) -> Result<(), E> {
27 //!         s.emit_struct("Node", 1, |this| {
28 //!             this.emit_struct_field("id", 0, |this| {
29 //!                 Encodable::encode(&self.id, this)
30 //!                 /* this.emit_usize(self.id) can also be used */
31 //!             })
32 //!         })
33 //!     }
34 //! }
35 //!
36 //! impl<D: Decoder<E>, E> Decodable<D, E> for Node {
37 //!     fn decode(d: &mut D) -> Result<Node, E> {
38 //!         d.read_struct("Node", 1, |this| {
39 //!             match this.read_struct_field("id", 0, |this| Decodable::decode(this)) {
40 //!                 Ok(id) => Ok(Node { id: id }),
41 //!                 Err(e) => Err(e),
42 //!             }
43 //!         })
44 //!     }
45 //! }
46 //! ```
47 //!
48 //! Other interesting scenarios are when the item has type parameters or
49 //! references other non-built-in types.  A type definition like:
50 //!
51 //! ```ignore
52 //! #[derive(Encodable, Decodable)]
53 //! struct Spanned<T> { node: T, span: Span }
54 //! ```
55 //!
56 //! would yield functions like:
57 //!
58 //! ```ignore
59 //! impl<
60 //!     S: Encoder<E>,
61 //!     E,
62 //!     T: Encodable<S, E>
63 //! > Encodable<S, E> for Spanned<T> {
64 //!     fn encode(&self, s: &mut S) -> Result<(), E> {
65 //!         s.emit_struct("Spanned", 2, |this| {
66 //!             this.emit_struct_field("node", 0, |this| self.node.encode(this))
67 //!                 .ok().unwrap();
68 //!             this.emit_struct_field("span", 1, |this| self.span.encode(this))
69 //!         })
70 //!     }
71 //! }
72 //!
73 //! impl<
74 //!     D: Decoder<E>,
75 //!     E,
76 //!     T: Decodable<D, E>
77 //! > Decodable<D, E> for Spanned<T> {
78 //!     fn decode(d: &mut D) -> Result<Spanned<T>, E> {
79 //!         d.read_struct("Spanned", 2, |this| {
80 //!             Ok(Spanned {
81 //!                 node: this.read_struct_field("node", 0, |this| Decodable::decode(this))
82 //!                     .ok().unwrap(),
83 //!                 span: this.read_struct_field("span", 1, |this| Decodable::decode(this))
84 //!                     .ok().unwrap(),
85 //!             })
86 //!         })
87 //!     }
88 //! }
89 //! ```
90
91 use ast::{MetaItem, Item, Expr, ExprRet, MutMutable};
92 use codemap::Span;
93 use ext::base::ExtCtxt;
94 use ext::build::AstBuilder;
95 use ext::deriving::generic::*;
96 use ext::deriving::generic::ty::*;
97 use parse::token;
98 use ptr::P;
99
100 pub fn expand_deriving_rustc_encodable<F>(cx: &mut ExtCtxt,
101                                           span: Span,
102                                           mitem: &MetaItem,
103                                           item: &Item,
104                                           push: F) where
105     F: FnOnce(P<Item>),
106 {
107     expand_deriving_encodable_imp(cx, span, mitem, item, push, "rustc_serialize")
108 }
109
110 pub fn expand_deriving_encodable<F>(cx: &mut ExtCtxt,
111                                     span: Span,
112                                     mitem: &MetaItem,
113                                     item: &Item,
114                                     push: F) where
115     F: FnOnce(P<Item>),
116 {
117     expand_deriving_encodable_imp(cx, span, mitem, item, push, "serialize")
118 }
119
120 fn expand_deriving_encodable_imp<F>(cx: &mut ExtCtxt,
121                                     span: Span,
122                                     mitem: &MetaItem,
123                                     item: &Item,
124                                     push: F,
125                                     krate: &'static str) where
126     F: FnOnce(P<Item>),
127 {
128     let trait_def = TraitDef {
129         span: span,
130         attributes: Vec::new(),
131         path: Path::new_(vec!(krate, "Encodable"), None, vec!(), true),
132         additional_bounds: Vec::new(),
133         generics: LifetimeBounds::empty(),
134         methods: vec!(
135             MethodDef {
136                 name: "encode",
137                 generics: LifetimeBounds {
138                     lifetimes: Vec::new(),
139                     bounds: vec!(("__S", vec!(Path::new_(
140                                     vec!(krate, "Encoder"), None,
141                                     vec!(), true))))
142                 },
143                 explicit_self: borrowed_explicit_self(),
144                 args: vec!(Ptr(box Literal(Path::new_local("__S")),
145                             Borrowed(None, MutMutable))),
146                 ret_ty: Literal(Path::new_(
147                     vec!("std", "result", "Result"),
148                     None,
149                     vec!(box Tuple(Vec::new()), box Literal(Path::new_(
150                         vec!["__S", "Error"], None, vec![], false
151                     ))),
152                     true
153                 )),
154                 attributes: Vec::new(),
155                 combine_substructure: combine_substructure(box |a, b, c| {
156                     encodable_substructure(a, b, c)
157                 }),
158             }
159         ),
160         associated_types: Vec::new(),
161     };
162
163     trait_def.expand(cx, mitem, item, push)
164 }
165
166 fn encodable_substructure(cx: &mut ExtCtxt, trait_span: Span,
167                           substr: &Substructure) -> P<Expr> {
168     let encoder = substr.nonself_args[0].clone();
169     // throw an underscore in front to suppress unused variable warnings
170     let blkarg = cx.ident_of("_e");
171     let blkencoder = cx.expr_ident(trait_span, blkarg);
172     let encode = cx.ident_of("encode");
173
174     return match *substr.fields {
175         Struct(ref fields) => {
176             let emit_struct_field = cx.ident_of("emit_struct_field");
177             let mut stmts = Vec::new();
178             let last = fields.len() - 1;
179             for (i, &FieldInfo {
180                     name,
181                     ref self_,
182                     span,
183                     ..
184                 }) in fields.iter().enumerate() {
185                 let name = match name {
186                     Some(id) => token::get_ident(id),
187                     None => {
188                         token::intern_and_get_ident(&format!("_field{}", i)[])
189                     }
190                 };
191                 let enc = cx.expr_method_call(span, self_.clone(),
192                                               encode, vec!(blkencoder.clone()));
193                 let lambda = cx.lambda_expr_1(span, enc, blkarg);
194                 let call = cx.expr_method_call(span, blkencoder.clone(),
195                                                emit_struct_field,
196                                                vec!(cx.expr_str(span, name),
197                                                  cx.expr_usize(span, i),
198                                                  lambda));
199
200                 // last call doesn't need a try!
201                 let call = if i != last {
202                     cx.expr_try(span, call)
203                 } else {
204                     cx.expr(span, ExprRet(Some(call)))
205                 };
206                 stmts.push(cx.stmt_expr(call));
207             }
208
209             // unit structs have no fields and need to return Ok()
210             if stmts.is_empty() {
211                 let ret_ok = cx.expr(trait_span,
212                                      ExprRet(Some(cx.expr_ok(trait_span,
213                                                              cx.expr_tuple(trait_span, vec![])))));
214                 stmts.push(cx.stmt_expr(ret_ok));
215             }
216
217             let blk = cx.lambda_stmts_1(trait_span, stmts, blkarg);
218             cx.expr_method_call(trait_span,
219                                 encoder,
220                                 cx.ident_of("emit_struct"),
221                                 vec!(
222                 cx.expr_str(trait_span, token::get_ident(substr.type_ident)),
223                 cx.expr_usize(trait_span, fields.len()),
224                 blk
225             ))
226         }
227
228         EnumMatching(idx, variant, ref fields) => {
229             // We're not generating an AST that the borrow checker is expecting,
230             // so we need to generate a unique local variable to take the
231             // mutable loan out on, otherwise we get conflicts which don't
232             // actually exist.
233             let me = cx.stmt_let(trait_span, false, blkarg, encoder);
234             let encoder = cx.expr_ident(trait_span, blkarg);
235             let emit_variant_arg = cx.ident_of("emit_enum_variant_arg");
236             let mut stmts = Vec::new();
237             let last = fields.len() - 1;
238             for (i, &FieldInfo { ref self_, span, .. }) in fields.iter().enumerate() {
239                 let enc = cx.expr_method_call(span, self_.clone(),
240                                               encode, vec!(blkencoder.clone()));
241                 let lambda = cx.lambda_expr_1(span, enc, blkarg);
242                 let call = cx.expr_method_call(span, blkencoder.clone(),
243                                                emit_variant_arg,
244                                                vec!(cx.expr_usize(span, i),
245                                                  lambda));
246                 let call = if i != last {
247                     cx.expr_try(span, call)
248                 } else {
249                     cx.expr(span, ExprRet(Some(call)))
250                 };
251                 stmts.push(cx.stmt_expr(call));
252             }
253
254             // enums with no fields need to return Ok()
255             if stmts.len() == 0 {
256                 let ret_ok = cx.expr(trait_span,
257                                      ExprRet(Some(cx.expr_ok(trait_span,
258                                                              cx.expr_tuple(trait_span, vec![])))));
259                 stmts.push(cx.stmt_expr(ret_ok));
260             }
261
262             let blk = cx.lambda_stmts_1(trait_span, stmts, blkarg);
263             let name = cx.expr_str(trait_span, token::get_ident(variant.node.name));
264             let call = cx.expr_method_call(trait_span, blkencoder,
265                                            cx.ident_of("emit_enum_variant"),
266                                            vec!(name,
267                                              cx.expr_usize(trait_span, idx),
268                                              cx.expr_usize(trait_span, fields.len()),
269                                              blk));
270             let blk = cx.lambda_expr_1(trait_span, call, blkarg);
271             let ret = cx.expr_method_call(trait_span,
272                                           encoder,
273                                           cx.ident_of("emit_enum"),
274                                           vec!(
275                 cx.expr_str(trait_span, token::get_ident(substr.type_ident)),
276                 blk
277             ));
278             cx.expr_block(cx.block(trait_span, vec!(me), Some(ret)))
279         }
280
281         _ => cx.bug("expected Struct or EnumMatching in derive(Encodable)")
282     };
283 }