Skip to main content

katana_document_viewer/
document.rs

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;