1use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8use std::time::{SystemTime, UNIX_EPOCH};
9
10use crate::analytics::{AnalyticsConfig, AnalyticsEventType, AnalyticsManager, PerformanceMetrics};
11use crate::canvas_types::CanvasOgParams;
12use crate::error::{ErrorKind, MetadataError};
13use crate::themes::Theme;
14
15pub struct MetadataAnalyticsIntegration {
17 manager: AnalyticsManager,
19 operation_timers: HashMap<String, u64>,
21}
22
23#[derive(Debug, Clone, Serialize, Deserialize)]
25pub struct IntegrationConfig {
26 pub auto_track: bool,
28 pub track_metadata: bool,
30 pub track_og_images: bool,
32 pub track_themes: bool,
34 pub track_validation: bool,
36 pub track_errors: bool,
38 pub include_performance: bool,
40 pub include_user_context: bool,
42}
43
44impl Default for IntegrationConfig {
45 fn default() -> Self {
46 Self {
47 auto_track: true,
48 track_metadata: true,
49 track_og_images: true,
50 track_themes: true,
51 track_validation: true,
52 track_errors: true,
53 include_performance: true,
54 include_user_context: true,
55 }
56 }
57}
58
59impl MetadataAnalyticsIntegration {
60 pub fn new(config: AnalyticsConfig, _integration_config: IntegrationConfig) -> Self {
62 let mut manager = AnalyticsManager::new(config);
63
64 #[cfg(target_arch = "wasm32")]
66 {
67 use crate::analytics::handlers::AnalyticsHandlerFactory;
68 manager.add_handler(AnalyticsHandlerFactory::console(None, None));
69 manager.add_handler(AnalyticsHandlerFactory::local_storage(None, None, None));
70 }
71
72 #[cfg(not(target_arch = "wasm32"))]
73 {
74 use crate::analytics::handlers::AnalyticsHandlerFactory;
75 manager.add_handler(AnalyticsHandlerFactory::console(None, None));
76 manager.add_handler(AnalyticsHandlerFactory::file(None, None, None));
77 }
78
79 Self {
80 manager,
81 operation_timers: HashMap::new(),
82 }
83 }
84
85 pub fn default() -> Self {
87 let analytics_config = AnalyticsConfig::default();
88 let integration_config = IntegrationConfig::default();
89 Self::new(analytics_config, integration_config)
90 }
91
92 pub fn start_operation(&mut self, operation_id: String) -> Result<(), MetadataError> {
94 let start_time = SystemTime::now()
95 .duration_since(UNIX_EPOCH)
96 .map_err(|e| MetadataError::new(ErrorKind::Unknown, e.to_string()))?
97 .as_millis() as u64;
98
99 self.operation_timers.insert(operation_id, start_time);
100 Ok(())
101 }
102
103 pub fn end_operation(
105 &mut self,
106 operation_id: String,
107 operation_type: &str,
108 success: bool,
109 properties: HashMap<String, serde_json::Value>,
110 ) -> Result<u64, MetadataError> {
111 let end_time = SystemTime::now()
112 .duration_since(UNIX_EPOCH)
113 .map_err(|e| MetadataError::new(ErrorKind::Unknown, e.to_string()))?
114 .as_millis() as u64;
115
116 let duration_ms = if let Some(start_time) = self.operation_timers.remove(&operation_id) {
117 end_time - start_time
118 } else {
119 0
120 };
121
122 self.manager
124 .track_performance(operation_type, duration_ms, success, None)?;
125
126 let mut event_properties = properties;
128 event_properties.insert(
129 "operation_type".to_string(),
130 serde_json::Value::String(operation_type.to_string()),
131 );
132 event_properties.insert("success".to_string(), serde_json::Value::Bool(success));
133
134 self.manager.track_event(
135 AnalyticsEventType::PerformanceMeasured,
136 event_properties,
137 Some(duration_ms),
138 )?;
139
140 Ok(duration_ms)
141 }
142
143 pub fn track_metadata_generation(
145 &mut self,
146 metadata_type: &str,
147 success: bool,
148 properties: HashMap<String, serde_json::Value>,
149 ) -> Result<(), MetadataError> {
150 let mut event_properties = properties;
151 event_properties.insert(
152 "metadata_type".to_string(),
153 serde_json::Value::String(metadata_type.to_string()),
154 );
155 event_properties.insert("success".to_string(), serde_json::Value::Bool(success));
156
157 self.manager.track_event(
158 AnalyticsEventType::MetadataGenerated,
159 event_properties,
160 None,
161 )?;
162
163 Ok(())
164 }
165
166 pub fn track_og_image_generation(
168 &mut self,
169 params: &CanvasOgParams,
170 success: bool,
171 generation_time_ms: u64,
172 image_size: Option<(u32, u32)>,
173 ) -> Result<(), MetadataError> {
174 let mut properties = HashMap::new();
175 properties.insert(
176 "title".to_string(),
177 serde_json::Value::String(params.title.clone()),
178 );
179 properties.insert("success".to_string(), serde_json::Value::Bool(success));
180 properties.insert(
181 "generation_time_ms".to_string(),
182 serde_json::Value::Number(serde_json::Number::from(generation_time_ms)),
183 );
184
185 if let Some(description) = ¶ms.description {
186 properties.insert(
187 "description".to_string(),
188 serde_json::Value::String(description.clone()),
189 );
190 }
191
192 if let Some(width) = params.width {
193 properties.insert(
194 "width".to_string(),
195 serde_json::Value::Number(serde_json::Number::from(width)),
196 );
197 }
198
199 if let Some(height) = params.height {
200 properties.insert(
201 "height".to_string(),
202 serde_json::Value::Number(serde_json::Number::from(height)),
203 );
204 }
205
206 if let Some(background_color) = ¶ms.background_color {
207 properties.insert(
208 "background_color".to_string(),
209 serde_json::Value::String(background_color.clone()),
210 );
211 }
212
213 if let Some(font_family) = ¶ms.font_family {
214 properties.insert(
215 "font_family".to_string(),
216 serde_json::Value::String(font_family.clone()),
217 );
218 }
219
220 if let Some((width, height)) = image_size {
221 properties.insert(
222 "generated_width".to_string(),
223 serde_json::Value::Number(serde_json::Number::from(width)),
224 );
225 properties.insert(
226 "generated_height".to_string(),
227 serde_json::Value::Number(serde_json::Number::from(height)),
228 );
229 }
230
231 if params.text_gradient.is_some()
233 || params.text_shadow.is_some()
234 || params.text_outline.is_some()
235 {
236 properties.insert(
237 "has_theme_effects".to_string(),
238 serde_json::Value::Bool(true),
239 );
240 }
241
242 if params.layers.is_some() {
243 properties.insert(
244 "has_custom_layers".to_string(),
245 serde_json::Value::Bool(true),
246 );
247 }
248
249 self.manager.track_event(
250 AnalyticsEventType::OgImageGenerated,
251 properties,
252 Some(generation_time_ms),
253 )?;
254
255 self.manager
257 .track_performance("og_image_generation", generation_time_ms, success, None)?;
258
259 Ok(())
260 }
261
262 pub fn track_theme_application(
264 &mut self,
265 theme: &Theme,
266 success: bool,
267 application_time_ms: u64,
268 ) -> Result<(), MetadataError> {
269 let mut properties = HashMap::new();
270 properties.insert(
271 "theme_id".to_string(),
272 serde_json::Value::String(theme.id.clone()),
273 );
274 properties.insert(
275 "theme_name".to_string(),
276 serde_json::Value::String(theme.name.clone()),
277 );
278 properties.insert(
279 "theme_category".to_string(),
280 serde_json::Value::String(format!("{:?}", theme.category)),
281 );
282 properties.insert("success".to_string(), serde_json::Value::Bool(success));
283 properties.insert(
284 "application_time_ms".to_string(),
285 serde_json::Value::Number(serde_json::Number::from(application_time_ms)),
286 );
287
288 properties.insert(
290 "has_text_shadow".to_string(),
291 serde_json::Value::Bool(theme.effects.text.shadow.is_some()),
292 );
293 properties.insert(
294 "has_text_outline".to_string(),
295 serde_json::Value::Bool(theme.effects.text.outline.is_some()),
296 );
297 properties.insert(
298 "has_text_gradient".to_string(),
299 serde_json::Value::Bool(theme.effects.text.gradient.is_some()),
300 );
301 properties.insert(
302 "has_background_gradient".to_string(),
303 serde_json::Value::Bool(theme.effects.background.gradient.is_some()),
304 );
305 properties.insert(
306 "has_background_pattern".to_string(),
307 serde_json::Value::Bool(theme.effects.background.pattern.is_some()),
308 );
309 properties.insert(
310 "has_border".to_string(),
311 serde_json::Value::Bool(theme.effects.border.width > 0.0),
312 );
313
314 self.manager.track_event(
315 AnalyticsEventType::ThemeApplied,
316 properties,
317 Some(application_time_ms),
318 )?;
319
320 Ok(())
321 }
322
323 pub fn track_metadata_validation(
325 &mut self,
326 metadata_type: &str,
327 is_valid: bool,
328 validation_time_ms: u64,
329 errors: Vec<String>,
330 ) -> Result<(), MetadataError> {
331 let mut properties = HashMap::new();
332 properties.insert(
333 "metadata_type".to_string(),
334 serde_json::Value::String(metadata_type.to_string()),
335 );
336 properties.insert("is_valid".to_string(), serde_json::Value::Bool(is_valid));
337 properties.insert(
338 "validation_time_ms".to_string(),
339 serde_json::Value::Number(serde_json::Number::from(validation_time_ms)),
340 );
341 properties.insert(
342 "error_count".to_string(),
343 serde_json::Value::Number(serde_json::Number::from(errors.len())),
344 );
345
346 if !errors.is_empty() {
347 properties.insert(
348 "errors".to_string(),
349 serde_json::Value::Array(
350 errors
351 .into_iter()
352 .map(|e| serde_json::Value::String(e))
353 .collect(),
354 ),
355 );
356 }
357
358 self.manager.track_event(
359 AnalyticsEventType::MetadataValidated,
360 properties,
361 Some(validation_time_ms),
362 )?;
363
364 Ok(())
365 }
366
367 pub fn track_error(
369 &mut self,
370 error: MetadataError,
371 context: HashMap<String, serde_json::Value>,
372 ) -> Result<(), MetadataError> {
373 self.manager.track_error(error, context)?;
374 Ok(())
375 }
376
377 pub fn track_custom_event(
379 &mut self,
380 event_name: &str,
381 properties: HashMap<String, serde_json::Value>,
382 duration_ms: Option<u64>,
383 ) -> Result<(), MetadataError> {
384 self.manager.track_event(
385 AnalyticsEventType::Custom(event_name.to_string()),
386 properties,
387 duration_ms,
388 )?;
389
390 Ok(())
391 }
392
393 pub fn get_performance_metrics(&self) -> &PerformanceMetrics {
395 self.manager.get_performance_metrics()
396 }
397
398 pub fn get_config(&self) -> &AnalyticsConfig {
400 self.manager.get_config()
401 }
402
403 pub fn generate_insights(&self) -> Result<crate::analytics::AnalyticsInsights, MetadataError> {
405 self.manager.generate_insights()
406 }
407
408 pub fn get_manager(&self) -> &AnalyticsManager {
410 &self.manager
411 }
412
413 pub fn get_manager_mut(&mut self) -> &mut AnalyticsManager {
415 &mut self.manager
416 }
417
418 pub fn start_session(
420 &mut self,
421 session_id: String,
422 user_agent: Option<String>,
423 ) -> Result<(), MetadataError> {
424 self.manager.start_session(session_id, user_agent)
425 }
426
427 pub fn end_session(&mut self) -> Result<(), MetadataError> {
429 self.manager.end_session()
430 }
431}
432
433pub struct AnalyticsWrapper<T> {
435 inner: T,
437 analytics: MetadataAnalyticsIntegration,
439}
440
441impl<T> AnalyticsWrapper<T> {
442 pub fn new(inner: T, analytics: MetadataAnalyticsIntegration) -> Self {
444 Self { inner, analytics }
445 }
446
447 pub fn inner(&self) -> &T {
449 &self.inner
450 }
451
452 pub fn inner_mut(&mut self) -> &mut T {
454 &mut self.inner
455 }
456
457 pub fn analytics(&self) -> &MetadataAnalyticsIntegration {
459 &self.analytics
460 }
461
462 pub fn analytics_mut(&mut self) -> &mut MetadataAnalyticsIntegration {
464 &mut self.analytics
465 }
466
467 pub fn into_parts(self) -> (T, MetadataAnalyticsIntegration) {
469 (self.inner, self.analytics)
470 }
471}
472
473pub trait AnalyticsTrackable {
475 fn with_analytics(self, analytics: MetadataAnalyticsIntegration) -> AnalyticsWrapper<Self>
477 where
478 Self: Sized,
479 {
480 AnalyticsWrapper::new(self, analytics)
481 }
482}
483
484impl<T> AnalyticsTrackable for T {}
485
486pub struct AnalyticsContext {
488 operation_id: String,
490 analytics: MetadataAnalyticsIntegration,
492}
493
494impl AnalyticsContext {
495 pub fn new(operation_id: String, analytics: MetadataAnalyticsIntegration) -> Self {
497 Self {
498 operation_id,
499 analytics,
500 }
501 }
502
503 pub fn complete_operation(
505 mut self,
506 operation_type: &str,
507 success: bool,
508 properties: HashMap<String, serde_json::Value>,
509 ) -> Result<u64, MetadataError> {
510 self.analytics
511 .end_operation(self.operation_id, operation_type, success, properties)
512 }
513
514 pub fn complete_with_error(
516 mut self,
517 operation_type: &str,
518 error: MetadataError,
519 context: HashMap<String, serde_json::Value>,
520 ) -> Result<u64, MetadataError> {
521 self.analytics.track_error(error.clone(), context.clone())?;
523
524 self.analytics
526 .end_operation(self.operation_id, operation_type, false, context)
527 }
528
529 pub fn analytics(&self) -> &MetadataAnalyticsIntegration {
531 &self.analytics
532 }
533
534 pub fn analytics_mut(&mut self) -> &mut MetadataAnalyticsIntegration {
536 &mut self.analytics
537 }
538}
539
540#[macro_export]
542macro_rules! track_operation {
543 ($analytics:expr, $operation_type:expr, $operation:block) => {{
544 let operation_id = format!(
545 "{}_{}",
546 $operation_type,
547 std::time::SystemTime::now()
548 .duration_since(std::time::UNIX_EPOCH)
549 .unwrap()
550 .as_millis()
551 );
552 $analytics.start_operation(operation_id.clone())?;
553
554 let result = $operation;
555
556 let success = result.is_ok();
557 let properties = std::collections::HashMap::new();
558 $analytics.end_operation(operation_id, $operation_type, success, properties)?;
559
560 result
561 }};
562}