use std::rc::Rc;
use std::cell::{RefCell, Ref, RefMut};
pub type RefList = Option<Vec<Box<CyclicReference+'static>>>;
pub trait CyclicReference {
fn break_references(&mut self) -> bool;
fn get_references(&self) -> RefList;
fn get_id(&self) -> Option<uint>;
}
impl<R: CyclicReference> CyclicReference for Rc<RefCell<R>> {
fn break_references(&mut self) -> bool {
self.try_borrow_mut().map(|mut r| r.break_references()).unwrap_or(false)
}
fn get_references(&self) -> RefList {
self.try_borrow().as_ref().and_then(|r| r.get_references())
}
fn get_id(&self) -> Option<uint> { Some(&*self as *const _ as uint) }
}
impl<'a, R: CyclicReference> CyclicReference for RefMut<'a, R> {
fn break_references(&mut self) -> bool { (**self).break_references() }
fn get_references(&self) -> RefList { (**self).get_references() }
fn get_id(&self) -> Option<uint> { (**self).get_id() }
}
impl<'a, R: CyclicReference> CyclicReference for &'a mut R {
fn break_references(&mut self) -> bool { (**self).break_references() }
fn get_references(&self) -> RefList { (**self).get_references() }
fn get_id(&self) -> Option<uint> { (**self).get_id() }
}
impl<'a, R: CyclicReference> CyclicReference for Ref<'a, R> {
fn break_references(&mut self) -> bool { false }
fn get_references(&self) -> RefList { (**self).get_references() }
fn get_id(&self) -> Option<uint> { (**self).get_id() }
}
impl<'a, R: CyclicReference> CyclicReference for &'a R {
fn break_references(&mut self) -> bool { false }
fn get_references(&self) -> RefList { (**self).get_references() }
fn get_id(&self) -> Option<uint> { (**self).get_id() }
}
impl<R: CyclicReference> CyclicReference for Option<R> {
fn break_references(&mut self) -> bool { *self = None; true }
fn get_references(&self) -> RefList { self.as_ref().and_then(|r| r.get_references()) }
fn get_id(&self) -> Option<uint> { self.as_ref().and_then(|r| r.get_id()) }
}
pub fn collect<R>(reference: &mut R) -> Option<u32> where R : CyclicReference {
use std::collections::BTreeSet;
let mut seen = BTreeSet::new();
let mut to_visit = Vec::new();
let mut broken = 0;
seen.insert(match reference.get_id() {
None => return None,
Some(id) => id
});
match reference.get_references() {
None => return None,
Some(refs) => to_visit.extend(refs.into_iter())
}
while !to_visit.is_empty() {
let mut refe = to_visit.pop().expect("to_visit was empty but we just checked it wasn't!?");
if seen.insert(match refe.get_id() {
None => continue,
Some(id) => id
}) {
if refe.break_references() { broken += 1 }
match refe.get_references() {
None => continue,
Some(refs) => to_visit.extend(refs.into_iter())
}
}
}
Some(broken)
}