use std::path::PathBuf;
use mos_core::{Diagnostic, Severity, SourceSpan};
#[derive(Debug, Clone)]
pub struct SyntaxTree {
pub file: PathBuf,
pub items: Vec<Item>,
}
#[derive(Debug, Clone)]
pub enum Item {
Heading {
level: u8,
inlines: Vec<Inline>,
label: Option<String>,
label_span: Option<SourceSpan>,
span: SourceSpan,
},
Paragraph {
inlines: Vec<Inline>,
label: Option<String>,
label_span: Option<SourceSpan>,
span: SourceSpan,
},
Set {
kind: DirectiveKind,
name: String,
args: Vec<SetArg>,
span: SourceSpan,
},
RawBlock {
kind: RawBlockKind,
args: Vec<SetArg>,
text: String,
label: Option<String>,
label_span: Option<SourceSpan>,
span: SourceSpan,
},
List {
ordered: bool,
items: Vec<ListItem>,
span: SourceSpan,
},
}
#[derive(Debug, Clone)]
pub struct ListItem {
pub inlines: Vec<Inline>,
pub children: Vec<Item>,
pub span: SourceSpan,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum DirectiveKind {
Set,
Image,
Figure,
Bibliography,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum RawBlockKind {
Pre,
Code,
}
#[derive(Debug, Clone, Copy)]
pub struct RawBlockView<'a> {
pub kind: RawBlockKind,
pub args: &'a [SetArg],
pub text: &'a str,
pub label: Option<&'a str>,
pub label_span: Option<&'a SourceSpan>,
pub span: &'a SourceSpan,
}
#[derive(Debug, Clone)]
pub enum SetArg {
Named {
key: String,
value: SetValue,
key_span: SourceSpan,
value_span: SourceSpan,
},
Positional {
value: SetValue,
value_span: SourceSpan,
},
}
impl SetArg {
#[must_use]
pub fn value(&self) -> &SetValue {
match self {
Self::Named { value, .. } | Self::Positional { value, .. } => value,
}
}
#[must_use]
pub fn value_span(&self) -> &SourceSpan {
match self {
Self::Named { value_span, .. } | Self::Positional { value_span, .. } => value_span,
}
}
#[must_use]
pub fn key(&self) -> Option<&str> {
match self {
Self::Named { key, .. } => Some(key.as_str()),
Self::Positional { .. } => None,
}
}
#[must_use]
pub fn key_span(&self) -> Option<&SourceSpan> {
match self {
Self::Named { key_span, .. } => Some(key_span),
Self::Positional { .. } => None,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum SetValue {
Str(String),
Int(i64),
Float(f64),
Length(f64, LengthUnit),
Ident(String),
}
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum LengthUnit {
Mm,
Pt,
Em,
}
#[derive(Debug, Clone)]
pub struct Inline {
pub kind: InlineKind,
pub text: String,
pub span: SourceSpan,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum InlineKind {
Text,
Emphasis,
Strong,
BoldItalic,
Code,
Reference,
Citation,
HardBreak,
}
impl Item {
#[must_use]
pub fn as_heading(&self) -> Option<(u8, &[Inline], &SourceSpan)> {
if let Self::Heading {
level,
inlines,
span,
..
} = self
{
Some((*level, inlines, span))
} else {
None
}
}
#[must_use]
pub fn as_paragraph(&self) -> Option<(&[Inline], &SourceSpan)> {
if let Self::Paragraph { inlines, span, .. } = self {
Some((inlines, span))
} else {
None
}
}
#[must_use]
pub fn as_set(&self) -> Option<(&str, &[SetArg], &SourceSpan)> {
if let Self::Set {
name, args, span, ..
} = self
{
Some((name.as_str(), args.as_slice(), span))
} else {
None
}
}
#[must_use]
pub fn as_raw_block(&self) -> Option<RawBlockView<'_>> {
if let Self::RawBlock {
kind,
args,
text,
label,
label_span,
span,
} = self
{
Some(RawBlockView {
kind: *kind,
args: args.as_slice(),
text: text.as_str(),
label: label.as_deref(),
label_span: label_span.as_ref(),
span,
})
} else {
None
}
}
#[must_use]
pub fn directive_kind(&self) -> Option<DirectiveKind> {
if let Self::Set { kind, .. } = self {
Some(*kind)
} else {
None
}
}
#[must_use]
pub fn as_list(&self) -> Option<(bool, &[ListItem], &SourceSpan)> {
if let Self::List {
ordered,
items,
span,
} = self
{
Some((*ordered, items.as_slice(), span))
} else {
None
}
}
#[must_use]
pub fn label(&self) -> Option<&str> {
match self {
Self::Heading { label, .. }
| Self::Paragraph { label, .. }
| Self::RawBlock { label, .. } => label.as_deref(),
Self::Set { .. } | Self::List { .. } => None,
}
}
#[must_use]
pub fn label_span(&self) -> Option<&SourceSpan> {
match self {
Self::Heading { label_span, .. }
| Self::Paragraph { label_span, .. }
| Self::RawBlock { label_span, .. } => label_span.as_ref(),
Self::Set { .. } | Self::List { .. } => None,
}
}
}
#[derive(Debug)]
pub struct ParseResult {
pub tree: SyntaxTree,
pub diagnostics: Vec<Diagnostic>,
}
impl ParseResult {
#[must_use]
pub fn has_errors(&self) -> bool {
self.diagnostics
.iter()
.any(|d| d.severity() == Severity::Error)
}
}