orgize/src/parsers.rs

659 lines
20 KiB
Rust
Raw Normal View History

use std::iter::once;
2019-08-10 13:17:39 +01:00
use std::marker::PhantomData;
2019-08-06 07:03:16 +01:00
use indextree::{Arena, NodeId};
use jetscii::{bytes, BytesConst};
use memchr::{memchr, memchr_iter};
2020-04-14 10:59:45 +01:00
use nom::bytes::complete::take_while1;
2019-08-06 07:03:16 +01:00
use crate::config::ParseConfig;
2019-10-02 09:50:36 +01:00
use crate::elements::{
2020-04-14 10:59:45 +01:00
block::RawBlock, emphasis::Emphasis, keyword::RawKeyword, radio_target::parse_radio_target,
Clock, Comment, Cookie, Drawer, DynBlock, Element, FixedWidth, FnDef, FnRef, InlineCall,
InlineSrc, Link, List, ListItem, Macros, Rule, Snippet, Table, TableCell, TableRow, Target,
Timestamp, Title,
2019-10-02 09:50:36 +01:00
};
2020-04-14 10:59:45 +01:00
use crate::parse::combinators::lines_while;
2019-08-06 07:03:16 +01:00
pub trait ElementArena<'a> {
2019-10-30 03:31:37 +00:00
fn append<T>(&mut self, element: T, parent: NodeId) -> NodeId
where
T: Into<Element<'a>>;
fn insert_before_last_child<T>(&mut self, element: T, parent: NodeId) -> NodeId
where
T: Into<Element<'a>>;
fn set<T>(&mut self, node: NodeId, element: T)
where
T: Into<Element<'a>>;
}
2020-04-14 10:59:45 +01:00
pub type BorrowedArena<'a> = Arena<Element<'a>>;
impl<'a> ElementArena<'a> for BorrowedArena<'a> {
2019-10-30 03:31:37 +00:00
fn append<T>(&mut self, element: T, parent: NodeId) -> NodeId
where
T: Into<Element<'a>>,
{
let node = self.new_node(element.into());
parent.append(node, self);
node
}
2019-10-30 03:31:37 +00:00
fn insert_before_last_child<T>(&mut self, element: T, parent: NodeId) -> NodeId
where
T: Into<Element<'a>>,
{
if let Some(child) = self[parent].last_child() {
let node = self.new_node(element.into());
child.insert_before(node, self);
node
} else {
2019-10-30 03:31:37 +00:00
self.append(element, parent)
}
}
2019-10-30 03:31:37 +00:00
fn set<T>(&mut self, node: NodeId, element: T)
where
T: Into<Element<'a>>,
{
*self[node].get_mut() = element.into();
}
}
2019-08-11 04:40:52 +01:00
pub struct OwnedArena<'a, 'b, 'c> {
arena: &'b mut Arena<Element<'c>>,
phantom: PhantomData<&'a ()>,
}
impl<'a, 'b, 'c> OwnedArena<'a, 'b, 'c> {
pub fn new(arena: &'b mut Arena<Element<'c>>) -> OwnedArena<'a, 'b, 'c> {
OwnedArena {
arena,
phantom: PhantomData,
}
}
}
impl<'a> ElementArena<'a> for OwnedArena<'a, '_, '_> {
2019-10-30 03:31:37 +00:00
fn append<T>(&mut self, element: T, parent: NodeId) -> NodeId
where
T: Into<Element<'a>>,
{
self.arena.append(element.into().into_owned(), parent)
2019-08-11 04:40:52 +01:00
}
2019-10-30 03:31:37 +00:00
fn insert_before_last_child<T>(&mut self, element: T, parent: NodeId) -> NodeId
where
T: Into<Element<'a>>,
{
2019-10-02 09:50:36 +01:00
self.arena
.insert_before_last_child(element.into().into_owned(), parent)
2019-08-11 04:40:52 +01:00
}
2019-10-30 03:31:37 +00:00
fn set<T>(&mut self, node: NodeId, element: T)
where
T: Into<Element<'a>>,
{
self.arena.set(node, element.into().into_owned());
}
2019-08-11 04:40:52 +01:00
}
#[derive(Debug)]
2019-08-06 07:03:16 +01:00
pub enum Container<'a> {
// Block, List Item
2019-10-30 03:31:37 +00:00
Block { content: &'a str, node: NodeId },
2020-03-14 02:21:20 +00:00
// Paragraph, Inline Markup
2019-10-30 03:31:37 +00:00
Inline { content: &'a str, node: NodeId },
2019-08-06 07:03:16 +01:00
// Headline
2019-10-30 03:31:37 +00:00
Headline { content: &'a str, node: NodeId },
2019-08-06 07:03:16 +01:00
// Document
2019-10-30 03:31:37 +00:00
Document { content: &'a str, node: NodeId },
2019-08-06 07:03:16 +01:00
}
2019-08-10 13:17:39 +01:00
pub fn parse_container<'a, T: ElementArena<'a>>(
arena: &mut T,
container: Container<'a>,
config: &ParseConfig,
) {
let containers = &mut vec![container];
while let Some(container) = containers.pop() {
match container {
Container::Document { content, node } => {
parse_section_and_headlines(arena, content, node, containers);
}
Container::Headline { content, node } => {
parse_headline_content(arena, content, node, containers, config);
}
Container::Block { content, node } => {
parse_blocks(arena, content, node, containers);
}
Container::Inline { content, node } => {
parse_inlines(arena, content, node, containers);
}
}
}
}
pub fn parse_headline_content<'a, T: ElementArena<'a>>(
arena: &mut T,
2019-08-06 07:03:16 +01:00
content: &'a str,
parent: NodeId,
containers: &mut Vec<Container<'a>>,
config: &ParseConfig,
) {
let (tail, (title, content)) = Title::parse(content, config).unwrap();
2019-10-30 03:31:37 +00:00
let node = arena.append(title, parent);
2019-08-06 07:03:16 +01:00
containers.push(Container::Inline { content, node });
parse_section_and_headlines(arena, tail, parent, containers);
2019-08-06 07:03:16 +01:00
}
pub fn parse_section_and_headlines<'a, T: ElementArena<'a>>(
arena: &mut T,
2019-08-06 07:03:16 +01:00
content: &'a str,
parent: NodeId,
containers: &mut Vec<Container<'a>>,
) {
2020-04-14 10:59:45 +01:00
let content = blank_lines_count(content).0;
2019-08-06 07:03:16 +01:00
if content.is_empty() {
return;
}
let mut last_end = 0;
for i in memchr_iter(b'\n', content.as_bytes()).chain(once(content.len())) {
2019-10-02 09:50:36 +01:00
if let Some((mut tail, (headline_content, level))) = parse_headline(&content[last_end..]) {
2019-08-06 07:03:16 +01:00
if last_end != 0 {
2019-10-30 03:31:37 +00:00
let node = arena.append(Element::Section, parent);
let content = &content[0..last_end];
containers.push(Container::Block { content, node });
2019-08-06 07:03:16 +01:00
}
2019-10-30 03:31:37 +00:00
let node = arena.append(Element::Headline { level }, parent);
2019-08-06 07:03:16 +01:00
containers.push(Container::Headline {
content: headline_content,
node,
});
2019-10-02 09:50:36 +01:00
while let Some((new_tail, (content, level))) = parse_headline(tail) {
debug_assert_ne!(tail, new_tail);
2019-10-30 03:31:37 +00:00
let node = arena.append(Element::Headline { level }, parent);
2019-08-06 07:03:16 +01:00
containers.push(Container::Headline { content, node });
tail = new_tail;
}
return;
}
last_end = i + 1;
}
2019-10-30 03:31:37 +00:00
let node = arena.append(Element::Section, parent);
2019-08-06 07:03:16 +01:00
containers.push(Container::Block { content, node });
}
pub fn parse_blocks<'a, T: ElementArena<'a>>(
arena: &mut T,
2019-08-06 07:03:16 +01:00
content: &'a str,
parent: NodeId,
containers: &mut Vec<Container<'a>>,
) {
2020-04-14 10:59:45 +01:00
let mut tail = blank_lines_count(content).0;
2019-08-06 07:03:16 +01:00
if let Some(new_tail) = parse_block(content, arena, parent, containers) {
2020-04-14 10:59:45 +01:00
tail = blank_lines_count(new_tail).0;
2019-08-06 07:03:16 +01:00
}
let mut text = tail;
let mut pos = 0;
while !tail.is_empty() {
let i = memchr(b'\n', tail.as_bytes())
.map(|i| i + 1)
.unwrap_or_else(|| tail.len());
if tail.as_bytes()[0..i].iter().all(u8::is_ascii_whitespace) {
2020-04-14 10:59:45 +01:00
let (tail_, blank) = blank_lines_count(&tail[i..]);
2019-10-28 05:33:18 +00:00
debug_assert_ne!(tail, tail_);
tail = tail_;
2019-10-30 03:31:37 +00:00
let node = arena.append(
2019-10-28 05:33:18 +00:00
Element::Paragraph {
2020-04-14 10:59:45 +01:00
// including the current line (&tail[0..i])
2019-10-28 05:33:18 +00:00
post_blank: blank + 1,
},
parent,
);
containers.push(Container::Inline {
2019-10-28 05:33:18 +00:00
content: &text[0..pos].trim_end(),
node,
});
pos = 0;
2019-08-06 07:03:16 +01:00
text = tail;
} else if let Some(new_tail) = parse_block(tail, arena, parent, containers) {
2019-08-06 07:03:16 +01:00
if pos != 0 {
2019-10-28 05:33:18 +00:00
let node =
arena.insert_before_last_child(Element::Paragraph { post_blank: 0 }, parent);
containers.push(Container::Inline {
2019-10-28 05:33:18 +00:00
content: &text[0..pos].trim_end(),
node,
});
2019-08-06 07:03:16 +01:00
pos = 0;
}
2020-04-14 10:59:45 +01:00
debug_assert_ne!(tail, blank_lines_count(new_tail).0);
tail = blank_lines_count(new_tail).0;
2019-08-06 07:03:16 +01:00
text = tail;
} else {
debug_assert_ne!(tail, &tail[i..]);
2019-08-06 07:03:16 +01:00
tail = &tail[i..];
pos += i;
}
}
if !text.is_empty() {
2019-10-30 03:31:37 +00:00
let node = arena.append(Element::Paragraph { post_blank: 0 }, parent);
containers.push(Container::Inline {
2019-10-28 05:33:18 +00:00
content: &text[0..pos].trim_end(),
node,
});
2019-08-06 07:03:16 +01:00
}
}
pub fn parse_block<'a, T: ElementArena<'a>>(
2019-08-06 07:03:16 +01:00
contents: &'a str,
arena: &mut T,
parent: NodeId,
2019-08-06 07:03:16 +01:00
containers: &mut Vec<Container<'a>>,
) -> Option<&'a str> {
2019-10-30 06:43:55 +00:00
match contents
.as_bytes()
.iter()
.find(|c| !c.is_ascii_whitespace())?
{
b'[' => {
let (tail, (fn_def, content)) = FnDef::parse(contents)?;
let node = arena.append(fn_def, parent);
containers.push(Container::Block { content, node });
Some(tail)
}
b'0'..=b'9' | b'*' => {
let tail = parse_list(arena, contents, parent, containers)?;
Some(tail)
}
b'C' => {
2019-10-02 09:50:36 +01:00
let (tail, clock) = Clock::parse(contents)?;
2019-10-30 03:31:37 +00:00
arena.append(clock, parent);
2019-10-02 09:50:36 +01:00
Some(tail)
2019-08-06 07:03:16 +01:00
}
b'\'' => {
// TODO: LaTeX environment
2019-10-02 09:50:36 +01:00
None
2019-08-06 07:03:16 +01:00
}
b'-' => {
2019-10-30 06:43:55 +00:00
if let Some((tail, rule)) = Rule::parse(contents) {
arena.append(rule, parent);
Some(tail)
} else {
let tail = parse_list(arena, contents, parent, containers)?;
Some(tail)
}
2019-08-06 07:03:16 +01:00
}
b':' => {
2019-10-02 09:50:36 +01:00
if let Some((tail, (drawer, content))) = Drawer::parse(contents) {
2019-10-30 03:31:37 +00:00
let node = arena.append(drawer, parent);
containers.push(Container::Block { content, node });
2019-10-02 09:50:36 +01:00
Some(tail)
} else {
2019-10-28 05:33:18 +00:00
let (tail, fixed_width) = FixedWidth::parse(contents)?;
2019-10-30 03:31:37 +00:00
arena.append(fixed_width, parent);
2019-10-02 09:50:36 +01:00
Some(tail)
2019-08-06 07:03:16 +01:00
}
}
b'|' => {
2019-10-30 06:43:55 +00:00
let tail = parse_org_table(arena, contents, containers, parent);
2019-10-02 09:50:36 +01:00
Some(tail)
}
2019-10-30 06:43:55 +00:00
b'+' => {
if let Some((tail, table)) = Table::parse_table_el(contents) {
arena.append(table, parent);
Some(tail)
} else {
let tail = parse_list(arena, contents, parent, containers)?;
Some(tail)
}
}
b'#' => {
2020-04-14 10:59:45 +01:00
if let Some((tail, block)) = RawBlock::parse(contents) {
let (element, content) = block.into_element();
// avoid use after free
let is_block_container = match element {
Element::CenterBlock(_)
| Element::QuoteBlock(_)
| Element::VerseBlock(_)
| Element::SpecialBlock(_) => true,
_ => false,
};
let node = arena.append(element, parent);
if is_block_container {
containers.push(Container::Block { content, node });
}
2019-10-02 09:50:36 +01:00
Some(tail)
} else if let Some((tail, (dyn_block, content))) = DynBlock::parse(contents) {
2019-10-30 03:31:37 +00:00
let node = arena.append(dyn_block, parent);
containers.push(Container::Block { content, node });
2019-10-02 09:50:36 +01:00
Some(tail)
2020-04-14 10:59:45 +01:00
} else if let Some((tail, keyword)) = RawKeyword::parse(contents) {
arena.append(keyword.into_element(), parent);
2019-10-02 09:50:36 +01:00
Some(tail)
} else {
2019-10-28 05:33:18 +00:00
let (tail, comment) = Comment::parse(contents)?;
2019-10-30 03:31:37 +00:00
arena.append(comment, parent);
2019-10-02 09:50:36 +01:00
Some(tail)
}
}
2019-10-02 09:50:36 +01:00
_ => None,
2019-08-06 07:03:16 +01:00
}
}
struct InlinePositions<'a> {
bytes: &'a [u8],
pos: usize,
next: Option<usize>,
}
impl InlinePositions<'_> {
fn new(bytes: &[u8]) -> InlinePositions {
InlinePositions {
bytes,
pos: 0,
next: Some(0),
}
}
}
impl Iterator for InlinePositions<'_> {
type Item = usize;
fn next(&mut self) -> Option<Self::Item> {
lazy_static::lazy_static! {
static ref PRE_BYTES: BytesConst =
bytes!(b'@', b'<', b'[', b' ', b'(', b'{', b'\'', b'"', b'\n');
}
self.next.take().or_else(|| {
PRE_BYTES.find(&self.bytes[self.pos..]).map(|i| {
self.pos += i + 1;
match self.bytes[self.pos - 1] {
b'{' => {
self.next = Some(self.pos);
self.pos - 1
}
b' ' | b'(' | b'\'' | b'"' | b'\n' => self.pos,
_ => self.pos - 1,
}
})
})
}
}
pub fn parse_inlines<'a, T: ElementArena<'a>>(
arena: &mut T,
2019-08-06 07:03:16 +01:00
content: &'a str,
parent: NodeId,
containers: &mut Vec<Container<'a>>,
) {
let mut tail = content;
if let Some(tail_) = parse_inline(tail, arena, containers, parent) {
tail = tail_;
2019-08-06 07:03:16 +01:00
}
while let Some((tail_, i)) = InlinePositions::new(tail.as_bytes())
.filter_map(|i| parse_inline(&tail[i..], arena, containers, parent).map(|tail| (tail, i)))
.next()
{
if i != 0 {
2019-08-10 13:17:39 +01:00
arena.insert_before_last_child(
Element::Text {
value: tail[0..i].into(),
2019-08-10 13:17:39 +01:00
},
parent,
);
2019-08-06 07:03:16 +01:00
}
tail = tail_;
2019-08-06 07:03:16 +01:00
}
if !tail.is_empty() {
2019-10-30 03:31:37 +00:00
arena.append(Element::Text { value: tail.into() }, parent);
2019-08-06 07:03:16 +01:00
}
}
pub fn parse_inline<'a, T: ElementArena<'a>>(
2019-08-06 07:03:16 +01:00
contents: &'a str,
arena: &mut T,
2019-08-06 07:03:16 +01:00
containers: &mut Vec<Container<'a>>,
parent: NodeId,
) -> Option<&'a str> {
2019-08-06 07:03:16 +01:00
if contents.len() < 3 {
return None;
}
2020-04-14 10:59:45 +01:00
let byte = contents.as_bytes()[0];
match byte {
b'@' => {
2019-10-02 09:50:36 +01:00
let (tail, snippet) = Snippet::parse(contents)?;
2019-10-30 03:31:37 +00:00
arena.append(snippet, parent);
2019-10-02 09:50:36 +01:00
Some(tail)
}
b'{' => {
2019-10-02 09:50:36 +01:00
let (tail, macros) = Macros::parse(contents)?;
2019-10-30 03:31:37 +00:00
arena.append(macros, parent);
2019-10-02 09:50:36 +01:00
Some(tail)
}
b'<' => {
2019-10-02 09:50:36 +01:00
if let Some((tail, _content)) = parse_radio_target(contents) {
2019-10-30 03:31:37 +00:00
arena.append(Element::RadioTarget, parent);
2019-10-02 09:50:36 +01:00
Some(tail)
} else if let Some((tail, target)) = Target::parse(contents) {
2019-10-30 03:31:37 +00:00
arena.append(target, parent);
2019-10-02 09:50:36 +01:00
Some(tail)
} else if let Some((tail, timestamp)) = Timestamp::parse_active(contents) {
2019-10-30 03:31:37 +00:00
arena.append(timestamp, parent);
2019-10-02 09:50:36 +01:00
Some(tail)
} else {
let (tail, timestamp) = Timestamp::parse_diary(contents)?;
2019-10-30 03:31:37 +00:00
arena.append(timestamp, parent);
2019-10-02 09:50:36 +01:00
Some(tail)
}
}
2019-08-06 07:03:16 +01:00
b'[' => {
2019-10-02 09:50:36 +01:00
if let Some((tail, fn_ref)) = FnRef::parse(contents) {
2019-10-30 03:31:37 +00:00
arena.append(fn_ref, parent);
2019-10-02 09:50:36 +01:00
Some(tail)
} else if let Some((tail, link)) = Link::parse(contents) {
2019-10-30 03:31:37 +00:00
arena.append(link, parent);
2019-10-02 09:50:36 +01:00
Some(tail)
} else if let Some((tail, cookie)) = Cookie::parse(contents) {
2019-10-30 03:31:37 +00:00
arena.append(cookie, parent);
2019-10-02 09:50:36 +01:00
Some(tail)
} else {
let (tail, timestamp) = Timestamp::parse_inactive(contents)?;
2019-10-30 03:31:37 +00:00
arena.append(timestamp, parent);
2019-10-02 09:50:36 +01:00
Some(tail)
2019-08-06 07:03:16 +01:00
}
}
2020-04-14 10:59:45 +01:00
b'*' | b'+' | b'/' | b'_' | b'=' | b'~' => {
let (tail, emphasis) = Emphasis::parse(contents, byte)?;
let (element, content) = emphasis.into_element();
let is_inline_container = match element {
Element::Bold | Element::Strike | Element::Italic | Element::Underline => true,
_ => false,
};
let node = arena.append(element, parent);
if is_inline_container {
containers.push(Container::Inline { content, node });
}
2019-10-02 09:50:36 +01:00
Some(tail)
}
b's' => {
2019-10-02 09:50:36 +01:00
let (tail, inline_src) = InlineSrc::parse(contents)?;
2019-10-30 03:31:37 +00:00
arena.append(inline_src, parent);
2019-10-02 09:50:36 +01:00
Some(tail)
}
b'c' => {
2019-10-02 09:50:36 +01:00
let (tail, inline_call) = InlineCall::parse(contents)?;
2019-10-30 03:31:37 +00:00
arena.append(inline_call, parent);
2019-10-02 09:50:36 +01:00
Some(tail)
2019-08-06 07:03:16 +01:00
}
2019-10-02 09:50:36 +01:00
_ => None,
2019-08-06 07:03:16 +01:00
}
}
2019-10-30 03:31:37 +00:00
pub fn parse_list<'a, T: ElementArena<'a>>(
arena: &mut T,
2019-10-30 03:31:37 +00:00
contents: &'a str,
2019-08-06 07:03:16 +01:00
parent: NodeId,
containers: &mut Vec<Container<'a>>,
2019-10-30 03:31:37 +00:00
) -> Option<&'a str> {
let (mut tail, (first_item, content)) = ListItem::parse(contents)?;
let first_item_indent = first_item.indent;
let first_item_ordered = first_item.ordered;
let parent = arena.append(Element::Document { pre_blank: 0 }, parent); // placeholder
let node = arena.append(first_item, parent);
containers.push(Container::Block { content, node });
while let Some((tail_, (item, content))) = ListItem::parse(tail) {
if item.indent == first_item_indent {
let node = arena.append(item, parent);
containers.push(Container::Block { content, node });
debug_assert_ne!(tail, tail_);
tail = tail_;
} else {
break;
}
2019-08-06 07:03:16 +01:00
}
2019-10-30 03:31:37 +00:00
2020-04-14 10:59:45 +01:00
let (tail, post_blank) = blank_lines_count(tail);
2019-10-30 03:31:37 +00:00
arena.set(
parent,
List {
indent: first_item_indent,
ordered: first_item_ordered,
2020-04-14 10:59:45 +01:00
post_blank,
2019-10-30 03:31:37 +00:00
},
);
Some(tail)
2019-08-06 07:03:16 +01:00
}
2019-10-30 06:43:55 +00:00
pub fn parse_org_table<'a, T: ElementArena<'a>>(
arena: &mut T,
2019-08-06 07:03:16 +01:00
contents: &'a str,
containers: &mut Vec<Container<'a>>,
parent: NodeId,
2019-10-30 06:43:55 +00:00
) -> &'a str {
2020-04-14 10:59:45 +01:00
let (tail, contents) =
lines_while::<_, ()>(|line| line.trim_start().starts_with('|'))(contents)
.unwrap_or((contents, ""));
let (tail, post_blank) = blank_lines_count(tail);
2019-08-06 07:03:16 +01:00
2019-11-05 11:37:58 +00:00
let mut iter = contents.trim_end().lines().peekable();
let mut lines = vec![];
let mut has_header = false;
// TODO: merge contiguous rules
2019-11-05 11:37:58 +00:00
if let Some(line) = iter.next() {
let line = line.trim_start();
if !line.starts_with("|-") {
lines.push(line);
}
}
while let Some(line) = iter.next() {
let line = line.trim_start();
if iter.peek().is_none() && line.starts_with("|-") {
break;
} else if line.starts_with("|-") {
has_header = true;
}
lines.push(line);
}
2019-10-30 06:43:55 +00:00
let parent = arena.append(
Table::Org {
tblfm: None,
2020-04-14 10:59:45 +01:00
post_blank,
2019-11-05 11:37:58 +00:00
has_header,
2019-10-30 06:43:55 +00:00
},
parent,
);
2019-10-02 09:50:36 +01:00
2019-11-05 11:37:58 +00:00
for line in lines {
2019-10-30 06:43:55 +00:00
if line.starts_with("|-") {
2019-11-05 11:37:58 +00:00
if has_header {
arena.append(Element::TableRow(TableRow::HeaderRule), parent);
has_header = false;
} else {
arena.append(Element::TableRow(TableRow::BodyRule), parent);
}
} else {
if has_header {
let parent = arena.append(Element::TableRow(TableRow::Header), parent);
for content in line.split_terminator('|').skip(1) {
let node = arena.append(Element::TableCell(TableCell::Header), parent);
containers.push(Container::Inline {
content: content.trim(),
node,
});
}
} else {
let parent = arena.append(Element::TableRow(TableRow::Body), parent);
for content in line.split_terminator('|').skip(1) {
let node = arena.append(Element::TableCell(TableCell::Body), parent);
containers.push(Container::Inline {
content: content.trim(),
node,
});
}
2019-10-30 06:43:55 +00:00
}
}
2019-08-06 07:03:16 +01:00
}
2019-10-30 06:43:55 +00:00
tail
2019-08-06 07:03:16 +01:00
}
2020-04-14 10:59:45 +01:00
pub fn blank_lines_count(input: &str) -> (&str, usize) {
crate::parse::combinators::blank_lines_count::<()>(input).unwrap_or((input, 0))
}
2019-10-02 09:50:36 +01:00
pub fn parse_headline(input: &str) -> Option<(&str, (&str, usize))> {
2019-08-11 04:40:52 +01:00
let (input_, level) = parse_headline_level(input)?;
2020-04-14 10:59:45 +01:00
let (input_, content) = lines_while::<_, ()>(move |line| {
2019-10-02 09:50:36 +01:00
parse_headline_level(line)
.map(|(_, l)| l > level)
.unwrap_or(true)
2020-04-14 10:59:45 +01:00
})(input_)
.unwrap_or((input_, ""));
2019-10-02 09:50:36 +01:00
Some((input_, (&input[0..level + content.len()], level)))
}
2019-10-02 09:50:36 +01:00
pub fn parse_headline_level(input: &str) -> Option<(&str, usize)> {
let (input, stars) = take_while1::<_, _, ()>(|c: char| c == '*')(input).ok()?;
if input.starts_with(' ') || input.starts_with('\n') || input.is_empty() {
2019-10-02 09:50:36 +01:00
Some((input, stars.len()))
} else {
2019-10-02 09:50:36 +01:00
None
}
}