use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Document {
pub nodes: Vec<Node>,
}
impl Document {
pub fn new(nodes: Vec<Node>) -> Self {
Self { nodes }
}
pub fn is_empty(&self) -> bool {
self.nodes.is_empty()
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum Node {
Heading { level: u8, children: Vec<Node> },
Paragraph { children: Vec<Node> },
List {
ordered: bool,
start: Option<u64>,
items: Vec<ListItem>,
},
BlockQuote { children: Vec<Node> },
CodeBlock {
language: Option<String>,
code: String,
},
Table {
headers: Vec<Vec<Node>>,
rows: Vec<Vec<Vec<Node>>>,
},
ThematicBreak,
Text(String),
Bold { children: Vec<Node> },
Italic { children: Vec<Node> },
Strikethrough { children: Vec<Node> },
InlineCode(String),
Link {
url: String,
title: Option<String>,
children: Vec<Node>,
},
Image { src: String, alt: String },
LineBreak,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ListItem {
pub children: Vec<Node>,
}
impl ListItem {
pub fn new(children: Vec<Node>) -> Self {
Self { children }
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn document_is_empty() {
assert!(Document::new(vec![]).is_empty());
assert!(!Document::new(vec![Node::ThematicBreak]).is_empty());
}
#[test]
fn ir_roundtrip_json() {
let doc = Document::new(vec![
Node::Heading {
level: 1,
children: vec![Node::Text("Hello".into())],
},
Node::Paragraph {
children: vec![
Node::Text("Some ".into()),
Node::Bold {
children: vec![Node::Text("bold".into())],
},
Node::Text(" text".into()),
],
},
]);
let json = serde_json::to_string(&doc).unwrap();
let parsed: Document = serde_json::from_str(&json).unwrap();
assert_eq!(doc, parsed);
}
}