diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..f745259 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,325 @@ +// src/main.rs +//! Главный модуль сервера Futriix +//! +//! Точка входа в приложение, инициализирует сервер и запускает его. +//! Использует wait-free архитектуру с lock-free структурами данных. + +mod common; +mod server; +mod lua_shell; + +use std::env; +use std::fs::OpenOptions; +use std::io::Write; + +use crate::common::FutriixError; + +/// Функция для логирования в файл +fn log_to_file(message: &str) { + match OpenOptions::new() + .create(true) + .append(true) + .open("futriix.log") + { + Ok(mut file) => { + // ИСПРАВЛЕНИЕ: Используем системное время с миллисекундами + let timestamp = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f").to_string(); + let log_message = format!("[{}] {}\n", timestamp, message); + let _ = file.write_all(log_message.as_bytes()); + } + Err(e) => eprintln!("Failed to write to log file: {}", e), + } +} + +/// Простая структура для аргументов командной строки +struct Args { + config: String, + debug: bool, + http_port: Option, + https_port: Option, + host: Option, +} + +/// Простой парсер аргументов командной строки +fn parse_args() -> Args { + let mut args = Args { + config: "config.toml".to_string(), + debug: false, + http_port: None, + https_port: None, + host: None, + }; + + let mut iter = env::args().skip(1); + while let Some(arg) = iter.next() { + match arg.as_str() { + "--config" | "-c" => { + if let Some(value) = iter.next() { + args.config = value; + } + } + "--debug" | "-d" => { + args.debug = true; + } + "--http-port" => { + if let Some(value) = iter.next() { + if let Ok(port) = value.parse() { + args.http_port = Some(port); + } + } + } + "--https-port" => { + if let Some(value) = iter.next() { + if let Ok(port) = value.parse() { + args.https_port = Some(port); + } + } + } + "--host" => { + if let Some(value) = iter.next() { + args.host = Some(value); + } + } + _ => { + if arg.starts_with("--config=") { + args.config = arg.trim_start_matches("--config=").to_string(); + } else if arg.starts_with("-c=") { + args.config = arg.trim_start_matches("-c=").to_string(); + } + } + } + } + + args +} + +/// Функция для вывода текста с ANSI цветом +fn print_colored(text: &str, ansi_color: &str) { + println!("{}{}\x1b[0m", ansi_color, text); +} + +/// Функция для вывода текста с цветом #00bfff для проекта futriix +fn print_futriix(text: &str) { + let futriix_color = "\x1b[38;2;0;191;255m"; // ANSI код для #00bfff + println!("{}{}\x1b[0m", futriix_color, text); +} + +/// Конвертация HEX цвета в ANSI escape code +fn hex_to_ansi(hex_color: &str) -> String { + let hex = hex_color.trim_start_matches('#'); + + if hex.len() == 6 { + if let (Ok(r), Ok(g), Ok(b)) = ( + u8::from_str_radix(&hex[0..2], 16), + u8::from_str_radix(&hex[2..4], 16), + u8::from_str_radix(&hex[4..6], 16), + ) { + return format!("\x1b[38;2;{};{};{}m", r, g, b); + } + } + + "\x1b[38;2;255;255;255m".to_string() +} + +/// Функция для получения цвета #00bfff как строки ANSI кода для проекта futriix +fn get_futriix_color() -> String { + "\x1b[38;2;0;191;255m".to_string() +} + +/// Структура для хранения информации о дистрибутиве Linux +#[derive(Debug)] +struct LinuxDistribution { + name: String, + version: String, + pretty_name: String, +} + +impl LinuxDistribution { + /// Создает строку с полным именем дистрибутива и версией + fn full_name(&self) -> String { + if !self.version.is_empty() { + format!("{} {}", self.name, self.version) + } else { + self.name.clone() + } + } +} + +/// Получение информации о дистрибутиве Linux +/// Читает информацию из файлов /etc/os-release или /etc/lsb-release +fn get_linux_distribution() -> LinuxDistribution { + // Инициализируем структуру значениями по умолчанию + let mut distro = LinuxDistribution { + name: "Linux".to_string(), + version: String::new(), + pretty_name: "Linux".to_string(), + }; + + // Попробуем прочитать /etc/os-release (современные дистрибутивы) + if let Ok(content) = std::fs::read_to_string("/etc/os-release") { + for line in content.lines() { + if line.starts_with("PRETTY_NAME=") { + // Убираем PRETTY_NAME= и кавычки + let name = line.trim_start_matches("PRETTY_NAME="); + distro.pretty_name = name.trim_matches('"').to_string(); + } else if line.starts_with("NAME=") { + let name = line.trim_start_matches("NAME="); + distro.name = name.trim_matches('"').to_string(); + } else if line.starts_with("VERSION=") || line.starts_with("VERSION_ID=") { + // Предпочитаем VERSION_ID для номеров версий, но используем VERSION если есть + let version_line = if line.starts_with("VERSION_ID=") { + line.trim_start_matches("VERSION_ID=") + } else { + line.trim_start_matches("VERSION=") + }; + distro.version = version_line.trim_matches('"').to_string(); + } + } + + // Если нашли pretty_name, но не нашли отдельное имя - используем pretty_name + if distro.name == "Linux" && distro.pretty_name != "Linux" { + // Попробуем извлечь имя и версию из pretty_name + let pretty = &distro.pretty_name; + if let Some(pos) = pretty.find(char::is_numeric) { + distro.name = pretty[..pos].trim().to_string(); + distro.version = pretty[pos..].trim().to_string(); + } else { + distro.name = pretty.clone(); + } + } + } + + // Если не нашли версию в /etc/os-release, попробуем /etc/lsb-release + if distro.version.is_empty() { + if let Ok(content) = std::fs::read_to_string("/etc/lsb-release") { + for line in content.lines() { + if line.starts_with("DISTRIB_DESCRIPTION=") { + let name = line.trim_start_matches("DISTRIB_DESCRIPTION="); + distro.pretty_name = name.trim_matches('"').to_string(); + + // Попробуем извлечь версию из описания + let desc = &distro.pretty_name; + if let Some(pos) = desc.find(char::is_numeric) { + distro.name = desc[..pos].trim().to_string(); + distro.version = desc[pos..] + .chars() + .take_while(|c| c.is_numeric() || *c == '.') + .collect::(); + } + } else if line.starts_with("DISTRIB_ID=") && distro.name == "Linux" { + let name = line.trim_start_matches("DISTRIB_ID="); + distro.name = name.trim_matches('"').to_string(); + } else if line.starts_with("DISTRIB_RELEASE=") { + let version = line.trim_start_matches("DISTRIB_RELEASE="); + distro.version = version.trim_matches('"').to_string(); + } + } + } + } + + // Если все еще не нашли версию, попробуем прочитать /etc/issue + if distro.version.is_empty() { + if let Ok(content) = std::fs::read_to_string("/etc/issue") { + // Берем первую строку + if let Some(first_line) = content.lines().next() { + let line = first_line.trim(); + + // Ищем номер версии в строке + if let Some(pos) = line.find(char::is_numeric) { + distro.name = line[..pos].trim().to_string(); + // Извлекаем версию (первые цифры до пробела) + let version_part = &line[pos..]; + distro.version = version_part + .chars() + .take_while(|c| c.is_numeric() || *c == '.') + .collect::(); + } else { + distro.name = line.to_string(); + } + + // Если pretty_name еще не установлен, используем имя из issue + if distro.pretty_name == "Linux" { + distro.pretty_name = distro.name.clone(); + } + } + } + } + + // Для Arch Linux, которая не всегда показывает версию в os-release + if distro.name.to_lowercase().contains("arch") && distro.version.is_empty() { + // Архивные версии Arch Linux не имеют номеров версий, но можно проверить наличие файла pacman + if std::path::Path::new("/etc/pacman.conf").exists() { + distro.version = "Rolling Release".to_string(); + } + } + + // Убираем возможные дублирования имени в версии + if !distro.version.is_empty() && distro.version.starts_with(&distro.name) { + distro.version = distro.version.trim_start_matches(&distro.name).trim().to_string(); + } + + // Очищаем версию от нечисловых символов в конце + if !distro.version.is_empty() { + let cleaned_version: String = distro.version + .chars() + .take_while(|c| c.is_numeric() || *c == '.') + .collect(); + if !cleaned_version.is_empty() { + distro.version = cleaned_version; + } + } + + distro +} + +#[tokio::main] +async fn main() -> Result<(), FutriixError> { + // Инициализация логирования в файл + log_to_file("Starting Futriix server"); + + // Вывод приветственного сообщения с цветом #00bfff для проекта futriix + let futriix_color = get_futriix_color(); + println!(); // Добавляем пустую строку перед фразой + print_futriix("Futriix Database Server"); + print_futriix("futriix 3i²(by 26.11.2025)"); + println!(); // Добавляем пустую строку после фразы + + // Парсим аргументы командной строки + let args = parse_args(); + let config_path = args.config; + + let message = format!("Loading configuration from: {}", config_path); + print_futriix(&message); + + // Добавляем пустую строку после сообщения о загрузке конфигурации + println!(); + + // Выводим информацию о дистрибутиве Linux с версией + let distro = get_linux_distribution(); + print_futriix(&format!("Running on: {}", distro.full_name())); + + log_to_file(&message); + log_to_file(&format!("Linux distribution: {} {}", distro.name, distro.version)); + + // Создание и запуск сервера + match server::FutriixServer::new(&config_path).await { + Ok(server) => { + log_to_file("Server created successfully"); + if let Err(e) = server.run().await { + let error_message = format!("Server error: {}", e); + eprintln!("{}", error_message); + log_to_file(&error_message); + std::process::exit(1); + } + } + Err(e) => { + let error_message = format!("Failed to create server: {}", e); + eprintln!("{}", error_message); + log_to_file(&error_message); + std::process::exit(1); + } + } + + log_to_file("Futriix server stopped"); + Ok(()) +}