1use serde::{Deserialize, Serialize};
2use serde_json::Value;
3use std::collections::HashMap;
4use std::path::Path;
5use std::sync::Mutex;
6
7lazy_static::lazy_static! {
8 static ref REPLAY_CONTEXT: Mutex<Option<ReplayConfig>> = Mutex::new(None);
9}
10
11#[derive(Debug, Clone, Default, Serialize, Deserialize)]
12pub struct ReplayConfig {
13 #[serde(default)]
14 pub mocked_spans: HashMap<String, Value>,
15 #[serde(default)]
16 pub mocked_span_ids: HashMap<String, Value>,
17 #[serde(default)]
18 pub block_side_effects: bool,
19}
20
21impl ReplayConfig {
22 pub fn load_from_env() -> Option<Self> {
23 if let Ok(path_str) = std::env::var("TRACE_WEFT_REPLAY_FILE") {
24 let path = Path::new(&path_str);
25 if path.exists()
26 && let Ok(content) = std::fs::read_to_string(path)
27 {
28 if let Ok(config) = serde_json::from_str::<Self>(&content) {
29 tracing::info!("Loaded TraceWeft replay config from {}", path_str);
30 return Some(config);
31 } else {
32 tracing::error!("Failed to parse TraceWeft replay config at {}", path_str);
33 }
34 }
35 }
36 None
37 }
38}
39
40pub fn init_replay(config: ReplayConfig) {
41 if let Ok(mut ctx) = REPLAY_CONTEXT.lock() {
42 *ctx = Some(config);
43 }
44}
45
46pub fn get_mocked_output(span_id: &str, span_name: &str) -> Option<Value> {
47 if let Ok(ctx) = REPLAY_CONTEXT.lock()
48 && let Some(config) = ctx.as_ref()
49 {
50 return config
51 .mocked_span_ids
52 .get(span_id)
53 .or_else(|| config.mocked_spans.get(span_name))
54 .cloned();
55 }
56 None
57}