-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfrontmatter.rs
More file actions
152 lines (131 loc) · 4.4 KB
/
Copy pathfrontmatter.rs
File metadata and controls
152 lines (131 loc) · 4.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
//! Frontmatter parsing module
//!
//! Supports YAML and TOML frontmatter extraction from wiki markup.
//! Frontmatter is metadata placed at the beginning of a document.
use once_cell::sync::Lazy;
use regex::Regex;
/// Supported frontmatter formats
#[derive(Debug, Clone, PartialEq)]
pub enum FrontmatterFormat {
/// YAML format (delimited by ---)
Yaml,
/// TOML format (delimited by +++)
Toml,
}
/// Extracted frontmatter data
#[derive(Debug, Clone)]
pub struct Frontmatter {
/// The format of the frontmatter
pub format: FrontmatterFormat,
/// The raw frontmatter content (without delimiters)
pub content: String,
}
static YAML_FRONTMATTER: Lazy<Regex> = Lazy::new(|| {
// Match YAML frontmatter: ---\n...content...\n---
Regex::new(r"^---\s*\n([\s\S]*?)\n---\s*\n").unwrap()
});
static TOML_FRONTMATTER: Lazy<Regex> = Lazy::new(|| {
// Match TOML frontmatter: +++\n...content...\n+++
Regex::new(r"^\+\+\+\s*\n([\s\S]*?)\n\+\+\+\s*\n").unwrap()
});
/// Extract frontmatter from input text
///
/// Checks for YAML or TOML frontmatter at the beginning of the text.
/// If found, returns the frontmatter data and the remaining content.
///
/// # Arguments
///
/// * `input` - The input text that may contain frontmatter
///
/// # Returns
///
/// A tuple of (optional frontmatter, remaining content)
///
/// # Examples
///
/// ```
/// use umd::frontmatter::extract_frontmatter;
///
/// let input = "---\ntitle: Hello\nauthor: John\n---\n\n# Content";
/// let (frontmatter, content) = extract_frontmatter(input);
/// assert!(frontmatter.is_some());
/// assert!(content.contains("# Content"));
/// ```
pub fn extract_frontmatter(input: &str) -> (Option<Frontmatter>, String) {
// Try YAML first
if let Some(caps) = YAML_FRONTMATTER.captures(input) {
let fm_content = caps.get(1).map_or("", |m| m.as_str());
let remaining = YAML_FRONTMATTER.replace(input, "").to_string();
return (
Some(Frontmatter {
format: FrontmatterFormat::Yaml,
content: fm_content.to_string(),
}),
remaining,
);
}
// Try TOML
if let Some(caps) = TOML_FRONTMATTER.captures(input) {
let fm_content = caps.get(1).map_or("", |m| m.as_str());
let remaining = TOML_FRONTMATTER.replace(input, "").to_string();
return (
Some(Frontmatter {
format: FrontmatterFormat::Toml,
content: fm_content.to_string(),
}),
remaining,
);
}
// No frontmatter found
(None, input.to_string())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_yaml_frontmatter() {
let input = "---\ntitle: Test\nauthor: John\n---\n\n# Content";
let (fm, content) = extract_frontmatter(input);
assert!(fm.is_some());
let fm = fm.unwrap();
assert_eq!(fm.format, FrontmatterFormat::Yaml);
assert!(fm.content.contains("title: Test"));
assert!(content.contains("# Content"));
assert!(!content.contains("---"));
}
#[test]
fn test_toml_frontmatter() {
let input = "+++\ntitle = \"Test\"\nauthor = \"John\"\n+++\n\n# Content";
let (fm, content) = extract_frontmatter(input);
assert!(fm.is_some());
let fm = fm.unwrap();
assert_eq!(fm.format, FrontmatterFormat::Toml);
assert!(fm.content.contains("title = \"Test\""));
assert!(content.contains("# Content"));
assert!(!content.contains("+++"));
}
#[test]
fn test_no_frontmatter() {
let input = "# Just a heading\n\nSome content";
let (fm, content) = extract_frontmatter(input);
assert!(fm.is_none());
assert_eq!(content, input);
}
#[test]
fn test_yaml_with_complex_content() {
let input = "---\ntitle: Complex\ntags:\n - rust\n - wiki\ndate: 2024-01-01\n---\n\n**Bold** text";
let (fm, content) = extract_frontmatter(input);
assert!(fm.is_some());
let fm = fm.unwrap();
assert!(fm.content.contains("tags:"));
assert!(content.contains("**Bold**"));
}
#[test]
fn test_frontmatter_must_be_at_start() {
let input = "Some text\n---\ntitle: Test\n---\n\nMore content";
let (fm, content) = extract_frontmatter(input);
// Should not detect frontmatter if not at the beginning
assert!(fm.is_none());
assert_eq!(content, input);
}
}