1use katana_markdown_model::{KmmDocument, KmmError, KmmNode, KmmNodeId, KmmNodeKind, SourceSpan};
2use serde::{Deserialize, Serialize};
3use std::path::PathBuf;
4use thiserror::Error;
5
6#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
7pub struct DocumentId(pub String);
8
9#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
10pub struct SourceUri(pub String);
11
12#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
13pub struct SourceRevision(pub String);
14
15#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
16pub enum SourceKind {
17 Markdown,
18 Image,
19 Pdf,
20 Binary,
21}
22
23#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
24pub enum DocumentKind {
25 Markdown,
26 Image,
27 Pdf,
28 Office,
29 Unknown,
30}
31
32#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
33pub struct DocumentSource {
34 pub uri: SourceUri,
35 pub kind: SourceKind,
36 pub revision: SourceRevision,
37 pub content: String,
38}
39
40#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
41pub struct DocumentMetadataView {
42 pub unresolved_count: usize,
43 pub diagnostic_keys: Vec<String>,
44}
45
46#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
47pub struct DocumentOutline {
48 pub items: Vec<DocumentOutlineItem>,
49}
50
51#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
52pub struct DocumentOutlineItem {
53 pub node_id: KmmNodeId,
54 pub level: u8,
55 pub text: String,
56 pub source: SourceSpan,
57}
58
59#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
60pub struct DocumentSnapshot {
61 pub id: DocumentId,
62 pub kind: DocumentKind,
63 pub source_uri: SourceUri,
64 pub revision: SourceRevision,
65 pub source_path: PathBuf,
66 pub document: KmmDocument,
67 pub outline: DocumentOutline,
68 pub metadata: DocumentMetadataView,
69}
70
71#[derive(Debug, Error)]
72pub enum DocumentModelError {
73 #[error("KMM parse failed: {0}")]
74 KmmParse(#[from] KmmError),
75}
76
77pub struct DocumentSnapshotFactory;
78
79impl DocumentSnapshotFactory {
80 pub fn from_kmm(source: DocumentSource, document: KmmDocument) -> DocumentSnapshot {
81 let outline = DocumentOutlineBuilder::build(&document);
82 DocumentSnapshot {
83 id: DocumentId(document.fingerprint.value.clone()),
84 kind: DocumentKind::Markdown,
85 source_uri: source.uri,
86 revision: source.revision,
87 source_path: document.path.clone(),
88 document,
89 outline,
90 metadata: DocumentMetadataView {
91 unresolved_count: 0,
92 diagnostic_keys: Vec::new(),
93 },
94 }
95 }
96
97 pub fn from_parse_result(
98 source: DocumentSource,
99 result: Result<KmmDocument, KmmError>,
100 ) -> Result<DocumentSnapshot, DocumentModelError> {
101 Ok(Self::from_kmm(source, result?))
102 }
103}
104
105struct DocumentOutlineBuilder;
106
107impl DocumentOutlineBuilder {
108 fn build(document: &KmmDocument) -> DocumentOutline {
109 let mut items = Vec::new();
110 for node in &document.nodes {
111 Self::collect_node(node, &mut items);
112 }
113 DocumentOutline { items }
114 }
115
116 fn collect_node(node: &KmmNode, items: &mut Vec<DocumentOutlineItem>) {
117 if let KmmNodeKind::Heading(heading) = &node.kind {
118 items.push(DocumentOutlineItem {
119 node_id: node.id.clone(),
120 level: heading.level,
121 text: heading.text.clone(),
122 source: node.source.clone(),
123 });
124 }
125 for child in &node.children {
126 Self::collect_node(child, items);
127 }
128 }
129}
130
131#[cfg(test)]
132#[path = "document_tests.rs"]
133mod tests;