use std::ops::Range;
use once_cell::unsync::OnceCell;
use crate::error::FrontmatterError;
use crate::frontmatter::{self, Frontmatter};
use crate::page::{BlockId, Heading, WikilinkOccurrence};
mod parse;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RangeKind {
Prose,
Heading,
Frontmatter,
CodeBlock,
InlineCode,
Wikilink,
Embed,
Url,
HtmlBlock,
HtmlInline,
}
#[derive(Debug, Clone)]
pub struct ClassifiedRange {
pub kind: RangeKind,
pub byte_range: Range<usize>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MarkdownLinkDestination {
pub destination: String,
pub byte_range: Range<usize>,
}
pub struct MarkdownDocument {
source: String,
frontmatter: OnceCell<Option<Frontmatter>>,
headings: OnceCell<Vec<Heading>>,
wikilinks: OnceCell<Vec<WikilinkOccurrence>>,
markdown_links: OnceCell<Vec<MarkdownLinkDestination>>,
classified_ranges: OnceCell<Vec<ClassifiedRange>>,
block_ids: OnceCell<Vec<BlockId>>,
}
impl MarkdownDocument {
pub(crate) fn new(source: String) -> Self {
Self {
source,
frontmatter: OnceCell::new(),
headings: OnceCell::new(),
wikilinks: OnceCell::new(),
markdown_links: OnceCell::new(),
classified_ranges: OnceCell::new(),
block_ids: OnceCell::new(),
}
}
pub fn source(&self) -> &str {
&self.source
}
pub fn frontmatter(&self) -> Result<Option<&Frontmatter>, FrontmatterError> {
self.frontmatter
.get_or_try_init(|| frontmatter::parse_frontmatter(&self.source))
.map(Option::as_ref)
}
pub(crate) fn set_frontmatter_field_edit(
&self,
field: &str,
value: &str,
) -> Result<(Range<usize>, String), FrontmatterError> {
frontmatter::set_field_edit(self.frontmatter()?, field, value)
}
pub fn headings(&self) -> &[Heading] {
self.headings
.get_or_init(|| parse::extract_headings(&self.source))
}
pub fn wikilinks(&self) -> &[WikilinkOccurrence] {
self.wikilinks
.get_or_init(|| parse::extract_wikilinks(&self.source))
}
pub fn markdown_links(&self) -> &[MarkdownLinkDestination] {
self.markdown_links
.get_or_init(|| parse::extract_markdown_links(&self.source))
}
pub fn classified_ranges(&self) -> &[ClassifiedRange] {
self.classified_ranges
.get_or_init(|| parse::classify_ranges(&self.source))
}
pub fn block_ids(&self) -> &[BlockId] {
self.block_ids
.get_or_init(|| parse::extract_block_ids(&self.source))
}
}