Skip to main content

rust_microservice/
server.rs

1//! # Server Module
2//!
3//! This module provides the entry point for the server, including configuration
4//! loading, environment variable overrides, and the web server bootstrap process.
5//!
6//! ## Overview
7//!
8//! The server module is responsible for:
9//!
10//! - Initializing the logger
11//! - Loading the configuration from YAML files and environment variables
12//! - Bootstrapping the web server
13//!
14//! ## Configuration
15//!
16//! The server module loads its configuration from the following sources, in order of
17//! precedence:
18//!
19//! 1. Environment variables
20//! 2. CLI parameters
21//! 3. YAML configuration file
22//!
23//! The configuration is represented by the `Settings` struct, which is
24//! serialized and deserialized using `serde`.
25//!
26//! ## Web Server
27//!
28//! The web server is bootstrapped using `actix-web` and is responsible for:
29//!
30//! - Registering routers and microservice endpoints
31//! - Managing middlewares and shared application state
32//! - Running the asynchronous runtime using `tokio::main` or a custom runtime
33
34//! ## Environment Variables
35//!
36//! The server module loads environment variables from the system environment and overrides
37//! the configuration accordingly.
38//!
39//! ## CLI Parameters
40//!
41//! The server module loads CLI parameters from the command line and overrides the configuration
42//! accordingly.
43
44use crate::settings::{OAuth2Configuration, Security, Settings};
45use crate::{cmd::root::Cli, data::bigquery};
46use crate::{data, security};
47use actix_web::dev::ServiceRequest;
48use actix_web::http::header;
49use actix_web::web::ServiceConfig;
50use clap::Parser;
51use colored::Colorize;
52use env_logger::{Builder, Env};
53use jsonwebtoken::jwk::JwkSet;
54use log::{info, warn};
55use reqwest_middleware::ClientBuilder;
56use sea_orm::DatabaseConnection;
57use std::any::Any;
58use std::io::Write;
59use std::sync::OnceLock;
60use thiserror::Error;
61use tracing::{debug, error};
62
63#[cfg(feature = "memory-database")]
64use sea_orm::MockDatabase;
65
66#[cfg(feature = "memory-database")]
67use std::sync::Arc;
68
69/// Global static instance of the [`Server`].
70static SERVER: OnceLock<Box<dyn GlobalServer + Send + Sync>> = OnceLock::new();
71
72/// Represents the high-level server controller responsible for
73/// loading configuration, applying custom setup, and running the
74/// application.
75///
76/// This structure encapsulates CLI arguments, server settings,
77/// and an optional configuration callback for the Actix-Web service.
78#[derive(Clone)]
79pub struct Server {
80    running: bool,
81    args: Option<Cli>,
82    settings: Option<Settings>,
83    fnconfig: Option<fn(&mut ServiceConfig)>,
84    database: Option<data::ServerDatabase>,
85}
86
87// Implementation of the `GlobalServer` trait for the `Server` struct.
88impl Server {
89    /// Returns a reference to the globally initialized [`Server`].
90    ///
91    /// # Examples
92    ///
93    /// ```rust
94    /// use rust_microservice::Server;
95    ///
96    /// let server = Server::global();
97    /// ```
98    pub fn global() -> Result<&'static (dyn GlobalServer + Send + Sync)> {
99        SERVER
100            .get()
101            .map(|server| server.as_ref())
102            .ok_or(ServerError::NotInitialized)
103    }
104
105    /// Returns a reference to the globally initialized `Server`, if it exists.
106    ///
107    /// This function provides access to the global server instance managed
108    /// through the `GlobalServer` trait.
109    ///
110    /// # Examples
111    ///
112    /// ```rust
113    /// use rust_microservice::Server;
114    ///
115    /// let server = Server::global_server();
116    /// ```
117    pub fn global_server() -> Option<&'static Server> {
118        SERVER
119            .get()
120            .map(|s| s.as_any().downcast_ref::<Server>())
121            .unwrap_or_default()
122    }
123
124    /// Sets the global [`Server`] instance.
125    ///
126    /// This function should be called **exactly once**, typically during
127    /// application startup.
128    ///
129    /// # Examples
130    ///
131    /// ```rust
132    /// use rust_microservice::Server;
133    ///
134    /// let server = Server::new("0.1.3".to_string(), None);
135    /// Server::set_global(server);
136    /// ```
137    pub fn set_global(server: Server) {
138        if SERVER.set(Box::new(server)).is_err() {
139            debug!(
140                "{}",
141                "Server is already initialized. The new instance will be ignored.".yellow()
142            );
143        }
144    }
145
146    /// Checks whether the server has already been initialized.
147    /// This function should be called before any server initialization
148    /// to prevent multiple initializations.
149    ///
150    /// # Panics
151    ///
152    /// Panics if the server is already initialized.  
153    /// This method logs an error message before aborting execution.
154    ///
155    /// # Behavior
156    ///
157    /// - Verifies if the global `SERVER` instance is set.
158    /// - If initialized, logs an error and terminates the program.
159    fn check_initialized() {
160        if SERVER.get().is_some() {
161            error!("{}", "Server is already initialized.".red());
162            panic!()
163        }
164    }
165
166    /// Performs pre-flight initialization tasks such as configuring the server logger
167    /// and printing out the ASCII art banner. This function is called once during
168    /// server startup, before the server starts accepting requests.
169    fn preflight(app_version: String, banner: Option<String>) {
170        // Configure server logger
171        Server::configure_log().expect("Log is already initialized!");
172
173        //println!("Starting server...");
174        let _standard_ascii_art = r#"
175         ____               _     ____                                
176        |  _ \  _   _  ___ | |_  / ___|   ___  _ __ __   __ ___  _ __ 
177        | |_) || | | |/ __|| __| \___ \  / _ \| '__|\ \ / // _ \| '__|
178        |  _ < | |_| |\__ \| |_   ___) ||  __/| |    \ V /|  __/| |   
179        |_| \_\ \__,_||___/ \__| |____/  \___||_|     \_/  \___||_|   
180        "#;
181
182        let ascii_art = r#"
183            ___             __     ____                         
184           / _ \ __ __ ___ / /_   / __/___  ____ _  __ ___  ____
185          / , _// // /(_-</ __/  _\ \ / -_)/ __/| |/ // -_)/ __/
186         /_/|_| \_,_//___/\__/  /___/ \__//_/   |___/ \__//_/   
187        "#;
188
189        if let Some(banner) = banner
190            && !banner.is_empty()
191        {
192            println!("{}", banner);
193        } else {
194            println!("{}", ascii_art);
195        }
196
197        //println!("{}", _standard_ascii_art);
198        println!(
199            "\t{} {}\n\t{} {}\n\t{} {}\n",
200            "License:".green(),
201            env!("CARGO_PKG_LICENSE").bright_blue(),
202            "Server Version:".green(),
203            env!("CARGO_PKG_VERSION").bright_blue(),
204            "Application Version:".green(),
205            app_version.bright_blue(),
206        );
207    }
208
209    /// Creates a new empty `Server` instance with no configuration loaded.
210    ///
211    /// Useful as the starting point for building and initializing
212    /// the server lifecycle.
213    pub fn new(app_version: String, banner: Option<String>) -> Self {
214        Server::check_initialized();
215
216        Server::preflight(app_version, banner);
217
218        Server {
219            running: false,
220            args: None,
221            settings: None,
222            fnconfig: None,
223            database: None,
224        }
225    }
226
227    /// Creates a new `Server` instance with a mock database.
228    ///
229    /// # Arguments
230    ///
231    /// * `name` - The name of the mock database.
232    /// * `database` - The mock database connection.
233    ///
234    /// # Returns
235    ///
236    /// A new `Server` instance with the mock database loaded.
237    #[cfg(feature = "memory-database")]
238    pub fn new_with_mock_database(name: String, database: MockDatabase) -> Self {
239        Server::preflight("".into(), None);
240
241        let databases = data::ServerDatabase::new_with_mock_database(name, database);
242
243        Server {
244            running: false,
245            args: None,
246            settings: None,
247            fnconfig: None,
248            database: Some(databases),
249        }
250    }
251
252    /// Creates a new `Server` instance with a memory database.
253    ///
254    /// This method creates a new `Server` instance with a memory database
255    /// connection. It is useful for testing purposes only and should not be used
256    /// in production code.
257    ///
258    /// # Parameters
259    ///
260    /// - `name`: The name of the memory database.
261    ///
262    /// # Returns
263    ///
264    /// A new `Server` instance with the memory database loaded.
265    ///
266    /// # Erros
267    ///
268    /// An error if the memory database connection cannot be established.
269    #[cfg(feature = "memory-database")]
270    pub async fn new_with_memory_database(name: String) -> Result<Self> {
271        Server::preflight("".into(), None);
272
273        let databases = data::ServerDatabase::new_with_memory_database(name)
274            .await
275            .map_err(|e| ServerError::Database(e.to_string()))?;
276
277        Ok(Server {
278            running: false,
279            args: None,
280            settings: None,
281            fnconfig: None,
282            database: Some(databases),
283        })
284    }
285
286    pub async fn new_with_settings(settings: Settings) -> Result<Self> {
287        Server::preflight("".into(), None);
288
289        let result = Server::discover_oauth_security_settings(&settings).await;
290
291        let settings = match result {
292            Ok(s) => s,
293            Err(e) => {
294                info!("Failed to discover OAuth2 security settings: {}", e);
295                settings
296            }
297        };
298
299        let server = Server {
300            running: false,
301            args: None,
302            settings: Some(settings),
303            fnconfig: None,
304            database: None,
305        };
306
307        Ok(server)
308    }
309
310    /// Loads CLI arguments and resolves the application configuration.
311    ///
312    /// This method parses command-line arguments, attempts to load the
313    /// server settings, and stores both inside the `Server` instance.
314    /// Panics if configuration loading fails.
315    ///
316    /// # Returns
317    /// The updated `Server` instance.
318    pub async fn init(mut self) -> Result<Self> {
319        Server::check_initialized();
320
321        let args = Cli::parse();
322        let settings =
323            Cli::load_config(&args).map_err(|e| ServerError::Configuration(e.to_string()))?;
324
325        let result = Server::discover_oauth_security_settings(&settings).await;
326        let settings = match result {
327            Ok(s) => s,
328            Err(e) => {
329                info!("Failed to discover OAuth2 security settings: {}", e);
330                settings
331            }
332        };
333
334        self.settings = Some(settings);
335        self.args = Some(args);
336
337        Ok(self)
338    }
339
340    /// Discovers OAuth2 security settings from the configuration or an
341    /// optional discovery URL.
342    ///
343    /// This function looks for the OAuth2 security settings in the
344    /// provided configuration. If the settings are not found, it panics.
345    /// If the discovery URL is provided, it attempts to fetch the settings
346    /// from the URL. If the fetch fails, it panics. If the fetch succeeds,
347    /// it updates the provided configuration with the discovered settings.
348    ///
349    /// # Parameters
350    ///
351    /// - `settings`: The configuration to search for OAuth2 security settings.
352    ///
353    /// # Returns
354    ///
355    /// A `Result` containing the updated configuration or an error if
356    /// configuration loading or discovery fails.
357    async fn discover_oauth_security_settings(settings: &Settings) -> Result<Settings> {
358        let oauth2 = settings
359            .security
360            .as_ref()
361            .and_then(|s| s.oauth2.as_ref())
362            .ok_or(ServerError::Configuration(
363                "Oauth2 security settings not found.".to_string(),
364            ))?;
365
366        if oauth2.discovery_enabled.unwrap_or(false)
367            && let Some(discovery_url) = &oauth2.discovery_url
368        {
369            let client = ClientBuilder::new(reqwest::Client::new()).build();
370
371            info!(
372                "Discovering OAuth2 security settings from {}",
373                discovery_url.bright_blue()
374            );
375
376            let mut discovery = client
377                .get(discovery_url)
378                .send()
379                .await
380                .map_err(|e| {
381                    info!("Failed to fetch OAuth2 discovery settings: {}", e);
382                    ServerError::Configuration(e.to_string())
383                })?
384                .json::<OAuth2Configuration>()
385                .await
386                .map_err(|e| {
387                    info!("Failed to parse OAuth2 discovery settings: {}", e);
388                    ServerError::Configuration(e.to_string())
389                })?;
390
391            discovery.enabled = oauth2.enabled;
392            discovery.discovery_url = Some(discovery_url.clone());
393            discovery.discovery_enabled = Some(true);
394
395            //info!("Discovered OAuth2 security settings: {:#?}", discovery);
396
397            if let Some(jwks_uri) = discovery.jwks_uri.clone() {
398                info!("Fetching JWKs Certs from {}", jwks_uri.bright_blue());
399
400                let jwks = client
401                    .get(jwks_uri)
402                    .send()
403                    .await
404                    .map_err(|e| {
405                        info!("Failed to fetch JWKs: {}", e);
406                        ServerError::Configuration(e.to_string())
407                    })?
408                    .json::<JwkSet>()
409                    .await
410                    .map_err(|e| {
411                        info!("Failed to parse JWKs: {}", e);
412                        ServerError::Configuration(e.to_string())
413                    })?;
414
415                //info!("Discovered JWKs: {:#?}", jwks);
416
417                discovery.jwks = Some(jwks);
418            }
419
420            let mut settings = settings.clone();
421            settings.security = Some(Security {
422                oauth2: Some(discovery),
423            });
424
425            //info!("Updated OAuth2 security settings: {:#?}", settings);
426
427            return Ok(settings);
428        }
429
430        Ok(settings.clone())
431    }
432
433    /// Configures and initializes the application logger.
434    ///
435    /// This method sets up the logger using environment variables, applying a default
436    /// log level configuration when none is provided. It defines a custom log format
437    /// with colored log levels, timestamps, module paths, and messages to improve
438    /// readability during development and debugging.
439    ///
440    /// # Behavior
441    ///
442    /// - Uses `RUST_LOG` environment variable when available.
443    /// - Defaults to `info` level and suppresses noisy logs from `actix_web`
444    ///   and `actix_web_prom`.
445    /// - Applies colorized output based on the log level.
446    /// - Formats log entries with timestamp, level, module path, and message.
447    ///
448    /// # Returns
449    ///
450    /// Returns `Self` to allow method chaining during application configuration.
451    fn configure_log() -> Result<()> {
452        // Initialize Logger ENV
453        let level = Env::default().default_filter_or("info,actix_web=error,actix_web_prom=error");
454
455        let _ = Builder::from_env(level)
456            .format(|buf, record| {
457                let level = match record.level() {
458                    log::Level::Info => record.level().as_str().bright_green(),
459                    log::Level::Debug => record.level().as_str().bright_blue(),
460                    log::Level::Trace => record.level().as_str().bright_cyan(),
461                    log::Level::Warn => record.level().as_str().bright_yellow(),
462                    log::Level::Error => record.level().as_str().bright_red(),
463                };
464
465                let datetime = chrono::Local::now()
466                    .format("%d-%m-%YT%H:%M:%S%.3f%:z")
467                    .to_string()
468                    .white();
469
470                // Align timestamp, level, and module path
471                writeln!(
472                    buf,
473                    "{:<24}  {:<5} [{:<60}] - {}",
474                    datetime,                                         // Timestamp
475                    level,                                            // Log level
476                    record.module_path().unwrap_or("unknown").blue(), // Module path
477                    record.args()                                     // Log message
478                )
479            })
480            .try_init();
481
482        Ok(())
483
484        //env_logger::init_from_env(level);
485    }
486
487    /// Applies a custom Actix-Web configuration callback to the server.
488    ///
489    /// This allows the application to register routes or middlewares
490    /// before the server is executed.
491    ///
492    /// # Parameters
493    /// - `fnconfig`: Optional function used to configure `ServiceConfig`.
494    ///
495    /// # Returns
496    /// The updated `Server` instance.
497    pub fn configure(mut self, fnconfig: Option<fn(&mut ServiceConfig)>) -> Self {
498        Server::check_initialized();
499        self.fnconfig = fnconfig;
500        self
501    }
502
503    /// Initializes the database connections using the previously loaded settings.
504    ///
505    /// This method creates and initializes all required database connections,
506    /// including the BigQuery client, based on the application settings.
507    /// It must be called **after** the settings have been loaded.
508    ///
509    /// # Behavior
510    ///
511    /// - Validates that the application settings are available.
512    /// - Instantiates the `ServerDatabase` using the provided settings.
513    /// - Stores the initialized database instance in the application state.
514    /// - Retrieves and logs the list of available BigQuery tables.
515    ///
516    /// # Panics
517    ///
518    /// This method will panic if:
519    /// - The settings have not been loaded before calling this method.
520    ///
521    /// # Returns
522    ///
523    /// Returns `Self` with the database field initialized.
524    ///
525    /// # Example
526    ///
527    /// ```rust
528    /// use rust_microservice::Server;
529    ///
530    /// async fn start_server() -> rust_microservice::Result<(), String> {
531    ///     let server = Server::new("0.1.3".to_string(), None)
532    ///         .init()
533    ///         .await
534    ///         .map_err(|e|e.to_string())?
535    ///         .intialize_database()
536    ///         .await
537    ///         .map_err(|e|e.to_string())?;
538    ///
539    ///     Ok(())
540    /// }
541    /// ```
542    pub async fn intialize_database(mut self) -> Result<Self> {
543        Server::check_initialized();
544
545        // Initialize the database connections based on loaded settings.
546        let settings = self.settings.as_ref().ok_or_else(|| {
547            ServerError::InvalidState("Cannot initialize database before calling init()".into())
548        })?;
549
550        let database = data::ServerDatabase::new_with_settings(settings)
551            .await
552            .map_err(|e| ServerError::Database(e.to_string()))?;
553
554        self.database = Some(database);
555        Ok(self)
556    }
557
558    /// Executes the server using the previously loaded settings and CLI input.
559    ///
560    /// If both arguments and settings are available, this method delegates
561    /// execution to the CLI dispatcher, starting the server workflow.
562    pub async fn run(&self) {
563        if self.running {
564            warn!("The server is already running and cannot be started again.");
565            return;
566        }
567
568        self.clone().running = true;
569
570        if let (Some(args), Some(settings)) = (&self.args, &self.settings) {
571            Cli::init(args, settings, self.fnconfig).await;
572        }
573
574        if let Some(database) = &self.database {
575            database.close();
576        }
577    }
578}
579
580/// Default implementation for the `Server` structure.
581impl Default for Server {
582    /// Returns a default instance of the `Server` structure.
583    ///
584    /// This method is used to provide a default instance of the server
585    /// when no configuration is provided.
586    ///
587    /// # Returns
588    ///
589    /// A default instance of the `Server` structure.
590    fn default() -> Self {
591        Server::new("".into(), None)
592    }
593}
594
595/// Trait that defines access to global server resources and configuration.
596///
597/// This trait provides read-only access to shared server components,
598/// such as application settings and database connections.
599pub trait GlobalServer {
600    // Returns a reference to the underlying `Any` object.
601    fn as_any(&self) -> &dyn Any;
602
603    // Returns a reference to the application settings.
604    fn settings(&self) -> &Settings;
605
606    // Returns a reference to the server database configuration, if available.
607    #[cfg(feature = "memory-database")]
608    fn database_with_name(&self, name: &str) -> Result<Arc<DatabaseConnection>>;
609
610    // Returns a clone to the server database configuration, if available.
611    #[cfg(not(feature = "memory-database"))]
612    fn database_with_name(&self, name: &str) -> Result<DatabaseConnection>;
613
614    // Returns a reference to the BigQuery client, if available.
615    fn bigquery(&self) -> Option<&bigquery::BigQueryClient>;
616
617    // Returns a boolean indicating whether the server is currently running.
618    fn is_running(&self) -> bool;
619
620    // Validates the JWT token in the given request and checks whether the
621    // associated roles match the provided list.
622    fn validate_jwt(
623        &self,
624        request: &ServiceRequest,
625        authorize: String,
626    ) -> security::oauth2::Result<()>;
627}
628
629/// Trait implementation for the `Server` structure.
630impl GlobalServer for Server {
631    /// Returns a reference to the underlying `Any` value.
632    ///
633    /// Allows access to the internal server component by
634    /// enabling safe downcasting to a concrete type.
635    fn as_any(&self) -> &dyn Any {
636        self
637    }
638
639    /// Returns a reference to the server database configuration, if available.
640    ///
641    /// # Returns
642    /// - `Some(&ServerDatabase)` if the database is configured.
643    /// - `None` if no database configuration is present.
644    #[cfg(feature = "memory-database")]
645    fn database_with_name(&self, name: &str) -> Result<Arc<DatabaseConnection>> {
646        let database = self
647            .database
648            .clone()
649            .ok_or_else(|| ServerError::InvalidState("Database not initialized".into()))?;
650
651        for database in database.databases {
652            if database.name == name {
653                return Ok(database.connection);
654            }
655        }
656
657        Err(ServerError::Database("Database not found".into()))
658    }
659
660    /// Returns a reference to the server database configuration, if available.
661    ///
662    /// # Returns
663    /// - `Some(&ServerDatabase)` if the database is configured.
664    /// - `None` if no database configuration is present.
665    #[cfg(not(feature = "memory-database"))]
666    fn database_with_name(&self, name: &str) -> Result<DatabaseConnection> {
667        let database = self
668            .database
669            .as_ref()
670            .ok_or_else(|| ServerError::InvalidState("Database not initialized".into()))?;
671
672        for database in &database.databases {
673            if database.name == name {
674                return Ok(database.connection.clone());
675            }
676        }
677
678        Err(ServerError::Database("Database not found".into()))
679    }
680
681    /// Returns a reference to the server settings, if available.
682    ///
683    /// # Returns
684    /// - `Some(&Settings)` if the settings are configured.
685    /// - `None` if no settings are present.
686    fn settings(&self) -> &Settings {
687        self.settings
688            .as_ref()
689            .expect("Settings must be initialized before calling settings()")
690    }
691
692    /// Returns a reference to the BigQuery client, if available.
693    ///
694    /// # Returns
695    /// - `Some(&BigQueryClient)` if the BigQuery client is configured.
696    /// - `None` if no BigQuery client configuration is present.
697    fn bigquery(&self) -> Option<&bigquery::BigQueryClient> {
698        self.database.as_ref().and_then(|db| db.bigquery.as_ref())
699    }
700
701    /// Returns a boolean indicating whether the server is currently running.
702    ///
703    /// # Returns
704    /// - `true` if the server is running.
705    /// - `false` if the server is not running.
706    fn is_running(&self) -> bool {
707        self.running
708    }
709
710    /// Validates the JWT token in the given request and checks whether the
711    /// associated roles match the provided list.
712    ///
713    /// # Parameters
714    /// - `request`: The request containing the JWT token to validate.
715    /// - `roles`: A list of roles to check against the JWT token.
716    ///
717    /// # Returns
718    /// - `Ok(())` if the JWT token is valid and the roles match.
719    /// - `Err(securityity::oauth2::OAuth2Error)` if the JWT token is invalid or the roles do
720    ///   not match.
721    fn validate_jwt(
722        &self,
723        request: &ServiceRequest,
724        authorize: String,
725    ) -> security::oauth2::Result<()> {
726        let token: &str = request
727            .headers()
728            .get(header::AUTHORIZATION)
729            .and_then(|value| value.to_str().ok())
730            .ok_or_else(|| {
731                security::oauth2::OAuth2Error::InvalidJwt("Invalid JWT Header in request.".into())
732            })?
733            .trim_start_matches("Bearer ");
734
735        // Retrieves the server settings required to proceed with the security configuration
736        let settings = self.settings.as_ref().ok_or_else(|| {
737            warn!("Settings not configured.");
738            security::oauth2::OAuth2Error::Configuration("Settings not configured.".into())
739        })?;
740
741        // Validate JWT
742        security::oauth2::validate_jwt(token, settings, authorize)?;
743
744        Ok(())
745    }
746}
747
748/// A type alias for a `Result` with the `ServerError` error type.
749pub type Result<T, E = ServerError> = std::result::Result<T, E>;
750
751/// Represents all possible errors that can occur within the server lifecycle.
752///
753/// `ServerError` centralizes failures related to server state management,
754/// configuration validation, runtime availability, and database interaction.
755/// It is designed to provide clear, descriptive messages for logging and
756/// error propagation across the framework.
757///
758/// # Variants
759///
760/// - `InvalidState` — The server is in an unexpected or inconsistent state.
761/// - `Configuration` — The provided server configuration is invalid or incomplete.
762/// - `RuntimeNotFound` — The Tokio runtime could not be located or accessed.
763/// - `Database` — A database-related error occurred during server operation.
764/// - `NotInitialized` — An operation was attempted before the server was initialized.
765/// - `AlreadyInitialized` — Initialization was attempted more than once.
766///
767/// # Usage
768///
769/// This error type is typically used as the unified error for server setup,
770/// startup, and runtime operations.
771///
772/// # Example
773///
774/// ```no_run
775/// use rust_microservice::ServerError;
776///
777/// fn start_server() -> Result<(), ServerError> {
778///     // Example failure
779///     Err(ServerError::NotInitialized)
780/// }
781/// ```
782#[derive(Debug, Error)]
783pub enum ServerError {
784    #[error("Invalid server state: {0}")]
785    InvalidState(String),
786
787    #[error("Invalid server configuration: {0}")]
788    Configuration(String),
789
790    #[error("Tokio runtime not found. Details: {0}")]
791    RuntimeNotFound(String),
792
793    #[error("Server database error: {0}")]
794    Database(String),
795
796    #[error("Server is not initialized.")]
797    NotInitialized,
798
799    #[error("Server is already initialized. The new instance will be ignored.")]
800    AlreadyInitialized,
801}