69 lines
1.8 KiB
Rust
69 lines
1.8 KiB
Rust
use memchr::memchr_iter;
|
|
|
|
#[cfg_attr(test, derive(PartialEq))]
|
|
#[derive(Debug)]
|
|
pub struct Drawer<'a> {
|
|
pub name: &'a str,
|
|
}
|
|
|
|
impl<'a> Drawer<'a> {
|
|
#[inline]
|
|
// return (drawer, contents-begin, contents-end , end)
|
|
pub(crate) fn parse(text: &'a str) -> Option<(Drawer<'a>, usize, usize, usize)> {
|
|
debug_assert!(text.starts_with(':'));
|
|
|
|
let mut lines = memchr_iter(b'\n', text.as_bytes());
|
|
|
|
let (name, off) = lines
|
|
.next()
|
|
.map(|i| (text[1..i].trim_end(), i + 1))
|
|
.filter(|(name, _)| {
|
|
name.ends_with(':')
|
|
&& name[0..name.len() - 1]
|
|
.as_bytes()
|
|
.iter()
|
|
.all(|&c| c.is_ascii_alphabetic() || c == b'-' || c == b'_')
|
|
})?;
|
|
|
|
let mut pos = off;
|
|
for i in lines {
|
|
if text[pos..i].trim().eq_ignore_ascii_case(":END:") {
|
|
return Some((
|
|
Drawer {
|
|
name: &name[0..name.len() - 1],
|
|
},
|
|
off,
|
|
pos,
|
|
i + 1,
|
|
));
|
|
}
|
|
pos = i + 1;
|
|
}
|
|
|
|
if text[pos..].trim().eq_ignore_ascii_case(":END:") {
|
|
Some((
|
|
Drawer {
|
|
name: &name[0..name.len() - 1],
|
|
},
|
|
off,
|
|
pos,
|
|
text.len(),
|
|
))
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn parse() {
|
|
assert_eq!(
|
|
Drawer::parse(":PROPERTIES:\n :CUSTOM_ID: id\n :END:"),
|
|
Some((
|
|
Drawer { name: "PROPERTIES" },
|
|
":PROPERTIES:\n".len(),
|
|
":PROPERTIES:\n :CUSTOM_ID: id\n".len(),
|
|
":PROPERTIES:\n :CUSTOM_ID: id\n :END:".len()
|
|
))
|
|
)
|
|
}
|