65 lines
1.8 KiB
Rust
65 lines
1.8 KiB
Rust
use memchr::{memchr, memchr_iter};
|
|
|
|
// return (name, parameters, offset, limit, end)
|
|
#[inline]
|
|
pub fn parse(text: &str) -> Option<(&str, Option<&str>, usize, usize, usize)> {
|
|
debug_assert!(text.starts_with("#+"));
|
|
|
|
if text.len() <= "#+BEGIN: ".len() || !text[2..9].eq_ignore_ascii_case("BEGIN: ") {
|
|
return None;
|
|
}
|
|
|
|
let bytes = text.as_bytes();
|
|
let mut lines = memchr_iter(b'\n', bytes);
|
|
|
|
let (name, para, off) = lines
|
|
.next()
|
|
.map(|i| {
|
|
memchr(b' ', &bytes["#+BEGIN: ".len()..i])
|
|
.map(|x| {
|
|
(
|
|
&text["#+BEGIN: ".len().."#+BEGIN: ".len() + x],
|
|
Some(text["#+BEGIN: ".len() + x..i].trim()),
|
|
i + 1,
|
|
)
|
|
})
|
|
.unwrap_or((&text["#+BEGIN: ".len()..i], None, i + 1))
|
|
})
|
|
.filter(|(name, _, _)| name.as_bytes().iter().all(|&c| c.is_ascii_alphabetic()))?;
|
|
|
|
let mut pos = off;
|
|
|
|
for i in lines {
|
|
if text[pos..i].trim().eq_ignore_ascii_case("#+END:") {
|
|
return Some((name, para, off, pos, i + 1));
|
|
}
|
|
|
|
pos = i + 1;
|
|
}
|
|
|
|
if text[pos..].trim().eq_ignore_ascii_case("#+END:") {
|
|
Some((name, para, off, pos, text.len()))
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
#[test]
|
|
fn parse() {
|
|
use super::parse;
|
|
|
|
// TODO: testing
|
|
assert_eq!(
|
|
parse("#+BEGIN: clocktable :scope file\nCONTENTS\n#+END:\n"),
|
|
Some((
|
|
"clocktable",
|
|
Some(":scope file"),
|
|
"#+BEGIN: clocktable :scope file\n".len(),
|
|
"#+BEGIN: clocktable :scope file\nCONTENTS\n".len(),
|
|
"#+BEGIN: clocktable :scope file\nCONTENTS\n#+END:\n".len(),
|
|
))
|
|
);
|
|
}
|
|
}
|