40 lines
1.3 KiB
Python
40 lines
1.3 KiB
Python
"""
|
|
Frontmatter parsing and serialization
|
|
"""
|
|
import re
|
|
import yaml
|
|
from typing import Optional, Tuple
|
|
|
|
|
|
class FrontmatterParser:
|
|
FRONTMATTER_PATTERN = re.compile(r'^---\s*\n(.*?\n)---\s*\n', re.DOTALL)
|
|
|
|
@staticmethod
|
|
def parse(content: str) -> Tuple[Optional[dict], str]:
|
|
"""Parse frontmatter from markdown content"""
|
|
match = FrontmatterParser.FRONTMATTER_PATTERN.match(content)
|
|
|
|
if match:
|
|
yaml_content = match.group(1)
|
|
body = content[match.end():]
|
|
|
|
try:
|
|
frontmatter = yaml.safe_load(yaml_content) or {}
|
|
return frontmatter, body
|
|
except yaml.YAMLError:
|
|
return None, content
|
|
|
|
return None, content
|
|
|
|
@staticmethod
|
|
def serialize(frontmatter: dict, body: str) -> str:
|
|
"""Serialize frontmatter and body to markdown"""
|
|
yaml_str = yaml.dump(frontmatter, default_flow_style=False, allow_unicode=True, sort_keys=False)
|
|
return f"---\n{yaml_str}---\n{body}"
|
|
|
|
@staticmethod
|
|
def extract_title(body: str) -> Optional[str]:
|
|
"""Extract title from markdown body"""
|
|
match = re.search(r'^#\s+(.+)$', body, re.MULTILINE)
|
|
return match.group(1).strip() if match else None
|