use crate::error::ParseError;
use crate::version::QuillReference;
use crate::Diagnostic;
pub mod assemble;
pub mod edit;
pub mod emit;
pub mod fences;
pub mod frontmatter;
pub mod limits;
pub mod prescan;
pub mod sentinel;
pub use edit::EditError;
pub use frontmatter::{Frontmatter, FrontmatterItem};
#[cfg(test)]
mod tests;
#[derive(Debug)]
pub struct ParseOutput {
pub document: Document,
pub warnings: Vec<Diagnostic>,
}
#[derive(Debug, Clone, PartialEq)]
pub enum Sentinel {
Main(QuillReference),
Card(String),
}
impl Sentinel {
pub fn as_str(&self) -> String {
match self {
Sentinel::Main(r) => r.to_string(),
Sentinel::Card(t) => t.clone(),
}
}
pub fn is_main(&self) -> bool {
matches!(self, Sentinel::Main(_))
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Card {
sentinel: Sentinel,
frontmatter: Frontmatter,
body: String,
}
impl Card {
pub fn new_with_sentinel(sentinel: Sentinel, frontmatter: Frontmatter, body: String) -> Self {
Self {
sentinel,
frontmatter,
body,
}
}
pub fn sentinel(&self) -> &Sentinel {
&self.sentinel
}
pub fn tag(&self) -> String {
self.sentinel.as_str()
}
pub fn frontmatter(&self) -> &Frontmatter {
&self.frontmatter
}
pub fn frontmatter_mut(&mut self) -> &mut Frontmatter {
&mut self.frontmatter
}
pub fn body(&self) -> &str {
&self.body
}
pub fn is_main(&self) -> bool {
self.sentinel.is_main()
}
pub(crate) fn replace_sentinel(&mut self, sentinel: Sentinel) {
self.sentinel = sentinel;
}
pub(crate) fn overwrite_body(&mut self, body: String) {
self.body = body;
}
}
#[derive(Debug, Clone)]
pub struct Document {
main: Card,
cards: Vec<Card>,
warnings: Vec<Diagnostic>,
}
impl PartialEq for Document {
fn eq(&self, other: &Self) -> bool {
self.main == other.main && self.cards == other.cards
}
}
impl Document {
pub fn from_main_and_cards(main: Card, cards: Vec<Card>, warnings: Vec<Diagnostic>) -> Self {
debug_assert!(main.sentinel.is_main(), "main card must be Sentinel::Main");
debug_assert!(
cards.iter().all(|c| !c.sentinel.is_main()),
"composable cards must be Sentinel::Card"
);
Self {
main,
cards,
warnings,
}
}
pub fn from_markdown(markdown: &str) -> Result<Self, ParseError> {
assemble::decompose(markdown)
}
pub fn from_markdown_with_warnings(markdown: &str) -> Result<ParseOutput, ParseError> {
assemble::decompose_with_warnings(markdown)
.map(|(document, warnings)| ParseOutput { document, warnings })
}
pub fn main(&self) -> &Card {
&self.main
}
pub fn main_mut(&mut self) -> &mut Card {
&mut self.main
}
pub fn quill_reference(&self) -> &QuillReference {
match &self.main.sentinel {
Sentinel::Main(r) => r,
Sentinel::Card(_) => {
unreachable!("main card must carry Sentinel::Main by construction")
}
}
}
pub fn cards(&self) -> &[Card] {
&self.cards
}
pub fn cards_mut(&mut self) -> &mut [Card] {
&mut self.cards
}
pub(crate) fn cards_vec_mut(&mut self) -> &mut Vec<Card> {
&mut self.cards
}
pub fn warnings(&self) -> &[Diagnostic] {
&self.warnings
}
pub fn to_plate_json(&self) -> serde_json::Value {
let mut map = serde_json::Map::new();
map.insert(
"QUILL".to_string(),
serde_json::Value::String(self.quill_reference().to_string()),
);
for (key, value) in self.main.frontmatter.iter() {
map.insert(key.clone(), value.as_json().clone());
}
map.insert(
"BODY".to_string(),
serde_json::Value::String(self.main.body.clone()),
);
let cards_array: Vec<serde_json::Value> = self
.cards
.iter()
.map(|card| {
let mut card_map = serde_json::Map::new();
card_map.insert("CARD".to_string(), serde_json::Value::String(card.tag()));
for (key, value) in card.frontmatter.iter() {
card_map.insert(key.clone(), value.as_json().clone());
}
card_map.insert(
"BODY".to_string(),
serde_json::Value::String(card.body.clone()),
);
serde_json::Value::Object(card_map)
})
.collect();
map.insert("CARDS".to_string(), serde_json::Value::Array(cards_array));
serde_json::Value::Object(map)
}
}