(index<- )        ./libsyntax/ext/deriving/show.rs

    git branch:    * master           5200215 auto merge of #14035 : alexcrichton/rust/experimental, r=huonw
    modified:    Fri May  9 13:02:28 2014
   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                              spanSpan,
  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, spanSpan,
  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  }