62 lines
1.6 KiB
Rust
62 lines
1.6 KiB
Rust
use crate::lines::Lines;
|
|
use memchr::memchr2;
|
|
|
|
#[cfg_attr(test, derive(PartialEq))]
|
|
#[derive(Debug)]
|
|
pub struct DynBlock;
|
|
|
|
impl DynBlock {
|
|
// return (name, parameters, contents-begin, contents-end, end)
|
|
pub fn parse(src: &str) -> Option<(&str, Option<&str>, usize, usize, usize)> {
|
|
debug_assert!(src.starts_with("#+"));
|
|
|
|
if !src[2..9].eq_ignore_ascii_case("BEGIN: ") {
|
|
return None;
|
|
}
|
|
|
|
let bytes = src.as_bytes();
|
|
let args = eol!(src);
|
|
let name = memchr2(b' ', b'\n', &bytes[9..])
|
|
.map(|i| i + 9)
|
|
.filter(|&i| {
|
|
src.as_bytes()[9..i]
|
|
.iter()
|
|
.all(|&c| c.is_ascii_alphabetic())
|
|
})?;
|
|
let mut lines = Lines::new(src);
|
|
let (mut pre_cont_end, _, _) = lines.next()?;
|
|
|
|
for (cont_end, end, line) in lines {
|
|
if line.trim().eq_ignore_ascii_case("#+END:") {
|
|
return Some((
|
|
&src[8..name].trim(),
|
|
if name == args {
|
|
None
|
|
} else {
|
|
Some(&src[name..args].trim())
|
|
},
|
|
args,
|
|
pre_cont_end,
|
|
end,
|
|
));
|
|
}
|
|
pre_cont_end = cont_end;
|
|
}
|
|
|
|
None
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn parse() {
|
|
// TODO: testing
|
|
assert_eq!(
|
|
DynBlock::parse(
|
|
r"#+BEGIN: clocktable :scope file
|
|
CONTENTS
|
|
#+END:
|
|
"
|
|
),
|
|
Some(("clocktable", Some(":scope file"), 31, 40, 48))
|
|
)
|
|
}
|