]> git.lizzy.rs Git - rust.git/blob - src/libsyntax/ext/deriving/encodable.rs
Merge pull request #20510 from tshepang/patch-6
[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: uint }
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_uint(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,
132                          vec!(box Literal(Path::new_local("__S")),
133                               box Literal(Path::new_local("__E"))), true),
134         additional_bounds: Vec::new(),
135         generics: LifetimeBounds {
136             lifetimes: Vec::new(),
137             bounds: vec!(("__S", vec!(Path::new_(
138                             vec!(krate, "Encoder"), None,
139                             vec!(box Literal(Path::new_local("__E"))), true))),
140                          ("__E", vec!()))
141         },
142         methods: vec!(
143             MethodDef {
144                 name: "encode",
145                 generics: LifetimeBounds::empty(),
146                 explicit_self: borrowed_explicit_self(),
147                 args: vec!(Ptr(box Literal(Path::new_local("__S")),
148                             Borrowed(None, MutMutable))),
149                 ret_ty: Literal(Path::new_(vec!("std", "result", "Result"),
150                                            None,
151                                            vec!(box Tuple(Vec::new()),
152                                                 box Literal(Path::new_local("__E"))),
153                                            true)),
154                 attributes: Vec::new(),
155                 combine_substructure: combine_substructure(|a, b, c| {
156                     encodable_substructure(a, b, c)
157                 }),
158             })
159     };
160
161     trait_def.expand(cx, mitem, item, push)
162 }
163
164 fn encodable_substructure(cx: &mut ExtCtxt, trait_span: Span,
165                           substr: &Substructure) -> P<Expr> {
166     let encoder = substr.nonself_args[0].clone();
167     // throw an underscore in front to suppress unused variable warnings
168     let blkarg = cx.ident_of("_e");
169     let blkencoder = cx.expr_ident(trait_span, blkarg);
170     let encode = cx.ident_of("encode");
171
172     return match *substr.fields {
173         Struct(ref fields) => {
174             let emit_struct_field = cx.ident_of("emit_struct_field");
175             let mut stmts = Vec::new();
176             let last = fields.len() - 1;
177             for (i, &FieldInfo {
178                     name,
179                     ref self_,
180                     span,
181                     ..
182                 }) in fields.iter().enumerate() {
183                 let name = match name {
184                     Some(id) => token::get_ident(id),
185                     None => {
186                         token::intern_and_get_ident(format!("_field{}", i)[])
187                     }
188                 };
189                 let enc = cx.expr_method_call(span, self_.clone(),
190                                               encode, vec!(blkencoder.clone()));
191                 let lambda = cx.lambda_expr_1(span, enc, blkarg);
192                 let call = cx.expr_method_call(span, blkencoder.clone(),
193                                                emit_struct_field,
194                                                vec!(cx.expr_str(span, name),
195                                                  cx.expr_uint(span, i),
196                                                  lambda));
197
198                 // last call doesn't need a try!
199                 let call = if i != last {
200                     cx.expr_try(span, call)
201                 } else {
202                     cx.expr(span, ExprRet(Some(call)))
203                 };
204                 stmts.push(cx.stmt_expr(call));
205             }
206
207             // unit structs have no fields and need to return Ok()
208             if stmts.is_empty() {
209                 let ret_ok = cx.expr(trait_span,
210                                      ExprRet(Some(cx.expr_ok(trait_span,
211                                                              cx.expr_tuple(trait_span, vec![])))));
212                 stmts.push(cx.stmt_expr(ret_ok));
213             }
214
215             let blk = cx.lambda_stmts_1(trait_span, stmts, blkarg);
216             cx.expr_method_call(trait_span,
217                                 encoder,
218                                 cx.ident_of("emit_struct"),
219                                 vec!(
220                 cx.expr_str(trait_span, token::get_ident(substr.type_ident)),
221                 cx.expr_uint(trait_span, fields.len()),
222                 blk
223             ))
224         }
225
226         EnumMatching(idx, variant, ref fields) => {
227             // We're not generating an AST that the borrow checker is expecting,
228             // so we need to generate a unique local variable to take the
229             // mutable loan out on, otherwise we get conflicts which don't
230             // actually exist.
231             let me = cx.stmt_let(trait_span, false, blkarg, encoder);
232             let encoder = cx.expr_ident(trait_span, blkarg);
233             let emit_variant_arg = cx.ident_of("emit_enum_variant_arg");
234             let mut stmts = Vec::new();
235             let last = fields.len() - 1;
236             for (i, &FieldInfo { ref self_, span, .. }) in fields.iter().enumerate() {
237                 let enc = cx.expr_method_call(span, self_.clone(),
238                                               encode, vec!(blkencoder.clone()));
239                 let lambda = cx.lambda_expr_1(span, enc, blkarg);
240                 let call = cx.expr_method_call(span, blkencoder.clone(),
241                                                emit_variant_arg,
242                                                vec!(cx.expr_uint(span, i),
243                                                  lambda));
244                 let call = if i != last {
245                     cx.expr_try(span, call)
246                 } else {
247                     cx.expr(span, ExprRet(Some(call)))
248                 };
249                 stmts.push(cx.stmt_expr(call));
250             }
251
252             // enums with no fields need to return Ok()
253             if stmts.len() == 0 {
254                 let ret_ok = cx.expr(trait_span,
255                                      ExprRet(Some(cx.expr_ok(trait_span,
256                                                              cx.expr_tuple(trait_span, vec![])))));
257                 stmts.push(cx.stmt_expr(ret_ok));
258             }
259
260             let blk = cx.lambda_stmts_1(trait_span, stmts, blkarg);
261             let name = cx.expr_str(trait_span, token::get_ident(variant.node.name));
262             let call = cx.expr_method_call(trait_span, blkencoder,
263                                            cx.ident_of("emit_enum_variant"),
264                                            vec!(name,
265                                              cx.expr_uint(trait_span, idx),
266                                              cx.expr_uint(trait_span, fields.len()),
267                                              blk));
268             let blk = cx.lambda_expr_1(trait_span, call, blkarg);
269             let ret = cx.expr_method_call(trait_span,
270                                           encoder,
271                                           cx.ident_of("emit_enum"),
272                                           vec!(
273                 cx.expr_str(trait_span, token::get_ident(substr.type_ident)),
274                 blk
275             ));
276             cx.expr_block(cx.block(trait_span, vec!(me), Some(ret)))
277         }
278
279         _ => cx.bug("expected Struct or EnumMatching in deriving(Encodable)")
280     };
281 }