67 lines
1.7 KiB
Rust
67 lines
1.7 KiB
Rust
use jetscii::Substring;
|
|
use memchr::memchr;
|
|
|
|
#[cfg_attr(test, derive(PartialEq))]
|
|
#[derive(Debug)]
|
|
pub struct Link<'a> {
|
|
pub path: &'a str,
|
|
pub desc: Option<&'a str>,
|
|
}
|
|
|
|
impl<'a> Link<'a> {
|
|
#[inline]
|
|
pub(crate) fn parse(text: &str) -> Option<(Link<'_>, usize)> {
|
|
debug_assert!(text.starts_with("[["));
|
|
|
|
let (path, off) = memchr(b']', text.as_bytes())
|
|
.map(|i| (&text["[[".len()..i], i))
|
|
.filter(|(path, _)| {
|
|
path.as_bytes()
|
|
.iter()
|
|
.all(|&c| c != b'<' && c != b'>' && c != b'\n')
|
|
})?;
|
|
|
|
if *text.as_bytes().get(off + 1)? == b']' {
|
|
Some((Link { path, desc: None }, off + 2))
|
|
} else if text.as_bytes()[off + 1] == b'[' {
|
|
let (desc, off) = Substring::new("]]")
|
|
.find(&text[off + 1..])
|
|
.map(|i| (&text[off + 2..off + 1 + i], off + 1 + i + "]]".len()))
|
|
.filter(|(desc, _)| desc.as_bytes().iter().all(|&c| c != b'[' && c != b']'))?;
|
|
Some((
|
|
Link {
|
|
path,
|
|
desc: Some(desc),
|
|
},
|
|
off,
|
|
))
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn parse() {
|
|
assert_eq!(
|
|
Link::parse("[[#id]]"),
|
|
Some((
|
|
Link {
|
|
path: "#id",
|
|
desc: None
|
|
},
|
|
"[[#id]]".len()
|
|
))
|
|
);
|
|
assert_eq!(
|
|
Link::parse("[[#id][desc]]"),
|
|
Some((
|
|
Link {
|
|
path: "#id",
|
|
desc: Some("desc")
|
|
},
|
|
"[[#id][desc]]".len()
|
|
))
|
|
);
|
|
assert_eq!(Link::parse("[[#id][desc]"), None);
|
|
}
|