#[derive(Debug, Clone)]
pub enum Block {
Heading { level: u8, runs: Vec<InlineRun> },
Paragraph { runs: Vec<InlineRun> },
CodeBlock { lines: Vec<String> },
HorizontalRule,
List { entries: Vec<ListEntry> },
BlockQuote { body: Vec<Block> },
Table {
headers: Vec<Vec<InlineRun>>,
aligns: Vec<crate::markdown::TableAlignment>,
rows: Vec<Vec<Vec<InlineRun>>>,
},
Image {
path: std::path::PathBuf,
alt: String,
caption: Option<String>,
},
HtmlBlock { content: String },
PageBreak,
FootnoteDefinitions { entries: Vec<FootnoteEntry> },
DefinitionList { entries: Vec<DefinitionEntry> },
}
#[derive(Debug, Clone)]
pub struct DefinitionEntry {
pub term: Vec<InlineRun>,
pub definitions: Vec<Vec<InlineRun>>,
}
#[derive(Debug, Clone)]
pub struct FootnoteEntry {
#[allow(dead_code)]
pub label: String,
pub number: usize,
pub runs: Vec<InlineRun>,
}
#[derive(Debug, Clone)]
pub struct ListEntry {
pub bullet: ListBullet,
pub runs: Vec<InlineRun>,
pub children: Vec<Block>,
pub loose: bool,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ListBullet {
Unordered(char),
Ordered(usize),
TaskChecked,
TaskUnchecked,
}
#[derive(Debug, Clone)]
pub struct InlineRun {
pub text: String,
pub flags: RunFlags,
pub link: Option<String>,
}
impl InlineRun {
#[cfg_attr(not(test), allow(dead_code))]
pub fn new(text: impl Into<String>) -> Self {
Self {
text: text.into(),
flags: RunFlags::default(),
link: None,
}
}
}
#[derive(Debug, Default, Clone, Copy)]
pub struct VariantUsage {
pub body_bold: bool,
pub body_italic: bool,
pub body_bold_italic: bool,
pub mono_regular: bool,
pub mono_bold: bool,
pub mono_italic: bool,
pub mono_bold_italic: bool,
}
impl VariantUsage {
pub fn analyze(blocks: &[Block]) -> Self {
let mut u = Self::default();
for b in blocks {
walk_block(b, &mut u);
}
u
}
}
fn walk_block(block: &Block, u: &mut VariantUsage) {
match block {
Block::Heading { runs, .. } | Block::Paragraph { runs } => {
for r in runs {
walk_run(r, u);
}
}
Block::List { entries } => {
for entry in entries {
for r in &entry.runs {
walk_run(r, u);
}
for child in &entry.children {
walk_block(child, u);
}
}
}
Block::BlockQuote { body } => {
for child in body {
walk_block(child, u);
}
}
Block::Table { headers, rows, .. } => {
if !headers.is_empty() {
u.body_bold = true;
}
for header in headers {
for r in header {
walk_run(r, u);
}
}
for row in rows {
for cell in row {
for r in cell {
walk_run(r, u);
}
}
}
}
Block::CodeBlock { .. } | Block::HtmlBlock { .. } => {
u.mono_regular = true;
}
Block::FootnoteDefinitions { entries } => {
for entry in entries {
for r in &entry.runs {
walk_run(r, u);
}
}
}
Block::DefinitionList { entries } => {
if !entries.is_empty() {
u.body_bold = true;
}
for entry in entries {
for r in &entry.term {
walk_run(r, u);
}
for def in &entry.definitions {
for r in def {
walk_run(r, u);
}
}
}
}
Block::HorizontalRule | Block::Image { .. } | Block::PageBreak => {}
}
}
fn walk_run(run: &InlineRun, u: &mut VariantUsage) {
let f = run.flags;
match (f.monospace, f.bold, f.italic) {
(false, false, false) => {}
(false, true, false) => u.body_bold = true,
(false, false, true) => u.body_italic = true,
(false, true, true) => u.body_bold_italic = true,
(true, false, false) => u.mono_regular = true,
(true, true, false) => u.mono_bold = true,
(true, false, true) => u.mono_italic = true,
(true, true, true) => u.mono_bold_italic = true,
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct RunFlags {
pub bold: bool,
pub italic: bool,
pub monospace: bool,
pub strikethrough: bool,
pub underline: bool,
pub superscript: bool,
pub subscript: bool,
pub small_caps: bool,
pub small: bool,
}
impl RunFlags {
pub fn with_bold(mut self) -> Self {
self.bold = true;
self
}
pub fn with_italic(mut self) -> Self {
self.italic = true;
self
}
pub fn with_monospace(mut self) -> Self {
self.monospace = true;
self
}
pub fn with_strikethrough(mut self) -> Self {
self.strikethrough = true;
self
}
pub fn with_underline(mut self) -> Self {
self.underline = true;
self
}
pub fn with_superscript(mut self) -> Self {
self.superscript = true;
self
}
pub fn with_subscript(mut self) -> Self {
self.subscript = true;
self
}
pub fn with_small(mut self) -> Self {
self.small = true;
self
}
}