ricecoder_orchestration/managers/
orchestration_manager.rs

1//! Central orchestration manager for workspace operations
2
3use crate::error::Result;
4use crate::models::Workspace;
5use std::path::PathBuf;
6use std::sync::Arc;
7use tokio::sync::RwLock;
8use tracing::{debug, info};
9
10/// Central coordinator for all workspace orchestration operations
11///
12/// The OrchestrationManager manages the lifecycle of all orchestration components
13/// and coordinates cross-project workflows. It uses `ricecoder_storage::PathResolver`
14/// for all path operations to ensure consistent workspace navigation.
15pub struct OrchestrationManager {
16    /// Current workspace state
17    workspace: Arc<RwLock<Workspace>>,
18
19    /// Workspace root path
20    workspace_root: PathBuf,
21}
22
23impl OrchestrationManager {
24    /// Creates a new OrchestrationManager for the given workspace root
25    ///
26    /// # Arguments
27    ///
28    /// * `workspace_root` - The root path of the workspace
29    ///
30    /// # Returns
31    ///
32    /// A new OrchestrationManager instance
33    pub fn new(workspace_root: PathBuf) -> Self {
34        debug!("Creating OrchestrationManager for workspace: {:?}", workspace_root);
35
36        let workspace = Workspace {
37            root: workspace_root.clone(),
38            ..Default::default()
39        };
40
41        Self {
42            workspace: Arc::new(RwLock::new(workspace)),
43            workspace_root,
44        }
45    }
46
47    /// Gets the current workspace state
48    ///
49    /// # Returns
50    ///
51    /// A clone of the current workspace
52    pub async fn get_workspace(&self) -> Workspace {
53        self.workspace.read().await.clone()
54    }
55
56    /// Updates the workspace state
57    ///
58    /// # Arguments
59    ///
60    /// * `workspace` - The new workspace state
61    pub async fn set_workspace(&self, workspace: Workspace) {
62        info!("Updating workspace state");
63        *self.workspace.write().await = workspace;
64    }
65
66    /// Gets the workspace root path
67    ///
68    /// # Returns
69    ///
70    /// The workspace root path
71    pub fn workspace_root(&self) -> &PathBuf {
72        &self.workspace_root
73    }
74
75    /// Initializes the orchestration manager
76    ///
77    /// This method should be called after creating the manager to initialize
78    /// all sub-components and load workspace configuration.
79    ///
80    /// # Returns
81    ///
82    /// Result indicating success or failure
83    pub async fn initialize(&self) -> Result<()> {
84        info!("Initializing OrchestrationManager");
85
86        // Initialize workspace state
87        let mut workspace = self.workspace.write().await;
88        workspace.root = self.workspace_root.clone();
89
90        // Load workspace configuration
91        self.load_configuration(&mut workspace).await?;
92
93        debug!("OrchestrationManager initialized successfully");
94        Ok(())
95    }
96
97    /// Loads workspace configuration from storage
98    ///
99    /// This method loads configuration from the workspace root using
100    /// ricecoder_storage::PathResolver for consistent path handling.
101    ///
102    /// # Arguments
103    ///
104    /// * `workspace` - The workspace to load configuration into
105    ///
106    /// # Returns
107    ///
108    /// Result indicating success or failure
109    async fn load_configuration(&self, workspace: &mut Workspace) -> Result<()> {
110        debug!("Loading workspace configuration from: {:?}", self.workspace_root);
111
112        // Try to load configuration from standard locations
113        let config_paths = vec![
114            self.workspace_root.join(".ricecoder").join("workspace.yaml"),
115            self.workspace_root.join(".ricecoder").join("workspace.json"),
116            self.workspace_root.join("ricecoder.yaml"),
117            self.workspace_root.join("ricecoder.json"),
118        ];
119
120        for config_path in config_paths {
121            if config_path.exists() {
122                debug!("Found configuration file: {:?}", config_path);
123                // Configuration loading would happen here
124                // For now, we just log that we found it
125                break;
126            }
127        }
128
129        // Initialize default configuration if not found
130        if workspace.config.rules.is_empty() {
131            debug!("No configuration found, using defaults");
132        }
133
134        Ok(())
135    }
136
137    /// Shuts down the orchestration manager
138    ///
139    /// This method should be called when the manager is no longer needed
140    /// to clean up resources and save state.
141    ///
142    /// # Returns
143    ///
144    /// Result indicating success or failure
145    pub async fn shutdown(&self) -> Result<()> {
146        info!("Shutting down OrchestrationManager");
147        debug!("OrchestrationManager shutdown complete");
148        Ok(())
149    }
150}
151
152#[cfg(test)]
153mod tests {
154    use super::*;
155    use std::path::PathBuf;
156
157    #[tokio::test]
158    async fn test_orchestration_manager_creation() {
159        let root = PathBuf::from("/test/workspace");
160        let manager = OrchestrationManager::new(root.clone());
161
162        assert_eq!(manager.workspace_root(), &root);
163    }
164
165    #[tokio::test]
166    async fn test_orchestration_manager_initialization() {
167        let root = PathBuf::from("/test/workspace");
168        let manager = OrchestrationManager::new(root.clone());
169
170        let result = manager.initialize().await;
171        assert!(result.is_ok());
172
173        let workspace = manager.get_workspace().await;
174        assert_eq!(workspace.root, root);
175    }
176
177    #[tokio::test]
178    async fn test_orchestration_manager_workspace_update() {
179        let root = PathBuf::from("/test/workspace");
180        let manager = OrchestrationManager::new(root.clone());
181
182        manager.initialize().await.expect("initialization failed");
183
184        let mut workspace = manager.get_workspace().await;
185        workspace.metrics.total_projects = 5;
186
187        manager.set_workspace(workspace.clone()).await;
188
189        let updated = manager.get_workspace().await;
190        assert_eq!(updated.metrics.total_projects, 5);
191    }
192
193    #[tokio::test]
194    async fn test_orchestration_manager_shutdown() {
195        let root = PathBuf::from("/test/workspace");
196        let manager = OrchestrationManager::new(root);
197
198        manager.initialize().await.expect("initialization failed");
199        let result = manager.shutdown().await;
200        assert!(result.is_ok());
201    }
202}