emacs_org_link_parser/
lib.rs

1//! # Emacs Org Link Parser
2//!
3//! This is a Rust library to parse links, formatted as [Emacs Org-mode hyperlinks](https://orgmode.org/guide/Hyperlinks.html), from a string.
4//!
5//! ## Example usage:
6//!
7//! ```
8//! use emacs_org_link_parser as org;
9//!
10//! fn main() {
11//!     let line_to_parse = "*** [[#mycookbook][page 3]] dumplings, [[www.best-sauce.com][sauce here: number 4]] [[#pictures][how it looks]] [[forum.com]]";
12//!
13//!     let parsed: Vec<org::Link> = org::parse_line(line_to_parse);
14//!
15//!     println!("{:?}", parsed);
16//! }
17//! ```
18//! Expected output:
19//! ```
20//! [Link { link: Some("#mycookbook"), description: Some("page 3") }, Link { link: Some("www.best-sauce.com"), description: Some("sauce here: number 4") }, Link { link: Some("#pictures"), description: Some("how it looks") }, Link { link: Some("forum.com"), description: None }]
21//! ```
22
23#[derive(Debug, PartialEq, Eq)]
24pub struct Link {
25    pub link: Option<String>,
26    pub description: Option<String>,
27}
28
29pub fn parse_line(line: &str) -> Vec<Link> {
30    let mut link_begin: Option<usize> = None;
31    let mut link_end: Option<usize> = None;
32
33    let mut line_links: Vec<Link> = Vec::new();
34
35    for (index, character) in line.chars().enumerate() {
36        if character == '[' && line.chars().nth(index-1) == Some('[') {
37            link_begin = Some(index+1);
38        } else if character == ']' && line.chars().nth(index-1) == Some(']') {
39            link_end = Some(index-1);
40        }
41
42        if let Some(begin) = link_begin {
43            if let Some(end) = link_end {
44                let text: Vec<String> = line[begin..end].to_string()
45                    .split("][")
46                    .map(|x| x.to_string())
47                    .collect();
48                let mut text = text.into_iter();
49
50                line_links.push(Link {
51                    link: text.next(),
52                    description: text.next(),
53                });
54                link_begin = None;
55                link_end = None;
56            }
57        }
58    }
59
60    line_links
61}
62
63#[cfg(test)]
64mod tests {
65    use super::{Link, parse_line};
66
67    #[test]
68    fn correctly_parsed() {
69        let line_to_parse = "*** [[#examp_3le][p3]] dumplings, [[www.best-sauce.com][p34]] [[#anothr][pretty54]] [[alonelink]]";
70
71        let expected_output: Vec<Link> = vec![Link { link: Some("#examp_3le".to_string()), description: Some("p3".to_string()) }, Link { link: Some("www.best-sauce.com".to_string()), description: Some("p34".to_string()) }, Link { link: Some("#anothr".to_string()), description: Some("pretty54".to_string()) }, Link { link: Some("alonelink".to_string()), description: None }];
72
73        assert_eq!(parse_line(line_to_parse), expected_output);
74    }
75}