use crate::doc_parts::*;
use crate::util::md_tools::*;
use crate::util::*;
use pulldown_cmark::Event;
use std::cell::{Ref, RefCell, RefMut};
use std::rc::{Rc, Weak};
const COPY_GUARD: &str = "!copy_guard";
#[derive(Clone)]
pub(crate) struct DocChunk<'a>(Rc<RefCell<DocChunkCore<'a>>>);
#[derive(Default)]
pub(crate) struct DocChunkCore<'a> {
level: u8,
rs_id: Option<String>,
md_id: Option<String>,
meta: Rc<DocMeta<'a>>,
parent: Option<Weak<RefCell<DocChunkCore<'a>>>>,
head_events: Vec<Event<'a>>,
body_events: Vec<Event<'a>>,
chunks: Vec<DocChunk<'a>>,
}
impl<'a> DocChunk<'a> {
pub(crate) fn new_empty_root(meta: DocMeta<'a>) -> Self {
Self(Rc::new(RefCell::new(DocChunkCore {
meta: Rc::new(meta),
..Default::default()
})))
}
pub(crate) fn new_empty_chunk(level: u8) -> Self {
Self(Rc::new(RefCell::new(DocChunkCore {
level,
..Default::default()
})))
}
pub fn borrow(&self) -> Ref<'_, DocChunkCore<'a>> {
self.0.borrow()
}
pub fn borrow_mut(&self) -> RefMut<'_, DocChunkCore<'a>> {
self.0.borrow_mut()
}
pub fn parent(&self) -> Option<DocChunk<'a>> {
self.borrow()
.parent
.as_ref()
.map(|x| DocChunk(x.upgrade().unwrap()))
}
pub fn append_block(&mut self, block: Vec<Event<'a>>) {
let this = &mut self.borrow_mut();
if this.head_events.is_empty() {
this.head_events = block;
} else {
this.body_events.extend(block);
}
}
pub fn append_chunk(&mut self, child: DocChunk<'a>) -> Self {
let this = &mut self.borrow_mut();
let child_clone = child.clone();
let child_edit = &mut child.borrow_mut();
child_edit.meta = this.meta.clone();
child_edit.parent = Some(Rc::downgrade(&self.0));
this.chunks.push(child_clone);
this.chunks.last().unwrap().clone()
}
pub fn extract(&mut self, key: Option<&str>) -> Option<Self> {
let chunk = &mut find(self, key)?;
adjust_root_heading(chunk, matches!(key, Some(MdPath::ANONYMOUS_ROOT)));
adjust_tree_level(chunk, level_delta(chunk, key));
return Some(chunk.clone());
fn find<'a>(chunk: &DocChunk<'a>, key: Option<&str>) -> Option<DocChunk<'a>> {
if is_hit(chunk, key) {
Some(chunk.clone())
} else {
chunk.borrow().chunks().find_map(|x| find(&x, key))
}
}
fn is_hit(chunk: &DocChunk, key: Option<&str>) -> bool {
let Some(key) = key else { return true };
let root_hit = chunk.borrow().level() == 1 && key == MdPath::ANONYMOUS_ROOT;
let normal_hit = chunk.borrow().md_id() == Some(key);
root_hit || normal_hit
}
fn level_delta(chunk: &DocChunk, key: Option<&str>) -> i8 {
match key {
None => 0,
Some(MdPath::ANONYMOUS_ROOT) => -1,
_ => 1 - (chunk.borrow().level() as i8),
}
}
fn adjust_root_heading(chunk: &mut DocChunk, no_heading: bool) {
if no_heading {
chunk.borrow_mut().head_events.clear();
}
}
fn adjust_tree_level(chunk: &mut DocChunk, delta: i8) {
adjust_chunk_level(chunk, delta);
for mut chunk in chunk.borrow().chunks.iter().cloned() {
adjust_tree_level(&mut chunk, delta);
}
}
fn adjust_chunk_level(chunk: &mut DocChunk, delta: i8) {
let chunk = &mut chunk.borrow_mut();
let old_events = chunk.head_events.drain(..);
let new_events = old_events.map(|x| md_tools::add_level(x, delta));
let imports = new_events.collect::<Vec<_>>();
chunk.head_events.extend(imports);
}
}
}
impl<'a> DocChunkCore<'a> {
pub fn is_root(&self) -> bool {
self.parent.is_none()
}
pub fn level(&self) -> u8 {
self.level
}
pub fn title(&self) -> String {
md_tools::text(self.head_events.iter().cloned())
}
pub fn rs_id(&self) -> Option<&str> {
self.rs_id.as_deref()
}
pub fn md_id(&self) -> Option<&str> {
self.md_id.as_deref()
}
pub fn self_item(&self) -> Option<&'a syn::Item> {
self.meta.self_item()
}
pub fn copy_guard(&self) -> Option<&str> {
self.meta.defs().get(COPY_GUARD).map(|x| x.as_str())
}
pub fn head_events(&self) -> impl Iterator<Item = Event<'a>> {
self.head_events.iter().cloned()
}
pub fn body_events(&self) -> impl Iterator<Item = Event<'a>> {
self.body_events.iter().cloned()
}
pub fn defs(&self) -> impl Iterator<Item = (&str, &str)> {
self.meta
.defs()
.iter()
.map(|(k, v)| (k.as_str(), v.as_str()))
.filter(move |(_, url)| !self.is_guarding(url))
}
pub fn chunks(&self) -> impl Iterator<Item = DocChunk<'a>> {
self.chunks.iter().cloned()
}
pub fn is_guarding(&self, url: &str) -> bool {
self.copy_guard().is_some_and(|x| url.starts_with(x))
}
pub fn set_rs_id(&mut self, value: String) {
self.rs_id = Some(value);
}
pub fn set_md_id(&mut self, value: String) {
self.md_id = Some(value);
}
}