]> git.lizzy.rs Git - rust.git/blob - src/librustdoc/html/format.rs
remove unnecessary parentheses from range notation
[rust.git] / src / librustdoc / html / format.rs
1 // Copyright 2013-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 //! HTML formatting module
12 //!
13 //! This module contains a large number of `fmt::String` implementations for
14 //! various types in `rustdoc::clean`. These implementations all currently
15 //! assume that HTML output is desired, although it may be possible to redesign
16 //! them in the future to instead emit any format desired.
17
18 use std::fmt;
19 use std::iter::repeat;
20
21 use syntax::ast;
22 use syntax::ast_util;
23
24 use clean;
25 use stability_summary::ModuleSummary;
26 use html::item_type::ItemType;
27 use html::render;
28 use html::render::{cache, CURRENT_LOCATION_KEY};
29
30 /// Helper to render an optional visibility with a space after it (if the
31 /// visibility is preset)
32 #[derive(Copy)]
33 pub struct VisSpace(pub Option<ast::Visibility>);
34 /// Similarly to VisSpace, this structure is used to render a function style with a
35 /// space after it.
36 #[derive(Copy)]
37 pub struct UnsafetySpace(pub ast::Unsafety);
38 /// Wrapper struct for properly emitting a method declaration.
39 pub struct Method<'a>(pub &'a clean::SelfTy, pub &'a clean::FnDecl);
40 /// Similar to VisSpace, but used for mutability
41 #[derive(Copy)]
42 pub struct MutableSpace(pub clean::Mutability);
43 /// Similar to VisSpace, but used for mutability
44 #[derive(Copy)]
45 pub struct RawMutableSpace(pub clean::Mutability);
46 /// Wrapper struct for properly emitting the stability level.
47 pub struct Stability<'a>(pub &'a Option<clean::Stability>);
48 /// Wrapper struct for emitting the stability level concisely.
49 pub struct ConciseStability<'a>(pub &'a Option<clean::Stability>);
50 /// Wrapper struct for emitting a where clause from Generics.
51 pub struct WhereClause<'a>(pub &'a clean::Generics);
52 /// Wrapper struct for emitting type parameter bounds.
53 pub struct TyParamBounds<'a>(pub &'a [clean::TyParamBound]);
54 /// Wrapper struct for emitting a comma-separated list of items
55 pub struct CommaSep<'a, T: 'a>(pub &'a [T]);
56
57 impl VisSpace {
58     pub fn get(&self) -> Option<ast::Visibility> {
59         let VisSpace(v) = *self; v
60     }
61 }
62
63 impl UnsafetySpace {
64     pub fn get(&self) -> ast::Unsafety {
65         let UnsafetySpace(v) = *self; v
66     }
67 }
68
69 impl<'a, T: fmt::String> fmt::String for CommaSep<'a, T> {
70     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
71         for (i, item) in self.0.iter().enumerate() {
72             if i != 0 { try!(write!(f, ", ")); }
73             try!(write!(f, "{}", item));
74         }
75         Ok(())
76     }
77 }
78
79 impl<'a> fmt::String for TyParamBounds<'a> {
80     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
81         let &TyParamBounds(bounds) = self;
82         for (i, bound) in bounds.iter().enumerate() {
83             if i > 0 {
84                 try!(f.write_str(" + "));
85             }
86             try!(write!(f, "{}", *bound));
87         }
88         Ok(())
89     }
90 }
91
92 impl fmt::String for clean::Generics {
93     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
94         if self.lifetimes.len() == 0 && self.type_params.len() == 0 { return Ok(()) }
95         try!(f.write_str("&lt;"));
96
97         for (i, life) in self.lifetimes.iter().enumerate() {
98             if i > 0 {
99                 try!(f.write_str(", "));
100             }
101             try!(write!(f, "{}", *life));
102         }
103
104         if self.type_params.len() > 0 {
105             if self.lifetimes.len() > 0 {
106                 try!(f.write_str(", "));
107             }
108             for (i, tp) in self.type_params.iter().enumerate() {
109                 if i > 0 {
110                     try!(f.write_str(", "))
111                 }
112                 try!(f.write_str(tp.name.as_slice()));
113
114                 if tp.bounds.len() > 0 {
115                     try!(write!(f, ": {}", TyParamBounds(tp.bounds.as_slice())));
116                 }
117
118                 match tp.default {
119                     Some(ref ty) => { try!(write!(f, " = {}", ty)); },
120                     None => {}
121                 };
122             }
123         }
124         try!(f.write_str("&gt;"));
125         Ok(())
126     }
127 }
128
129 impl<'a> fmt::String for WhereClause<'a> {
130     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
131         let &WhereClause(gens) = self;
132         if gens.where_predicates.len() == 0 {
133             return Ok(());
134         }
135         try!(f.write_str(" <span class='where'>where "));
136         for (i, pred) in gens.where_predicates.iter().enumerate() {
137             if i > 0 {
138                 try!(f.write_str(", "));
139             }
140             match pred {
141                 &clean::WherePredicate::BoundPredicate { ref ty, ref bounds } => {
142                     let bounds = bounds.as_slice();
143                     try!(write!(f, "{}: {}", ty, TyParamBounds(bounds)));
144                 }
145                 &clean::WherePredicate::RegionPredicate { ref lifetime,
146                                                           ref bounds } => {
147                     try!(write!(f, "{}: ", lifetime));
148                     for (i, lifetime) in bounds.iter().enumerate() {
149                         if i > 0 {
150                             try!(f.write_str(" + "));
151                         }
152
153                         try!(write!(f, "{}", lifetime));
154                     }
155                 }
156                 &clean::WherePredicate::EqPredicate { ref lhs, ref rhs } => {
157                     try!(write!(f, "{} == {}", lhs, rhs));
158                 }
159             }
160         }
161         try!(f.write_str("</span>"));
162         Ok(())
163     }
164 }
165
166 impl fmt::String for clean::Lifetime {
167     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
168         try!(f.write_str(self.get_ref()));
169         Ok(())
170     }
171 }
172
173 impl fmt::String for clean::PolyTrait {
174     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
175         if self.lifetimes.len() > 0 {
176             try!(f.write_str("for&lt;"));
177             for (i, lt) in self.lifetimes.iter().enumerate() {
178                 if i > 0 {
179                     try!(f.write_str(", "));
180                 }
181                 try!(write!(f, "{}", lt));
182             }
183             try!(f.write_str("&gt; "));
184         }
185         write!(f, "{}", self.trait_)
186     }
187 }
188
189 impl fmt::String for clean::TyParamBound {
190     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
191         match *self {
192             clean::RegionBound(ref lt) => {
193                 write!(f, "{}", *lt)
194             }
195             clean::TraitBound(ref ty, modifier) => {
196                 let modifier_str = match modifier {
197                     ast::TraitBoundModifier::None => "",
198                     ast::TraitBoundModifier::Maybe => "?",
199                 };
200                 write!(f, "{}{}", modifier_str, *ty)
201             }
202         }
203     }
204 }
205
206 impl fmt::String for clean::PathParameters {
207     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
208         match *self {
209             clean::PathParameters::AngleBracketed {
210                 ref lifetimes, ref types, ref bindings
211             } => {
212                 if lifetimes.len() > 0 || types.len() > 0 || bindings.len() > 0 {
213                     try!(f.write_str("&lt;"));
214                     let mut comma = false;
215                     for lifetime in lifetimes.iter() {
216                         if comma {
217                             try!(f.write_str(", "));
218                         }
219                         comma = true;
220                         try!(write!(f, "{}", *lifetime));
221                     }
222                     for ty in types.iter() {
223                         if comma {
224                             try!(f.write_str(", "));
225                         }
226                         comma = true;
227                         try!(write!(f, "{}", *ty));
228                     }
229                     for binding in bindings.iter() {
230                         if comma {
231                             try!(f.write_str(", "));
232                         }
233                         comma = true;
234                         try!(write!(f, "{}", *binding));
235                     }
236                     try!(f.write_str("&gt;"));
237                 }
238             }
239             clean::PathParameters::Parenthesized { ref inputs, ref output } => {
240                 try!(f.write_str("("));
241                 let mut comma = false;
242                 for ty in inputs.iter() {
243                     if comma {
244                         try!(f.write_str(", "));
245                     }
246                     comma = true;
247                     try!(write!(f, "{}", *ty));
248                 }
249                 try!(f.write_str(")"));
250                 if let Some(ref ty) = *output {
251                     try!(f.write_str(" -&gt; "));
252                     try!(write!(f, "{}", ty));
253                 }
254             }
255         }
256         Ok(())
257     }
258 }
259
260 impl fmt::String for clean::PathSegment {
261     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
262         try!(f.write_str(self.name.as_slice()));
263         write!(f, "{}", self.params)
264     }
265 }
266
267 impl fmt::String for clean::Path {
268     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
269         if self.global {
270             try!(f.write_str("::"))
271         }
272
273         for (i, seg) in self.segments.iter().enumerate() {
274             if i > 0 {
275                 try!(f.write_str("::"))
276             }
277             try!(write!(f, "{}", seg));
278         }
279         Ok(())
280     }
281 }
282
283 /// Used when rendering a `ResolvedPath` structure. This invokes the `path`
284 /// rendering function with the necessary arguments for linking to a local path.
285 fn resolved_path(w: &mut fmt::Formatter, did: ast::DefId, p: &clean::Path,
286                  print_all: bool) -> fmt::Result {
287     path(w, p, print_all,
288         |cache, loc| {
289             if ast_util::is_local(did) || cache.inlined.contains(&did) {
290                 Some(repeat("../").take(loc.len()).collect::<String>())
291             } else {
292                 match cache.extern_locations[did.krate] {
293                     render::Remote(ref s) => Some(s.to_string()),
294                     render::Local => {
295                         Some(repeat("../").take(loc.len()).collect::<String>())
296                     }
297                     render::Unknown => None,
298                 }
299             }
300         },
301         |cache| {
302             match cache.paths.get(&did) {
303                 None => None,
304                 Some(&(ref fqp, shortty)) => Some((fqp.clone(), shortty))
305             }
306         })
307 }
308
309 fn path<F, G>(w: &mut fmt::Formatter,
310               path: &clean::Path,
311               print_all: bool,
312               root: F,
313               info: G)
314               -> fmt::Result where
315     F: FnOnce(&render::Cache, &[String]) -> Option<String>,
316     G: FnOnce(&render::Cache) -> Option<(Vec<String>, ItemType)>,
317 {
318     // The generics will get written to both the title and link
319     let last = path.segments.last().unwrap();
320     let generics = format!("{}", last.params);
321
322     let loc = CURRENT_LOCATION_KEY.with(|l| l.borrow().clone());
323     let cache = cache();
324     let abs_root = root(&*cache, loc.as_slice());
325     let rel_root = match path.segments[0].name.as_slice() {
326         "self" => Some("./".to_string()),
327         _ => None,
328     };
329
330     if print_all {
331         let amt = path.segments.len() - 1;
332         match rel_root {
333             Some(root) => {
334                 let mut root = String::from_str(root.as_slice());
335                 for seg in path.segments[..amt].iter() {
336                     if "super" == seg.name ||
337                             "self" == seg.name {
338                         try!(write!(w, "{}::", seg.name));
339                     } else {
340                         root.push_str(seg.name.as_slice());
341                         root.push_str("/");
342                         try!(write!(w, "<a class='mod'
343                                             href='{}index.html'>{}</a>::",
344                                       root.as_slice(),
345                                       seg.name));
346                     }
347                 }
348             }
349             None => {
350                 for seg in path.segments[..amt].iter() {
351                     try!(write!(w, "{}::", seg.name));
352                 }
353             }
354         }
355     }
356
357     match info(&*cache) {
358         // This is a documented path, link to it!
359         Some((ref fqp, shortty)) if abs_root.is_some() => {
360             let mut url = String::from_str(abs_root.unwrap().as_slice());
361             let to_link = &fqp[..fqp.len() - 1];
362             for component in to_link.iter() {
363                 url.push_str(component.as_slice());
364                 url.push_str("/");
365             }
366             match shortty {
367                 ItemType::Module => {
368                     url.push_str(fqp.last().unwrap().as_slice());
369                     url.push_str("/index.html");
370                 }
371                 _ => {
372                     url.push_str(shortty.to_static_str());
373                     url.push_str(".");
374                     url.push_str(fqp.last().unwrap().as_slice());
375                     url.push_str(".html");
376                 }
377             }
378
379             try!(write!(w, "<a class='{}' href='{}' title='{}'>{}</a>",
380                           shortty, url, fqp.connect("::"), last.name));
381         }
382
383         _ => {
384             try!(write!(w, "{}", last.name));
385         }
386     }
387     try!(write!(w, "{}", generics.as_slice()));
388     Ok(())
389 }
390
391 fn primitive_link(f: &mut fmt::Formatter,
392                   prim: clean::PrimitiveType,
393                   name: &str) -> fmt::Result {
394     let m = cache();
395     let mut needs_termination = false;
396     match m.primitive_locations.get(&prim) {
397         Some(&ast::LOCAL_CRATE) => {
398             let len = CURRENT_LOCATION_KEY.with(|s| s.borrow().len());
399             let len = if len == 0 {0} else {len - 1};
400             try!(write!(f, "<a href='{}primitive.{}.html'>",
401                         repeat("../").take(len).collect::<String>(),
402                         prim.to_url_str()));
403             needs_termination = true;
404         }
405         Some(&cnum) => {
406             let path = &m.paths[ast::DefId {
407                 krate: cnum,
408                 node: ast::CRATE_NODE_ID,
409             }];
410             let loc = match m.extern_locations[cnum] {
411                 render::Remote(ref s) => Some(s.to_string()),
412                 render::Local => {
413                     let len = CURRENT_LOCATION_KEY.with(|s| s.borrow().len());
414                     Some(repeat("../").take(len).collect::<String>())
415                 }
416                 render::Unknown => None,
417             };
418             match loc {
419                 Some(root) => {
420                     try!(write!(f, "<a href='{}{}/primitive.{}.html'>",
421                                 root,
422                                 path.0.first().unwrap(),
423                                 prim.to_url_str()));
424                     needs_termination = true;
425                 }
426                 None => {}
427             }
428         }
429         None => {}
430     }
431     try!(write!(f, "{}", name));
432     if needs_termination {
433         try!(write!(f, "</a>"));
434     }
435     Ok(())
436 }
437
438 /// Helper to render type parameters
439 fn tybounds(w: &mut fmt::Formatter,
440             typarams: &Option<Vec<clean::TyParamBound> >) -> fmt::Result {
441     match *typarams {
442         Some(ref params) => {
443             for param in params.iter() {
444                 try!(write!(w, " + "));
445                 try!(write!(w, "{}", *param));
446             }
447             Ok(())
448         }
449         None => Ok(())
450     }
451 }
452
453 impl fmt::String for clean::Type {
454     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
455         match *self {
456             clean::TyParamBinder(id) => {
457                 f.write_str(cache().typarams[ast_util::local_def(id)].as_slice())
458             }
459             clean::Generic(ref name) => {
460                 f.write_str(name.as_slice())
461             }
462             clean::ResolvedPath{ did, ref typarams, ref path } => {
463                 try!(resolved_path(f, did, path, false));
464                 tybounds(f, typarams)
465             }
466             clean::Infer => write!(f, "_"),
467             clean::Primitive(prim) => primitive_link(f, prim, prim.to_string()),
468             clean::BareFunction(ref decl) => {
469                 write!(f, "{}{}fn{}{}",
470                        UnsafetySpace(decl.unsafety),
471                        match decl.abi.as_slice() {
472                            "" => " extern ".to_string(),
473                            "\"Rust\"" => "".to_string(),
474                            s => format!(" extern {} ", s)
475                        },
476                        decl.generics,
477                        decl.decl)
478             }
479             clean::Tuple(ref typs) => {
480                 primitive_link(f, clean::PrimitiveTuple,
481                                match typs.as_slice() {
482                                     [ref one] => format!("({},)", one),
483                                     many => format!("({})",
484                                                     CommaSep(many.as_slice()))
485                                }.as_slice())
486             }
487             clean::Vector(ref t) => {
488                 primitive_link(f, clean::Slice, format!("[{}]", **t).as_slice())
489             }
490             clean::FixedVector(ref t, ref s) => {
491                 primitive_link(f, clean::Slice,
492                                format!("[{}; {}]", **t, *s).as_slice())
493             }
494             clean::Bottom => f.write_str("!"),
495             clean::RawPointer(m, ref t) => {
496                 write!(f, "*{}{}", RawMutableSpace(m), **t)
497             }
498             clean::BorrowedRef{ lifetime: ref l, mutability, type_: ref ty} => {
499                 let lt = match *l {
500                     Some(ref l) => format!("{} ", *l),
501                     _ => "".to_string(),
502                 };
503                 let m = MutableSpace(mutability);
504                 match **ty {
505                     clean::Vector(ref bt) => { // BorrowedRef{ ... Vector(T) } is &[T]
506                         match **bt {
507                             clean::Generic(_) =>
508                                 primitive_link(f, clean::Slice,
509                                     format!("&amp;{}{}[{}]", lt, m, **bt).as_slice()),
510                             _ => {
511                                 try!(primitive_link(f, clean::Slice,
512                                     format!("&amp;{}{}[", lt, m).as_slice()));
513                                 try!(write!(f, "{}", **bt));
514                                 primitive_link(f, clean::Slice, "]")
515                             }
516                         }
517                     }
518                     _ => {
519                         write!(f, "&amp;{}{}{}", lt, m, **ty)
520                     }
521                 }
522             }
523             clean::PolyTraitRef(ref bounds) => {
524                 for (i, bound) in bounds.iter().enumerate() {
525                     if i != 0 {
526                         try!(write!(f, " + "));
527                     }
528                     try!(write!(f, "{}", *bound));
529                 }
530                 Ok(())
531             }
532             clean::QPath { ref name, ref self_type, ref trait_ } => {
533                 write!(f, "&lt;{} as {}&gt;::{}", self_type, trait_, name)
534             }
535             clean::Unique(..) => {
536                 panic!("should have been cleaned")
537             }
538         }
539     }
540 }
541
542 impl fmt::String for clean::Arguments {
543     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
544         for (i, input) in self.values.iter().enumerate() {
545             if i > 0 { try!(write!(f, ", ")); }
546             if input.name.len() > 0 {
547                 try!(write!(f, "{}: ", input.name));
548             }
549             try!(write!(f, "{}", input.type_));
550         }
551         Ok(())
552     }
553 }
554
555 impl fmt::String for clean::FunctionRetTy {
556     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
557         match *self {
558             clean::Return(clean::Tuple(ref tys)) if tys.is_empty() => Ok(()),
559             clean::Return(ref ty) => write!(f, " -&gt; {}", ty),
560             clean::DefaultReturn => Ok(()),
561             clean::NoReturn => write!(f, " -&gt; !")
562         }
563     }
564 }
565
566 impl fmt::String for clean::FnDecl {
567     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
568         write!(f, "({args}){arrow}", args = self.inputs, arrow = self.output)
569     }
570 }
571
572 impl<'a> fmt::String for Method<'a> {
573     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
574         let Method(selfty, d) = *self;
575         let mut args = String::new();
576         match *selfty {
577             clean::SelfStatic => {},
578             clean::SelfValue => args.push_str("self"),
579             clean::SelfBorrowed(Some(ref lt), mtbl) => {
580                 args.push_str(format!("&amp;{} {}self", *lt,
581                                       MutableSpace(mtbl)).as_slice());
582             }
583             clean::SelfBorrowed(None, mtbl) => {
584                 args.push_str(format!("&amp;{}self",
585                                       MutableSpace(mtbl)).as_slice());
586             }
587             clean::SelfExplicit(ref typ) => {
588                 args.push_str(format!("self: {}", *typ).as_slice());
589             }
590         }
591         for (i, input) in d.inputs.values.iter().enumerate() {
592             if i > 0 || args.len() > 0 { args.push_str(", "); }
593             if input.name.len() > 0 {
594                 args.push_str(format!("{}: ", input.name).as_slice());
595             }
596             args.push_str(format!("{}", input.type_).as_slice());
597         }
598         write!(f, "({args}){arrow}", args = args, arrow = d.output)
599     }
600 }
601
602 impl fmt::String for VisSpace {
603     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
604         match self.get() {
605             Some(ast::Public) => write!(f, "pub "),
606             Some(ast::Inherited) | None => Ok(())
607         }
608     }
609 }
610
611 impl fmt::String for UnsafetySpace {
612     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
613         match self.get() {
614             ast::Unsafety::Unsafe => write!(f, "unsafe "),
615             ast::Unsafety::Normal => Ok(())
616         }
617     }
618 }
619
620 impl fmt::String for clean::ViewPath {
621     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
622         match *self {
623             clean::SimpleImport(ref name, ref src) => {
624                 if *name == src.path.segments.last().unwrap().name {
625                     write!(f, "use {};", *src)
626                 } else {
627                     write!(f, "use {} as {};", *src, *name)
628                 }
629             }
630             clean::GlobImport(ref src) => {
631                 write!(f, "use {}::*;", *src)
632             }
633             clean::ImportList(ref src, ref names) => {
634                 try!(write!(f, "use {}::{{", *src));
635                 for (i, n) in names.iter().enumerate() {
636                     if i > 0 {
637                         try!(write!(f, ", "));
638                     }
639                     try!(write!(f, "{}", *n));
640                 }
641                 write!(f, "}};")
642             }
643         }
644     }
645 }
646
647 impl fmt::String for clean::ImportSource {
648     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
649         match self.did {
650             Some(did) => resolved_path(f, did, &self.path, true),
651             _ => {
652                 for (i, seg) in self.path.segments.iter().enumerate() {
653                     if i > 0 {
654                         try!(write!(f, "::"))
655                     }
656                     try!(write!(f, "{}", seg.name));
657                 }
658                 Ok(())
659             }
660         }
661     }
662 }
663
664 impl fmt::String for clean::ViewListIdent {
665     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
666         match self.source {
667             Some(did) => {
668                 let path = clean::Path {
669                     global: false,
670                     segments: vec!(clean::PathSegment {
671                         name: self.name.clone(),
672                         params: clean::PathParameters::AngleBracketed {
673                             lifetimes: Vec::new(),
674                             types: Vec::new(),
675                             bindings: Vec::new()
676                         }
677                     })
678                 };
679                 resolved_path(f, did, &path, false)
680             }
681             _ => write!(f, "{}", self.name),
682         }
683     }
684 }
685
686 impl fmt::String for clean::TypeBinding {
687     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
688         write!(f, "{}={}", self.name, self.ty)
689     }
690 }
691
692 impl fmt::String for MutableSpace {
693     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
694         match *self {
695             MutableSpace(clean::Immutable) => Ok(()),
696             MutableSpace(clean::Mutable) => write!(f, "mut "),
697         }
698     }
699 }
700
701 impl fmt::String for RawMutableSpace {
702     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
703         match *self {
704             RawMutableSpace(clean::Immutable) => write!(f, "const "),
705             RawMutableSpace(clean::Mutable) => write!(f, "mut "),
706         }
707     }
708 }
709
710 impl<'a> fmt::String for Stability<'a> {
711     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
712         let Stability(stab) = *self;
713         match *stab {
714             Some(ref stability) => {
715                 write!(f, "<a class='stability {lvl}' title='{reason}'>{lvl}</a>",
716                        lvl = stability.level,
717                        reason = stability.text)
718             }
719             None => Ok(())
720         }
721     }
722 }
723
724 impl<'a> fmt::String for ConciseStability<'a> {
725     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
726         let ConciseStability(stab) = *self;
727         match *stab {
728             Some(ref stability) => {
729                 write!(f, "<a class='stability {lvl}' title='{lvl}{colon}{reason}'></a>",
730                        lvl = stability.level,
731                        colon = if stability.text.len() > 0 { ": " } else { "" },
732                        reason = stability.text)
733             }
734             None => {
735                 write!(f, "<a class='stability Unmarked' title='No stability level'></a>")
736             }
737         }
738     }
739 }
740
741 impl fmt::String for ModuleSummary {
742     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
743         fn fmt_inner<'a>(f: &mut fmt::Formatter,
744                          context: &mut Vec<&'a str>,
745                          m: &'a ModuleSummary)
746                      -> fmt::Result {
747             let cnt = m.counts;
748             let tot = cnt.total();
749             if tot == 0 { return Ok(()) }
750
751             context.push(m.name.as_slice());
752             let path = context.connect("::");
753
754             try!(write!(f, "<tr>"));
755             try!(write!(f, "<td><a href='{}'>{}</a></td>", {
756                             let mut url = context.slice_from(1).to_vec();
757                             url.push("index.html");
758                             url.connect("/")
759                         },
760                         path));
761             try!(write!(f, "<td class='summary-column'>"));
762             try!(write!(f, "<span class='summary Stable' \
763                             style='width: {:.4}%; display: inline-block'>&nbsp</span>",
764                         (100 * cnt.stable) as f64/tot as f64));
765             try!(write!(f, "<span class='summary Unstable' \
766                             style='width: {:.4}%; display: inline-block'>&nbsp</span>",
767                         (100 * cnt.unstable) as f64/tot as f64));
768             try!(write!(f, "<span class='summary Experimental' \
769                             style='width: {:.4}%; display: inline-block'>&nbsp</span>",
770                         (100 * cnt.experimental) as f64/tot as f64));
771             try!(write!(f, "<span class='summary Deprecated' \
772                             style='width: {:.4}%; display: inline-block'>&nbsp</span>",
773                         (100 * cnt.deprecated) as f64/tot as f64));
774             try!(write!(f, "<span class='summary Unmarked' \
775                             style='width: {:.4}%; display: inline-block'>&nbsp</span>",
776                         (100 * cnt.unmarked) as f64/tot as f64));
777             try!(write!(f, "</td></tr>"));
778
779             for submodule in m.submodules.iter() {
780                 try!(fmt_inner(f, context, submodule));
781             }
782             context.pop();
783             Ok(())
784         }
785
786         let mut context = Vec::new();
787
788         let tot = self.counts.total();
789         let (stable, unstable, experimental, deprecated, unmarked) = if tot == 0 {
790             (0, 0, 0, 0, 0)
791         } else {
792             ((100 * self.counts.stable)/tot,
793              (100 * self.counts.unstable)/tot,
794              (100 * self.counts.experimental)/tot,
795              (100 * self.counts.deprecated)/tot,
796              (100 * self.counts.unmarked)/tot)
797         };
798
799         try!(write!(f,
800 r"<h1 class='fqn'>Stability dashboard: crate <a class='mod' href='index.html'>{name}</a></h1>
801 This dashboard summarizes the stability levels for all of the public modules of
802 the crate, according to the total number of items at each level in the module and
803 its children (percentages total for {name}):
804 <blockquote>
805 <a class='stability Stable'></a> stable ({}%),<br/>
806 <a class='stability Unstable'></a> unstable ({}%),<br/>
807 <a class='stability Experimental'></a> experimental ({}%),<br/>
808 <a class='stability Deprecated'></a> deprecated ({}%),<br/>
809 <a class='stability Unmarked'></a> unmarked ({}%)
810 </blockquote>
811 The counts do not include methods or trait
812 implementations that are visible only through a re-exported type.",
813 stable, unstable, experimental, deprecated, unmarked,
814 name=self.name));
815         try!(write!(f, "<table>"));
816         try!(fmt_inner(f, &mut context, self));
817         write!(f, "</table>")
818     }
819 }