pulldown_html_ext/html/
state.rsuse pulldown_cmark::{Alignment, LinkType};
#[derive(Copy, Clone, Debug, PartialEq, Default)]
pub enum TableContext {
#[default]
NotInTable,
InHeader,
InBody,
}
#[derive(Copy, Clone, Debug, PartialEq, Default)]
pub enum ListContext {
Ordered(u32),
#[default]
Unordered,
}
pub struct HtmlState {
pub numbers: Vec<u32>,
pub table_state: TableContext,
pub table_cell_index: usize,
pub table_alignments: Vec<Alignment>,
pub list_stack: Vec<ListContext>,
pub link_stack: Vec<LinkType>,
pub heading_stack: Vec<String>,
pub currently_in_code_block: bool,
pub currently_in_footnote: bool,
}
impl HtmlState {
pub fn new() -> Self {
Self {
numbers: Vec::new(),
table_state: TableContext::default(),
table_cell_index: 0,
table_alignments: Vec::new(),
list_stack: Vec::new(),
link_stack: Vec::new(),
heading_stack: Vec::new(),
currently_in_code_block: false,
currently_in_footnote: false,
}
}
#[allow(dead_code)]
pub fn reset(&mut self) {
self.numbers.clear();
self.table_state = TableContext::default();
self.table_cell_index = 0;
self.table_alignments.clear();
self.list_stack.clear();
self.link_stack.clear();
self.heading_stack.clear();
self.currently_in_code_block = false;
}
#[allow(dead_code)]
pub fn in_table(&self) -> bool {
self.table_state != TableContext::NotInTable
}
#[allow(dead_code)]
pub fn in_table_header(&self) -> bool {
self.table_state == TableContext::InHeader
}
#[allow(dead_code)]
pub fn list_depth(&self) -> usize {
self.list_stack.len()
}
#[allow(dead_code)]
pub fn current_list_type(&self) -> Option<ListContext> {
self.list_stack.last().copied()
}
}
impl Default for HtmlState {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_renderer_state_new() {
let state = HtmlState::new();
assert_eq!(state.table_state, TableContext::NotInTable);
assert_eq!(state.table_cell_index, 0);
assert!(state.numbers.is_empty());
assert!(state.table_alignments.is_empty());
assert!(state.list_stack.is_empty());
assert!(state.link_stack.is_empty());
assert!(state.heading_stack.is_empty());
assert!(!state.currently_in_code_block);
}
#[test]
fn test_renderer_state_reset() {
let mut state = HtmlState::new();
state.numbers.push(1);
state.table_state = TableContext::InHeader;
state.table_cell_index = 2;
state.list_stack.push(ListContext::Ordered(1));
state.currently_in_code_block = true;
state.reset();
assert_eq!(state.table_state, TableContext::NotInTable);
assert_eq!(state.table_cell_index, 0);
assert!(state.numbers.is_empty());
assert!(state.list_stack.is_empty());
assert!(!state.currently_in_code_block);
}
#[test]
fn test_list_operations() {
let mut state = HtmlState::new();
assert_eq!(state.list_depth(), 0);
assert_eq!(state.current_list_type(), None);
state.list_stack.push(ListContext::Unordered);
assert_eq!(state.list_depth(), 1);
assert_eq!(state.current_list_type(), Some(ListContext::Unordered));
state.list_stack.push(ListContext::Ordered(1));
assert_eq!(state.list_depth(), 2);
assert_eq!(state.current_list_type(), Some(ListContext::Ordered(1)));
}
#[test]
fn test_table_state() {
let mut state = HtmlState::new();
assert!(!state.in_table());
assert!(!state.in_table_header());
state.table_state = TableContext::InHeader;
assert!(state.in_table());
assert!(state.in_table_header());
state.table_state = TableContext::InBody;
assert!(state.in_table());
assert!(!state.in_table_header());
}
}