futriix/src/main.rs

326 lines
13 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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<u16>,
https_port: Option<u16>,
host: Option<String>,
}
/// Простой парсер аргументов командной строки
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::<String>();
}
} 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::<String>();
} 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(())
}