Skip to main content

ferrox_app/
lib.rs

1use std::sync::Arc;
2use tokio::signal;
3use tokio::task::JoinHandle;
4use tracing::{info, error};
5use ferrox_errors::AppError;
6use ferrox_transports::Transport;
7
8pub struct FerroxApp {
9    transports: Vec<Arc<dyn Transport>>,
10}
11
12impl Default for FerroxApp {
13    fn default() -> Self {
14        Self::new()
15    }
16}
17
18impl FerroxApp {
19    pub fn new() -> Self {
20        Self {
21            transports: Vec::new(),
22        }
23    }
24
25    /// Add a transport layer to the app (e.g. HttpTransport, GrpcTransport, FtpTransport)
26    pub fn add_transport<T: Transport + 'static>(mut self, transport: T) -> Self {
27        self.transports.push(Arc::new(transport));
28        self
29    }
30
31    /// Starts all configured transports concurrently and waits for shutdown signal
32    pub async fn start(self) -> Result<(), AppError> {
33        info!("Starting FerroxApp multi-transport system...");
34
35        if self.transports.is_empty() {
36            return Err(AppError::InternalServerError(
37                "Cannot start FerroxApp: no transports configured!".into(),
38            ));
39        }
40
41        let mut join_handles: Vec<JoinHandle<Result<(), AppError>>> = Vec::new();
42
43        for transport in self.transports {
44            let t = Arc::clone(&transport);
45            
46            let handle = tokio::spawn(async move {
47                info!("Booting transport: {}", t.name());
48                if let Err(e) = t.start().await {
49                    error!("Transport {} crashed: {:?}", t.name(), e);
50                    return Err(e);
51                }
52                Ok(())
53            });
54            
55            join_handles.push(handle);
56        }
57
58        // Wait for shutdown signal
59        shutdown_signal().await;
60
61        info!("Graceful shutdown initiated...");
62        
63        // In a real implementation we would send a cancellation token to all join handles
64        for handle in join_handles {
65            handle.abort();
66        }
67
68        info!("FerroxApp stopped gracefully.");
69        Ok(())
70    }
71}
72
73async fn shutdown_signal() {
74    let ctrl_c = async {
75        signal::ctrl_c()
76            .await
77            .expect("failed to install Ctrl+C handler");
78    };
79
80    #[cfg(unix)]
81    let terminate = async {
82        signal::unix::signal(signal::unix::SignalKind::terminate())
83            .expect("failed to install signal handler")
84            .recv()
85            .await;
86    };
87
88    #[cfg(not(unix))]
89    let terminate = std::future::pending::<()>();
90
91    tokio::select! {
92        _ = ctrl_c => {},
93        _ = terminate => {},
94    }
95
96    info!("Shutdown signal received, starting graceful shutdown...");
97}
98
99pub fn setup() {
100    println!("ferrox-app initialized: Multi-Transport FerroxApp bootstrap ready.");
101}