quake_core/markdown/
entry_reference.rs1use lazy_static::lazy_static;
2use regex::Regex;
3use std::fmt;
4
5lazy_static! {
6 static ref ENTRY_LINK_RE: Regex =
7 Regex::new(r#"^(?P<type>[^#|:]+):(?P<id>\d+)(#(?P<section>.+?))??(\|(?P<label>.+?))??(\s["'“](?P<title>.+?)["'”])??$"#).unwrap();
8}
9
10#[derive(Serialize, Deserialize, PartialEq, Debug, Default, Clone)]
12pub struct PageReference {
13 pub(crate) entry_type: String,
14 pub(crate) entry_id: String,
15 pub(crate) entry_title: String,
16 #[serde(skip_serializing_if = "Option::is_none")]
17 pub(crate) label: Option<String>,
18 #[serde(skip_serializing_if = "Option::is_none")]
19 pub(crate) section: Option<String>,
20}
21
22impl PageReference {
23 #[allow(clippy::all)]
24 pub fn from_str(text: &str) -> PageReference {
25 let captures = match ENTRY_LINK_RE.captures(text) {
26 None => return PageReference::default(),
27 Some(capts) => capts,
28 };
29
30 let entry_type = captures.name("type").map(|v| v.as_str()).unwrap_or("");
31 let entry_id = captures.name("id").map(|v| v.as_str()).unwrap_or("");
32 let entry_title = captures.name("title").map(|v| v.as_str()).unwrap_or("");
33
34 let label = captures.name("label").map(|v| v.as_str().to_string());
35 let section = captures.name("section").map(|v| v.as_str().to_string());
36
37 PageReference {
38 entry_type: entry_type.to_string(),
39 entry_id: entry_id.to_string(),
40 entry_title: entry_title.to_string(),
41 label,
42 section,
43 }
44 }
45
46 pub fn display(&self) -> String {
47 format!("{}", self)
48 }
49}
50
51impl<'a> fmt::Display for PageReference {
52 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
53 let label = self
54 .label
55 .as_ref()
56 .map(|text| format!("|{:}", text))
57 .unwrap_or_else(|| "".to_string());
58 let section = self
59 .section
60 .as_ref()
61 .map(|text| format!("#{:}", text))
62 .unwrap_or_else(|| "".to_string());
63
64 write!(
65 f,
66 "{}:{}{}{} \"{}\"",
67 self.entry_type, self.entry_id, section, label, self.entry_title
68 )
69 }
70}
71
72#[cfg(test)]
73mod tests {
74 use crate::markdown::entry_reference::PageReference;
75
76 #[test]
77 fn parse_quake_down_link() {
78 let text = r#"note:0001#Heading|Label "file name""#;
79 let reference = PageReference::from_str(text);
80
81 assert_eq!("note", reference.entry_type);
82 assert_eq!("file name", reference.entry_title);
83 assert_eq!("0001", reference.entry_id);
84
85 assert_eq!("Label", reference.label.unwrap());
86 assert_eq!("Heading", reference.section.unwrap());
87 }
88
89 #[test]
90 fn parse_normal() {
91 let text = r#"note:0001 "file name""#;
92 let reference = PageReference::from_str(text);
93
94 assert_eq!("note", reference.entry_type);
95 assert_eq!("file name", reference.entry_title);
96 assert_eq!("0001", reference.entry_id);
97
98 assert!(reference.label.is_none());
99 assert!(reference.section.is_none());
100 }
101
102 #[test]
103 fn print_reference() {
104 let text = r#"note:0001 "file name""#;
105 let reference = PageReference::from_str(text);
106 assert_eq!(text, reference.to_string());
107
108 let with_title = r#"note:0001#heading "file name""#;
109 let title_ref = PageReference::from_str(with_title);
110 assert_eq!(with_title, title_ref.to_string());
111
112 let with_label = r#"note:0001#heading|label "file name""#;
113 let label_ref = PageReference::from_str(with_label);
114 assert_eq!(with_label, label_ref.to_string());
115
116 let only_label = r#"Note:0001|Label "file name""#;
117 let only_label_ref = PageReference::from_str(only_label);
118 assert_eq!(only_label, only_label_ref.to_string());
119 }
120}