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