metis_core/application/
mod.rs

1pub mod services;
2
3use crate::dal::Database;
4use crate::{MetisError, Result};
5use std::path::Path;
6
7/// Application layer coordinator
8/// Manages services and provides high-level application operations
9pub struct Application {
10    database: Database,
11}
12
13impl Application {
14    /// Create a new application instance
15    pub fn new(database: Database) -> Self {
16        Self { database }
17    }
18
19    /// Execute a database operation
20    pub fn with_database<F, R>(&mut self, f: F) -> R
21    where
22        F: FnOnce(&mut services::DatabaseService) -> R,
23    {
24        let repository = self
25            .database
26            .repository()
27            .expect("Failed to get database repository");
28        let mut service = services::DatabaseService::new(repository);
29        f(&mut service)
30    }
31
32    /// Execute a sync operation
33    pub fn with_sync<F, R>(&mut self, f: F) -> R
34    where
35        F: FnOnce(&mut services::SyncService) -> R,
36    {
37        let repository = self
38            .database
39            .repository()
40            .expect("Failed to get database repository");
41        let mut db_service = services::DatabaseService::new(repository);
42        let mut sync_service = services::SyncService::new(&mut db_service);
43        f(&mut sync_service)
44    }
45
46    /// Convenience method to sync a directory
47    ///
48    /// Automatically handles:
49    /// - Database corruption recovery
50    /// - Configuration sync from config.toml
51    /// - Counter recovery when needed
52    /// - File synchronization
53    pub async fn sync_directory<P: AsRef<Path>>(
54        self,
55        dir_path: P,
56    ) -> Result<Vec<services::synchronization::SyncResult>> {
57        let workspace_path = dir_path.as_ref().to_path_buf();
58        let db_path = workspace_path.join("metis.db");
59
60        // Step 1: Check if recovery is needed (DB missing or corrupt)
61        if services::workspace::ConfigurationRecoveryService::needs_recovery(&workspace_path) {
62            tracing::warn!("Database recovery needed, initiating full recovery");
63
64            // Recreate database if needed
65            if !db_path.exists() {
66                tracing::info!("Creating new database at {}", db_path.display());
67                let _ = Database::new(db_path.to_str().unwrap())
68                    .map_err(|e| MetisError::FileSystem(e.to_string()))?;
69            }
70
71            // Run full recovery
72            let report = services::workspace::ConfigurationRecoveryService::recover_configuration(
73                &workspace_path,
74                &db_path,
75            )?;
76
77            if report.had_recovery_actions() {
78                tracing::info!(
79                    "Recovery complete: config_created={}, prefix_synced={}, flight_levels_synced={}, counters_recovered={}",
80                    report.config_file_created,
81                    report.prefix_synced,
82                    report.flight_levels_synced,
83                    report.counters_recovered
84                );
85            }
86        } else {
87            // Step 2: Normal path - just sync config.toml to DB (lightweight)
88            match services::workspace::ConfigurationRecoveryService::sync_config_to_database(
89                &workspace_path,
90                &db_path,
91            ) {
92                Ok(synced) => {
93                    if synced {
94                        tracing::info!("Synced configuration from config.toml to database");
95                    }
96                }
97                Err(e) => {
98                    tracing::warn!("Failed to sync configuration: {}, continuing with file sync", e);
99                }
100            }
101        }
102
103        // Step 3: Perform normal file synchronization
104        let mut db_service = services::DatabaseService::new(self.database.into_repository());
105        let mut sync_service =
106            services::SyncService::new(&mut db_service).with_workspace_dir(&workspace_path);
107        sync_service.sync_directory(dir_path).await
108    }
109
110    /// Get access to the underlying database
111    pub fn database(&mut self) -> &mut Database {
112        &mut self.database
113    }
114}