use crate::entities::ListStyle;
use crate::types::EntityId;
#[derive(Default)]
pub struct ListGrouper {
active: Vec<Option<(EntityId, ListStyle, String, String)>>,
}
impl ListGrouper {
pub fn new() -> Self {
Self::default()
}
pub fn try_reuse(&mut self, style: &ListStyle, indent: u32) -> Option<EntityId> {
self.try_reuse_delim(style, indent, "", "")
}
pub fn try_reuse_delim(
&mut self,
style: &ListStyle,
indent: u32,
prefix: &str,
suffix: &str,
) -> Option<EntityId> {
let idx = indent as usize;
self.active.truncate(idx + 1);
if let Some(Some((id, existing_style, existing_prefix, existing_suffix))) =
self.active.get(idx)
&& existing_style == style
&& existing_prefix == prefix
&& existing_suffix == suffix
{
return Some(*id);
}
None
}
pub fn register(&mut self, id: EntityId, style: ListStyle, indent: u32) {
self.register_delim(id, style, indent, String::new(), String::new());
}
pub fn register_delim(
&mut self,
id: EntityId,
style: ListStyle,
indent: u32,
prefix: String,
suffix: String,
) {
let idx = indent as usize;
while self.active.len() <= idx {
self.active.push(None);
}
self.active[idx] = Some((id, style, prefix, suffix));
}
pub fn reset(&mut self) {
self.active.clear();
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn first_item_returns_none() {
let mut g = ListGrouper::new();
assert!(g.try_reuse(&ListStyle::Decimal, 0).is_none());
}
#[test]
fn consecutive_same_style_reuses() {
let mut g = ListGrouper::new();
g.register(42, ListStyle::Decimal, 0);
assert_eq!(g.try_reuse(&ListStyle::Decimal, 0), Some(42));
}
#[test]
fn different_style_creates_new() {
let mut g = ListGrouper::new();
g.register(42, ListStyle::Decimal, 0);
assert!(g.try_reuse(&ListStyle::Disc, 0).is_none());
}
#[test]
fn different_indent_creates_new() {
let mut g = ListGrouper::new();
g.register(42, ListStyle::Decimal, 0);
assert!(g.try_reuse(&ListStyle::Decimal, 1).is_none());
}
#[test]
fn reset_clears_all() {
let mut g = ListGrouper::new();
g.register(42, ListStyle::Decimal, 0);
g.reset();
assert!(g.try_reuse(&ListStyle::Decimal, 0).is_none());
}
#[test]
fn nested_indent_resumes_outer() {
let mut g = ListGrouper::new();
g.register(10, ListStyle::Decimal, 0);
g.register(20, ListStyle::LowerAlpha, 1);
assert_eq!(g.try_reuse(&ListStyle::Decimal, 0), Some(10));
}
#[test]
fn nested_indent_different_style_creates_new() {
let mut g = ListGrouper::new();
g.register(10, ListStyle::Decimal, 0);
g.register(20, ListStyle::LowerAlpha, 1);
assert!(g.try_reuse(&ListStyle::Disc, 0).is_none());
}
#[test]
fn same_style_different_delimiter_creates_new() {
let mut g = ListGrouper::new();
g.register_delim(10, ListStyle::Decimal, 0, String::new(), ".".to_string());
assert!(g.try_reuse_delim(&ListStyle::Decimal, 0, "", ")").is_none());
}
#[test]
fn same_style_same_delimiter_reuses() {
let mut g = ListGrouper::new();
g.register_delim(10, ListStyle::Decimal, 0, "(".to_string(), ")".to_string());
assert_eq!(
g.try_reuse_delim(&ListStyle::Decimal, 0, "(", ")"),
Some(10)
);
}
}