katana_markdown_engine/metadata/
resolver.rs1use super::types::{
2 ConflictedTarget, MetadataDocument, MetadataEntry, MetadataReconcileRequest,
3 MetadataReconcileResult, TargetResolution, TargetResolutionKind, UnresolvedTarget,
4};
5use crate::{KmeDocument, KmeNode, TextFingerprint};
6
7pub struct MetadataResolver;
8
9impl MetadataResolver {
10 pub fn new() -> Self {
11 Self
12 }
13
14 pub fn reconcile(
15 &self,
16 old_document: &KmeDocument,
17 new_document: &KmeDocument,
18 metadata: &MetadataDocument,
19 ) -> Vec<TargetResolution> {
20 metadata
21 .entries
22 .iter()
23 .map(|entry| self.resolve_entry(old_document, new_document, entry))
24 .collect()
25 }
26
27 pub fn reconcile_request(&self, request: MetadataReconcileRequest) -> MetadataReconcileResult {
28 let resolutions = self.reconcile(
29 &request.old_document,
30 &request.new_document,
31 &request.metadata,
32 );
33 MetadataReconcileResult {
34 metadata: request.metadata,
35 resolutions,
36 }
37 }
38
39 fn resolve_entry(
40 &self,
41 old_document: &KmeDocument,
42 new_document: &KmeDocument,
43 entry: &MetadataEntry,
44 ) -> TargetResolution {
45 if let Some(node) = new_document.node_by_id(&entry.target.node_id) {
46 return Self::resolved(entry, node);
47 }
48 let candidates = self.find_by_fingerprint(new_document, &entry.target.text_fingerprint);
49 match candidates.as_slice() {
50 [node] => return Self::moved(entry, node),
51 [_, _, ..] => return Self::conflict(entry, &candidates),
52 [] => {}
53 }
54 Self::unresolved(old_document, entry)
55 }
56
57 fn find_by_fingerprint<'a>(
58 &self,
59 document: &'a KmeDocument,
60 fingerprint: &TextFingerprint,
61 ) -> Vec<&'a KmeNode> {
62 document
63 .nodes
64 .iter()
65 .filter(|node| &node.source.raw.fingerprint() == fingerprint)
66 .collect()
67 }
68
69 fn resolved(entry: &MetadataEntry, node: &KmeNode) -> TargetResolution {
70 TargetResolution {
71 key: entry.key.clone(),
72 kind: TargetResolutionKind::Resolved {
73 node_id: node.id.clone(),
74 },
75 }
76 }
77
78 fn moved(entry: &MetadataEntry, node: &KmeNode) -> TargetResolution {
79 TargetResolution {
80 key: entry.key.clone(),
81 kind: TargetResolutionKind::Moved {
82 previous_node_id: entry.target.node_id.clone(),
83 node_id: node.id.clone(),
84 },
85 }
86 }
87
88 fn conflict(entry: &MetadataEntry, nodes: &[&KmeNode]) -> TargetResolution {
89 TargetResolution {
90 key: entry.key.clone(),
91 kind: TargetResolutionKind::Conflict(ConflictedTarget {
92 previous_node_id: entry.target.node_id.clone(),
93 candidate_node_ids: nodes.iter().map(|node| node.id.clone()).collect(),
94 reason: "multiple fingerprint matches".to_string(),
95 }),
96 }
97 }
98
99 fn unresolved(old_document: &KmeDocument, entry: &MetadataEntry) -> TargetResolution {
100 let old_exists = old_document.node_by_id(&entry.target.node_id).is_some();
101 let reason = if old_exists {
102 "target disappeared"
103 } else {
104 "target unknown"
105 };
106 TargetResolution {
107 key: entry.key.clone(),
108 kind: TargetResolutionKind::Unresolved(UnresolvedTarget {
109 node_id: entry.target.node_id.clone(),
110 reason: reason.to_string(),
111 }),
112 }
113 }
114}
115
116impl Default for MetadataResolver {
117 fn default() -> Self {
118 Self::new()
119 }
120}