use std::collections::HashMap;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use super::tables::TableGrid;
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct DocumentStructure {
pub nodes: Vec<DocumentNode>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub source_format: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct DocumentNode {
pub id: String,
pub content: NodeContent,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub parent: Option<u32>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Vec::is_empty", default))]
pub children: Vec<u32>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Vec::is_empty", default))]
pub annotations: Vec<TextAnnotation>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub attributes: Option<HashMap<String, String>>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(tag = "node_type", rename_all = "snake_case"))]
pub enum NodeContent {
Heading {
level: u8,
text: String,
},
Paragraph {
text: String,
},
List {
ordered: bool,
},
ListItem {
text: String,
},
Table {
grid: TableGrid,
},
Image {
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
description: Option<String>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
src: Option<String>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
image_index: Option<u32>,
},
Code {
text: String,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
language: Option<String>,
},
Quote,
DefinitionList,
DefinitionItem {
term: String,
definition: String,
},
RawBlock {
format: String,
content: String,
},
MetadataBlock {
entries: Vec<(String, String)>,
},
Group {
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
label: Option<String>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
heading_level: Option<u8>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
heading_text: Option<String>,
},
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct TextAnnotation {
pub start: u32,
pub end: u32,
pub kind: AnnotationKind,
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(tag = "annotation_type", rename_all = "snake_case"))]
pub enum AnnotationKind {
#[default]
Bold,
Italic,
Underline,
Strikethrough,
Code,
Subscript,
Superscript,
Highlight,
Link {
url: String,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
title: Option<String>,
},
}
impl Default for NodeContent {
fn default() -> Self {
Self::Heading {
level: 1,
text: String::new(),
}
}
}