cuenv_events/
metadata.rs

1//! Metadata management for cuenv events.
2//!
3//! Provides correlation ID tracking and metadata context for event emission.
4
5use std::sync::OnceLock;
6use uuid::Uuid;
7
8/// Global correlation ID for the current session.
9static CORRELATION_ID: OnceLock<Uuid> = OnceLock::new();
10
11/// Get or create a correlation ID for the current session.
12///
13/// This returns the same ID throughout the lifetime of the process,
14/// allowing all events to be correlated together.
15#[must_use]
16pub fn correlation_id() -> Uuid {
17    *CORRELATION_ID.get_or_init(Uuid::new_v4)
18}
19
20/// Set the correlation ID for the current session.
21///
22/// This can only be called once; subsequent calls will be ignored.
23/// Returns `true` if the ID was set, `false` if it was already set.
24pub fn set_correlation_id(id: Uuid) -> bool {
25    CORRELATION_ID.set(id).is_ok()
26}
27
28/// Metadata context for event emission.
29///
30/// Holds configuration and state used when creating events.
31#[derive(Debug, Clone)]
32pub struct MetadataContext {
33    /// The correlation ID for this context.
34    pub correlation_id: Uuid,
35    /// Optional default target for events.
36    pub default_target: Option<String>,
37}
38
39impl MetadataContext {
40    /// Create a new metadata context with the global correlation ID.
41    #[must_use]
42    pub fn new() -> Self {
43        Self {
44            correlation_id: correlation_id(),
45            default_target: None,
46        }
47    }
48
49    /// Create a new metadata context with a specific correlation ID.
50    #[must_use]
51    pub const fn with_correlation_id(id: Uuid) -> Self {
52        Self {
53            correlation_id: id,
54            default_target: None,
55        }
56    }
57
58    /// Set a default target for events created with this context.
59    #[must_use]
60    pub fn with_target(mut self, target: impl Into<String>) -> Self {
61        self.default_target = Some(target.into());
62        self
63    }
64}
65
66impl Default for MetadataContext {
67    fn default() -> Self {
68        Self::new()
69    }
70}
71
72#[cfg(test)]
73mod tests {
74    use super::*;
75
76    #[test]
77    fn test_correlation_id_consistency() {
78        let id1 = correlation_id();
79        let id2 = correlation_id();
80        assert_eq!(id1, id2);
81    }
82
83    #[test]
84    fn test_metadata_context_creation() {
85        let ctx = MetadataContext::new();
86        assert!(!ctx.correlation_id.is_nil());
87        assert!(ctx.default_target.is_none());
88    }
89
90    #[test]
91    fn test_metadata_context_with_target() {
92        let ctx = MetadataContext::new().with_target("cuenv::test");
93        assert_eq!(ctx.default_target, Some("cuenv::test".to_string()));
94    }
95
96    #[test]
97    fn test_metadata_context_default() {
98        let ctx = MetadataContext::default();
99        assert!(!ctx.correlation_id.is_nil());
100        assert!(ctx.default_target.is_none());
101    }
102
103    #[test]
104    fn test_metadata_context_with_correlation_id() {
105        let id = Uuid::new_v4();
106        let ctx = MetadataContext::with_correlation_id(id);
107        assert_eq!(ctx.correlation_id, id);
108        assert!(ctx.default_target.is_none());
109    }
110
111    #[test]
112    fn test_metadata_context_debug() {
113        let ctx = MetadataContext::new();
114        let debug = format!("{:?}", ctx);
115        assert!(debug.contains("MetadataContext"));
116        assert!(debug.contains("correlation_id"));
117    }
118
119    #[test]
120    fn test_metadata_context_clone() {
121        let ctx = MetadataContext::new().with_target("test");
122        let cloned = ctx.clone();
123        assert_eq!(ctx.correlation_id, cloned.correlation_id);
124        assert_eq!(ctx.default_target, cloned.default_target);
125    }
126
127    #[test]
128    fn test_metadata_context_with_string_target() {
129        let ctx = MetadataContext::new().with_target(String::from("owned-target"));
130        assert_eq!(ctx.default_target, Some("owned-target".to_string()));
131    }
132
133    #[test]
134    fn test_set_correlation_id_after_init() {
135        // After correlation_id() has been called, set_correlation_id should return false
136        let _ = correlation_id(); // Ensure it's initialized
137        let new_id = Uuid::new_v4();
138        let result = set_correlation_id(new_id);
139        // Since we've already called correlation_id(), this should return false
140        assert!(!result);
141    }
142}