chore: make the fields of Planning struct public

This commit is contained in:
PoiScript 2019-04-11 23:53:43 +08:00
parent c2c554a3d5
commit 9c82b268d6
5 changed files with 231 additions and 190 deletions

View file

@ -1,4 +1,4 @@
use crate::objects::timestamp::{self, Datetime, Delay, Repeater, Timestamp}; use crate::objects::timestamp::{Datetime, Delay, Repeater, Timestamp};
use memchr::memchr; use memchr::memchr;
#[cfg_attr(test, derive(PartialEq))] #[cfg_attr(test, derive(PartialEq))]
@ -32,7 +32,7 @@ impl<'a> Clock<'a> {
return None; return None;
} }
match timestamp::parse_inactive(tail).map(|(t, off)| (t, tail[off..].trim_start())) { match Timestamp::parse_inactive(tail).map(|(t, off)| (t, tail[off..].trim_start())) {
Some(( Some((
Timestamp::InactiveRange { Timestamp::InactiveRange {
start, start,

View file

@ -1,37 +1,36 @@
use crate::objects::timestamp::{self, Timestamp}; use crate::objects::timestamp::Timestamp;
use memchr::memchr; use memchr::memchr;
#[cfg_attr(test, derive(PartialEq))] #[cfg_attr(test, derive(PartialEq))]
#[derive(Debug)] #[derive(Debug)]
pub struct Planning<'a> { pub struct Planning<'a> {
deadline: Option<Timestamp<'a>>, pub deadline: Option<Timestamp<'a>>,
scheduled: Option<Timestamp<'a>>, pub scheduled: Option<Timestamp<'a>>,
closed: Option<Timestamp<'a>>, pub closed: Option<Timestamp<'a>>,
} }
impl<'a> Planning<'a> { impl<'a> Planning<'a> {
pub(crate) fn parse(text: &'a str) -> Option<(Planning<'a>, usize)> { pub(crate) fn parse(text: &'a str) -> Option<(Planning<'a>, usize)> {
let (text, off) = memchr(b'\n', text.as_bytes()) let (mut deadline, mut scheduled, mut closed) = (None, None, None);
let (mut tail, off) = memchr(b'\n', text.as_bytes())
.map(|i| (text[..i].trim(), i + 1)) .map(|i| (text[..i].trim(), i + 1))
.unwrap_or_else(|| (text.trim(), text.len())); .unwrap_or_else(|| (text.trim(), text.len()));
let mut words = text.split_ascii_whitespace(); while let Some(i) = memchr(b' ', tail.as_bytes()) {
let (mut deadline, mut scheduled, mut closed) = (None, None, None); let next = &tail[i + 1..].trim_start();
while let Some(word) = words.next() {
let next = words.next()?;
macro_rules! set_timestamp { macro_rules! set_timestamp {
($timestamp:expr) => { ($timestamp:expr) => {
if $timestamp.is_none() { if $timestamp.is_none() {
$timestamp = if next.starts_with('<') { if next.starts_with('<') {
Some( let (timestamp, off) = Timestamp::parse_active(next)
timestamp::parse_active(next) .or_else(|| Timestamp::parse_diary(next))?;
.or_else(|| timestamp::parse_diary(next))? $timestamp = Some(timestamp);
.0, tail = &next[off..].trim_start();
) } else if next.starts_with('<') {
} else if next.starts_with('[') { let (timestamp, off) = Timestamp::parse_active(next)?;
Some(timestamp::parse_inactive(next)?.0) $timestamp = Some(timestamp);
tail = &next[off..].trim_start();
} else { } else {
return None; return None;
} }
@ -41,11 +40,11 @@ impl<'a> Planning<'a> {
}; };
} }
match word { match &tail[..i] {
"DEADLINE:" => set_timestamp!(deadline), "DEADLINE:" => set_timestamp!(deadline),
"SCHEDULED:" => set_timestamp!(scheduled), "SCHEDULED:" => set_timestamp!(scheduled),
"CLOSED:" => set_timestamp!(closed), "CLOSED:" => set_timestamp!(closed),
_ => (), _ => return None,
} }
} }
@ -63,3 +62,31 @@ impl<'a> Planning<'a> {
} }
} }
} }
#[cfg(test)]
mod tests {
#[test]
fn prase() {
use super::Planning;
use crate::objects::timestamp::{Datetime, Timestamp};
assert_eq!(
Planning::parse("SCHEDULED: <2019-04-08 Mon>\n"),
Some((
Planning {
scheduled: Some(Timestamp::Active {
start: Datetime {
date: (2019, 4, 8),
time: None
},
repeater: None,
delay: None
}),
closed: None,
deadline: None,
},
"SCHEDULED: <2019-04-08 Mon>\n".len()
))
)
}
}

View file

@ -1,5 +1,6 @@
//! Headline //! Headline
use jetscii::ByteSubstring;
use memchr::{memchr, memchr2, memrchr}; use memchr::{memchr, memchr2, memrchr};
pub(crate) const DEFAULT_KEYWORDS: &[&str] = pub(crate) const DEFAULT_KEYWORDS: &[&str] =
@ -28,7 +29,16 @@ impl<'a> Headline<'a> {
debug_assert!(text.as_bytes()[0..level].iter().all(|&c| c == b'*')); debug_assert!(text.as_bytes()[0..level].iter().all(|&c| c == b'*'));
let (off, end) = memchr(b'\n', text.as_bytes()) let (off, end) = memchr(b'\n', text.as_bytes())
.map(|i| (i + 1, Headline::find_level(&text[i + 1..], level) + i + 1)) .map(|i| {
(
i + 1,
if i + 1 == text.len() {
Headline::find_level(&text[i + 1..], level) + i + 1
} else {
i + 1
},
)
})
.unwrap_or_else(|| (text.len(), text.len())); .unwrap_or_else(|| (text.len(), text.len()));
if level == off { if level == off {
@ -98,8 +108,6 @@ impl<'a> Headline<'a> {
} }
pub(crate) fn find_level(text: &str, level: usize) -> usize { pub(crate) fn find_level(text: &str, level: usize) -> usize {
use jetscii::ByteSubstring;
let bytes = text.as_bytes(); let bytes = text.as_bytes();
if bytes[0] == b'*' { if bytes[0] == b'*' {
if let Some(stars) = memchr2(b'\n', b' ', bytes) { if let Some(stars) = memchr2(b'\n', b' ', bytes) {

View file

@ -76,152 +76,158 @@ pub enum Timestamp<'a> {
Diary(&'a str), Diary(&'a str),
} }
pub fn parse_active(text: &str) -> Option<(Timestamp<'_>, usize)> { impl<'a> Timestamp<'a> {
debug_assert!(text.starts_with('<')); pub(crate) fn parse_active(text: &str) -> Option<(Timestamp<'_>, usize)> {
debug_assert!(text.starts_with('<'));
let bytes = text.as_bytes(); let bytes = text.as_bytes();
let mut off = memchr(b'>', bytes)?; let mut off = memchr(b'>', bytes)?;
let (start, mut end) = parse_datetime(&bytes[1..off])?; let (start, mut end) = Self::parse_datetime(&bytes[1..off])?;
if end.is_none() if end.is_none()
&& off <= text.len() - 14 /* --<YYYY-MM-DD> */ && off <= text.len() - 14 /* --<YYYY-MM-DD> */
&& text[off + 1..].starts_with("--<") && text[off + 1..].starts_with("--<")
{ {
if let Some(new_off) = memchr(b'>', &bytes[off + 1..]) { if let Some(new_off) = memchr(b'>', &bytes[off + 1..]) {
if let Some((start, _)) = parse_datetime(&bytes[off + 4..off + 1 + new_off]) { if let Some((start, _)) = Self::parse_datetime(&bytes[off + 4..off + 1 + new_off]) {
end = Some(start); end = Some(start);
off += new_off + 1; off += new_off + 1;
}
} }
} }
Some((
if let Some(end) = end {
Timestamp::ActiveRange {
start,
end,
repeater: None,
delay: None,
}
} else {
Timestamp::Active {
start,
repeater: None,
delay: None,
}
},
off + 1,
))
} }
Some(( pub(crate) fn parse_inactive(text: &str) -> Option<(Timestamp<'_>, usize)> {
if let Some(end) = end { debug_assert!(text.starts_with('['));
Timestamp::ActiveRange {
start,
end,
repeater: None,
delay: None,
}
} else {
Timestamp::Active {
start,
repeater: None,
delay: None,
}
},
off + 1,
))
}
pub fn parse_inactive(text: &str) -> Option<(Timestamp<'_>, usize)> { let bytes = text.as_bytes();
debug_assert!(text.starts_with('[')); let mut off = memchr(b']', bytes)?;
let (start, mut end) = Self::parse_datetime(&bytes[1..off])?;
let bytes = text.as_bytes(); if end.is_none()
let mut off = memchr(b']', bytes)?;
let (start, mut end) = parse_datetime(&bytes[1..off])?;
if end.is_none()
&& off <= text.len() - 14 /* --[YYYY-MM-DD] */ && off <= text.len() - 14 /* --[YYYY-MM-DD] */
&& text[off + 1..].starts_with("--[") && text[off + 1..].starts_with("--[")
{ {
if let Some(new_off) = memchr(b']', &bytes[off + 1..]) { if let Some(new_off) = memchr(b']', &bytes[off + 1..]) {
if let Some((start, _)) = parse_datetime(&bytes[off + 4..off + 1 + new_off]) { if let Some((start, _)) = Self::parse_datetime(&bytes[off + 4..off + 1 + new_off]) {
end = Some(start); end = Some(start);
off += new_off + 1; off += new_off + 1;
}
} }
} }
Some((
if let Some(end) = end {
Timestamp::InactiveRange {
start,
end,
repeater: None,
delay: None,
}
} else {
Timestamp::Inactive {
start,
repeater: None,
delay: None,
}
},
off + 1,
))
} }
Some(( fn parse_datetime(bytes: &[u8]) -> Option<(Datetime, Option<Datetime>)> {
if let Some(end) = end { if bytes.is_empty()
Timestamp::InactiveRange { || !bytes[0].is_ascii_digit()
start, || !bytes[bytes.len() - 1].is_ascii_alphanumeric()
end, {
repeater: None, return None;
delay: None, }
}
} else {
Timestamp::Inactive {
start,
repeater: None,
delay: None,
}
},
off + 1,
))
}
fn parse_datetime(bytes: &[u8]) -> Option<(Datetime, Option<Datetime>)> { // similar to str::split_ascii_whitespace, but for &[u8]
if bytes.is_empty() let mut words = bytes
|| !bytes[0].is_ascii_digit() .split(u8::is_ascii_whitespace)
|| !bytes[bytes.len() - 1].is_ascii_alphanumeric() .filter(|s| !s.is_empty());
{
return None;
}
// similar to str::split_ascii_whitespace, but for &[u8] let date = words
let mut words = bytes .next()
.split(u8::is_ascii_whitespace) .filter(|word| {
.filter(|s| !s.is_empty()); word.len() == 10 /* YYYY-MM-DD */
let date = words
.next()
.filter(|word| {
word.len() == 10 /* YYYY-MM-DD */
&& word[0..4].iter().all(u8::is_ascii_digit) && word[0..4].iter().all(u8::is_ascii_digit)
&& word[4] == b'-' && word[4] == b'-'
&& word[5..7].iter().all(u8::is_ascii_digit) && word[5..7].iter().all(u8::is_ascii_digit)
&& word[7] == b'-' && word[7] == b'-'
&& word[8..10].iter().all(u8::is_ascii_digit) && word[8..10].iter().all(u8::is_ascii_digit)
}) })
.map(|word| { .map(|word| {
( (
(u16::from(word[0]) - u16::from(b'0')) * 1000 (u16::from(word[0]) - u16::from(b'0')) * 1000
+ (u16::from(word[1]) - u16::from(b'0')) * 100 + (u16::from(word[1]) - u16::from(b'0')) * 100
+ (u16::from(word[2]) - u16::from(b'0')) * 10 + (u16::from(word[2]) - u16::from(b'0')) * 10
+ (u16::from(word[3]) - u16::from(b'0')), + (u16::from(word[3]) - u16::from(b'0')),
(word[5] - b'0') * 10 + (word[6] - b'0'), (word[5] - b'0') * 10 + (word[6] - b'0'),
(word[8] - b'0') * 10 + (word[9] - b'0'), (word[8] - b'0') * 10 + (word[9] - b'0'),
) )
})?;
let _dayname = words.next().filter(|word| {
word.iter().all(|&c| {
!(c == b'+'
|| c == b'-'
|| c == b']'
|| c == b'>'
|| c.is_ascii_digit()
|| c == b'\n')
})
})?; })?;
let _dayname = words.next().filter(|word| { let (start, end) = if let Some(word) = words.next() {
word.iter().all(|&c| { macro_rules! datetime {
!(c == b'+' || c == b'-' || c == b']' || c == b'>' || c.is_ascii_digit() || c == b'\n') ($a:expr, $b:expr, $c:expr) => {
}) Datetime {
})?; date,
time: Some((word[$a] - b'0', (word[$b] - b'0') * 10 + (word[$c] - b'0'))),
}
};
($a:expr, $b:expr, $c:expr, $d:expr) => {
Datetime {
date,
time: Some((
(word[$a] - b'0') * 10 + (word[$b] - b'0'),
(word[$c] - b'0') * 10 + (word[$d] - b'0'),
)),
}
};
}
let (start, end) = if let Some(word) = words.next() { if word.len() == 4 // H:MM
macro_rules! datetime {
($a:expr, $b:expr, $c:expr) => {
Datetime {
date,
time: Some((word[$a] - b'0', (word[$b] - b'0') * 10 + (word[$c] - b'0'))),
}
};
($a:expr, $b:expr, $c:expr, $d:expr) => {
Datetime {
date,
time: Some((
(word[$a] - b'0') * 10 + (word[$b] - b'0'),
(word[$c] - b'0') * 10 + (word[$d] - b'0'),
)),
}
};
}
if word.len() == 4 // H:MM
&& word[0].is_ascii_digit() && word[0].is_ascii_digit()
&& word[1] == b':' && word[1] == b':'
&& word[2..4].iter().all(u8::is_ascii_digit) && word[2..4].iter().all(u8::is_ascii_digit)
{ {
(datetime!(0, 2, 3), None) (datetime!(0, 2, 3), None)
} else if word.len() == 5 // HH:MM } else if word.len() == 5 // HH:MM
&& word[0..2].iter().all(u8::is_ascii_digit) && word[0..2].iter().all(u8::is_ascii_digit)
&& word[2] == b':' && word[2] == b':'
&& word[3..5].iter().all(u8::is_ascii_digit) && word[3..5].iter().all(u8::is_ascii_digit)
{ {
(datetime!(0, 1, 3, 4), None) (datetime!(0, 1, 3, 4), None)
} else if word.len() == 9 // H:MM-H:MM } else if word.len() == 9 // H:MM-H:MM
&& word[0].is_ascii_digit() && word[0].is_ascii_digit()
&& word[1] == b':' && word[1] == b':'
&& word[2..4].iter().all(u8::is_ascii_digit) && word[2..4].iter().all(u8::is_ascii_digit)
@ -229,9 +235,9 @@ fn parse_datetime(bytes: &[u8]) -> Option<(Datetime, Option<Datetime>)> {
&& word[5].is_ascii_digit() && word[5].is_ascii_digit()
&& word[6] == b':' && word[6] == b':'
&& word[7..9].iter().all(u8::is_ascii_digit) && word[7..9].iter().all(u8::is_ascii_digit)
{ {
(datetime!(0, 2, 3), Some(datetime!(5, 7, 8))) (datetime!(0, 2, 3), Some(datetime!(5, 7, 8)))
} else if word.len() == 10 // H:MM-HH:MM } else if word.len() == 10 // H:MM-HH:MM
&& word[0].is_ascii_digit() && word[0].is_ascii_digit()
&& word[1] == b':' && word[1] == b':'
&& word[2..4].iter().all(u8::is_ascii_digit) && word[2..4].iter().all(u8::is_ascii_digit)
@ -239,9 +245,9 @@ fn parse_datetime(bytes: &[u8]) -> Option<(Datetime, Option<Datetime>)> {
&& word[5..7].iter().all(u8::is_ascii_digit) && word[5..7].iter().all(u8::is_ascii_digit)
&& word[7] == b':' && word[7] == b':'
&& word[8..10].iter().all(u8::is_ascii_digit) && word[8..10].iter().all(u8::is_ascii_digit)
{ {
(datetime!(0, 2, 3), Some(datetime!(5, 6, 8, 9))) (datetime!(0, 2, 3), Some(datetime!(5, 6, 8, 9)))
} else if word.len() == 10 // HH:MM-H:MM } else if word.len() == 10 // HH:MM-H:MM
&& word[0..2].iter().all(u8::is_ascii_digit) && word[0..2].iter().all(u8::is_ascii_digit)
&& word[2] == b':' && word[2] == b':'
&& word[3..5].iter().all(u8::is_ascii_digit) && word[3..5].iter().all(u8::is_ascii_digit)
@ -249,9 +255,9 @@ fn parse_datetime(bytes: &[u8]) -> Option<(Datetime, Option<Datetime>)> {
&& word[6].is_ascii_digit() && word[6].is_ascii_digit()
&& word[7] == b':' && word[7] == b':'
&& word[8..10].iter().all(u8::is_ascii_digit) && word[8..10].iter().all(u8::is_ascii_digit)
{ {
(datetime!(0, 1, 3, 4), Some(datetime!(6, 8, 9))) (datetime!(0, 1, 3, 4), Some(datetime!(6, 8, 9)))
} else if word.len() == 11 // HH:MM-HH:MM } else if word.len() == 11 // HH:MM-HH:MM
&& word[0..2].iter().all(u8::is_ascii_digit) && word[0..2].iter().all(u8::is_ascii_digit)
&& word[2] == b':' && word[2] == b':'
&& word[3..5].iter().all(u8::is_ascii_digit) && word[3..5].iter().all(u8::is_ascii_digit)
@ -259,37 +265,38 @@ fn parse_datetime(bytes: &[u8]) -> Option<(Datetime, Option<Datetime>)> {
&& word[6..8].iter().all(u8::is_ascii_digit) && word[6..8].iter().all(u8::is_ascii_digit)
&& word[8] == b':' && word[8] == b':'
&& word[9..11].iter().all(u8::is_ascii_digit) && word[9..11].iter().all(u8::is_ascii_digit)
{ {
(datetime!(0, 1, 3, 4), Some(datetime!(6, 7, 9, 10))) (datetime!(0, 1, 3, 4), Some(datetime!(6, 7, 9, 10)))
} else {
return None;
}
} else { } else {
(Datetime { date, time: None }, None)
};
// TODO: repeater and delay
if words.next().is_some() {
None
} else {
Some((start, end))
}
}
pub(crate) fn parse_diary(text: &str) -> Option<(Timestamp<'_>, usize)> {
debug_assert!(text.starts_with('<'));
if text.len() <= 6 /* <%%()> */ || &text[1..4] != "%%(" {
return None; return None;
} }
} else {
(Datetime { date, time: None }, None)
};
// TODO: repeater and delay let bytes = text.as_bytes();
if words.next().is_some() {
None memchr(b'>', bytes)
} else { .filter(|i| bytes[i - 1] == b')' && bytes[4..i - 1].iter().all(|&c| c != b'\n'))
Some((start, end)) .map(|i| (Timestamp::Diary(&text[4..i - 1]), i))
} }
} }
pub fn parse_diary(text: &str) -> Option<(Timestamp<'_>, usize)> {
debug_assert!(text.starts_with('<'));
if text.len() <= 6 /* <%%()> */ || &text[1..4] != "%%(" {
return None;
}
let bytes = text.as_bytes();
memchr(b'>', bytes)
.filter(|i| bytes[i - 1] == b')' && bytes[4..i - 1].iter().all(|&c| c != b'\n'))
.map(|i| (Timestamp::Diary(&text[4..i - 1]), i))
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
#[test] #[test]
@ -297,7 +304,7 @@ mod tests {
use super::*; use super::*;
assert_eq!( assert_eq!(
parse_inactive("[2003-09-16 Tue]"), Timestamp::parse_inactive("[2003-09-16 Tue]"),
Some(( Some((
Timestamp::Inactive { Timestamp::Inactive {
start: Datetime { start: Datetime {
@ -311,7 +318,7 @@ mod tests {
)) ))
); );
assert_eq!( assert_eq!(
parse_inactive("[2003-09-16 Tue 09:39]--[2003-09-16 Tue 10:39]"), Timestamp::parse_inactive("[2003-09-16 Tue 09:39]--[2003-09-16 Tue 10:39]"),
Some(( Some((
Timestamp::InactiveRange { Timestamp::InactiveRange {
start: Datetime { start: Datetime {
@ -329,7 +336,7 @@ mod tests {
)) ))
); );
assert_eq!( assert_eq!(
parse_active("<2003-09-16 Tue 09:39-10:39>"), Timestamp::parse_active("<2003-09-16 Tue 09:39-10:39>"),
Some(( Some((
Timestamp::ActiveRange { Timestamp::ActiveRange {
start: Datetime { start: Datetime {
@ -353,7 +360,7 @@ mod tests {
use super::*; use super::*;
assert_eq!( assert_eq!(
parse_datetime(b"2003-09-16 Tue"), Timestamp::parse_datetime(b"2003-09-16 Tue"),
Some(( Some((
Datetime { Datetime {
date: (2003, 9, 16), date: (2003, 9, 16),
@ -363,7 +370,7 @@ mod tests {
)) ))
); );
assert_eq!( assert_eq!(
parse_datetime(b"2003-09-16 Tue 9:39"), Timestamp::parse_datetime(b"2003-09-16 Tue 9:39"),
Some(( Some((
Datetime { Datetime {
date: (2003, 9, 16), date: (2003, 9, 16),
@ -373,7 +380,7 @@ mod tests {
)) ))
); );
assert_eq!( assert_eq!(
parse_datetime(b"2003-09-16 Tue 09:39"), Timestamp::parse_datetime(b"2003-09-16 Tue 09:39"),
Some(( Some((
Datetime { Datetime {
date: (2003, 9, 16), date: (2003, 9, 16),
@ -383,7 +390,7 @@ mod tests {
)) ))
); );
assert_eq!( assert_eq!(
parse_datetime(b"2003-09-16 Tue 9:39-10:39"), Timestamp::parse_datetime(b"2003-09-16 Tue 9:39-10:39"),
Some(( Some((
Datetime { Datetime {
date: (2003, 9, 16), date: (2003, 9, 16),
@ -396,9 +403,9 @@ mod tests {
)) ))
); );
assert_eq!(parse_datetime(b"2003-9-16 Tue"), None); assert_eq!(Timestamp::parse_datetime(b"2003-9-16 Tue"), None);
assert_eq!(parse_datetime(b"2003-09-16"), None); assert_eq!(Timestamp::parse_datetime(b"2003-09-16"), None);
assert_eq!(parse_datetime(b"2003-09-16 09:39"), None); assert_eq!(Timestamp::parse_datetime(b"2003-09-16 09:39"), None);
assert_eq!(parse_datetime(b"2003-09-16 Tue 0939"), None); assert_eq!(Timestamp::parse_datetime(b"2003-09-16 Tue 0939"), None);
} }
} }

View file

@ -485,10 +485,10 @@ impl<'a> Parser<'a> {
target::parse(text).map(|(target, off)| (Event::Target { target }, off, 0, 0)) target::parse(text).map(|(target, off)| (Event::Target { target }, off, 0, 0))
} }
} }
b'<' => timestamp::parse_active(text) b'<' => Timestamp::parse_active(text)
.map(|(timestamp, off)| (Event::Timestamp(timestamp), off, 0, 0)) .map(|(timestamp, off)| (Event::Timestamp(timestamp), off, 0, 0))
.or_else(|| { .or_else(|| {
timestamp::parse_diary(text) Timestamp::parse_diary(text)
.map(|(timestamp, off)| (Event::Timestamp(timestamp), off, 0, 0)) .map(|(timestamp, off)| (Event::Timestamp(timestamp), off, 0, 0))
}), }),
b'[' => { b'[' => {
@ -502,7 +502,7 @@ impl<'a> Parser<'a> {
cookie::parse(text) cookie::parse(text)
.map(|(cookie, off)| (Event::Cookie(cookie), off, 0, 0)) .map(|(cookie, off)| (Event::Cookie(cookie), off, 0, 0))
.or_else(|| { .or_else(|| {
timestamp::parse_inactive(text) Timestamp::parse_inactive(text)
.map(|(timestamp, off)| (Event::Timestamp(timestamp), off, 0, 0)) .map(|(timestamp, off)| (Event::Timestamp(timestamp), off, 0, 0))
}) })
} }
@ -601,7 +601,6 @@ impl<'a> Iterator for Parser<'a> {
Some(match container { Some(match container {
Container::Headline(beg) => { Container::Headline(beg) => {
debug_assert!(self.off >= beg);
if self.off >= limit { if self.off >= limit {
self.off = end; self.off = end;
self.stack.pop(); self.stack.pop();
@ -645,7 +644,7 @@ impl<'a> Iterator for Parser<'a> {
Container::List(ident, ordered) => { Container::List(ident, ordered) => {
if let Some(bullet) = self.next_item.pop().unwrap() { if let Some(bullet) = self.next_item.pop().unwrap() {
self.off += bullet.len() + ident; self.off += bullet.len() + ident;
let (limit, end, next) = list::parse(&self.text[self.off..limit], ident); let (limit, end, next) = list::parse(tail, ident);
self.push_stack(Container::ListItem, limit, end); self.push_stack(Container::ListItem, limit, end);
self.next_item.push(next); self.next_item.push(next);
Event::ListItemBeg { bullet } Event::ListItemBeg { bullet }