omni_orchestrator/config/
mod.rs

1use lazy_static::lazy_static;
2use serde::{Deserialize, Serialize};
3use std::sync::Arc;
4
5/// Configuration for the OmniOrchestrator server application.
6///
7/// This structure defines all the configurable parameters for the server,
8/// including network settings and behavior options. It supports serialization 
9/// to and deserialization from JSON for persistent configuration.
10///
11/// The configuration can be loaded from a file or generated with default
12/// values if no configuration file exists.
13#[derive(Debug, Clone, Serialize, Deserialize)]
14pub struct ServerConfig {
15    /// The port number on which the server will listen
16    pub port: u16,
17    
18    /// The IP address to which the server will bind
19    pub address: String,
20    
21    /// Whether to apply syntax highlighting to SQL logs
22    pub highlight_sql: bool,
23    
24    /// List of other server instances in the cluster
25    pub instances: Vec<Instance>,
26}
27
28/// Represents an instance of the server in the cluster.
29///
30/// This structure contains the network location information for a server
31/// instance that is part of the OmniOrchestrator cluster. It's used for
32/// peer discovery and communication between cluster nodes.
33#[derive(Debug, Clone, Serialize, Deserialize)]
34pub struct Instance {
35    /// The port number on which the instance is listening
36    pub port: u16,
37    
38    /// The hostname or IP address of the instance
39    pub address: String,
40}
41
42/// Default implementation for ServerConfig.
43///
44/// Provides reasonable default values for a server configuration to be
45/// used when no custom configuration is provided or when initializing
46/// a new configuration file.
47impl Default for ServerConfig {
48    fn default() -> Self {
49        Self {
50            port: 8000,
51            address: "127.0.0.1".to_string(),
52            highlight_sql: true,
53            instances: vec![Instance {
54                port: 8000,
55                address: "example.com".to_string(),
56            }],
57        }
58    }
59}
60
61/// Possible errors that can occur during configuration operations.
62///
63/// This enum represents the various error conditions that might arise
64/// when reading from or writing to the configuration file.
65#[derive(Debug, Clone, Serialize, Deserialize)]
66pub enum ConfigError {
67    /// Indicates that the configuration file could not be found
68    FileNotFound,
69    
70    /// Indicates that writing to the configuration file failed
71    FailedToWrite,
72    
73    /// Indicates that parsing the configuration file content failed
74    ParseError,
75}
76
77/// Global static reference to the server configuration.
78///
79/// This lazy_static provides thread-safe access to the server configuration
80/// throughout the application. It is initialized when first accessed,
81/// reading from the configuration file or creating default settings if
82/// no configuration exists.
83///
84/// # Panics
85///
86/// Panics if the configuration cannot be read or written, which would
87/// prevent the server from starting properly.
88lazy_static! {
89    pub static ref SERVER_CONFIG: Arc<ServerConfig> =
90        Arc::new(ServerConfig::read().expect("Failed to initalize server config"));
91}
92
93impl ServerConfig {
94    /// Reads the server configuration from the config file.
95    ///
96    /// Attempts to load the configuration from "config.json" in the current
97    /// directory. If the file doesn't exist or can't be read, it creates a new
98    /// configuration file with default values and returns those defaults.
99    ///
100    /// # Returns
101    ///
102    /// * `Ok(ServerConfig)` - Successfully loaded or created configuration
103    /// * `Err(ConfigError)` - Failed to parse existing configuration
104    ///
105    /// # Error Handling
106    ///
107    /// - If the file doesn't exist, creates a default configuration
108    /// - If the file exists but can't be parsed, returns a ParseError
109    pub fn read() -> Result<Self, ConfigError> {
110        let config_path = "config.json";
111        let config_content = match std::fs::read_to_string(config_path) {
112            Ok(content) => content,
113            Err(_) => {
114                // If file doesn't exist, create a default configuration
115                Self::write_default().expect("Failed to write default config");
116                return Ok(ServerConfig::default());
117            }
118        };
119        
120        // Parse the configuration from JSON
121        let config: ServerConfig = match serde_json5::from_str(&config_content) {
122            Ok(config) => config,
123            Err(_) => return Err(ConfigError::ParseError),
124        };
125        
126        Ok(config)
127    }
128    
129    /// Writes the current configuration to the config file.
130    ///
131    /// Serializes the configuration to JSON and writes it to "config.json"
132    /// in the current directory. This allows configuration changes to persist
133    /// across server restarts.
134    ///
135    /// # Returns
136    ///
137    /// * `Ok(())` - Successfully wrote configuration to file
138    /// * `Err(ConfigError)` - Failed to serialize or write configuration
139    ///
140    /// # Error Handling
141    ///
142    /// - Returns ParseError if serialization to JSON fails
143    /// - Returns FailedToWrite if writing to the file fails
144    pub fn write(&self) -> Result<(), ConfigError> {
145        let config_path = "config.json";
146        
147        // Serialize the configuration to pretty-printed JSON
148        let config_content = match serde_json::to_string_pretty(&self) {
149            Ok(content) => content,
150            Err(_) => return Err(ConfigError::ParseError),
151        };
152        
153        // Write the JSON to the configuration file
154        match std::fs::write(config_path, config_content) {
155            Ok(_) => Ok(()),
156            Err(_) => Err(ConfigError::FailedToWrite),
157        }
158    }
159    
160    /// Creates and writes a default configuration to the config file.
161    ///
162    /// This is a convenience method that creates a ServerConfig with default
163    /// values and writes it to the configuration file. It's typically used
164    /// when no configuration file exists yet.
165    ///
166    /// # Returns
167    ///
168    /// * `Ok(())` - Successfully wrote default configuration to file
169    /// * `Err(ConfigError)` - Failed to write default configuration
170    pub fn write_default() -> Result<(), ConfigError> {
171        let config = ServerConfig::default();
172        config.write()
173    }
174}