Skip to main content

confium_coordinator/
coordinator_factory.rs

1//! Coordinator factory — builder pattern with dependency injection.
2
3use crate::coordinator::coordinator::Coordinator;
4use crate::coordinator::session::SessionRequest;
5use crate::di_container::Container;
6
7/// Builder for assembling a fully configured coordinator.
8pub struct CoordinatorBuilder {
9    container: Container,
10    default_threshold: u32,
11    default_party_count: u32,
12    default_unlock_minutes: u32,
13}
14
15impl CoordinatorBuilder {
16    pub fn new() -> Self {
17        Self {
18            container: Container::new(),
19            default_threshold: 2,
20            default_party_count: 3,
21            default_unlock_minutes: 60,
22        }
23    }
24
25    /// Set default threshold for new sessions.
26    pub fn with_threshold(mut self, t: u32) -> Self {
27        self.default_threshold = t;
28        self
29    }
30
31    /// Set default party count.
32    pub fn with_party_count(mut self, n: u32) -> Self {
33        self.default_party_count = n;
34        self
35    }
36
37    /// Set default unlock window.
38    pub fn with_unlock_minutes(mut self, m: u32) -> Self {
39        self.default_unlock_minutes = m;
40        self
41    }
42
43    /// Register a custom dependency.
44    pub fn with_dependency<T, F>(mut self, factory: F) -> Self
45    where
46        T: Send + Sync + 'static,
47        F: Fn() -> T + Send + Sync + 'static,
48    {
49        self.container.register(factory);
50        self
51    }
52
53    /// Build the coordinator.
54    pub fn build(&mut self) -> Coordinator {
55        Coordinator::new()
56    }
57
58    /// Create a session with default parameters.
59    pub fn create_default_session(
60        &mut self,
61        coordinator: &mut Coordinator,
62        quorum_id: &str,
63        message: Vec<u8>,
64    ) -> Result<String, String> {
65        let request = SessionRequest {
66            quorum_id: quorum_id.into(),
67            scheme: "CMP20".into(),
68            message,
69            threshold: self.default_threshold,
70            num_parties: self.default_party_count,
71            unlock_window_minutes: self.default_unlock_minutes,
72            requested_by: "factory".into(),
73        };
74        coordinator
75            .create_session(request)
76            .map_err(|e| format!("{e:?}"))
77    }
78
79    /// Access the DI container.
80    pub fn container(&mut self) -> &mut Container {
81        &mut self.container
82    }
83}
84
85impl Default for CoordinatorBuilder {
86    fn default() -> Self {
87        Self::new()
88    }
89}
90
91/// Simple test helper: create a coordinator with preset config.
92pub fn test_coordinator(threshold: u32, party_count: u32) -> Coordinator {
93    let mut builder = CoordinatorBuilder::new()
94        .with_threshold(threshold)
95        .with_party_count(party_count);
96    builder.build()
97}
98
99#[cfg(test)]
100mod tests {
101    use super::*;
102
103    #[test]
104    fn builder_default() {
105        let mut builder = CoordinatorBuilder::new();
106        let coord = builder.build();
107        assert_eq!(coord.session_count(), 0);
108    }
109
110    #[test]
111    fn builder_custom_threshold() {
112        let mut builder = CoordinatorBuilder::new().with_threshold(3);
113        let mut coord = builder.build();
114        let result = builder.create_default_session(&mut coord, "q1", vec![0; 32]);
115        assert!(result.is_ok());
116    }
117
118    #[test]
119    fn builder_custom_party_count() {
120        let mut builder = CoordinatorBuilder::new()
121            .with_threshold(2)
122            .with_party_count(5);
123        let mut coord = builder.build();
124        let result = builder.create_default_session(&mut coord, "q1", vec![0; 32]);
125        assert!(result.is_ok());
126    }
127
128    #[test]
129    fn builder_custom_unlock() {
130        let mut builder = CoordinatorBuilder::new()
131            .with_threshold(2)
132            .with_unlock_minutes(120);
133        let mut coord = builder.build();
134        let result = builder.create_default_session(&mut coord, "q1", vec![0; 32]);
135        assert!(result.is_ok());
136    }
137
138    #[test]
139    fn builder_dependency_injection() {
140        let mut builder = CoordinatorBuilder::new().with_dependency(|| 42i32);
141        let container = builder.container();
142        let result: Option<i32> = container.resolve();
143        assert_eq!(result, Some(42));
144    }
145
146    #[test]
147    fn test_coordinator_helper() {
148        let coord = test_coordinator(2, 3);
149        assert_eq!(coord.session_count(), 0);
150    }
151
152    #[test]
153    fn multiple_sessions_via_builder() {
154        let mut builder = CoordinatorBuilder::new().with_threshold(2);
155        let mut coord = builder.build();
156        let s1 = builder
157            .create_default_session(&mut coord, "q1", vec![0; 32])
158            .unwrap();
159        let s2 = builder
160            .create_default_session(&mut coord, "q2", vec![1; 32])
161            .unwrap();
162        assert_ne!(s1, s2);
163        assert_eq!(coord.session_count(), 2);
164    }
165}