use crate::{common::m, events::ev, types::Stack, Event};
pub struct StackWrapper<TStack: Stack<StackEntry>> {
stack: TStack,
stack_entry_counts: StackEntryCounts,
ruby_state: RubyState,
top_leaf: Option<TopLeaf>,
}
impl<TStack: Stack<StackEntry>> StackWrapper<TStack> {
pub fn new() -> Self {
Self {
stack: TStack::new(),
stack_entry_counts: StackEntryCounts::default(),
ruby_state: RubyState::default(),
top_leaf: None,
}
}
pub fn is_empty(&self) -> bool {
self.top_leaf.is_none() && self.stack.as_slice().is_empty()
}
pub fn push_entry(&mut self, entry: StackEntry) -> crate::Result<()> {
debug_assert!(self.top_leaf.is_none());
match entry {
StackEntry::Strong => {
self.stack_entry_counts.strong += 1;
}
StackEntry::Strikethrough => {
self.stack_entry_counts.strikethrough += 1;
}
StackEntry::WikiLink => {
self.stack_entry_counts.wiki_link += 1;
}
StackEntry::_Ruby | StackEntry::_RubyText => {}
}
self.stack.try_push(entry)
}
pub fn is_in_ruby(&self) -> bool {
self.ruby_state != RubyState::None
}
pub fn is_in_ruby_but_not_in_ruby_text(&self) -> bool {
self.ruby_state == RubyState::Base
}
pub fn enter_ruby(&mut self) -> crate::Result<()> {
debug_assert_eq!(self.ruby_state, RubyState::None);
self.ruby_state = RubyState::Base;
self.push_entry(StackEntry::_Ruby)
}
pub fn enter_ruby_text(&mut self) -> crate::Result<()> {
debug_assert_eq!(self.ruby_state, RubyState::Base);
self.ruby_state = RubyState::Text;
self.push_entry(StackEntry::_RubyText)
}
pub fn exit_ruby(&mut self) {
debug_assert_ne!(self.ruby_state, RubyState::None);
self.ruby_state = RubyState::None;
}
pub fn push_top_leaf(&mut self, entry: TopLeaf) {
debug_assert!(self.top_leaf.is_none());
self.top_leaf = Some(entry);
}
pub fn pop_entry(&mut self) -> Option<StackEntry> {
debug_assert!(self.top_leaf.is_none());
let entry = self.stack.pop()?;
match entry {
StackEntry::Strong => {
self.stack_entry_counts.strong -= 1;
}
StackEntry::Strikethrough => {
self.stack_entry_counts.strikethrough -= 1;
}
StackEntry::WikiLink => {
self.stack_entry_counts.wiki_link -= 1;
}
StackEntry::_Ruby => {
self.exit_ruby();
}
StackEntry::_RubyText => {}
}
Some(entry)
}
pub fn pop_top_leaf(&mut self) -> Option<TopLeaf> {
self.top_leaf.take()
}
pub fn make_end_condition(&self) -> EndCondition {
debug_assert!(self.top_leaf.is_none());
EndCondition {
on_strong_closing: self.stack_entry_counts.strong > 0,
on_strikethrough_closing: self.stack_entry_counts.strikethrough > 0,
on_wiki_link_closing: self.stack_entry_counts.wiki_link > 0,
on_ruby_closing: self.is_in_ruby(),
}
}
}
#[derive(PartialEq, Eq)]
pub enum StackEntry {
Strong,
Strikethrough,
WikiLink,
_Ruby,
_RubyText,
}
#[derive(Default)]
struct StackEntryCounts {
strong: usize,
strikethrough: usize,
wiki_link: usize,
}
#[derive(PartialEq, Eq, Default, Debug)]
enum RubyState {
#[default]
None,
Base,
Text,
}
pub enum TopLeaf {
CodeSpan(TopLeafCodeSpan),
}
impl From<TopLeafCodeSpan> for TopLeaf {
fn from(value: TopLeafCodeSpan) -> Self {
Self::CodeSpan(value)
}
}
pub struct TopLeafCodeSpan {
pub backticks: usize,
}
impl TopLeafCodeSpan {
pub fn make_enter_event(&self) -> Event {
ev!(Inline, EnterCodeSpan)
}
pub fn make_exit_event(&self) -> Event {
ev!(Inline, ExitInline)
}
}
pub struct EndCondition {
pub on_strong_closing: bool,
pub on_strikethrough_closing: bool,
pub on_wiki_link_closing: bool,
pub on_ruby_closing: bool,
}
impl EndCondition {
pub fn test_1(&self, char: u8) -> Option<StackEntry> {
if self.on_ruby_closing && char == m!(']') {
Some(StackEntry::_Ruby)
} else {
None
}
}
pub fn test_2(&self, char: u8, char_next: Option<u8>) -> Option<StackEntry> {
if self.on_strong_closing && char == m!('\'') && char_next == Some(m!(']')) {
Some(StackEntry::Strong)
} else if self.on_strikethrough_closing && char == m!('~') && char_next == Some(m!(']')) {
Some(StackEntry::Strikethrough)
} else if self.on_wiki_link_closing && char == m!(']') && char_next == Some(m!(']')) {
Some(StackEntry::WikiLink)
} else {
None
}
}
}