use itertools::Itertools;
use pulldown_cmark::Event;
use regex::Regex;
use std::borrow::Cow;
use parser::SidenoteParser;
use sidenote_error::SidenoteError;
static START_SIDENOTE: &str = r#"<label class="sidenote-number"></label><span class="sidenote">"#;
impl<'a> SidenoteParser<'a> {
fn parse_first_sidenote<'b>(&'b mut self, text: Cow<'a, str>) -> Event<'a> {
let re = Regex::new(r"[{}]").unwrap();
match re.find(&text) {
Some(m) => {
assert_eq!(m.start() + 1, m.end());
let first = text[..m.start()].to_string();
self.remaining_text = text[m.start()..].to_string();
if self.in_sidenote_block {
self.sidenotes.last_mut().unwrap().push_str(&first);
self.sidenotes.last_mut().unwrap().push_str(" ");
}
Event::Text(Cow::from(first))
},
None => {
self.remaining_text = "".to_string();
if self.in_sidenote_block {
self.sidenotes.last_mut().unwrap().push_str(&text);
self.sidenotes.last_mut().unwrap().push_str(" ");
}
Event::Text(Cow::from(text.to_string()))
}
}
}
fn cycle_remaining_text(&mut self) -> char {
let first_char :char;
let remaining :String;
{
let mut remaining_iter = self.remaining_text.chars();
first_char = remaining_iter.next()
.expect("Remaining text unexpectedly empty!");
remaining = remaining_iter.join("");
}
self.remaining_text = remaining;
first_char
}
pub fn parse_remaining_text<'b>(&'b mut self) -> Result<Event<'a>, SidenoteError> {
let first_char = self.cycle_remaining_text();
match first_char {
'{' => {
if self.in_sidenote_block {
Err(SidenoteError::Nested)
} else {
self.in_sidenote_block = true;
self.sidenotes.push("".to_string());
Ok(Event::InlineHtml(Cow::from(START_SIDENOTE)))
}
},
'}' => {
if self.in_sidenote_block {
self.in_sidenote_block = false;
Ok(Event::InlineHtml(Cow::from("</span>")))
} else {
Err(SidenoteError::NotMatched)
}
},
_ => {
let mut next_to_parse = first_char.to_string();
next_to_parse.push_str(&self.remaining_text);
Ok(self.parse_first_sidenote(Cow::from(next_to_parse)))
}
}
}
pub fn parse_text_block<'b>(&'b mut self, text: Cow<'a, str>) -> Event<'a> {
if self.in_title {
match self.title {
Some(s) => {
s.push_str(&text);
},
None => {
*self.title = Some(text.to_string());
}
}
}
if self.in_image {
self.remaining_events = vec![
Event::InlineHtml(Cow::from("</span>")),
Event::Text(text)
];
Event::InlineHtml(Cow::from("<br /><span class=\"image-caption\">"))
}
else if self.in_code_block {
Event::Text(text)
} else {
self.parse_first_sidenote(text)
}
}
}
#[cfg(test)]
mod tests {
use regex::Regex;
use std::borrow::Cow;
use pulldown_cmark::{Event, Parser};
use super::{SidenoteParser, START_SIDENOTE};
#[test]
fn can_use_regex() {
let re = Regex::new(r"[{}]").unwrap();
let expected = vec!["a", "separated", "string"];
let actual: Vec<&str> = re.split("a{separated}string").collect();
assert_eq!(expected, actual);
}
#[test]
fn can_get_first_sidenote() {
let text = "here is some text {with sidenotes}";
let mut title: Option<String> = None;
let mut sidenotes: Vec<String> = vec![];
let mut parser = SidenoteParser::new(Parser::new(""), &mut title, &mut sidenotes);
assert_eq!(parser.parse_first_sidenote(Cow::from(text)),
Event::Text(Cow::from("here is some text ")));
}
#[test]
fn can_parse_remaining() {
let mut title: Option<String> = None;
let mut sidenotes: Vec<String> = vec![];
let mut parser = SidenoteParser::new(Parser::new(""), &mut title, &mut sidenotes);
parser.remaining_text = String::from("some remaining { text");
let mut event = parser.parse_remaining_text().unwrap();
assert_eq!(event, Event::Text(Cow::from("some remaining ")));
event = parser.parse_remaining_text().unwrap();
assert_eq!(event, Event::InlineHtml(Cow::from(START_SIDENOTE)));
event = parser.parse_remaining_text().unwrap();
assert_eq!(event, Event::Text(Cow::from(" text")));
assert_eq!(parser.remaining_text, "");
}
}