use super::{
ContentBlockStep, ContentInlineRoot, ContentLocation, ContentLocationRef, MAX_CONTENT_DEPTH,
content_blocks, resolve_content_block, resolve_inline_path,
};
use crate::{Block, Document, EntryContentSlice, EntryInlineRoot, EntryOwner, Inline};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct EntryOwnerLocationRef<'a> {
pub sections: &'a [u32],
pub blocks: &'a [ContentBlockStep],
pub item_index: u32,
}
impl EntryOwnerLocationRef<'_> {
#[must_use]
pub fn resolve(self, document: &Document) -> Option<EntryOwner<'_>> {
if self.sections.len().saturating_add(self.blocks.len()) > MAX_CONTENT_DEPTH {
return None;
}
match resolve_content_block(content_blocks(document, self.sections)?, self.blocks)? {
Block::List { items, .. } => {
Some(EntryOwner::List(items.get(self.item_index as usize)?))
}
Block::DefinitionList { items, .. } => {
Some(EntryOwner::Definition(items.get(self.item_index as usize)?))
}
_ => None,
}
}
#[must_use]
pub fn map_slice(
self,
document: &Document,
slice: &EntryContentSlice,
) -> Option<ContentLocation> {
let owner = self.resolve(document)?;
let nodes = owner.inline_root(&slice.root)?;
let extra = usize::from(matches!(slice.root, EntryInlineRoot::Block { .. })) * 2;
if self
.sections
.len()
.saturating_add(self.blocks.len())
.saturating_add(extra)
.saturating_add(slice.path.len())
> MAX_CONTENT_DEPTH
{
return None;
}
let mut path_storage = [0_u32; MAX_CONTENT_DEPTH];
for (out, index) in path_storage.iter_mut().zip(&slice.path) {
*out = u32::try_from(*index).ok()?;
}
let path = &path_storage[..slice.path.len()];
let selected = resolve_inline_path(nodes, path)?;
if let Some(bytes) = &slice.bytes {
if path.is_empty() || bytes.start >= bytes.end {
return None;
}
let [Inline::Text { value } | Inline::Code { value }] = selected else {
return None;
};
value.get(bytes.clone())?;
}
let mut block_storage = [ContentBlockStep::Block { index: 0 }; MAX_CONTENT_DEPTH];
block_storage[..self.blocks.len()].copy_from_slice(self.blocks);
let mut blocks_len = self.blocks.len();
let root = match slice.root {
EntryInlineRoot::Term { index } => ContentInlineRoot::DefinitionTerm {
item_index: self.item_index,
term_index: u32::try_from(index).ok()?,
},
EntryInlineRoot::Block { index } => {
block_storage[blocks_len] = match owner {
EntryOwner::List(_) => ContentBlockStep::ListItem {
index: self.item_index,
},
EntryOwner::Definition(_) => ContentBlockStep::DefinitionItem {
index: self.item_index,
},
};
block_storage[blocks_len + 1] = ContentBlockStep::Block {
index: u32::try_from(index).ok()?,
};
blocks_len += 2;
ContentInlineRoot::Inlines
}
};
ContentLocationRef::Content {
sections: self.sections,
blocks: &block_storage[..blocks_len],
root,
path,
}
.to_owned()
}
}