1use moduforge_transform::{node_step::AddNodeStep, step::Step};
2use moduforge_model::tree::Tree;
3use std::collections::HashMap;
4use crate::{StepResult, NodeData, MarkData, RoomSnapshot};
5use yrs::{Map, Transact, MapPrelim, ArrayPrelim, Array, TransactionMut, WriteTxn};
6
7pub trait StepConverter {
9 fn apply_to_yrs_txn(&self, step: &dyn Step, txn: &mut yrs::TransactionMut, client_id: &str) -> Result<StepResult, Box<dyn std::error::Error>>;
11
12 fn name(&self) -> &'static str;
14
15 fn supports(&self, step: &dyn Step) -> bool;
17
18 fn get_description(&self, step: &dyn Step) -> String {
20 format!("执行操作: {} ({})", step.name(), self.name())
21 }
22}
23
24pub struct DefaultStepConverter;
26
27impl StepConverter for DefaultStepConverter {
28 fn apply_to_yrs_txn(&self, step: &dyn Step, _txn: &mut yrs::TransactionMut, client_id: &str) -> Result<StepResult, Box<dyn std::error::Error>> {
29
30
31 Ok(StepResult {
32 step_id: uuid::Uuid::new_v4().to_string(),
33 step_name: step.name().to_string(),
34 description: format!("默认处理器处理了未知步骤: {}", step.name()),
35 timestamp: std::time::SystemTime::now()
36 .duration_since(std::time::UNIX_EPOCH)
37 .unwrap()
38 .as_millis() as u64,
39 client_id: client_id.to_string(),
40 })
41 }
42
43 fn name(&self) -> &'static str {
44 "DefaultStepConverter"
45 }
46
47 fn supports(&self, _step: &dyn Step) -> bool {
48 true }
50}
51
52pub struct NodeStepConverter;
54
55impl StepConverter for NodeStepConverter {
56 fn apply_to_yrs_txn(&self, step: &dyn Step, txn: &mut yrs::TransactionMut, client_id: &str) -> Result<StepResult, Box<dyn std::error::Error>> {
57 if let Some(add_step) = step.downcast_ref::<AddNodeStep>() {
58 let nodes_map = txn.get_or_insert_map("nodes");
59
60 for node_enum in &add_step.nodes {
61 let node = &node_enum.0;
62
63 let node_data_map = nodes_map.insert(txn, node.id.clone(), MapPrelim::default());
65
66 node_data_map.insert(txn, "type", node.r#type.clone());
68
69 let attrs_map = node_data_map.insert(txn, "attrs", MapPrelim::default());
71 for (key, value) in node.attrs.iter() {
72 attrs_map.insert(txn, key.clone(), value.to_string());
73 }
74
75 let content_array = node_data_map.insert(txn, "content", ArrayPrelim::default());
77 for child_id in &node.content {
78 content_array.push_back(txn, child_id.clone());
79 }
80 }
81
82 return Ok(StepResult {
83 step_id: uuid::Uuid::new_v4().to_string(),
84 step_name: step.name().to_string(),
85 description: format!("添加了 {} 个节点", add_step.nodes.len()),
86 timestamp: std::time::SystemTime::now()
87 .duration_since(std::time::UNIX_EPOCH)
88 .unwrap()
89 .as_millis() as u64,
90 client_id: client_id.to_string(),
91 });
92
93 } else if step.name().contains("RemoveNode") {
94 let _nodes_map = txn.get_or_insert_map("nodes");
95 return Ok(StepResult {
100 step_id: uuid::Uuid::new_v4().to_string(),
101 step_name: step.name().to_string(),
102 description: "模拟删除了节点".to_string(),
103 timestamp: std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_millis() as u64,
104 client_id: client_id.to_string(),
105 });
106 }
107
108 Err("不支持的节点操作".into())
109 }
110
111 fn name(&self) -> &'static str {
112 "NodeStepConverter"
113 }
114
115 fn supports(&self, step: &dyn Step) -> bool {
116 step.name().contains("add_node_step") || step.name().contains("RemoveNode")
117 }
118}
119
120pub struct AttrStepConverter;
122
123impl StepConverter for AttrStepConverter {
124 fn apply_to_yrs_txn(&self, step: &dyn Step, _txn: &mut yrs::TransactionMut, client_id: &str) -> Result<StepResult, Box<dyn std::error::Error>> {
125 let step_name = step.name();
126
127 if step_name.contains("Attr") {
128
129 Ok(StepResult {
130 step_id: uuid::Uuid::new_v4().to_string(),
131 step_name: step.name().to_string(),
132 description: format!("更新了节点属性"),
133 timestamp: std::time::SystemTime::now()
134 .duration_since(std::time::UNIX_EPOCH)
135 .unwrap()
136 .as_millis() as u64,
137 client_id: client_id.to_string(),
138 })
139 } else {
140 Err("不支持的属性操作".into())
141 }
142 }
143
144 fn name(&self) -> &'static str {
145 "AttrStepConverter"
146 }
147
148 fn supports(&self, step: &dyn Step) -> bool {
149 let step_name = step.name();
150 step_name.contains("Attr")
151 }
152}
153
154pub struct MarkStepConverter;
156
157impl StepConverter for MarkStepConverter {
158 fn apply_to_yrs_txn(&self, _step: &dyn Step, _txn: &mut yrs::TransactionMut, _client_id: &str) -> Result<StepResult, Box<dyn std::error::Error>> {
159
160 Err("不支持的标记操作".into())
161 }
162
163 fn name(&self) -> &'static str {
164 "MarkStepConverter"
165 }
166
167 fn supports(&self, step: &dyn Step) -> bool {
168 let step_name = step.name();
169 step_name.contains("Mark")
170 }
171}
172
173pub struct StepConverterRegistry {
175 converters: Vec<Box<dyn StepConverter + Send + Sync>>,
176}
177
178impl StepConverterRegistry {
179 pub fn new() -> Self {
181 let mut registry = Self {
182 converters: Vec::new(),
183 };
184
185 registry.register(Box::new(NodeStepConverter));
187 registry.register(Box::new(AttrStepConverter));
188 registry.register(Box::new(MarkStepConverter));
189 registry.register(Box::new(DefaultStepConverter)); registry
192 }
193
194 pub fn register(&mut self, converter: Box<dyn StepConverter + Send + Sync>) {
196 tracing::info!("注册Step转换器: {}", converter.name());
197 self.converters.push(converter);
198 }
199
200 pub fn find_converter(&self, step: &dyn Step) -> Option<&(dyn StepConverter + Send + Sync)> {
202 for converter in &self.converters {
203 if converter.supports(step) {
204 return Some(converter.as_ref());
205 }
206 }
207 None
208 }
209
210 pub fn list_converters(&self) -> Vec<&str> {
212 self.converters.iter().map(|c| c.name()).collect()
213 }
214}
215
216impl Default for StepConverterRegistry {
217 fn default() -> Self {
218 Self::new()
219 }
220}
221
222pub struct Mapper;
224
225impl Mapper {
226 pub fn global_registry() -> &'static StepConverterRegistry {
228 use std::sync::OnceLock;
229 static REGISTRY: OnceLock<StepConverterRegistry> = OnceLock::new();
230 REGISTRY.get_or_init(|| StepConverterRegistry::new())
231 }
232
233 pub fn tree_to_snapshot(tree: &Tree, room_id: String) -> RoomSnapshot {
235 let mut nodes = HashMap::new();
236
237 fn collect_nodes(tree: &Tree, node_id: &str, nodes: &mut HashMap<String, NodeData>) {
239 if let Some(node) = tree.get_node(&node_id.to_string()) {
240 let node_data = NodeData {
241 id: node.id.clone(),
242 node_type: node.r#type.clone(),
243 attrs: node.attrs.iter().map(|(k, v)| (k.clone(), v.clone())).collect(),
244 content: node.content.iter().cloned().collect(),
245 marks: node.marks.iter().map(|mark| MarkData {
246 mark_type: mark.r#type.clone(),
247 attrs: mark.attrs.iter().map(|(k, v)| (k.clone(), v.clone())).collect(),
248 }).collect(),
249 };
250
251 nodes.insert(node_id.to_string(), node_data);
252
253 for child_id in &node.content {
255 collect_nodes(tree, child_id, nodes);
256 }
257 }
258 }
259
260 collect_nodes(tree, &tree.root_id, &mut nodes);
261
262 RoomSnapshot {
263 room_id,
264 root_id: tree.root_id.clone(),
265 nodes,
266 version: 0, }
268 }
269
270 pub fn tree_to_yrs_doc(tree: &Tree) -> yrs::Doc {
272 let doc = yrs::Doc::new();
273 {
274 let mut txn = doc.transact_mut();
276 let nodes_map = txn.get_or_insert_map("nodes");
277
278 let mut queue = std::collections::VecDeque::new();
280 if !tree.root_id.is_empty() {
281 queue.push_back(tree.root_id.clone());
282 }
283
284 let mut visited = std::collections::HashSet::new();
285 if !tree.root_id.is_empty() {
286 visited.insert(tree.root_id.clone());
287 }
288
289 while let Some(node_id) = queue.pop_front() {
290 if let Some(node) = tree.get_node(&node_id) {
291 let node_data_map = nodes_map.insert(&mut txn, node.id.clone(), MapPrelim::default());
292
293 node_data_map.insert(&mut txn, "type", node.r#type.clone());
294
295 let attrs_map = node_data_map.insert(&mut txn, "attrs", MapPrelim::default());
296 for (key, value) in node.attrs.iter() {
297 attrs_map.insert(&mut txn, key.clone(), value.to_string());
298 }
299
300 let content_array = node_data_map.insert(&mut txn, "content", ArrayPrelim::default());
301 for child_id in &node.content {
302 content_array.push_back(&mut txn, child_id.clone());
303 if visited.insert(child_id.clone()) {
304 queue.push_back(child_id.clone());
305 }
306 }
307 }
308 }
309 }
310 doc
311 }
312}