use cycle_ptr::prelude::*;
use cycle_ptr::{GcMemberPtr, GcPtr, Metadata};
use indent_write::fmt::IndentWriter;
use std::cell::RefCell;
use std::fmt;
#[derive(Debug)]
struct AddChildError;
struct Person {
name: String,
parent: RefCell<Option<GcMemberPtr<Person>>>,
children: RefCell<Vec<GcMemberPtr<Person>>>,
metadata: Metadata,
}
impl Person {
fn new(name: String) -> GcPtr<Person> {
GcPtr::new(|metadata| Person {
name,
parent: RefCell::new(None),
children: RefCell::new(Vec::default()),
metadata,
})
}
fn new_with_parent(
name: String,
parent: &GcPtr<Person>,
) -> Result<GcPtr<Person>, AddChildError> {
let new_person = Person::new(name);
Self::add_child(parent, &new_person)?;
Ok(new_person)
}
fn add_child(parent: &GcPtr<Self>, child: &GcPtr<Person>) -> Result<(), AddChildError> {
let mut child_parent = child.parent.borrow_mut();
if child_parent.is_some() {
return Err(AddChildError);
}
*child_parent = Some(child.metadata.new_pointer(parent.clone()));
drop(child_parent);
parent
.children
.borrow_mut()
.push(parent.metadata.new_pointer(child.clone()));
Ok(())
}
}
impl Drop for Person {
fn drop(&mut self) {
eprintln!("{} died", self.name);
}
}
impl fmt::Display for Person {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
use fmt::Write;
write!(f, "{}\n", self.name)?;
let mut indent = IndentWriter::new(" ", f);
if let Some(parent) = self.parent.borrow().as_ref() {
write!(&mut indent, "parent: {}\n", parent.name)?;
}
let children = self.children.borrow();
if !children.is_empty() {
write!(&mut indent, "children:\n")?;
let mut indent = IndentWriter::new(" ", &mut indent);
for child_ptr in self.children.borrow().iter() {
write!(&mut indent, "{}", &**child_ptr)?;
}
}
Ok(())
}
}
fn main() {
let john = Person::new("John".into());
let eve = Person::new_with_parent("Eve".into(), &john).unwrap();
let mary = Person::new_with_parent("Mary".into(), &john).unwrap();
let sabine = Person::new_with_parent("Sabine".into(), &mary).unwrap();
let evelyn = Person::new_with_parent("Evelyn".into(), &mary).unwrap();
let dennis = Person::new_with_parent("Dennis".into(), &eve).unwrap();
drop(eve);
drop(mary);
drop(sabine);
drop(evelyn);
drop(dennis);
print!("Family tree: {}", &*john);
}