751 lines
26 KiB
Rust
751 lines
26 KiB
Rust
//! Конфигурационный модуль для flusql
|
||
//!
|
||
//! Этот модуль отвечает за загрузку и управление конфигурацией
|
||
//! сервера flusql из файла config.toml и переменных окружения.
|
||
|
||
use serde::{Deserialize, Serialize};
|
||
use std::fs;
|
||
use std::path::Path;
|
||
use thiserror::Error;
|
||
|
||
/// Основная конфигурация flusql
|
||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||
pub struct Config {
|
||
/// Общие настройки сервера
|
||
#[serde(default = "default_server_config")]
|
||
pub server: ServerConfig,
|
||
|
||
/// Настройки базы данных
|
||
#[serde(default = "default_database_config")]
|
||
pub database: DatabaseConfig,
|
||
|
||
/// Настройки логгера
|
||
#[serde(default = "default_logging_config")]
|
||
pub logging: LoggingConfig,
|
||
|
||
/// Настройки Lua интерпретатора
|
||
#[serde(default = "default_lua_config")]
|
||
pub lua: LuaConfig,
|
||
|
||
/// Настройки кластера
|
||
#[serde(default = "default_cluster_config")]
|
||
pub cluster: ClusterConfig,
|
||
|
||
/// Настройки плагинов
|
||
#[serde(default = "default_plugins_config")]
|
||
pub plugins: PluginsConfig,
|
||
|
||
/// Настройки HTTP сервера (если включен)
|
||
#[serde(default = "default_http_config")]
|
||
pub http: HttpConfig,
|
||
|
||
/// Настройки репликации
|
||
#[serde(default = "default_replication_config")]
|
||
pub replication: ReplicationConfig,
|
||
|
||
/// Настройки сети
|
||
#[serde(default = "default_network_config")]
|
||
pub network: NetworkConfig,
|
||
}
|
||
|
||
/// Конфигурация сети
|
||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||
pub struct NetworkConfig {
|
||
/// IP-адрес для прослушивания
|
||
#[serde(default = "default_network_host")]
|
||
pub host: String,
|
||
|
||
/// Порт для прослушивания
|
||
#[serde(default = "default_network_port")]
|
||
pub port: u16,
|
||
|
||
/// Разрешить удаленные подключения
|
||
#[serde(default = "default_allow_remote")]
|
||
pub allow_remote: bool,
|
||
|
||
/// Таймаут соединения в секундах
|
||
#[serde(default = "default_connection_timeout")]
|
||
pub connection_timeout: u64,
|
||
|
||
/// Максимальное количество соединений
|
||
#[serde(default = "default_max_connections")]
|
||
pub max_connections: u32,
|
||
|
||
/// Размер буфера для сетевых операций в байтах
|
||
#[serde(default = "default_buffer_size")]
|
||
pub buffer_size: usize,
|
||
}
|
||
|
||
/// Значения по умолчанию для NetworkConfig
|
||
fn default_network_config() -> NetworkConfig {
|
||
NetworkConfig {
|
||
host: default_network_host(),
|
||
port: default_network_port(),
|
||
allow_remote: default_allow_remote(),
|
||
connection_timeout: default_connection_timeout(),
|
||
max_connections: default_max_connections(),
|
||
buffer_size: default_buffer_size(),
|
||
}
|
||
}
|
||
|
||
fn default_network_host() -> String { "127.0.0.1".to_string() }
|
||
fn default_network_port() -> u16 { 8080 }
|
||
fn default_allow_remote() -> bool { false }
|
||
fn default_connection_timeout() -> u64 { 30 }
|
||
fn default_max_connections() -> u32 { 100 }
|
||
fn default_buffer_size() -> usize { 8192 }
|
||
|
||
/// Конфигурация плагинов
|
||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||
pub struct PluginsConfig {
|
||
/// Включена ли система плагинов
|
||
#[serde(default = "default_plugins_enabled")]
|
||
pub enabled: bool,
|
||
|
||
/// Директория для плагинов
|
||
#[serde(default = "default_plugins_dir")]
|
||
pub plugins_dir: String,
|
||
|
||
/// Включить изоляцию плагинов (sandbox)
|
||
#[serde(default = "default_sandbox_enabled")]
|
||
pub sandbox_enabled: bool,
|
||
|
||
/// Максимальное количество плагинов
|
||
#[serde(default = "default_max_plugins")]
|
||
pub max_plugins: usize,
|
||
|
||
/// Автозагрузка плагинов при старте
|
||
#[serde(default = "default_auto_load")]
|
||
pub auto_load: bool,
|
||
|
||
/// Включить горячую перезагрузку плагинов
|
||
#[serde(default = "default_hot_reload")]
|
||
pub hot_reload: bool,
|
||
|
||
/// Таймаут выполнения плагина в секундах
|
||
#[serde(default = "default_plugin_timeout")]
|
||
pub plugin_timeout_sec: u64,
|
||
|
||
/// Максимальный размер памяти плагина в МБ
|
||
#[serde(default = "default_max_memory_mb")]
|
||
pub max_memory_mb: u64,
|
||
|
||
/// Разрешенные API для плагинов
|
||
#[serde(default = "default_allowed_apis")]
|
||
pub allowed_apis: Vec<String>,
|
||
|
||
/// Запрещенные функции Lua
|
||
#[serde(default = "default_blocked_functions")]
|
||
pub blocked_functions: Vec<String>,
|
||
}
|
||
|
||
/// Значения по умолчанию для PluginsConfig
|
||
fn default_plugins_config() -> PluginsConfig {
|
||
PluginsConfig {
|
||
enabled: default_plugins_enabled(),
|
||
plugins_dir: default_plugins_dir(),
|
||
sandbox_enabled: default_sandbox_enabled(),
|
||
max_plugins: default_max_plugins(),
|
||
auto_load: default_auto_load(),
|
||
hot_reload: default_hot_reload(),
|
||
plugin_timeout_sec: default_plugin_timeout(),
|
||
max_memory_mb: default_max_memory_mb(),
|
||
allowed_apis: default_allowed_apis(),
|
||
blocked_functions: default_blocked_functions(),
|
||
}
|
||
}
|
||
|
||
fn default_plugins_enabled() -> bool { true }
|
||
fn default_plugins_dir() -> String { "./plugins".to_string() }
|
||
fn default_sandbox_enabled() -> bool { true }
|
||
fn default_max_plugins() -> usize { 50 }
|
||
fn default_auto_load() -> bool { true }
|
||
fn default_hot_reload() -> bool { false }
|
||
fn default_plugin_timeout() -> u64 { 30 }
|
||
fn default_max_memory_mb() -> u64 { 100 }
|
||
fn default_allowed_apis() -> Vec<String> {
|
||
vec![
|
||
"database".to_string(),
|
||
"table".to_string(),
|
||
"query".to_string(),
|
||
"index".to_string(),
|
||
"event".to_string(),
|
||
"log".to_string(),
|
||
]
|
||
}
|
||
fn default_blocked_functions() -> Vec<String> {
|
||
vec![
|
||
"io.popen".to_string(),
|
||
"os.execute".to_string(),
|
||
"os.exit".to_string(),
|
||
"debug.debug".to_string(),
|
||
"debug.getregistry".to_string(),
|
||
"debug.setmetatable".to_string(),
|
||
]
|
||
}
|
||
|
||
/// Конфигурация HTTP сервера
|
||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||
pub struct HttpConfig {
|
||
/// Включен ли HTTP сервер
|
||
pub enabled: bool,
|
||
|
||
/// Хост для HTTP сервера
|
||
#[serde(default = "default_http_host")]
|
||
pub host: String,
|
||
|
||
/// Порт HTTP сервера
|
||
#[serde(default = "default_http_port")]
|
||
pub port: u16,
|
||
|
||
/// Порт HTTPS сервера
|
||
#[serde(default = "default_https_port")]
|
||
pub https_port: u16,
|
||
|
||
/// Включена ли поддержка HTTP/2
|
||
#[serde(default)]
|
||
pub http2_enabled: bool,
|
||
|
||
/// Включена ли поддержка TLS
|
||
#[serde(default)]
|
||
pub tls_enabled: bool,
|
||
|
||
/// Путь к сертификату TLS
|
||
#[serde(default)]
|
||
pub tls_cert_path: Option<String>,
|
||
|
||
/// Путь к приватному ключу TLS
|
||
#[serde(default)]
|
||
pub tls_key_path: Option<String>,
|
||
}
|
||
|
||
/// Значения по умолчанию для HttpConfig
|
||
fn default_http_config() -> HttpConfig {
|
||
HttpConfig {
|
||
enabled: false,
|
||
host: default_http_host(),
|
||
port: default_http_port(),
|
||
https_port: default_https_port(),
|
||
http2_enabled: false,
|
||
tls_enabled: false,
|
||
tls_cert_path: None,
|
||
tls_key_path: None,
|
||
}
|
||
}
|
||
|
||
fn default_http_host() -> String { "127.0.0.1".to_string() }
|
||
fn default_http_port() -> u16 { 8080 }
|
||
fn default_https_port() -> u16 { 8443 }
|
||
|
||
/// Конфигурация репликации
|
||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||
pub struct ReplicationConfig {
|
||
/// Включена ли репликация
|
||
pub enabled: bool,
|
||
|
||
/// Режим репликации
|
||
#[serde(default = "default_replication_mode")]
|
||
pub mode: String,
|
||
|
||
/// Мастер-сервер для репликации
|
||
#[serde(default)]
|
||
pub master: Option<String>,
|
||
|
||
/// Список слейв-серверов
|
||
#[serde(default)]
|
||
pub slaves: Vec<String>,
|
||
}
|
||
|
||
/// Значения по умолчанию для ReplicationConfig
|
||
fn default_replication_config() -> ReplicationConfig {
|
||
ReplicationConfig {
|
||
enabled: false,
|
||
mode: default_replication_mode(),
|
||
master: None,
|
||
slaves: vec![],
|
||
}
|
||
}
|
||
|
||
fn default_replication_mode() -> String { "async".to_string() }
|
||
|
||
/// Конфигурация сервера
|
||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||
pub struct ServerConfig {
|
||
/// Порт сервера
|
||
#[serde(default = "default_server_port")]
|
||
pub port: u16,
|
||
|
||
/// Хост сервера
|
||
#[serde(default = "default_server_host")]
|
||
pub host: String,
|
||
|
||
/// Максимальное количество одновременных соединений
|
||
#[serde(default = "default_server_max_connections")]
|
||
pub max_connections: u32,
|
||
|
||
/// Таймаут соединения в секундах
|
||
#[serde(default = "default_server_timeout")]
|
||
pub timeout: u64,
|
||
|
||
/// Размер пула потоков
|
||
#[serde(default = "default_thread_pool_size")]
|
||
pub thread_pool_size: usize,
|
||
|
||
/// Включить отладку
|
||
#[serde(default = "default_debug_enabled")]
|
||
pub debug: bool,
|
||
|
||
/// Путь к PID файлу
|
||
#[serde(default)]
|
||
pub pid_file: Option<String>,
|
||
}
|
||
|
||
/// Значения по умолчанию для ServerConfig
|
||
fn default_server_config() -> ServerConfig {
|
||
ServerConfig {
|
||
port: default_server_port(),
|
||
host: default_server_host(),
|
||
max_connections: default_server_max_connections(),
|
||
timeout: default_server_timeout(),
|
||
thread_pool_size: default_thread_pool_size(),
|
||
debug: default_debug_enabled(),
|
||
pid_file: None,
|
||
}
|
||
}
|
||
|
||
fn default_server_port() -> u16 { 5432 }
|
||
fn default_server_host() -> String { "127.0.0.1".to_string() }
|
||
fn default_server_max_connections() -> u32 { 100 }
|
||
fn default_server_timeout() -> u64 { 30 }
|
||
fn default_thread_pool_size() -> usize { 4 }
|
||
fn default_debug_enabled() -> bool { false }
|
||
|
||
/// Конфигурация базы данных
|
||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||
pub struct DatabaseConfig {
|
||
/// Директория для хранения данных
|
||
#[serde(default = "default_data_dir")]
|
||
pub data_dir: String,
|
||
|
||
/// Автоматически создавать базу данных при первом подключении
|
||
#[serde(default = "default_auto_create")]
|
||
pub auto_create: bool,
|
||
|
||
/// Режим транзакций
|
||
#[serde(default = "default_transaction_mode")]
|
||
pub transaction_mode: String,
|
||
|
||
/// Размер кэша в МБ
|
||
#[serde(default = "default_cache_size")]
|
||
pub cache_size_mb: u64,
|
||
|
||
/// Размер страницы в байтах
|
||
#[serde(default = "default_page_size")]
|
||
pub page_size: u32,
|
||
|
||
/// Включить MVCC (Multi-Version Concurrency Control)
|
||
#[serde(default = "default_mvcc_enabled")]
|
||
pub mvcc_enabled: bool,
|
||
|
||
/// Включить WAL (Write-Ahead Logging)
|
||
#[serde(default = "default_wal_enabled")]
|
||
pub wal_enabled: bool,
|
||
|
||
/// Максимальный размер WAL в МБ
|
||
#[serde(default = "default_max_wal_size")]
|
||
pub max_wal_size_mb: u64,
|
||
|
||
/// Автоматическая проверка целостности при запуске
|
||
#[serde(default = "default_integrity_check")]
|
||
pub integrity_check: bool,
|
||
|
||
/// Частота автоматического сохранения в секундах
|
||
#[serde(default = "default_auto_save_interval")]
|
||
pub auto_save_interval: u64,
|
||
|
||
/// Максимальное количество открытых файлов БД
|
||
#[serde(default = "default_max_open_files")]
|
||
pub max_open_files: u32,
|
||
}
|
||
|
||
/// Значения по умолчанию для DatabaseConfig
|
||
fn default_database_config() -> DatabaseConfig {
|
||
DatabaseConfig {
|
||
data_dir: default_data_dir(),
|
||
auto_create: default_auto_create(),
|
||
transaction_mode: default_transaction_mode(),
|
||
cache_size_mb: default_cache_size(),
|
||
page_size: default_page_size(),
|
||
mvcc_enabled: default_mvcc_enabled(),
|
||
wal_enabled: default_wal_enabled(),
|
||
max_wal_size_mb: default_max_wal_size(),
|
||
integrity_check: default_integrity_check(),
|
||
auto_save_interval: default_auto_save_interval(),
|
||
max_open_files: default_max_open_files(),
|
||
}
|
||
}
|
||
|
||
fn default_data_dir() -> String { "./data".to_string() }
|
||
fn default_auto_create() -> bool { true }
|
||
fn default_transaction_mode() -> String { "write_ahead_log".to_string() }
|
||
fn default_cache_size() -> u64 { 100 }
|
||
fn default_page_size() -> u32 { 8192 } // 8KB страницы по умолчанию
|
||
fn default_mvcc_enabled() -> bool { true }
|
||
fn default_wal_enabled() -> bool { true }
|
||
fn default_max_wal_size() -> u64 { 100 }
|
||
fn default_integrity_check() -> bool { true }
|
||
fn default_auto_save_interval() -> u64 { 60 } // 60 секунд
|
||
fn default_max_open_files() -> u32 { 1000 }
|
||
|
||
/// Конфигурация логгера
|
||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||
pub struct LoggingConfig {
|
||
/// Уровень логирования
|
||
#[serde(default = "default_log_level")]
|
||
pub level: String,
|
||
|
||
/// Путь к файлу логов
|
||
#[serde(default = "default_log_path")]
|
||
pub log_file: String,
|
||
|
||
/// Максимальный размер файла логов в МБ
|
||
#[serde(default = "default_max_log_size")]
|
||
pub max_size_mb: u64,
|
||
|
||
/// Количество ротируемых файлов
|
||
#[serde(default = "default_backup_count")]
|
||
pub backup_count: u32,
|
||
|
||
/// Формат логов
|
||
#[serde(default = "default_log_format")]
|
||
pub format: String,
|
||
|
||
/// Включить логирование в stdout
|
||
#[serde(default = "default_stdout_enabled")]
|
||
pub stdout_enabled: bool,
|
||
|
||
/// Включить логирование в stderr
|
||
#[serde(default = "default_stderr_enabled")]
|
||
pub stderr_enabled: bool,
|
||
|
||
/// Включить логирование SQL запросов
|
||
#[serde(default = "default_sql_logging")]
|
||
pub sql_logging: bool,
|
||
|
||
/// Включить медленный лог (запросы дольше N секунд)
|
||
#[serde(default)]
|
||
pub slow_query_threshold_sec: Option<u64>,
|
||
}
|
||
|
||
/// Значения по умолчанию для LoggingConfig
|
||
fn default_logging_config() -> LoggingConfig {
|
||
LoggingConfig {
|
||
level: default_log_level(),
|
||
log_file: default_log_path(),
|
||
max_size_mb: default_max_log_size(),
|
||
backup_count: default_backup_count(),
|
||
format: default_log_format(),
|
||
stdout_enabled: default_stdout_enabled(),
|
||
stderr_enabled: default_stderr_enabled(),
|
||
sql_logging: default_sql_logging(),
|
||
slow_query_threshold_sec: None,
|
||
}
|
||
}
|
||
|
||
fn default_log_level() -> String { "info".to_string() }
|
||
fn default_log_path() -> String { "flusql.log".to_string() }
|
||
fn default_max_log_size() -> u64 { 10 }
|
||
fn default_backup_count() -> u32 { 5 }
|
||
fn default_log_format() -> String { "json".to_string() }
|
||
fn default_stdout_enabled() -> bool { true }
|
||
fn default_stderr_enabled() -> bool { false }
|
||
fn default_sql_logging() -> bool { true }
|
||
|
||
/// Конфигурация Lua интерпретатора
|
||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||
pub struct LuaConfig {
|
||
/// Включен ли Lua интерпретатор
|
||
#[serde(default = "default_lua_enabled")]
|
||
pub enabled: bool,
|
||
|
||
/// Путь к директории со скриптами
|
||
#[serde(default = "default_lua_scripts_dir")]
|
||
pub scripts_dir: String,
|
||
|
||
/// Максимальное время выполнения скрипта в секундах
|
||
#[serde(default = "default_lua_timeout")]
|
||
pub timeout_seconds: u64,
|
||
|
||
/// Максимальная память для Lua VM в МБ
|
||
#[serde(default = "default_lua_memory_limit")]
|
||
pub memory_limit_mb: u64,
|
||
|
||
/// Разрешить доступ к файловой системе
|
||
#[serde(default = "default_lua_filesystem_access")]
|
||
pub filesystem_access: bool,
|
||
|
||
/// Разрешить сетевые операции
|
||
#[serde(default = "default_lua_network_access")]
|
||
pub network_access: bool,
|
||
|
||
/// Список разрешенных модулей
|
||
#[serde(default = "default_lua_allowed_modules")]
|
||
pub allowed_modules: Vec<String>,
|
||
}
|
||
|
||
/// Значения по умолчанию для LuaConfig
|
||
fn default_lua_config() -> LuaConfig {
|
||
LuaConfig {
|
||
enabled: default_lua_enabled(),
|
||
scripts_dir: default_lua_scripts_dir(),
|
||
timeout_seconds: default_lua_timeout(),
|
||
memory_limit_mb: default_lua_memory_limit(),
|
||
filesystem_access: default_lua_filesystem_access(),
|
||
network_access: default_lua_network_access(),
|
||
allowed_modules: default_lua_allowed_modules(),
|
||
}
|
||
}
|
||
|
||
fn default_lua_enabled() -> bool { true }
|
||
fn default_lua_scripts_dir() -> String { "./lua-scripts".to_string() }
|
||
fn default_lua_timeout() -> u64 { 30 }
|
||
fn default_lua_memory_limit() -> u64 { 100 }
|
||
fn default_lua_filesystem_access() -> bool { false }
|
||
fn default_lua_network_access() -> bool { false }
|
||
fn default_lua_allowed_modules() -> Vec<String> {
|
||
vec![
|
||
"string".to_string(),
|
||
"table".to_string(),
|
||
"math".to_string(),
|
||
"os".to_string(),
|
||
]
|
||
}
|
||
|
||
/// Конфигурация кластера
|
||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||
pub struct ClusterConfig {
|
||
/// Включен ли режим кластера
|
||
#[serde(default = "default_cluster_enabled")]
|
||
pub enabled: bool,
|
||
|
||
/// Идентификатор узла
|
||
#[serde(default = "default_node_id")]
|
||
pub node_id: String,
|
||
|
||
/// Адрес узла
|
||
#[serde(default = "default_node_address")]
|
||
pub node_address: String,
|
||
|
||
/// Режим кластера
|
||
#[serde(default = "default_cluster_mode")]
|
||
pub mode: String,
|
||
|
||
/// Список узлов кластера
|
||
#[serde(default)]
|
||
pub nodes: Vec<String>,
|
||
|
||
/// Интервал heartbeat в секундах
|
||
#[serde(default = "default_heartbeat_interval")]
|
||
pub heartbeat_interval: u64,
|
||
|
||
/// Таймаут heartbeat в секундах
|
||
#[serde(default = "default_heartbeat_timeout")]
|
||
pub heartbeat_timeout: u64,
|
||
|
||
/// Включить автоматическое восстановление
|
||
#[serde(default = "default_auto_recovery")]
|
||
pub auto_recovery: bool,
|
||
|
||
/// Максимальное количество реплик
|
||
#[serde(default = "default_max_replicas")]
|
||
pub max_replicas: u32,
|
||
}
|
||
|
||
/// Значения по умолчанию для ClusterConfig
|
||
fn default_cluster_config() -> ClusterConfig {
|
||
ClusterConfig {
|
||
enabled: default_cluster_enabled(),
|
||
node_id: default_node_id(),
|
||
node_address: default_node_address(),
|
||
mode: default_cluster_mode(),
|
||
nodes: vec![],
|
||
heartbeat_interval: default_heartbeat_interval(),
|
||
heartbeat_timeout: default_heartbeat_timeout(),
|
||
auto_recovery: default_auto_recovery(),
|
||
max_replicas: default_max_replicas(),
|
||
}
|
||
}
|
||
|
||
fn default_cluster_enabled() -> bool { false }
|
||
fn default_node_id() -> String { "node_1".to_string() }
|
||
fn default_node_address() -> String { "127.0.0.1:8080".to_string() }
|
||
fn default_cluster_mode() -> String { "single".to_string() }
|
||
fn default_heartbeat_interval() -> u64 { 5 }
|
||
fn default_heartbeat_timeout() -> u64 { 30 }
|
||
fn default_auto_recovery() -> bool { true }
|
||
fn default_max_replicas() -> u32 { 3 }
|
||
|
||
impl Default for Config {
|
||
fn default() -> Self {
|
||
Self {
|
||
server: default_server_config(),
|
||
database: default_database_config(),
|
||
logging: default_logging_config(),
|
||
lua: default_lua_config(),
|
||
cluster: default_cluster_config(),
|
||
plugins: default_plugins_config(),
|
||
http: default_http_config(),
|
||
replication: default_replication_config(),
|
||
network: default_network_config(),
|
||
}
|
||
}
|
||
}
|
||
|
||
impl Config {
|
||
/// Загрузка конфигурации из файла
|
||
pub fn load(path: &str) -> Result<Self, ConfigError> {
|
||
let config_content = fs::read_to_string(path)
|
||
.map_err(|e| ConfigError::IoError(e))?;
|
||
|
||
let config: Config = toml::from_str(&config_content)
|
||
.map_err(|e| ConfigError::ParseError(e))?;
|
||
|
||
Ok(config)
|
||
}
|
||
|
||
/// Создание конфигурации по умолчанию
|
||
pub fn default_with_path(data_dir: &str) -> Self {
|
||
let mut config = Self::default();
|
||
config.database.data_dir = data_dir.to_string();
|
||
config
|
||
}
|
||
|
||
/// Получение пути к директории данных для базы данных
|
||
pub fn get_data_path(&self, db_name: &str) -> String {
|
||
format!("{}/{}", self.database.data_dir, db_name)
|
||
}
|
||
|
||
/// Сохранение конфигурации в файл
|
||
pub fn save(&self, path: &str) -> Result<(), ConfigError> {
|
||
let config_content = toml::to_string_pretty(self)
|
||
.map_err(|e| ConfigError::SerializeError(e))?;
|
||
|
||
// Создаем директорию если она не существует
|
||
if let Some(parent) = Path::new(path).parent() {
|
||
fs::create_dir_all(parent)
|
||
.map_err(|e| ConfigError::IoError(e))?;
|
||
}
|
||
|
||
fs::write(path, config_content)
|
||
.map_err(|e| ConfigError::IoError(e))
|
||
}
|
||
|
||
/// Создание файла конфигурации по умолчанию
|
||
pub fn create_default_config(path: &str) -> Result<(), ConfigError> {
|
||
let default_config = Self::default();
|
||
default_config.save(path)
|
||
}
|
||
|
||
/// Получение конфигурации из переменных окружения или файла
|
||
pub fn from_env_or_file(default_path: &str) -> Result<Self, ConfigError> {
|
||
// Сначала пробуем загрузить из переменной окружения
|
||
if let Ok(config_path) = std::env::var("FLUSQL_CONFIG") {
|
||
return Self::load(&config_path);
|
||
}
|
||
|
||
// Пробуем загрузить из текущей директории
|
||
if Path::new(default_path).exists() {
|
||
return Self::load(default_path);
|
||
}
|
||
|
||
// Пробуем загрузить из домашней директории
|
||
if let Ok(home_dir) = std::env::var("HOME") {
|
||
let home_config = format!("{}/.config/flusql/config.toml", home_dir);
|
||
if Path::new(&home_config).exists() {
|
||
return Self::load(&home_config);
|
||
}
|
||
}
|
||
|
||
// Пробуем загрузить из /etc
|
||
let etc_config = "/etc/flusql/config.toml";
|
||
if Path::new(etc_config).exists() {
|
||
return Self::load(etc_config);
|
||
}
|
||
|
||
// Создаем конфигурацию по умолчанию
|
||
Ok(Self::default())
|
||
}
|
||
|
||
/// Проверка валидности конфигурации
|
||
pub fn validate(&self) -> Result<(), ConfigError> {
|
||
// Проверяем размер страницы (должен быть степенью двойки и в разумных пределах)
|
||
if self.database.page_size < 512 || self.database.page_size > 65536 {
|
||
return Err(ConfigError::Invalid(format!(
|
||
"Page size must be between 512 and 65536 bytes, got {}",
|
||
self.database.page_size
|
||
)));
|
||
}
|
||
|
||
// Проверяем что page_size является степенью двойки
|
||
if self.database.page_size & (self.database.page_size - 1) != 0 {
|
||
return Err(ConfigError::Invalid(format!(
|
||
"Page size must be a power of two, got {}",
|
||
self.database.page_size
|
||
)));
|
||
}
|
||
|
||
// Проверяем порты
|
||
// Исправление: убраны все проверки на > 65535, так как тип u16 гарантирует этот предел
|
||
// Оставляем только проверку на 0 (порт 0 недопустим для сервера)
|
||
if self.server.port == 0 {
|
||
return Err(ConfigError::Invalid(
|
||
"Server port cannot be 0".to_string()
|
||
));
|
||
}
|
||
|
||
if self.http.enabled {
|
||
// Исправление: убраны все проверки на > 65535, так как тип u16 гарантирует этот предел
|
||
// Оставляем только проверку на 0 (порт 0 недопустим для HTTP сервера)
|
||
if self.http.port == 0 {
|
||
return Err(ConfigError::Invalid(
|
||
"HTTP port cannot be 0".to_string()
|
||
));
|
||
}
|
||
|
||
if self.http.tls_enabled {
|
||
if self.http.tls_cert_path.is_none() || self.http.tls_key_path.is_none() {
|
||
return Err(ConfigError::Invalid(
|
||
"TLS requires both certificate and key paths".to_string()
|
||
));
|
||
}
|
||
}
|
||
}
|
||
|
||
// Проверяем директорию данных
|
||
if self.database.data_dir.trim().is_empty() {
|
||
return Err(ConfigError::Invalid("Data directory cannot be empty".to_string()));
|
||
}
|
||
|
||
Ok(())
|
||
}
|
||
}
|
||
|
||
/// Ошибки конфигурации
|
||
#[derive(Debug, Error)]
|
||
pub enum ConfigError {
|
||
#[error("IO error: {0}")]
|
||
IoError(std::io::Error),
|
||
|
||
#[error("Parse error: {0}")]
|
||
ParseError(toml::de::Error),
|
||
|
||
#[error("Serialize error: {0}")]
|
||
SerializeError(toml::ser::Error),
|
||
|
||
#[error("Configuration not found")]
|
||
NotFound,
|
||
|
||
#[error("Invalid configuration: {0}")]
|
||
Invalid(String),
|
||
}
|