1use std::sync::Arc;
12
13use crate::audit::AuditEventSender;
14use crate::auto_approve::defer::DeferRegistry;
15use crate::command_sender::CommandSender;
16use crate::config::Settings;
17use crate::hooks::registry::{HookRegistry, SessionPaneMap};
18use crate::ipc::server::IpcServer;
19use crate::pty::PtyRegistry;
20use crate::runtime::RuntimeAdapter;
21use crate::state::{AppState, SharedState};
22use crate::transcript::TranscriptRegistry;
23
24use super::core::TmaiCore;
25
26pub struct TmaiCoreBuilder {
28 settings: Arc<Settings>,
29 state: Option<SharedState>,
30 command_sender: Option<Arc<CommandSender>>,
31 ipc_server: Option<Arc<IpcServer>>,
32 audit_tx: Option<AuditEventSender>,
33 hook_registry: Option<HookRegistry>,
34 session_pane_map: Option<SessionPaneMap>,
35 hook_token: Option<String>,
36 pty_registry: Option<Arc<PtyRegistry>>,
37 runtime: Option<Arc<dyn RuntimeAdapter>>,
38 transcript_registry: Option<TranscriptRegistry>,
39}
40
41impl TmaiCoreBuilder {
42 pub fn new(settings: Settings) -> Self {
44 Self {
45 settings: Arc::new(settings),
46 state: None,
47 command_sender: None,
48 ipc_server: None,
49 audit_tx: None,
50 hook_registry: None,
51 session_pane_map: None,
52 hook_token: None,
53 pty_registry: None,
54 runtime: None,
55 transcript_registry: None,
56 }
57 }
58
59 pub fn from_shared_settings(settings: Arc<Settings>) -> Self {
61 Self {
62 settings,
63 state: None,
64 command_sender: None,
65 ipc_server: None,
66 audit_tx: None,
67 hook_registry: None,
68 session_pane_map: None,
69 hook_token: None,
70 pty_registry: None,
71 runtime: None,
72 transcript_registry: None,
73 }
74 }
75
76 pub fn with_state(mut self, state: SharedState) -> Self {
78 self.state = Some(state);
79 self
80 }
81
82 pub fn with_ipc_server(mut self, ipc_server: Arc<IpcServer>) -> Self {
84 self.ipc_server = Some(ipc_server);
85 self
86 }
87
88 pub fn with_command_sender(mut self, sender: Arc<CommandSender>) -> Self {
90 self.command_sender = Some(sender);
91 self
92 }
93
94 pub fn with_audit_sender(mut self, tx: AuditEventSender) -> Self {
96 self.audit_tx = Some(tx);
97 self
98 }
99
100 pub fn with_hook_registry(mut self, registry: HookRegistry) -> Self {
102 self.hook_registry = Some(registry);
103 self
104 }
105
106 pub fn with_session_pane_map(mut self, map: SessionPaneMap) -> Self {
108 self.session_pane_map = Some(map);
109 self
110 }
111
112 pub fn with_hook_token(mut self, token: String) -> Self {
114 self.hook_token = Some(token);
115 self
116 }
117
118 pub fn with_pty_registry(mut self, registry: Arc<PtyRegistry>) -> Self {
120 self.pty_registry = Some(registry);
121 self
122 }
123
124 pub fn with_runtime(mut self, runtime: Arc<dyn RuntimeAdapter>) -> Self {
126 self.runtime = Some(runtime);
127 self
128 }
129
130 pub fn with_transcript_registry(mut self, registry: TranscriptRegistry) -> Self {
132 self.transcript_registry = Some(registry);
133 self
134 }
135
136 pub fn build(self) -> TmaiCore {
141 let state = self.state.unwrap_or_else(AppState::shared);
142 let hook_registry = self
143 .hook_registry
144 .unwrap_or_else(crate::hooks::new_hook_registry);
145 let session_pane_map = self
146 .session_pane_map
147 .unwrap_or_else(crate::hooks::new_session_pane_map);
148
149 let pty_registry = self
150 .pty_registry
151 .unwrap_or_else(|| Arc::new(PtyRegistry::default()));
152
153 TmaiCore::new(
154 state,
155 self.command_sender,
156 self.settings,
157 self.ipc_server,
158 self.audit_tx,
159 hook_registry,
160 session_pane_map,
161 self.hook_token,
162 pty_registry,
163 self.runtime,
164 self.transcript_registry,
165 DeferRegistry::new(),
166 )
167 }
168}
169
170#[cfg(test)]
171mod tests {
172 use super::*;
173
174 #[test]
175 fn test_builder_defaults() {
176 let core = TmaiCoreBuilder::new(Settings::default()).build();
177
178 assert_eq!(core.settings().poll_interval_ms, 500);
179 assert!(core.ipc_server().is_none());
180 assert!(core.command_sender_ref().is_none());
181 }
182
183 #[test]
184 fn test_builder_with_state() {
185 let state = AppState::shared();
186 let state_clone = state.clone();
187
188 let core = TmaiCoreBuilder::new(Settings::default())
189 .with_state(state)
190 .build();
191
192 #[allow(deprecated)]
193 let raw = core.raw_state();
194 assert!(Arc::ptr_eq(raw, &state_clone));
195 }
196
197 #[test]
198 fn test_builder_from_shared_settings() {
199 let settings = Arc::new(Settings::default());
200 let settings_clone = settings.clone();
201
202 let core = TmaiCoreBuilder::from_shared_settings(settings).build();
203
204 assert_eq!(
206 core.settings().poll_interval_ms,
207 settings_clone.poll_interval_ms
208 );
209 }
210}