use crate::attributes::Attributes;
use crate::source::SourceId;
use std::collections::BTreeMap;
use std::sync::Arc;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Span {
pub source_id: SourceId,
pub start: usize,
pub end: usize,
pub provenance: Option<usize>,
}
impl Span {
pub fn new(start: usize, end: usize) -> Self {
Self::in_source(SourceId::default(), start, end)
}
pub fn in_source(source_id: SourceId, start: usize, end: usize) -> Self {
Self {
source_id,
start,
end,
provenance: None,
}
}
pub fn len(&self) -> usize {
self.end - self.start
}
pub fn is_empty(&self) -> bool {
self.end == self.start
}
}
#[derive(Debug, Clone, Default, PartialEq)]
pub struct Document {
pub attributes: Attributes,
pub final_attributes: Attributes,
pub header: Option<DocumentHeader>,
pub blocks: Vec<Block>,
pub has_title: bool,
pub footnotes: Vec<FootnoteDefinition>,
pub manpage: Option<Manpage>,
pub htmlsyntax: Option<&'static str>,
pub output: Option<crate::OutputPaths>,
pub toc: Option<Toc>,
pub base_dir: Option<String>,
pub references: Vec<Reference>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Reference {
pub id: String,
pub inline: bool,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Toc {
pub levels: i128,
pub entries: Vec<TocEntry>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct TocEntry {
pub caption: Option<Vec<crate::AttributePart>>,
pub sections: Vec<TocEntry>,
}
impl Drop for Document {
fn drop(&mut self) {
crate::visit::dismantle(std::mem::take(&mut self.blocks));
}
}
#[derive(Debug, Clone, Default, PartialEq)]
pub struct Manpage {
pub names: Vec<String>,
pub name_section: Option<NameSection>,
pub section_title: bool,
}
#[derive(Debug, Clone, PartialEq)]
pub struct NameSection {
pub heading: Block,
pub body: Option<Span>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct FootnoteDefinition {
pub index: String,
pub id: Option<String>,
pub content: Vec<crate::AttributePart>,
pub span: Span,
}
impl Document {
pub fn authors(&self) -> Vec<AuthorDetails> {
crate::author_values::document_authors(&self.attributes)
}
pub fn authors_line(&self) -> Option<Vec<crate::AttributePart>> {
crate::author_values::replaced(&self.attributes, "authors")
}
pub fn flat_blocks(&self) -> Vec<&Block> {
fn push<'a>(blocks: &'a [Block], out: &mut Vec<&'a Block>) {
for block in blocks {
match &block.kind {
BlockKind::Section { blocks, .. } | BlockKind::Preamble { blocks } => {
push(blocks, out)
}
_ => out.push(block),
}
}
}
let mut out = Vec::new();
push(&self.blocks, &mut out);
out
}
pub fn into_blocks(mut self) -> Vec<Block> {
std::mem::take(&mut self.blocks)
}
pub fn into_flat_blocks(mut self) -> Vec<Block> {
fn push(blocks: Vec<Block>, out: &mut Vec<Block>) {
for block in blocks {
match block.kind {
BlockKind::Section { blocks, .. } | BlockKind::Preamble { blocks } => {
push(blocks, out)
}
_ => out.push(block),
}
}
}
let mut out = Vec::new();
push(std::mem::take(&mut self.blocks), &mut out);
out
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct DocumentHeader {
pub span: Span,
pub title_span: Span,
pub title: String,
pub authors: Vec<Author>,
pub revision: Option<Revision>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Author {
pub span: Span,
pub name: String,
pub firstname: String,
pub middlename: Option<String>,
pub lastname: Option<String>,
pub initials: String,
pub email: Option<String>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct AuthorDetails {
pub name: Vec<crate::AttributePart>,
pub email: Option<Vec<crate::AttributePart>>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Revision {
pub span: Span,
pub number: Option<String>,
pub date: Option<String>,
pub remark: Option<String>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Block {
pub span: Span,
pub attribute_context: Arc<Attributes>,
pub conversion_context: Option<Arc<Attributes>>,
pub id: Option<String>,
pub reftext: Option<Vec<Inline>>,
pub roles: Vec<String>,
pub title: Option<Vec<Inline>>,
pub caption: Option<String>,
pub caption_parts: Vec<crate::AttributePart>,
pub toc: Option<Box<Toc>>,
pub attrs: BlockAttrs,
pub simple: bool,
pub kind: BlockKind,
}
impl Block {
pub fn new(span: Span, kind: BlockKind) -> Self {
Self {
span,
attribute_context: Arc::new(Attributes::new()),
conversion_context: None,
id: None,
reftext: None,
roles: Vec::new(),
title: None,
caption: None,
caption_parts: Vec::new(),
toc: None,
attrs: BlockAttrs::default(),
simple: false,
kind,
}
}
pub fn set_caption(&mut self, parts: Option<Vec<crate::AttributePart>>) {
self.caption = parts.as_deref().map(crate::attributes::parts_text);
self.caption_parts = match parts {
Some(parts) if !matches!(parts.as_slice(), [] | [crate::AttributePart::Text(_)]) => {
parts
}
_ => Vec::new(),
};
}
pub fn conversion_attributes(&self) -> &Attributes {
self.conversion_context
.as_deref()
.unwrap_or(&self.attribute_context)
}
}
#[derive(Debug, Clone, Default, PartialEq)]
pub struct BlockAttrs {
pub style: Option<String>,
pub named: BTreeMap<String, String>,
pub options: Vec<String>,
pub positional: Vec<String>,
pub(crate) caption: Option<Vec<crate::AttributePart>>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AdmonitionKind {
Note,
Tip,
Important,
Warning,
Caution,
}
impl AdmonitionKind {
pub fn as_str(self) -> &'static str {
match self {
Self::Note => "note",
Self::Tip => "tip",
Self::Important => "important",
Self::Warning => "warning",
Self::Caution => "caution",
}
}
pub fn label(self) -> &'static str {
match self {
Self::Note => "Note",
Self::Tip => "Tip",
Self::Important => "Important",
Self::Warning => "Warning",
Self::Caution => "Caution",
}
}
pub fn from_style(style: &str) -> Option<Self> {
match style {
"NOTE" => Some(Self::Note),
"TIP" => Some(Self::Tip),
"IMPORTANT" => Some(Self::Important),
"WARNING" => Some(Self::Warning),
"CAUTION" => Some(Self::Caution),
_ => None,
}
}
pub fn from_label_prefix(line: &str) -> Option<(Self, usize)> {
const LABELS: [(&str, AdmonitionKind); 5] = [
("NOTE", AdmonitionKind::Note),
("TIP", AdmonitionKind::Tip),
("IMPORTANT", AdmonitionKind::Important),
("WARNING", AdmonitionKind::Warning),
("CAUTION", AdmonitionKind::Caution),
];
for (word, kind) in LABELS {
if let Some(rest) = line.strip_prefix(word) {
if rest.strip_prefix(": ").is_some() {
return Some((kind, word.len() + 2));
}
}
}
None
}
}
#[derive(Debug, Clone, PartialEq, Default)]
pub struct Attribution {
pub author: Option<Vec<Inline>>,
pub citetitle: Option<Vec<Inline>>,
pub trailing: Vec<Inline>,
}
#[derive(Debug, Clone, PartialEq)]
pub enum BlockKind {
Heading { level: u8, inlines: Vec<Inline> },
Paragraph { inlines: Vec<Inline> },
Listing {
text: String,
callouts: Vec<Callout>,
inlines: Option<Vec<Inline>>,
blank_edges: BlankEdges,
},
Literal {
text: String,
callouts: Vec<Callout>,
inlines: Option<Vec<Inline>>,
},
Passthrough {
text: String,
inlines: Option<Vec<Inline>>,
},
Stem {
style: crate::StemStyle,
inlines: Vec<Inline>,
},
List {
ordered: bool,
items: Vec<ListItem>,
icons: Option<String>,
},
CalloutList { items: Vec<CalloutListItem> },
DescriptionList { items: Vec<DescriptionListItem> },
Admonition {
kind: AdmonitionKind,
label: Vec<crate::AttributePart>,
blocks: Vec<Block>,
icons: Option<String>,
src: String,
},
Example { blocks: Vec<Block> },
Quote {
attribution: Option<Attribution>,
blocks: Vec<Block>,
},
Verse {
attribution: Option<Attribution>,
inlines: Vec<Inline>,
},
Sidebar { blocks: Vec<Block> },
Open { blocks: Vec<Block> },
Section { level: u8, blocks: Vec<Block> },
Preamble { blocks: Vec<Block> },
Toc,
ThematicBreak,
PageBreak,
Image {
target: String,
src: String,
alt: Vec<crate::AttributePart>,
svg: Option<crate::Svg>,
},
Video {
target: String,
src: String,
poster: Option<String>,
embedded: bool,
},
Audio { target: String, src: String },
Table(Box<Table>),
}
#[derive(Debug, Clone, PartialEq)]
pub struct Table {
pub columns: Vec<TableColumn>,
pub head: Vec<Vec<TableCell>>,
pub body: Vec<Vec<TableCell>>,
pub foot: Vec<Vec<TableCell>>,
pub width: u32,
pub frame: String,
pub grid: String,
pub stripes: Option<String>,
}
impl Table {
pub fn cells(&self) -> impl Iterator<Item = &TableCell> {
self.head
.iter()
.chain(&self.body)
.chain(&self.foot)
.flatten()
}
pub fn cells_mut(&mut self) -> impl Iterator<Item = &mut TableCell> {
self.head
.iter_mut()
.chain(&mut self.body)
.chain(&mut self.foot)
.flatten()
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct TableColumn {
pub halign: HAlign,
pub valign: VAlign,
pub style: Option<CellStyle>,
pub width: String,
pub autowidth: bool,
}
#[derive(Debug, Clone, PartialEq)]
pub struct TableCell {
pub content: CellContent,
pub style: Option<CellStyle>,
pub halign: HAlign,
pub valign: VAlign,
pub colspan: Option<u32>,
pub rowspan: Option<u32>,
pub background: Option<String>,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq)]
pub enum CellContent {
Paragraphs(Vec<Vec<Inline>>),
Blocks {
blocks: Vec<Block>,
footnotes: Vec<FootnoteDefinition>,
has_title: bool,
attributes: Arc<Attributes>,
toc: Option<Toc>,
},
Literal(String),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum HAlign {
Left,
Center,
Right,
}
impl HAlign {
pub fn as_str(self) -> &'static str {
match self {
HAlign::Left => "left",
HAlign::Center => "center",
HAlign::Right => "right",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum VAlign {
Top,
Middle,
Bottom,
}
impl VAlign {
pub fn as_str(self) -> &'static str {
match self {
VAlign::Top => "top",
VAlign::Middle => "middle",
VAlign::Bottom => "bottom",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CellStyle {
Default,
Strong,
Emphasis,
Monospaced,
Header,
Literal,
Asciidoc,
}
impl CellStyle {
pub fn from_letter(letter: char) -> Option<Self> {
Some(match letter {
'd' => CellStyle::Default,
's' => CellStyle::Strong,
'e' => CellStyle::Emphasis,
'm' => CellStyle::Monospaced,
'h' => CellStyle::Header,
'l' => CellStyle::Literal,
'a' => CellStyle::Asciidoc,
_ => return None,
})
}
pub fn as_str(self) -> &'static str {
match self {
CellStyle::Default => "none",
CellStyle::Strong => "strong",
CellStyle::Emphasis => "emphasis",
CellStyle::Monospaced => "monospaced",
CellStyle::Header => "header",
CellStyle::Literal => "literal",
CellStyle::Asciidoc => "asciidoc",
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct ListItem {
pub inlines: Vec<Inline>,
pub blocks: Vec<Block>,
pub span: Span,
pub checkbox: Option<bool>,
pub marker: Option<String>,
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct BlankEdges {
pub leading: usize,
pub trailing: usize,
pub empty: bool,
}
impl BlankEdges {
pub fn source(&self, text: &str) -> Option<String> {
if self.empty {
return None;
}
if self.leading == 0 && self.trailing == 0 {
return Some(text.to_owned());
}
let mut lines = vec![""; self.leading];
if !(text.is_empty() && self.leading > 0) {
lines.push(text);
}
lines.resize(lines.len() + self.trailing, "");
Some(lines.join("\n"))
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Callout {
pub start: usize,
pub end: usize,
pub number: String,
pub guard: Option<String>,
pub xml: bool,
pub id: String,
pub span: Span,
pub icons: Option<String>,
pub src: String,
}
#[derive(Debug, Clone, PartialEq)]
pub struct CalloutListItem {
pub item: ListItem,
pub coids: Vec<String>,
pub icons: Option<String>,
pub src: String,
}
#[derive(Debug, Clone, PartialEq)]
pub struct DescriptionListItem {
pub terms: Vec<ListTerm>,
pub description: Option<ListItem>,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq)]
pub struct ListTerm {
pub inlines: Vec<Inline>,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq)]
pub enum Inline {
Text { value: String, span: Span },
Raw { value: String, span: Span },
Substituted {
value: String,
parts: Vec<crate::AttributePart>,
span: Span,
},
Strong { children: Vec<Inline>, span: Span },
Emphasis { children: Vec<Inline>, span: Span },
Monospace { children: Vec<Inline>, span: Span },
Mark { children: Vec<Inline>, span: Span },
Superscript { children: Vec<Inline>, span: Span },
Subscript { children: Vec<Inline>, span: Span },
AttributeRef {
name: String,
value: Option<String>,
parts: Vec<crate::AttributePart>,
span: Span,
},
Link {
target: String,
children: Vec<Inline>,
bare: bool,
span: Span,
},
CrossReference {
xref: crate::Xref,
children: Vec<Inline>,
span: Span,
},
Anchor { id: String, span: Span },
Image {
image: crate::InlineImage,
span: Span,
},
Footnote {
footnote: crate::Footnote,
span: Span,
},
Ui { ui: crate::UiMacro, span: Span },
IndexTerm { term: crate::IndexTerm, span: Span },
Stem { stem: crate::Stem, span: Span },
}
impl Inline {
pub fn span_mut(&mut self) -> &mut Span {
match self {
Inline::Text { span, .. }
| Inline::Raw { span, .. }
| Inline::Substituted { span, .. }
| Inline::Strong { span, .. }
| Inline::Emphasis { span, .. }
| Inline::Monospace { span, .. }
| Inline::Mark { span, .. }
| Inline::Superscript { span, .. }
| Inline::Subscript { span, .. }
| Inline::AttributeRef { span, .. }
| Inline::Link { span, .. }
| Inline::CrossReference { span, .. }
| Inline::Anchor { span, .. }
| Inline::Image { span, .. }
| Inline::Footnote { span, .. }
| Inline::Ui { span, .. }
| Inline::IndexTerm { span, .. }
| Inline::Stem { span, .. } => span,
}
}
pub fn span(&self) -> Span {
match self {
Inline::Text { span, .. }
| Inline::Raw { span, .. }
| Inline::Substituted { span, .. }
| Inline::Strong { span, .. }
| Inline::Emphasis { span, .. }
| Inline::Monospace { span, .. }
| Inline::Mark { span, .. }
| Inline::Superscript { span, .. }
| Inline::Subscript { span, .. }
| Inline::AttributeRef { span, .. }
| Inline::Link { span, .. }
| Inline::CrossReference { span, .. }
| Inline::Anchor { span, .. }
| Inline::Image { span, .. }
| Inline::Footnote { span, .. }
| Inline::Ui { span, .. }
| Inline::IndexTerm { span, .. }
| Inline::Stem { span, .. } => *span,
}
}
}