dx_forge/api/
lifecycle.rs1use anyhow::{Context, Result};
4use parking_lot::RwLock;
5use std::sync::{Arc, Once};
6use std::path::PathBuf;
7use std::collections::HashMap;
8
9use crate::orchestrator::{DxTool, ExecutionContext};
10use crate::core::Forge;
11
12static INIT: Once = Once::new();
14static mut FORGE_INSTANCE: Option<Arc<RwLock<Forge>>> = None;
15static mut TOOL_REGISTRY: Option<Arc<RwLock<HashMap<String, Arc<RwLock<Box<dyn DxTool>>>>>>> = None;
16static mut CURRENT_CONTEXT: Option<Arc<RwLock<ExecutionContext>>> = None;
17
18pub fn initialize_forge() -> Result<()> {
34 let mut init_result = Ok(());
35
36 INIT.call_once(|| {
37 tracing::info!("๐ Initializing Forge v{}", crate::VERSION);
38
39 let project_root = detect_workspace_root().unwrap_or_else(|_| {
41 std::env::current_dir().expect("Failed to get current directory")
42 });
43
44 tracing::info!("๐ Project root: {:?}", project_root);
45
46 match Forge::new(&project_root) {
48 Ok(forge) => {
49 unsafe {
50 FORGE_INSTANCE = Some(Arc::new(RwLock::new(forge)));
51 TOOL_REGISTRY = Some(Arc::new(RwLock::new(HashMap::new())));
52
53 let forge_path = project_root.join(".dx/forge");
55 let context = ExecutionContext::new(project_root.clone(), forge_path);
56 CURRENT_CONTEXT = Some(Arc::new(RwLock::new(context)));
57 }
58
59 tracing::info!("โ
Forge initialization complete");
60 }
61 Err(e) => {
62 init_result = Err(e).context("Failed to initialize forge");
63 }
64 }
65 });
66
67 init_result
68}
69
70pub fn register_tool(tool: Box<dyn DxTool>) -> Result<String> {
102 ensure_initialized()?;
103
104 let tool_name = tool.name().to_string();
105 let tool_version = tool.version().to_string();
106 let tool_id = format!("{}@{}", tool_name, tool_version);
107
108 tracing::info!("๐ฆ Registering tool: {}", tool_id);
109
110 unsafe {
111 if let Some(registry) = (*std::ptr::addr_of!(TOOL_REGISTRY)).as_ref() {
112 let tool_arc = Arc::new(RwLock::new(tool));
113 registry.write().insert(tool_id.clone(), tool_arc);
114 }
115 }
116
117 Ok(tool_id)
118}
119
120pub fn get_tool_context() -> Result<ExecutionContext> {
139 ensure_initialized()?;
140
141 unsafe {
142 if let Some(context) = (*std::ptr::addr_of!(CURRENT_CONTEXT)).as_ref() {
143 Ok(context.read().clone())
144 } else {
145 anyhow::bail!("Tool context not available")
146 }
147 }
148}
149
150pub fn shutdown_forge() -> Result<()> {
167 tracing::info!("๐ Shutting down Forge...");
168
169 unsafe {
170 if let Some(registry) = (*std::ptr::addr_of_mut!(TOOL_REGISTRY)).take() {
172 let count = registry.read().len();
173 tracing::info!("๐ฆ Unregistering {} tools", count);
174 drop(registry);
175 }
176
177 if let Some(forge) = (*std::ptr::addr_of_mut!(FORGE_INSTANCE)).take() {
179 tracing::info!("๐งน Cleaning up forge instance");
180 drop(forge);
181 }
182
183 CURRENT_CONTEXT = None;
185 }
186
187 tracing::info!("โ
Forge shutdown complete");
188 Ok(())
189}
190
191fn ensure_initialized() -> Result<()> {
194 unsafe {
195 if (*std::ptr::addr_of!(FORGE_INSTANCE)).is_none() {
196 anyhow::bail!("Forge not initialized. Call initialize_forge() first.");
197 }
198 }
199 Ok(())
200}
201
202fn detect_workspace_root() -> Result<PathBuf> {
203 let mut current = std::env::current_dir()?;
204
205 loop {
206 if current.join(".dx").exists() {
208 return Ok(current);
209 }
210
211 if current.join(".git").exists() {
213 return Ok(current);
214 }
215
216 if let Some(parent) = current.parent() {
218 current = parent.to_path_buf();
219 } else {
220 break;
222 }
223 }
224
225 Ok(std::env::current_dir()?)
227}
228
229#[cfg(test)]
230mod tests {
231 use super::*;
232 use crate::orchestrator::ToolOutput;
233
234 struct TestTool;
235
236 impl DxTool for TestTool {
237 fn name(&self) -> &str { "test-tool" }
238 fn version(&self) -> &str { "1.0.0" }
239 fn priority(&self) -> u32 { 50 }
240 fn execute(&mut self, _ctx: &ExecutionContext) -> Result<ToolOutput> {
241 Ok(ToolOutput::success())
242 }
243 }
244
245 #[test]
246 fn test_lifecycle() {
247 initialize_forge().ok();
249
250 let result = register_tool(Box::new(TestTool));
251 assert!(result.is_ok());
252
253 let ctx = get_tool_context();
254 assert!(ctx.is_ok());
255 }
256}