mod visitor;
use serde_derive::{Deserialize, Serialize};
use serde_json::{from_str, to_string};
pub use std::collections::BTreeMap as Map;
pub use visitor::*;
pub type Int = i64;
pub type Double = f64;
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct Pandoc {
pub meta: Map<String, MetaValue>,
pub blocks: Vec<Block>,
#[serde(rename = "pandoc-api-version")]
pub pandoc_api_version: Vec<u32>,
}
impl Pandoc {
pub fn from_json(json: &str) -> Self {
let v: serde_json::Value = from_str(json).unwrap();
let obj = v.as_object().expect("broken pandoc json");
fn pandoc_version(obj: &serde_json::Map<String, serde_json::Value>) -> Option<(i64, i64)> {
let version = obj
.get("pandoc-api-version")?
.as_array()?
.iter()
.map(|v| v.as_i64())
.collect::<Vec<_>>();
match version[..] {
[Some(major), Some(minor), ..] => Some((major, minor)),
_ => None,
}
}
if let Some((major, minor)) = pandoc_version(obj) {
if !(major == 1 && minor >= 20) {
panic!(
"Pandoc version mismatch: \
`pandoc-ast` expects pandoc version 1.20 or newer, got {}.{}",
major, minor
);
}
} else {
panic!(
"Unable to parse pandoc version from JSON. \
Please update your pandoc to at least version 1.18 or use an older version of `pandoc-ast`"
);
}
let s = serde_json::to_string_pretty(&v).unwrap();
let data: Self = match from_str(&s) {
Ok(data) => data,
Err(err) => panic!("json is not in the pandoc format: {:?}\n{}", err, s),
};
data
}
pub fn to_json(&self) -> String {
to_string(self).expect("serialization failed")
}
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(tag = "t", content = "c")]
pub enum MetaValue {
MetaMap(Map<String, Box<MetaValue>>),
MetaList(Vec<MetaValue>),
MetaBool(bool),
MetaString(String),
MetaInlines(Vec<Inline>),
MetaBlocks(Vec<Block>),
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(tag = "t", content = "c")]
pub enum Block {
Plain(Vec<Inline>),
Para(Vec<Inline>),
LineBlock(Vec<Vec<Inline>>),
CodeBlock(Attr, String),
RawBlock(Format, String),
BlockQuote(Vec<Block>),
OrderedList(ListAttributes, Vec<Vec<Block>>),
BulletList(Vec<Vec<Block>>),
DefinitionList(Vec<(Vec<Inline>, Vec<Vec<Block>>)>),
Figure(Attr, Caption, Vec<Block>),
Header(Int, Attr, Vec<Inline>),
HorizontalRule,
Table(
Attr,
Caption,
Vec<ColSpec>,
TableHead,
Vec<TableBody>,
TableFoot,
),
Div(Attr, Vec<Block>),
Null,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(tag = "t", content = "c")]
pub enum Inline {
Str(String),
Emph(Vec<Inline>),
Underline(Vec<Inline>),
Strong(Vec<Inline>),
Strikeout(Vec<Inline>),
Superscript(Vec<Inline>),
Subscript(Vec<Inline>),
SmallCaps(Vec<Inline>),
Quoted(QuoteType, Vec<Inline>),
Cite(Vec<Citation>, Vec<Inline>),
Code(Attr, String),
Space,
SoftBreak,
LineBreak,
Math(MathType, String),
RawInline(Format, String),
Link(Attr, Vec<Inline>, Target),
Image(Attr, Vec<Inline>, Target),
Note(Vec<Block>),
Span(Attr, Vec<Inline>),
}
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[serde(tag = "t")]
pub enum Alignment {
AlignLeft,
AlignRight,
AlignCenter,
AlignDefault,
}
pub type ListAttributes = (Int, ListNumberStyle, ListNumberDelim);
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[serde(tag = "t")]
pub enum ListNumberStyle {
DefaultStyle,
Example,
Decimal,
LowerRoman,
UpperRoman,
LowerAlpha,
UpperAlpha,
}
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[serde(tag = "t")]
pub enum ListNumberDelim {
DefaultDelim,
Period,
OneParen,
TwoParens,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)]
pub struct Format(pub String);
pub type Attr = (String, Vec<String>, Vec<(String, String)>);
pub type TableCell = Vec<Block>;
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[serde(tag = "t")]
pub enum QuoteType {
SingleQuote,
DoubleQuote,
}
pub type Caption = (Option<ShortCaption>, Vec<Block>);
pub type ShortCaption = Vec<Inline>;
pub type RowHeadColumns = Int;
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq)]
#[serde(tag = "t", content = "c")]
pub enum ColWidth {
ColWidth(Double),
ColWidthDefault,
}
pub type ColSpec = (Alignment, ColWidth);
pub type Row = (Attr, Vec<Cell>);
pub type TableHead = (Attr, Vec<Row>);
pub type TableBody = (Attr, RowHeadColumns, Vec<Row>, Vec<Row>);
pub type TableFoot = (Attr, Vec<Row>);
pub type Cell = (Attr, Alignment, RowSpan, ColSpan, Vec<Block>);
pub type RowSpan = Int;
pub type ColSpan = Int;
pub type Target = (String, String);
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[serde(tag = "t")]
pub enum MathType {
DisplayMath,
InlineMath,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[allow(non_snake_case)]
pub struct Citation {
pub citationId: String,
pub citationPrefix: Vec<Inline>,
pub citationSuffix: Vec<Inline>,
pub citationMode: CitationMode,
pub citationNoteNum: Int,
pub citationHash: Int,
}
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[serde(tag = "t")]
pub enum CitationMode {
AuthorInText,
SuppressAuthor,
NormalCitation,
}
pub fn filter<F: FnOnce(Pandoc) -> Pandoc>(json: String, f: F) -> String {
f(Pandoc::from_json(&json)).to_json()
}