1use moduforge_transform::{
2 attr_step::AttrStep,
3 mark_step::{AddMarkStep, RemoveMarkStep},
4 node_step::{AddNodeStep, RemoveNodeStep},
5 step::Step,
6};
7use moduforge_model::{mark::Mark, node::Node, tree::Tree};
8use std::{any::TypeId, collections::HashMap};
9
10use crate::{NodeData, MarkData, RoomSnapshot, StepResult};
11use serde_json::Value as JsonValue;
12use yrs::{
13 types::{array::ArrayRef, map::MapRef, Value},
14 Array, ArrayPrelim, Map, MapPrelim, TransactionMut, WriteTxn,
15};
16
17pub trait StepConverter: Send + Sync {
20 fn apply_to_yrs_txn(
22 &self,
23 step: &dyn Step,
24 txn: &mut TransactionMut,
25 ) -> Result<StepResult, Box<dyn std::error::Error>>;
26
27 fn name(&self) -> &'static str;
29
30 fn supports(
32 &self,
33 step: &dyn Step,
34 ) -> bool;
35
36 fn get_description(
38 &self,
39 step: &dyn Step,
40 ) -> String {
41 format!("Executing operation: {} ({})", step.name(), self.name())
42 }
43}
44
45pub struct DefaultStepConverter;
47
48impl StepConverter for DefaultStepConverter {
49 fn apply_to_yrs_txn(
50 &self,
51 step: &dyn Step,
52 txn: &mut TransactionMut,
53 ) -> Result<StepResult, Box<dyn std::error::Error>> {
54 Ok(StepResult {
55 step_id: uuid::Uuid::new_v4().to_string(),
56 step_name: step.name().to_string(),
57 description: format!("默认处理程序处理未知步骤: {}", step.name()),
58 timestamp: std::time::SystemTime::now()
59 .duration_since(std::time::UNIX_EPOCH)
60 .unwrap()
61 .as_millis() as u64,
62 client_id: txn
63 .origin()
64 .as_ref()
65 .map(|s| s.to_string())
66 .unwrap_or_default(),
67 })
68 }
69
70 fn name(&self) -> &'static str {
71 "DefaultStepConverter"
72 }
73
74 fn supports(
75 &self,
76 _step: &dyn Step,
77 ) -> bool {
78 true }
80}
81
82fn json_value_to_yrs_any(value: &JsonValue) -> yrs::Any {
85 match value {
86 JsonValue::Null => yrs::Any::Null,
87 JsonValue::Bool(b) => yrs::Any::Bool(*b),
88 JsonValue::Number(n) => {
89 if let Some(i) = n.as_i64() {
90 yrs::Any::BigInt(i)
91 } else if let Some(f) = n.as_f64() {
92 yrs::Any::Number(f)
93 } else {
94 yrs::Any::Null
95 }
96 },
97 JsonValue::String(s) => yrs::Any::String(s.clone().into()),
98 JsonValue::Array(arr) => {
99 let yrs_array: Vec<yrs::Any> =
100 arr.iter().map(json_value_to_yrs_any).collect();
101 yrs::Any::Array(yrs_array.into())
102 },
103 JsonValue::Object(obj) => {
104 let yrs_map: std::collections::HashMap<String, yrs::Any> = obj
105 .iter()
106 .map(|(k, v)| (k.clone(), json_value_to_yrs_any(v)))
107 .collect();
108 yrs::Any::Map(yrs_map.into())
109 },
110 }
111}
112
113fn add_mark_to_array(
115 marks_array: &ArrayRef,
116 txn: &mut TransactionMut,
117 mark: &Mark,
118) {
119 let mark_map = MapPrelim::<yrs::Any>::from([
120 ("type".to_string(), yrs::Any::String(mark.r#type.clone().into())),
121 ("attrs".to_string(), {
122 let attrs_map: std::collections::HashMap<String, yrs::Any> = mark
123 .attrs
124 .iter()
125 .map(|(k, v)| (k.clone(), json_value_to_yrs_any(v)))
126 .collect();
127 yrs::Any::Map(attrs_map.into())
128 }),
129 ]);
130 marks_array.push_back(txn, mark_map);
131}
132
133fn get_or_create_node_data_map(
135 nodes_map: &MapRef,
136 txn: &mut TransactionMut,
137 node_id: &str,
138) -> MapRef {
139 if let Some(Value::YMap(map)) = nodes_map.get(txn, node_id) {
140 map
141 } else {
142 nodes_map.insert(txn, node_id.to_string(), MapPrelim::<yrs::Any>::new())
143 }
144}
145
146fn get_or_create_node_attrs_map(
148 node_data_map: &MapRef,
149 txn: &mut TransactionMut,
150) -> MapRef {
151 if let Some(Value::YMap(map)) = node_data_map.get(txn, "attrs") {
152 map
153 } else {
154 node_data_map.insert(txn, "attrs", MapPrelim::<yrs::Any>::new())
155 }
156}
157
158fn get_or_create_marks_array(
160 node_data_map: &MapRef,
161 txn: &mut TransactionMut,
162) -> ArrayRef {
163 if let Some(Value::YArray(array)) = node_data_map.get(txn, "marks") {
164 array
165 } else {
166 node_data_map.insert(
167 txn,
168 "marks",
169 ArrayPrelim::from(Vec::<yrs::Any>::new()),
170 )
171 }
172}
173
174pub struct NodeStepConverter;
176
177impl NodeStepConverter {
178 fn insert_node_data(
179 &self,
180 txn: &mut TransactionMut,
181 nodes_map: &MapRef,
182 node: &Node,
183 ) {
184 let node_data_map =
185 get_or_create_node_data_map(nodes_map, txn, &node.id);
186 node_data_map.insert(txn, "type", node.r#type.clone());
188 let attrs_map =
190 node_data_map.insert(txn, "attrs", MapPrelim::<yrs::Any>::new());
191 for (key, value) in node.attrs.iter() {
192 attrs_map.insert(txn, key.clone(), json_value_to_yrs_any(value));
193 }
194 let content_array = node_data_map.insert(
196 txn,
197 "content",
198 ArrayPrelim::from(Vec::<yrs::Any>::new()),
199 );
200 for child_id in &node.content {
201 content_array
202 .push_back(txn, yrs::Any::String(child_id.clone().into()));
203 }
204 let marks_array = node_data_map.insert(
206 txn,
207 "marks",
208 ArrayPrelim::from(Vec::<yrs::Any>::new()),
209 );
210 for mark in &node.marks {
211 add_mark_to_array(&marks_array, txn, mark);
212 }
213 }
214}
215
216impl StepConverter for NodeStepConverter {
217 fn apply_to_yrs_txn(
218 &self,
219 step: &dyn Step,
220 txn: &mut TransactionMut,
221 ) -> Result<StepResult, Box<dyn std::error::Error>> {
222 let client_id =
223 txn.origin().as_ref().map(|s| s.to_string()).unwrap_or_default();
224
225 if let Some(add_step) = step.downcast_ref::<AddNodeStep>() {
226 let nodes_map = txn.get_or_insert_map("nodes");
227 let all_nodes = &add_step.nodes;
228
229 for node_enum in all_nodes {
230 self.insert_node_data(txn, &nodes_map, &node_enum.0);
231 }
232
233 return Ok(StepResult {
234 step_id: uuid::Uuid::new_v4().to_string(),
235 step_name: step.name().to_string(),
236 description: format!("添加 {} 个节点", all_nodes.len()),
237 timestamp: std::time::SystemTime::now()
238 .duration_since(std::time::UNIX_EPOCH)
239 .unwrap()
240 .as_millis() as u64,
241 client_id,
242 });
243 } else if let Some(remove_step) = step.downcast_ref::<RemoveNodeStep>()
244 {
245 let nodes_map = txn.get_or_insert_map("nodes");
246 for node_id in &remove_step.node_ids {
247 nodes_map.remove(txn, &node_id.to_string());
248 }
249
250 return Ok(StepResult {
251 step_id: uuid::Uuid::new_v4().to_string(),
252 step_name: step.name().to_string(),
253 description: format!(
254 "Removed {} nodes",
255 remove_step.node_ids.len()
256 ),
257 timestamp: std::time::SystemTime::now()
258 .duration_since(std::time::UNIX_EPOCH)
259 .unwrap()
260 .as_millis() as u64,
261 client_id,
262 });
263 }
264
265 Err("不支持的节点操作".into())
266 }
267
268 fn name(&self) -> &'static str {
269 "NodeStepConverter"
270 }
271
272 fn supports(
273 &self,
274 step: &dyn Step,
275 ) -> bool {
276 step.type_id() == TypeId::of::<AddNodeStep>()
277 || step.type_id() == TypeId::of::<RemoveNodeStep>()
278 }
279}
280
281pub struct AttrStepConverter;
283
284impl StepConverter for AttrStepConverter {
285 fn apply_to_yrs_txn(
286 &self,
287 step: &dyn Step,
288 txn: &mut TransactionMut,
289 ) -> Result<StepResult, Box<dyn std::error::Error>> {
290 let client_id =
291 txn.origin().as_ref().map(|s| s.to_string()).unwrap_or_default();
292 if let Some(attr_step) = step.downcast_ref::<AttrStep>() {
293 let nodes_map = txn.get_or_insert_map("nodes");
294 let node_data_map =
295 get_or_create_node_data_map(&nodes_map, txn, &attr_step.id);
296 let attrs_map = get_or_create_node_attrs_map(&node_data_map, txn);
298 for (key, value) in attr_step.values.iter() {
300 attrs_map.insert(
301 txn,
302 key.clone(),
303 json_value_to_yrs_any(value),
304 );
305 }
306
307 Ok(StepResult {
308 step_id: uuid::Uuid::new_v4().to_string(),
309 step_name: step.name().to_string(),
310 description: format!(
311 "更新 {} 个属性 for node {}",
312 attr_step.values.len(),
313 attr_step.id
314 ),
315 timestamp: std::time::SystemTime::now()
316 .duration_since(std::time::UNIX_EPOCH)
317 .unwrap()
318 .as_millis() as u64,
319 client_id,
320 })
321 } else {
322 Err("不支持的属性操作".into())
323 }
324 }
325
326 fn name(&self) -> &'static str {
327 "AttrStepConverter"
328 }
329
330 fn supports(
331 &self,
332 step: &dyn Step,
333 ) -> bool {
334 step.type_id() == TypeId::of::<AttrStep>()
335 }
336}
337
338pub struct MarkStepConverter;
340
341impl MarkStepConverter {
342 fn remove_mark_from_array(
344 &self,
345 marks_array: &ArrayRef,
346 txn: &mut TransactionMut,
347 mark_type_to_remove: &str,
348 ) {
349 let len = marks_array.len(txn);
350 for i in (0..len).rev() {
351 if let Some(Value::YMap(mark_map)) = marks_array.get(txn, i) {
352 if let Some(mark_type_value) = mark_map.get(txn, "type") {
353 let mark_type = match mark_type_value {
354 Value::YText(_text_ref) => {
355 continue;
357 },
358 Value::Any(any) => any.to_string(),
359 _ => continue,
360 };
361 if mark_type == mark_type_to_remove {
362 marks_array.remove(txn, i);
363 return;
364 }
365 }
366 }
367 }
368 }
369}
370
371impl StepConverter for MarkStepConverter {
372 fn apply_to_yrs_txn(
373 &self,
374 step: &dyn Step,
375 txn: &mut TransactionMut,
376 ) -> Result<StepResult, Box<dyn std::error::Error>> {
377 let client_id =
378 txn.origin().as_ref().map(|s| s.to_string()).unwrap_or_default();
379
380 if let Some(add_mark_step) = step.downcast_ref::<AddMarkStep>() {
381 let nodes_map = txn.get_or_insert_map("nodes");
382 let node_data_map =
384 get_or_create_node_data_map(&nodes_map, txn, &add_mark_step.id);
385 let marks_array = get_or_create_marks_array(&node_data_map, txn);
387 for mark in &add_mark_step.marks {
389 add_mark_to_array(&marks_array, txn, mark);
390 }
391
392 return Ok(StepResult {
393 step_id: uuid::Uuid::new_v4().to_string(),
394 step_name: step.name().to_string(),
395 description: format!(
396 "添加 {} 个标记 to node {}",
397 add_mark_step.marks.len(),
398 add_mark_step.id
399 ),
400 timestamp: std::time::SystemTime::now()
401 .duration_since(std::time::UNIX_EPOCH)
402 .unwrap()
403 .as_millis() as u64,
404 client_id,
405 });
406 } else if let Some(remove_mark_step) =
407 step.downcast_ref::<RemoveMarkStep>()
408 {
409 let nodes_map = txn.get_or_insert_map("nodes");
410 let node_data_map = get_or_create_node_data_map(
411 &nodes_map,
412 txn,
413 &remove_mark_step.id,
414 );
415 let marks_array = get_or_create_marks_array(&node_data_map, txn);
416 for mark_type in &remove_mark_step.mark_types {
418 self.remove_mark_from_array(&marks_array, txn, mark_type);
419 }
420
421 return Ok(StepResult {
422 step_id: uuid::Uuid::new_v4().to_string(),
423 step_name: step.name().to_string(),
424 description: format!(
425 "Removed {} marks from node {}",
426 remove_mark_step.mark_types.len(),
427 remove_mark_step.id
428 ),
429 timestamp: std::time::SystemTime::now()
430 .duration_since(std::time::UNIX_EPOCH)
431 .unwrap()
432 .as_millis() as u64,
433 client_id,
434 });
435 }
436
437 Err("不支持的标记操作".into())
438 }
439
440 fn name(&self) -> &'static str {
441 "MarkStepConverter"
442 }
443
444 fn supports(
445 &self,
446 step: &dyn Step,
447 ) -> bool {
448 step.type_id() == TypeId::of::<AddMarkStep>()
449 || step.type_id() == TypeId::of::<RemoveMarkStep>()
450 }
451}
452
453pub struct StepConverterRegistry {
455 converters: Vec<Box<dyn StepConverter>>,
456}
457
458impl Default for StepConverterRegistry {
459 fn default() -> Self {
460 Self::new()
461 }
462}
463
464impl StepConverterRegistry {
465 pub fn new() -> Self {
467 let mut registry = Self { converters: Vec::new() };
468
469 registry.register(Box::new(NodeStepConverter));
470 registry.register(Box::new(AttrStepConverter));
471 registry.register(Box::new(MarkStepConverter));
472 registry.register(Box::new(DefaultStepConverter));
473
474 registry
475 }
476
477 pub fn register(
479 &mut self,
480 converter: Box<dyn StepConverter>,
481 ) {
482 tracing::info!("🔄 注册步骤转换器: {}", converter.name());
483 self.converters.push(converter);
484 }
485
486 pub fn find_converter(
488 &self,
489 step: &dyn Step,
490 ) -> Option<&(dyn StepConverter)> {
491 for converter in &self.converters {
492 if converter.supports(step) {
493 return Some(converter.as_ref());
494 }
495 }
496 None
497 }
498}
499
500#[derive(Debug)]
502pub struct Mapper;
503
504impl Mapper {
505 pub fn global_registry() -> &'static StepConverterRegistry {
507 use std::sync::OnceLock;
508 static REGISTRY: OnceLock<StepConverterRegistry> = OnceLock::new();
509 REGISTRY.get_or_init(StepConverterRegistry::new)
510 }
511
512 pub fn tree_to_snapshot(
514 tree: &Tree,
515 room_id: String,
516 ) -> RoomSnapshot {
517 let mut nodes = HashMap::new();
518
519 fn collect_nodes(
520 tree: &Tree,
521 node_id: &str,
522 nodes: &mut HashMap<String, NodeData>,
523 ) {
524 if let Some(node) = tree.get_node(&node_id.to_string()) {
525 let node_data = NodeData {
526 id: node.id.clone(),
527 node_type: node.r#type.clone(),
528 attrs: node
529 .attrs
530 .iter()
531 .map(|(k, v)| (k.clone(), v.clone()))
532 .collect(),
533 content: node.content.iter().cloned().collect(),
534 marks: node
535 .marks
536 .iter()
537 .map(|mark| MarkData {
538 mark_type: mark.r#type.clone(),
539 attrs: mark
540 .attrs
541 .iter()
542 .map(|(k, v)| (k.clone(), v.clone()))
543 .collect(),
544 })
545 .collect(),
546 };
547
548 nodes.insert(node_id.to_string(), node_data);
549
550 for child_id in &node.content {
551 collect_nodes(tree, child_id, nodes);
552 }
553 }
554 }
555
556 collect_nodes(tree, &tree.root_id, &mut nodes);
557
558 RoomSnapshot {
559 room_id,
560 root_id: tree.root_id.clone(),
561 nodes,
562 version: 0,
563 }
564 }
565}