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}