1use mf_model::{
2 mark::Mark, node::Node, node_definition::NodeTree, node_pool::NodePool,
3 types::NodeId,
4};
5use serde::Serialize;
6use mf_model::rpds::{HashTrieMapSync, VectorSync};
7
8#[derive(Debug, Clone, Serialize)]
10pub struct IndexDoc {
11 pub node_id: String,
12 pub node_type: String,
13 pub parent_id: Option<String>,
14 pub marks: Vec<String>,
16 pub marks_json: String,
18 pub attrs_flat: Vec<(String, String)>,
20 pub attrs_json: String,
22 pub text: Option<String>,
23 pub path: Vec<String>,
24 pub order_i64: Option<i64>,
26 pub created_at_i64: Option<i64>,
27 pub updated_at_i64: Option<i64>,
28}
29impl IndexDoc {
30 pub fn to_node(&self) -> anyhow::Result<Node> {
36 let marks: VectorSync<Mark> =
38 serde_json::from_str(&self.marks_json).unwrap_or_default();
39
40 let attrs_map: HashTrieMapSync<String, serde_json::Value> =
42 serde_json::from_str(&self.attrs_json).unwrap_or_default();
43
44 let node = Node {
45 id: self.node_id.as_str().into(),
46 r#type: self.node_type.clone(),
47 attrs: mf_model::attrs::Attrs::from(attrs_map),
48 content: VectorSync::new_sync(), marks: marks.into(),
50 };
51
52 Ok(node)
53 }
54
55 pub fn from_node(
57 pool: &NodePool,
58 node: &Node,
59 ) -> Self {
60 let parent_id = pool.parent_id(&node.id).cloned();
61
62 let marks: Vec<String> =
64 node.marks.iter().map(|m: &Mark| m.r#type.clone()).collect();
65
66 let marks_json = serde_json::to_string(&node.marks)
68 .unwrap_or_else(|_| "[]".to_string());
69
70 let mut attrs_flat = Vec::with_capacity(node.attrs.attrs.keys().len());
72 for (k, v) in node.attrs.attrs.iter() {
73 attrs_flat.push((k.clone(), flatten_value(v)));
74 }
75
76 let attrs_json = serde_json::to_string(&node.attrs.attrs)
78 .unwrap_or_else(|_| "{}".to_string());
79
80 let path: Vec<String> = pool
82 .get_node_path(&node.id)
83 .into_iter()
84 .map(|id| id.to_string())
85 .collect();
86
87 let text = extract_text(node);
88
89 let order_i64 = extract_i64(node, "order");
91 let created_at_i64 = extract_i64(node, "created_at");
92 let updated_at_i64 = extract_i64(node, "updated_at");
93
94 IndexDoc {
95 node_id: node.id.to_string(),
96 node_type: node.r#type.clone(),
97 parent_id: parent_id.map(|id| id.to_string()),
98 marks,
99 marks_json,
100 attrs_flat,
101 attrs_json,
102 text,
103 path,
104 order_i64,
105 created_at_i64,
106 updated_at_i64,
107 }
108 }
109}
110
111fn flatten_value(v: &serde_json::Value) -> String {
113 match v {
114 serde_json::Value::Null => "null".to_string(),
115 serde_json::Value::Bool(b) => b.to_string(),
116 serde_json::Value::Number(n) => n.to_string(),
117 serde_json::Value::String(s) => s.clone(),
118 _ => serde_json::to_string(v).unwrap_or_default(),
119 }
120}
121
122fn extract_text(node: &Node) -> Option<String> {
124 for key in ["text", "title", "content"] {
125 if let Some(serde_json::Value::String(s)) = node.attrs.get(key) {
126 if !s.is_empty() {
127 return Some(s.clone());
128 }
129 }
130 }
131 None
132}
133
134fn extract_i64(
136 node: &Node,
137 key: &str,
138) -> Option<i64> {
139 node.attrs.get(key).and_then(|v| match v {
140 serde_json::Value::Number(n) => n
141 .as_i64()
142 .or_else(|| n.as_u64().and_then(|u| i64::try_from(u).ok())),
143 serde_json::Value::String(s) => s.parse::<i64>().ok(),
144 _ => None,
145 })
146}
147
148pub fn collect_node_ids_from_enum(node_enum: &NodeTree) -> Vec<NodeId> {
150 let mut ids: Vec<NodeId> = vec![node_enum.0.id.clone()];
151 for child in &node_enum.1 {
152 ids.extend(collect_node_ids_from_enum(child));
153 }
154 ids
155}