]> git.lizzy.rs Git - rust.git/blob - src/libsyntax/ext/deriving/show.rs
Associated types support for deriving::generic::TraitDef
[rust.git] / src / libsyntax / ext / deriving / show.rs
1 // Copyright 2014 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 use ast;
12 use ast::{MetaItem, Item, Expr,};
13 use codemap::Span;
14 use ext::format;
15 use ext::base::ExtCtxt;
16 use ext::build::AstBuilder;
17 use ext::deriving::generic::*;
18 use ext::deriving::generic::ty::*;
19 use parse::token;
20 use ptr::P;
21
22 use std::collections::HashMap;
23
24 pub fn expand_deriving_show<F>(cx: &mut ExtCtxt,
25                                span: Span,
26                                mitem: &MetaItem,
27                                item: &Item,
28                                push: F) where
29     F: FnOnce(P<Item>),
30 {
31     // &mut ::std::fmt::Formatter
32     let fmtr = Ptr(box Literal(Path::new(vec!("std", "fmt", "Formatter"))),
33                    Borrowed(None, ast::MutMutable));
34
35     let trait_def = TraitDef {
36         span: span,
37         attributes: Vec::new(),
38         path: Path::new(vec!["std", "fmt", "Debug"]),
39         additional_bounds: Vec::new(),
40         generics: LifetimeBounds::empty(),
41         methods: vec![
42             MethodDef {
43                 name: "fmt",
44                 generics: LifetimeBounds::empty(),
45                 explicit_self: borrowed_explicit_self(),
46                 args: vec!(fmtr),
47                 ret_ty: Literal(Path::new(vec!("std", "fmt", "Result"))),
48                 attributes: Vec::new(),
49                 combine_substructure: combine_substructure(box |a, b, c| {
50                     show_substructure(a, b, c)
51                 })
52             }
53         ],
54         associated_types: Vec::new(),
55     };
56     trait_def.expand(cx, mitem, item, push)
57 }
58
59 /// We construct a format string and then defer to std::fmt, since that
60 /// knows what's up with formatting and so on.
61 fn show_substructure(cx: &mut ExtCtxt, span: Span,
62                      substr: &Substructure) -> P<Expr> {
63     // build `<name>`, `<name>({}, {}, ...)` or `<name> { <field>: {},
64     // <field>: {}, ... }` based on the "shape".
65     //
66     // Easy start: they all start with the name.
67     let name = match *substr.fields {
68         Struct(_) => substr.type_ident,
69         EnumMatching(_, v, _) => v.node.name,
70         EnumNonMatchingCollapsed(..) | StaticStruct(..) | StaticEnum(..) => {
71             cx.span_bug(span, "nonsensical .fields in `#[derive(Debug)]`")
72         }
73     };
74
75     let mut format_string = String::from_str(token::get_ident(name).get());
76     // the internal fields we're actually formatting
77     let mut exprs = Vec::new();
78
79     // Getting harder... making the format string:
80     match *substr.fields {
81         // unit struct/nullary variant: no work necessary!
82         Struct(ref fields) if fields.len() == 0 => {}
83         EnumMatching(_, _, ref fields) if fields.len() == 0 => {}
84
85         Struct(ref fields) | EnumMatching(_, _, ref fields) => {
86             if fields[0].name.is_none() {
87                 // tuple struct/"normal" variant
88
89                 format_string.push_str("(");
90
91                 for (i, field) in fields.iter().enumerate() {
92                     if i != 0 { format_string.push_str(", "); }
93
94                     format_string.push_str("{:?}");
95
96                     exprs.push(field.self_.clone());
97                 }
98
99                 format_string.push_str(")");
100             } else {
101                 // normal struct/struct variant
102
103                 format_string.push_str(" {{");
104
105                 for (i, field) in fields.iter().enumerate() {
106                     if i != 0 { format_string.push_str(","); }
107
108                     let name = token::get_ident(field.name.unwrap());
109                     format_string.push_str(" ");
110                     format_string.push_str(name.get());
111                     format_string.push_str(": {:?}");
112
113                     exprs.push(field.self_.clone());
114                 }
115
116                 format_string.push_str(" }}");
117             }
118         }
119         _ => unreachable!()
120     }
121
122     // AST construction!
123     // we're basically calling
124     //
125     // format_arg_method!(fmt, write_fmt, "<format_string>", exprs...)
126     //
127     // but doing it directly via ext::format.
128     let formatter = substr.nonself_args[0].clone();
129
130     let meth = cx.ident_of("write_fmt");
131     let s = token::intern_and_get_ident(&format_string[]);
132     let format_string = cx.expr_str(span, s);
133
134     // phew, not our responsibility any more!
135
136     let args = vec![
137         format::expand_preparsed_format_args(cx, span, format_string,
138                                              exprs, vec![], HashMap::new())
139     ];
140     cx.expr_method_call(span, formatter, meth, args)
141 }