61 lines
1.5 KiB
Rust
61 lines
1.5 KiB
Rust
use crate::lines::Lines;
|
|
use memchr::memchr2;
|
|
|
|
// return (name, args, contents-begin, contents-end, end)
|
|
#[inline]
|
|
pub fn parse(src: &str) -> Option<(&str, Option<&str>, usize, usize, usize)> {
|
|
debug_assert!(src.starts_with("#+"));
|
|
|
|
if src.len() <= 8 || src[2..8].to_uppercase() != "BEGIN_" {
|
|
return None;
|
|
}
|
|
|
|
let name = memchr2(b' ', b'\n', src.as_bytes())
|
|
.filter(|&i| src.as_bytes()[8..i].iter().all(u8::is_ascii_alphabetic))?;
|
|
let mut lines = Lines::new(src);
|
|
let (pre_cont_end, cont_beg, _) = lines.next()?;
|
|
let args = if pre_cont_end == name {
|
|
None
|
|
} else {
|
|
Some(&src[name..pre_cont_end])
|
|
};
|
|
let name = &src[8..name];
|
|
let end_line = format!(r"#+END_{}", name.to_uppercase());
|
|
let mut pre_end = cont_beg;
|
|
|
|
for (_, end, line) in lines {
|
|
if line.trim() == end_line {
|
|
return Some((name, args, cont_beg, pre_end, end));
|
|
} else {
|
|
pre_end = end;
|
|
}
|
|
}
|
|
|
|
None
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
#[test]
|
|
fn parse() {
|
|
use super::parse;
|
|
|
|
assert_eq!(
|
|
parse("#+BEGIN_SRC\n#+END_SRC"),
|
|
Some(("SRC", None, 12, 12, 21))
|
|
);
|
|
assert_eq!(
|
|
parse(
|
|
r#"#+BEGIN_SRC rust
|
|
fn main() {
|
|
// print "Hello World!" to the console
|
|
println!("Hello World!");
|
|
}
|
|
#+END_SRC
|
|
"#
|
|
),
|
|
Some(("SRC", Some(" rust"), 17, 104, 114))
|
|
);
|
|
// TODO: more testing
|
|
}
|
|
}
|