use serde::{Deserialize, Serialize};
pub const WDF_SCHEMA: &str = "weimo.document";
pub const WDF_VERSION: u32 = 1;
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct WdfDocument {
pub schema: String,
pub version: u32,
pub root: WdfRoot,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct WdfRoot {
#[serde(rename = "type")]
pub node_type: String,
pub content: Vec<WdfBlock>,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(tag = "type")]
pub enum WdfBlock {
#[serde(rename = "paragraph")]
Paragraph { content: Vec<WdfText> },
#[serde(rename = "heading")]
Heading {
attrs: HeadingAttrs,
content: Vec<WdfText>,
},
#[serde(rename = "blockquote")]
Blockquote { content: Vec<WdfParagraph> },
#[serde(rename = "bullet_list")]
BulletList { content: Vec<WdfListItem> },
#[serde(rename = "ordered_list")]
OrderedList {
#[serde(default)]
attrs: Option<OrderedListAttrs>,
content: Vec<WdfListItem>,
},
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct HeadingAttrs {
pub level: u8,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct OrderedListAttrs {
pub start: u32,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct WdfParagraph {
#[serde(rename = "type")]
pub node_type: String,
pub content: Vec<WdfText>,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct WdfListItem {
#[serde(rename = "type")]
pub node_type: String,
pub content: Vec<WdfParagraph>,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct WdfText {
#[serde(rename = "type")]
pub node_type: String,
pub text: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub marks: Option<Vec<WdfMark>>,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct WdfMark {
#[serde(rename = "type")]
pub mark_type: String,
}
impl WdfDocument {
pub fn empty() -> Self {
Self {
schema: WDF_SCHEMA.to_string(),
version: WDF_VERSION,
root: WdfRoot {
node_type: "doc".to_string(),
content: vec![WdfBlock::Paragraph { content: vec![] }],
},
}
}
}
impl WdfText {
pub fn plain(text: impl Into<String>) -> Self {
Self {
node_type: "text".to_string(),
text: text.into(),
marks: None,
}
}
pub fn bold(text: impl Into<String>) -> Self {
Self {
node_type: "text".to_string(),
text: text.into(),
marks: Some(vec![WdfMark {
mark_type: "bold".to_string(),
}]),
}
}
pub fn is_bold(&self) -> bool {
self.marks
.as_ref()
.is_some_and(|marks| marks.iter().any(|mark| mark.mark_type == "bold"))
}
}
pub fn paragraph(content: Vec<WdfText>) -> WdfParagraph {
WdfParagraph {
node_type: "paragraph".to_string(),
content,
}
}
pub fn list_item(content: Vec<WdfText>) -> WdfListItem {
WdfListItem {
node_type: "list_item".to_string(),
content: vec![paragraph(content)],
}
}