Skip to main content

aura_effects/
context.rs

1//! Context propagation handlers
2//!
3//! This module provides stateless helpers for creating `EffectContext` values.
4//! It intentionally defers to the canonical context type in `aura-core` to
5//! avoid introducing alternate identity models in Layer 3.
6//!
7//! # Key Characteristics
8//!
9//! - **Stateless**: Context is passed explicitly, no ambient state
10//! - **Single-party**: Context for one operation at a time
11//! - **Context-free**: No assumptions about execution environment
12//!
13//! # Usage
14//!
15//! ```rust,ignore
16//! use aura_effects::context::StandardContextHandler;
17//! use aura_core::{AuthorityId, ContextId, ExecutionMode};
18//!
19//! let context = StandardContextHandler::new()
20//!     .create_effect_context(authority_id, context_id, ExecutionMode::Production);
21//! ```
22
23use aura_core::effects::ExecutionMode;
24use aura_core::types::identifiers::{AuthorityId, ContextId};
25use std::collections::HashMap;
26
27// Re-export EffectContext from aura_core for convenience
28pub use aura_core::context::EffectContext;
29
30/// Standard context handler
31///
32/// Provides utilities for creating and managing execution contexts.
33/// This is a stateless handler that follows Layer 3 principles.
34#[derive(Debug, Clone)]
35pub struct StandardContextHandler;
36
37impl StandardContextHandler {
38    /// Create a new context handler
39    pub fn new() -> Self {
40        Self
41    }
42
43    /// Create an effect context.
44    pub fn create_effect_context(
45        &self,
46        authority_id: AuthorityId,
47        context_id: ContextId,
48        execution_mode: ExecutionMode,
49    ) -> EffectContext {
50        EffectContext::new(authority_id, context_id, execution_mode)
51    }
52
53    /// Convenience for creating a context with only an authority.
54    pub fn create_effect_context_for_authority(&self, authority_id: AuthorityId) -> EffectContext {
55        EffectContext::with_authority(authority_id)
56    }
57
58    /// Validate context for required metadata fields.
59    pub fn validate_context(&self, context: &EffectContext, required_fields: &[&str]) -> bool {
60        required_fields
61            .iter()
62            .all(|field| context.get_metadata(field).is_some())
63    }
64
65    /// Merge metadata from multiple contexts.
66    pub fn merge_metadata(&self, contexts: &[&EffectContext]) -> HashMap<String, String> {
67        let mut merged = HashMap::new();
68        for context in contexts {
69            for (key, value) in context.metadata() {
70                merged.insert(key.clone(), value.clone());
71            }
72        }
73        merged
74    }
75}
76
77impl Default for StandardContextHandler {
78    fn default() -> Self {
79        Self::new()
80    }
81}
82
83#[cfg(test)]
84mod tests {
85    use super::*;
86    use aura_core::types::identifiers::{AuthorityId, ContextId};
87
88    #[test]
89    fn test_effect_context_creation() {
90        let authority_id = AuthorityId::new_from_entropy([1u8; 32]);
91        let context_id = ContextId::new_from_entropy([2u8; 32]);
92
93        let context = EffectContext::new(authority_id, context_id, ExecutionMode::Testing);
94
95        assert_eq!(context.authority_id(), authority_id);
96        assert_eq!(context.context_id(), context_id);
97    }
98
99    #[test]
100    fn test_metadata_helpers() {
101        let authority_id = AuthorityId::new_from_entropy([3u8; 32]);
102        let context_id = ContextId::new_from_entropy([4u8; 32]);
103
104        let mut context = EffectContext::new(authority_id, context_id, ExecutionMode::Testing);
105        context.set_metadata("key1", "value1");
106        context.set_metadata("key2", "value2");
107
108        assert_eq!(context.get_metadata("key1"), Some(&"value1".to_string()));
109        assert_eq!(context.get_metadata("key2"), Some(&"value2".to_string()));
110    }
111
112    #[test]
113    fn test_standard_context_handler() {
114        let handler = StandardContextHandler::new();
115        let authority_id = AuthorityId::new_from_entropy([5u8; 32]);
116        let context_id = ContextId::new_from_entropy([6u8; 32]);
117
118        let context =
119            handler.create_effect_context(authority_id, context_id, ExecutionMode::Testing);
120
121        assert_eq!(context.authority_id(), authority_id);
122        assert_eq!(context.context_id(), context_id);
123    }
124
125    #[test]
126    fn test_context_validation() {
127        let handler = StandardContextHandler::new();
128        let authority_id = AuthorityId::new_from_entropy([7u8; 32]);
129        let context_id = ContextId::new_from_entropy([8u8; 32]);
130
131        let mut context = EffectContext::new(authority_id, context_id, ExecutionMode::Testing);
132        context.set_metadata("custom_field", "value");
133
134        assert!(handler.validate_context(&context, &["custom_field"]));
135        assert!(!handler.validate_context(&context, &["missing_field"]));
136    }
137
138    #[test]
139    fn test_metadata_merging() {
140        let handler = StandardContextHandler::new();
141        let authority_id = AuthorityId::new_from_entropy([9u8; 32]);
142        let context_id = ContextId::new_from_entropy([10u8; 32]);
143
144        let mut context1 = EffectContext::new(authority_id, context_id, ExecutionMode::Testing);
145        context1.set_metadata("key1", "value1");
146        context1.set_metadata("shared", "from_context1");
147
148        let mut context2 = EffectContext::new(authority_id, context_id, ExecutionMode::Testing);
149        context2.set_metadata("key2", "value2");
150        context2.set_metadata("shared", "from_context2");
151
152        let merged = handler.merge_metadata(&[&context1, &context2]);
153
154        assert_eq!(merged.get("key1"), Some(&"value1".to_string()));
155        assert_eq!(merged.get("key2"), Some(&"value2".to_string()));
156        assert_eq!(merged.get("shared"), Some(&"from_context2".to_string()));
157    }
158}