// 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(()) }