use crate::{Block, DefinitionItem, EntryKind, EntryOwner, ListItem};
pub fn visit_child_entries<'a>(blocks: &'a [Block], visit: &mut impl FnMut(EntryOwner<'a>)) {
walk::<false>(blocks, &mut Vec::new(), &mut |owner, _, _, _| visit(owner));
}
pub(super) fn visit_child_entry_locations<'a>(
blocks: &'a [Block],
prefix: &[crate::ContentBlockStep],
visit: &mut impl FnMut(EntryOwner<'a>, &'a Block, &[crate::ContentBlockStep], u32),
) {
walk::<true>(blocks, &mut prefix.to_vec(), visit);
}
fn walk<'a, const LOCATE: bool>(
blocks: &'a [Block],
path: &mut Vec<crate::ContentBlockStep>,
visit: &mut impl FnMut(EntryOwner<'a>, &'a Block, &[crate::ContentBlockStep], u32),
) {
use crate::ContentBlockStep as Step;
for (block_index, block) in blocks.iter().enumerate() {
if LOCATE {
path.push(Step::Block {
index: coordinate(block_index),
});
}
match block {
Block::DefinitionList { items, .. } => {
for (index, item) in items.iter().enumerate() {
visit_or_descend::<LOCATE>(
EntryOwner::Definition(item),
block,
coordinate(index),
path,
visit,
);
}
}
Block::List { items, .. } => {
for (index, item) in items.iter().enumerate() {
visit_or_descend::<LOCATE>(
EntryOwner::List(item),
block,
coordinate(index),
path,
visit,
);
}
}
Block::Table { rows, .. } => {
for (row, cells) in rows.iter().enumerate() {
for (column, cell) in cells.cells.iter().enumerate() {
if LOCATE {
path.push(Step::TableCell {
row: coordinate(row),
column: coordinate(column),
});
}
walk::<LOCATE>(&cell.blocks, path, visit);
if LOCATE {
path.pop();
}
}
}
}
Block::Paragraph { .. }
| Block::Preformatted { .. }
| Block::Equation { .. }
| Block::VerticalSpace { .. }
| Block::ThematicBreak { .. }
| Block::Unsupported { .. } => {}
}
if LOCATE {
path.pop();
}
}
}
fn visit_or_descend<'a, const LOCATE: bool>(
owner: EntryOwner<'a>,
container: &'a Block,
index: u32,
path: &mut Vec<crate::ContentBlockStep>,
visit: &mut impl FnMut(EntryOwner<'a>, &'a Block, &[crate::ContentBlockStep], u32),
) {
if owner.facts().is_some() {
visit(owner, container, path, index);
} else {
if LOCATE {
path.push(owner_child_step(owner, index));
}
walk::<LOCATE>(owner.blocks(), path, visit);
if LOCATE {
path.pop();
}
}
}
pub(super) const fn owner_child_step(owner: EntryOwner<'_>, index: u32) -> crate::ContentBlockStep {
match owner {
EntryOwner::Definition(_) => crate::ContentBlockStep::DefinitionItem { index },
EntryOwner::List(_) => crate::ContentBlockStep::ListItem { index },
}
}
fn coordinate(value: usize) -> u32 {
u32::try_from(value).unwrap_or(u32::MAX)
}
impl DefinitionItem {
#[must_use]
pub fn has_value_choices(&self) -> bool {
has_value_choices(&self.description)
}
}
impl ListItem {
#[must_use]
pub fn has_value_choices(&self) -> bool {
has_value_choices(&self.blocks)
}
}
fn has_value_choices(blocks: &[Block]) -> bool {
let mut found = false;
let mut only_values = true;
visit_child_entries(blocks, &mut |item| {
found = true;
only_values &= item
.facts()
.is_some_and(|identity| identity.kind == EntryKind::Value);
});
found && only_values
}