mod tests;
pub mod parsing;
pub mod output;
pub mod actions;
pub mod reference_doc;
use std::{
num::ParseIntError,
collections::{ HashMap, HashSet },
};
use pest_derive::Parser;
#[derive(Parser)]
#[grammar = "parse/incodoc.pest"]
pub struct IncodocParser;
#[derive(Clone, Default, Debug, Eq, PartialEq)]
pub struct Doc {
pub tags: Tags,
pub props: Props,
pub items: Vec<DocItem>,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum DocItem {
Nav(Nav),
Paragraph(Paragraph),
Section(Section),
}
pub type Tags = HashSet<String>;
pub type Props = HashMap<String, PropVal>;
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum PropVal {
String(String),
Text(String),
Int(i64),
Date(Date),
Error(PropValError),
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum PropValError {
Int(ParseIntError),
Date(DateError),
}
impl PropVal {
fn is_error(&self) -> bool {
matches![self, PropVal::Error(_)]
}
}
fn insert_prop(props: &mut Props, (k, v): (String, PropVal)) {
let mut insert = true;
if v.is_error() && let Some(ov) = props.get(&k) && !ov.is_error() {
insert = false;
}
if insert {
props.insert(k, v);
}
}
#[derive(Clone, Default, Debug, Eq, PartialEq)]
pub struct Section {
pub heading: Heading,
pub items: Vec<SectionItem>,
pub tags: Tags,
pub props: Props,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum SectionItem {
Paragraph(Paragraph),
Section(Section),
}
#[derive(Clone, Default, Debug, Eq, PartialEq)]
pub struct Heading {
pub level: u8,
pub items: Vec<HeadingItem>,
pub tags: Tags,
pub props: Props,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum HeadingItem {
String(String),
Em(Emphasis),
}
#[derive(Clone, Default, Debug, Eq, PartialEq)]
pub struct Paragraph {
pub items: Vec<ParagraphItem>,
pub tags: Tags,
pub props: Props,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum ParagraphItem {
Text(String),
MText(TextWithMeta),
Em(Emphasis),
Code(Result<CodeBlock, CodeIdentError>),
Link(Link),
List(List),
Table(Table),
}
#[derive(Clone, Default, Debug, Eq, PartialEq)]
pub struct Emphasis {
pub strength: EmStrength,
pub etype: EmType,
pub text: String,
pub tags: Tags,
pub props: Props,
}
#[derive(Clone, Copy, Default, Hash, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub enum EmStrength {
#[default]
Light,
Medium,
Strong,
}
#[derive(Clone, Copy, Default, Hash, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub enum EmType {
#[default]
Emphasis,
Deemphasis,
}
#[derive(Clone, Default, Debug, Eq, PartialEq)]
pub struct List {
pub ltype: ListType,
pub items: Vec<Paragraph>,
pub tags: Tags,
pub props: Props,
}
#[derive(Clone, Copy, Default, Hash, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub enum ListType {
Distinct,
#[default] Identical,
Checked,
}
#[derive(Clone, Default, Debug, Eq, PartialEq)]
pub struct Nav {
pub description: String,
pub subs: Vec<Nav>,
pub links: Vec<Link>,
pub tags: Tags,
pub props: Props,
}
#[derive(Clone, Default, Debug, Eq, PartialEq)]
pub struct Link {
pub url: String,
pub items: Vec<LinkItem>,
pub tags: Tags,
pub props: Props,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum LinkItem {
String(String),
Em(Emphasis),
}
#[derive(Clone, Default, Debug, Eq, PartialEq)]
pub struct CodeBlock {
pub language: String,
pub mode: CodeModeHint,
pub code: String,
pub tags: Tags,
pub props: Props,
}
#[derive(Clone, Copy, Default, Hash, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub enum CodeModeHint {
#[default] Show,
Runnable,
Run,
Replace,
}
#[derive(Clone, Default, Debug, Eq, PartialEq)]
pub struct Table {
pub rows: Vec<TableRow>,
pub tags: Tags,
pub props: Props,
}
#[derive(Clone, Default, Debug, Eq, PartialEq)]
pub struct TableRow {
pub items: Vec<Paragraph>,
pub is_header: bool,
pub tags: Tags,
pub props: Props,
}
#[derive(Clone, Default, Debug, Eq, PartialEq)]
pub struct TextWithMeta {
pub text: String,
pub tags: Tags,
pub props: Props,
}
impl TextWithMeta {
fn meta_is_empty(&self) -> bool {
self.tags.is_empty() && self.props.is_empty()
}
}
#[derive(Clone, Copy, Default, Hash, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub struct CodeIdentError;
#[derive(Clone, Copy, Default, Hash, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub struct Date {
pub year: i16,
pub month: u8,
pub day: u8,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum DateError {
YearRange(i64),
MonthRange(u64),
DayRange(u64),
Parsing(ParseIntError),
}
impl Date {
pub fn new(y: i64, m: u64, d: u64) -> Result<Self, DateError> {
let year: i16 = y.try_into().map_err(|_| DateError::YearRange(y))?;
let month: u8 = m.try_into()
.map_err(|_| DateError::MonthRange(m))
.and_then(|m| if m == 0 { Err(DateError::MonthRange(d)) } else { Ok(m) } )?;
let day: u8 = d.try_into()
.map_err(|_| DateError::DayRange(d))
.and_then(|d| if d == 0 { Err(DateError::DayRange(u64::from(d))) } else { Ok(d) } )?;
if month > 12 { return Err(DateError::MonthRange(m)); }
if day > 31 { return Err(DateError::DayRange(d)); }
Ok(Self { year, month, day })
}
}