mf_collab_client/
mapping.rs1use yrs::{Transact, ReadTxn, Map};
4
5pub use crate::mapping_v2::{
7 TypedStepConverter,
9 ConversionContext,
10 ErasedConverter,
11
12 StaticConverterRegistry,
14 global_registry,
15 convert_step_global as convert_step,
16 register_global_converter,
17 get_global_performance_stats,
18 PerformanceStats,
19 TypeConversionStats,
20
21 ConversionError,
23 ConversionResult,
24
25 SimpleNodeAddConverter,
27 SimpleNodeRemoveConverter,
28 SimpleAttrConverter,
29 SimpleMarkAddConverter,
30 SimpleMarkRemoveConverter,
31};
32
33pub fn convert_steps_batch(
35 steps: &[&dyn mf_transform::step::Step],
36 txn: &mut yrs::TransactionMut,
37 context: &ConversionContext,
38) -> Vec<ConversionResult<crate::types::StepResult>> {
39 match global_registry().read() {
40 Ok(registry) => registry.convert_steps_batch(steps, txn, context),
41 Err(e) => {
42 tracing::error!("无法获取全局注册表读锁进行批量转换: {}", e);
44 steps
45 .iter()
46 .map(|_| {
47 Err(ConversionError::Custom {
48 message: format!("全局注册表锁获取失败: {e}"),
49 })
50 })
51 .collect()
52 },
53 }
54}
55
56pub fn create_context(
58 client_id: String,
59 user_id: String,
60) -> ConversionContext {
61 ConversionContext::new(client_id, user_id)
62}
63
64pub fn register_converter<T, C>()
66where
67 T: mf_transform::step::Step + 'static,
68 C: TypedStepConverter<T> + Default + 'static,
69{
70 register_global_converter::<T, C>();
71}
72
73#[derive(Debug)]
75pub struct Mapper;
76
77impl Mapper {
78 pub fn global_registry()
80 -> &'static std::sync::RwLock<StaticConverterRegistry> {
81 global_registry()
82 }
83
84 pub fn get_yrs_doc_version(doc: &yrs::Doc) -> u64 {
86 let txn = doc.transact();
87 txn.state_vector().len() as u64
88 }
89
90 pub fn is_yrs_doc_empty(doc: &yrs::Doc) -> bool {
92 let txn = doc.transact();
93 let nodes_map = txn.get_map("nodes");
94 nodes_map.is_none_or(|map| map.len(&txn) == 0)
95 }
96
97 pub fn get_performance_stats() -> String {
99 match global_registry().read() {
100 Ok(registry) => {
101 let stats = registry.get_performance_stats();
102 format!(
103 "性能统计:\n\
104 - 总转换次数: {}\n\
105 - 成功率: {:.2}%\n\
106 - 运行时间: {:?}",
107 stats.get_total_conversions(),
108 stats.get_success_rate() * 100.0,
109 stats.get_uptime()
110 )
111 },
112 Err(e) => {
113 tracing::error!("无法获取全局注册表读锁以获取性能统计: {}", e);
114 format!("性能统计: 无法获取(锁错误: {e})")
115 },
116 }
117 }
118
119 pub fn converter_count() -> usize {
121 match global_registry().read() {
122 Ok(registry) => registry.converter_count(),
123 Err(e) => {
124 tracing::error!(
125 "无法获取全局注册表读锁以获取转换器数量: {}",
126 e
127 );
128 0 },
130 }
131 }
132}
133
134#[cfg(test)]
135mod tests {
136 use super::*;
137 use mf_transform::attr_step::AttrStep;
138 use std::time::Instant;
139 use rpds::HashTrieMapSync;
140
141 #[test]
142 fn test_api_simplicity() {
143 let context =
144 create_context("test_client".to_string(), "test_user".to_string());
145
146 assert_eq!(context.client_id, "test_client");
147 assert_eq!(context.user_id, "test_user");
148 }
149
150 #[test]
151 fn test_registry_access() {
152 let registry = Mapper::global_registry();
153 assert!(registry.read().is_ok());
154 }
155
156 #[tokio::test]
157 async fn test_performance_tracking() {
158 let start = Instant::now();
159
160 let doc = yrs::Doc::new();
161 let mut txn = doc.transact_mut();
162
163 let context =
164 create_context("perf_client".to_string(), "perf_user".to_string());
165
166 let mut attrs = HashTrieMapSync::new_sync();
167 attrs.insert("test_attr".to_string(), serde_json::json!("test_value"));
168
169 let step = AttrStep { id: "test_node".into(), values: attrs };
170
171 let result = convert_step(&step, &mut txn, &context);
172 let duration = start.elapsed();
173
174 println!("静态分发转换耗时: {duration:?}");
175
176 match result {
178 Ok(step_result) => {
179 assert_eq!(step_result.step_name, "attr_step");
180 assert!(!step_result.step_id.is_empty());
181 },
182 Err(e) => {
183 println!("转换失败(测试环境预期): {e}");
184 },
185 }
186 }
187
188 #[test]
189 fn test_performance_stats() {
190 let stats_str = Mapper::get_performance_stats();
191 assert!(!stats_str.is_empty());
192 assert!(stats_str.contains("性能统计"));
193 println!("{stats_str}");
194 }
195}