std: Modernize the local_data api

This commit brings the local_data api up to modern rust standards with a few key
improvements:

* The `pop` and `set` methods have been combined into one method, `replace`

* The `get_mut` method has been removed. All interior mutability should be done
  through `RefCell`.

* All functionality is now exposed as a method on the keys themselves. Instead
  of importing std::local_data, you now use "key.replace()" and "key.get()".

* All closures have been removed in favor of RAII functionality. This means that
  get() and get_mut() no long require closures, but rather return
  Option<SmartPointer> where the smart pointer takes care of relinquishing the
  borrow and also implements the necessary Deref traits

* The modify() function was removed to cut the local_data interface down to its
  bare essentials (similarly to how RefCell removed set/get).

[breaking-change]
This commit is contained in:
Alex Crichton 2014-04-28 20:36:08 -07:00
parent ef6daf9935
commit ab92ea526d
23 changed files with 444 additions and 661 deletions

View file

@ -17,7 +17,6 @@
use std::fmt;
use std::io;
use std::local_data;
use std::strbuf::StrBuf;
use syntax::ast;
@ -206,78 +205,72 @@ fn path(w: &mut io::Writer, path: &clean::Path, print_all: bool,
generics.push_str("&gt;");
}
// Did someone say rightward-drift?
local_data::get(current_location_key, |loc| {
let loc = loc.unwrap();
let loc = current_location_key.get().unwrap();
let cache = cache_key.get().unwrap();
let abs_root = root(&**cache, loc.as_slice());
let rel_root = match path.segments.get(0).name.as_slice() {
"self" => Some("./".to_owned()),
_ => None,
};
local_data::get(cache_key, |cache| {
let cache = cache.unwrap();
let abs_root = root(&**cache, loc.as_slice());
let rel_root = match path.segments.get(0).name.as_slice() {
"self" => Some("./".to_owned()),
_ => None,
};
if print_all {
let amt = path.segments.len() - 1;
match rel_root {
Some(root) => {
let mut root = StrBuf::from_str(root);
for seg in path.segments.slice_to(amt).iter() {
if "super" == seg.name || "self" == seg.name {
try!(write!(w, "{}::", seg.name));
} else {
root.push_str(seg.name);
root.push_str("/");
try!(write!(w, "<a class='mod'
href='{}index.html'>{}</a>::",
root.as_slice(),
seg.name));
}
}
}
None => {
for seg in path.segments.slice_to(amt).iter() {
try!(write!(w, "{}::", seg.name));
}
if print_all {
let amt = path.segments.len() - 1;
match rel_root {
Some(root) => {
let mut root = StrBuf::from_str(root);
for seg in path.segments.slice_to(amt).iter() {
if "super" == seg.name || "self" == seg.name {
try!(write!(w, "{}::", seg.name));
} else {
root.push_str(seg.name);
root.push_str("/");
try!(write!(w, "<a class='mod'
href='{}index.html'>{}</a>::",
root.as_slice(),
seg.name));
}
}
}
match info(&**cache) {
// This is a documented path, link to it!
Some((ref fqp, shortty)) if abs_root.is_some() => {
let mut url = StrBuf::from_str(abs_root.unwrap());
let to_link = fqp.slice_to(fqp.len() - 1);
for component in to_link.iter() {
url.push_str(*component);
url.push_str("/");
}
match shortty {
item_type::Module => {
url.push_str(*fqp.last().unwrap());
url.push_str("/index.html");
}
_ => {
url.push_str(shortty.to_static_str());
url.push_str(".");
url.push_str(*fqp.last().unwrap());
url.push_str(".html");
}
}
try!(write!(w, "<a class='{}' href='{}' title='{}'>{}</a>",
shortty, url, fqp.connect("::"), last.name));
None => {
for seg in path.segments.slice_to(amt).iter() {
try!(write!(w, "{}::", seg.name));
}
}
}
}
match info(&**cache) {
// This is a documented path, link to it!
Some((ref fqp, shortty)) if abs_root.is_some() => {
let mut url = StrBuf::from_str(abs_root.unwrap());
let to_link = fqp.slice_to(fqp.len() - 1);
for component in to_link.iter() {
url.push_str(*component);
url.push_str("/");
}
match shortty {
item_type::Module => {
url.push_str(*fqp.last().unwrap());
url.push_str("/index.html");
}
_ => {
try!(write!(w, "{}", last.name));
url.push_str(shortty.to_static_str());
url.push_str(".");
url.push_str(*fqp.last().unwrap());
url.push_str(".html");
}
}
try!(write!(w, "{}", generics.as_slice()));
Ok(())
})
})
try!(write!(w, "<a class='{}' href='{}' title='{}'>{}</a>",
shortty, url, fqp.connect("::"), last.name));
}
_ => {
try!(write!(w, "{}", last.name));
}
}
try!(write!(w, "{}", generics.as_slice()));
Ok(())
}
/// Helper to render type parameters
@ -302,10 +295,8 @@ impl fmt::Show for clean::Type {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
clean::TyParamBinder(id) | clean::Generic(id) => {
local_data::get(cache_key, |cache| {
let m = cache.unwrap();
f.buf.write(m.typarams.get(&id).as_bytes())
})
let m = cache_key.get().unwrap();
f.buf.write(m.typarams.get(&id).as_bytes())
}
clean::ResolvedPath{id, typarams: ref tp, path: ref path} => {
try!(resolved_path(f.buf, id, path, false));

View file

@ -27,11 +27,11 @@
#![allow(non_camel_case_types)]
use libc;
use std::cell::RefCell;
use std::fmt;
use std::io;
use std::local_data;
use std::str;
use std::slice;
use std::str;
use collections::HashMap;
use html::toc::TocBuilder;
@ -139,7 +139,7 @@ fn stripped_filtered_line<'a>(s: &'a str) -> Option<&'a str> {
}
}
local_data_key!(used_header_map: HashMap<~str, uint>)
local_data_key!(used_header_map: RefCell<HashMap<~str, uint>>)
pub fn render(w: &mut io::Writer, s: &str, print_toc: bool) -> fmt::Result {
extern fn block(ob: *mut hoedown_buffer, text: *hoedown_buffer,
@ -216,15 +216,12 @@ pub fn render(w: &mut io::Writer, s: &str, print_toc: bool) -> fmt::Result {
let opaque = unsafe { &mut *((*opaque).opaque as *mut MyOpaque) };
// Make sure our hyphenated ID is unique for this page
let id = local_data::get_mut(used_header_map, |map| {
let map = map.unwrap();
match map.find_mut(&id) {
None => {}
Some(a) => { *a += 1; return format!("{}-{}", id, *a - 1) }
}
map.insert(id.clone(), 1);
id.clone()
});
let map = used_header_map.get().unwrap();
let id = match map.borrow_mut().find_mut(&id) {
None => id,
Some(a) => { *a += 1; format!("{}-{}", id, *a - 1) }
};
map.borrow_mut().insert(id.clone(), 1);
let sec = match opaque.toc_builder {
Some(ref mut builder) => {
@ -348,7 +345,7 @@ pub fn find_testable_code(doc: &str, tests: &mut ::test::Collector) {
/// used at the beginning of rendering an entire HTML page to reset from the
/// previous state (if any).
pub fn reset_headers() {
local_data::set(used_header_map, HashMap::new())
used_header_map.replace(Some(RefCell::new(HashMap::new())));
}
impl<'a> fmt::Show for Markdown<'a> {

View file

@ -37,7 +37,6 @@ use collections::{HashMap, HashSet};
use std::fmt;
use std::io::{fs, File, BufferedWriter, MemWriter, BufferedReader};
use std::io;
use std::local_data;
use std::str;
use std::strbuf::StrBuf;
@ -243,24 +242,22 @@ pub fn run(mut krate: clean::Crate, dst: Path) -> io::IoResult<()> {
}
// Crawl the crate to build various caches used for the output
let mut cache = local_data::get(::analysiskey, |analysis| {
let public_items = analysis.map(|a| a.public_items.clone());
let public_items = public_items.unwrap_or(NodeSet::new());
Cache {
impls: HashMap::new(),
typarams: HashMap::new(),
paths: HashMap::new(),
traits: HashMap::new(),
implementors: HashMap::new(),
stack: Vec::new(),
parent_stack: Vec::new(),
search_index: Vec::new(),
extern_locations: HashMap::new(),
privmod: false,
public_items: public_items,
orphan_methods: Vec::new(),
}
});
let public_items = ::analysiskey.get().map(|a| a.public_items.clone());
let public_items = public_items.unwrap_or(NodeSet::new());
let mut cache = Cache {
impls: HashMap::new(),
typarams: HashMap::new(),
paths: HashMap::new(),
traits: HashMap::new(),
implementors: HashMap::new(),
stack: Vec::new(),
parent_stack: Vec::new(),
search_index: Vec::new(),
extern_locations: HashMap::new(),
privmod: false,
public_items: public_items,
orphan_methods: Vec::new(),
};
cache.stack.push(krate.name.clone());
krate = cache.fold_crate(krate);
@ -833,7 +830,7 @@ impl Context {
item.name = Some(krate.name);
// using a rwarc makes this parallelizable in the future
local_data::set(cache_key, Arc::new(cache));
cache_key.replace(Some(Arc::new(cache)));
let mut work = vec!((self, item));
loop {
@ -859,7 +856,7 @@ impl Context {
info!("Rendering an item to {}", w.path().display());
// A little unfortunate that this is done like this, but it sure
// does make formatting *a lot* nicer.
local_data::set(current_location_key, cx.current.clone());
current_location_key.replace(Some(cx.current.clone()));
let mut title = StrBuf::from_str(cx.current.connect("::"));
if pushname {
@ -1299,31 +1296,28 @@ fn item_trait(w: &mut Writer, it: &clean::Item,
try!(write!(w, "</div>"));
}
local_data::get(cache_key, |cache| {
let cache = cache.unwrap();
match cache.implementors.find(&it.id) {
Some(implementors) => {
try!(write!(w, "
<h2 id='implementors'>Implementors</h2>
<ul class='item-list'>
"));
for i in implementors.iter() {
match *i {
PathType(ref ty) => {
try!(write!(w, "<li><code>{}</code></li>", *ty));
}
OtherType(ref generics, ref trait_, ref for_) => {
try!(write!(w, "<li><code>impl{} {} for {}</code></li>",
*generics, *trait_, *for_));
}
match cache_key.get().unwrap().implementors.find(&it.id) {
Some(implementors) => {
try!(write!(w, "
<h2 id='implementors'>Implementors</h2>
<ul class='item-list'>
"));
for i in implementors.iter() {
match *i {
PathType(ref ty) => {
try!(write!(w, "<li><code>{}</code></li>", *ty));
}
OtherType(ref generics, ref trait_, ref for_) => {
try!(write!(w, "<li><code>impl{} {} for {}</code></li>",
*generics, *trait_, *for_));
}
}
try!(write!(w, "</ul>"));
}
None => {}
try!(write!(w, "</ul>"));
}
Ok(())
})
None => {}
}
Ok(())
}
fn render_method(w: &mut Writer, meth: &clean::Item) -> fmt::Result {
@ -1550,51 +1544,48 @@ fn render_struct(w: &mut Writer, it: &clean::Item,
}
fn render_methods(w: &mut Writer, it: &clean::Item) -> fmt::Result {
local_data::get(cache_key, |cache| {
let c = cache.unwrap();
match c.impls.find(&it.id) {
Some(v) => {
let mut non_trait = v.iter().filter(|p| {
p.ref0().trait_.is_none()
});
let non_trait = non_trait.collect::<Vec<&(clean::Impl, Option<~str>)>>();
let mut traits = v.iter().filter(|p| {
p.ref0().trait_.is_some()
});
let traits = traits.collect::<Vec<&(clean::Impl, Option<~str>)>>();
match cache_key.get().unwrap().impls.find(&it.id) {
Some(v) => {
let mut non_trait = v.iter().filter(|p| {
p.ref0().trait_.is_none()
});
let non_trait = non_trait.collect::<Vec<&(clean::Impl, Option<~str>)>>();
let mut traits = v.iter().filter(|p| {
p.ref0().trait_.is_some()
});
let traits = traits.collect::<Vec<&(clean::Impl, Option<~str>)>>();
if non_trait.len() > 0 {
try!(write!(w, "<h2 id='methods'>Methods</h2>"));
for &(ref i, ref dox) in non_trait.move_iter() {
if non_trait.len() > 0 {
try!(write!(w, "<h2 id='methods'>Methods</h2>"));
for &(ref i, ref dox) in non_trait.move_iter() {
try!(render_impl(w, i, dox));
}
}
if traits.len() > 0 {
try!(write!(w, "<h2 id='implementations'>Trait \
Implementations</h2>"));
let mut any_derived = false;
for & &(ref i, ref dox) in traits.iter() {
if !i.derived {
try!(render_impl(w, i, dox));
} else {
any_derived = true;
}
}
if traits.len() > 0 {
try!(write!(w, "<h2 id='implementations'>Trait \
Implementations</h2>"));
let mut any_derived = false;
for & &(ref i, ref dox) in traits.iter() {
if !i.derived {
if any_derived {
try!(write!(w, "<h3 id='derived_implementations'>Derived Implementations \
</h3>"));
for &(ref i, ref dox) in traits.move_iter() {
if i.derived {
try!(render_impl(w, i, dox));
} else {
any_derived = true;
}
}
if any_derived {
try!(write!(w, "<h3 id='derived_implementations'>Derived Implementations \
</h3>"));
for &(ref i, ref dox) in traits.move_iter() {
if i.derived {
try!(render_impl(w, i, dox));
}
}
}
}
}
None => {}
}
Ok(())
})
None => {}
}
Ok(())
}
fn render_impl(w: &mut Writer, i: &clean::Impl,
@ -1644,9 +1635,8 @@ fn render_impl(w: &mut Writer, i: &clean::Impl,
match trait_id {
None => {}
Some(id) => {
try!(local_data::get(cache_key, |cache| {
let cache = cache.unwrap();
match cache.traits.find(&id) {
try!({
match cache_key.get().unwrap().traits.find(&id) {
Some(t) => {
for method in t.methods.iter() {
let n = method.item().name.clone();
@ -1661,7 +1651,7 @@ fn render_impl(w: &mut Writer, i: &clean::Impl,
None => {}
}
Ok(())
}))
})
}
}
try!(write!(w, "</div>"));