[go: up one dir, main page]

corn 0.2.0

corn is a socket server free to chat & a web server display files
Documentation
use std::net::{TcpListener, TcpStream, UdpSocket};
use std::io::{Read, Write, self};
use std::sync::{Arc, Mutex};
use std::collections::HashMap;
use std::thread;
use std::fs::OpenOptions;
use std::time::SystemTime;
use std::fmt::Write as FmtWrite;

type ClientMap = Arc<Mutex<HashMap<String, TcpStream>>>;
type LogFile = Arc<Mutex<io::BufWriter<std::fs::File>>>;

// ‌Logging Function‌
fn log_message(log_file: &LogFile, msg: &str) {
    let mut file = log_file.lock().unwrap();
    let timestamp = SystemTime::now()
        .duration_since(SystemTime::UNIX_EPOCH)
        .unwrap()
        .as_secs();
    
    let mut log_entry = String::new();
    write!(&mut log_entry, "[{}] {}", timestamp, msg).unwrap();
    
    file.write_all(log_entry.as_bytes()).unwrap();
    file.flush().unwrap();
    print!("{}", msg);
}

fn get_local_ip() -> String {
    UdpSocket::bind("0.0.0.0:0")
        .and_then(|s| {
            s.connect("8.8.8.8:80")?;
            Ok(s.local_addr()?.ip().to_string())
        })
        .unwrap_or_else(|_| "0.0.0.0".into())
}

fn handle_client(mut stream: TcpStream, clients: ClientMap, log_file: LogFile) {
    let client_addr = stream.peer_addr().unwrap().to_string();
    let connect_msg = format!("[Connect‌ed] {}\n", client_addr);
    log_message(&log_file, &connect_msg);

    // ‌Add Client
    {
        let mut clients = clients.lock().unwrap();
        clients.insert(client_addr.clone(), stream.try_clone().unwrap());
    }

    let mut buffer = [0; 1024];
    loop {
        match stream.read(&mut buffer) {
            Ok(0) => break,
            Ok(n) => {
                // ‌Construct a message with address prefix‌
                let raw_msg = String::from_utf8_lossy(&buffer[..n]);
                let server_msg = format!("[Message] {}: {}\n", client_addr, raw_msg.trim());
                let broadcast_msg = format!("[{}] {}\n", client_addr, raw_msg.trim());
                
                // Log server-side activities‌
                log_message(&log_file, &server_msg);

                // ‌Broadcast to other clients‌
                let mut clients = clients.lock().unwrap();
                clients.retain(|addr, client| {
                    if addr == &client_addr { return true; }
                    client.write_all(broadcast_msg.as_bytes()).is_ok()
                });
            }
            Err(e) => {
                let err_msg = format!("[Error] {}: {}\n", client_addr, e);
                log_message(&log_file, &err_msg);
                break;
            }
        }
    }

    // Remove client
    clients.lock().unwrap().remove(&client_addr);
    let disconnect_msg = format!("‌[Disconnected]‌ {}\n", client_addr);
    log_message(&log_file, &disconnect_msg);
}

fn start(path: &str) -> io::Result<()> {
    // ‌Initialize the log file‌
    let file = OpenOptions::new()
        .create(true)
        .append(true)
        .open(path)?;
    let log_file = Arc::new(Mutex::new(io::BufWriter::new(file)));
    
    let listener = TcpListener::bind("0.0.0.0:0")?;
    let clients: ClientMap = Arc::new(Mutex::new(HashMap::new()));
    
    let local_ip = get_local_ip();
    let port = listener.local_addr()?.port();
    let startup_msg = format!("[Chat Server] {}:{}\n", local_ip, port);
    log_message(&log_file, &startup_msg);

    for stream in listener.incoming() {
        match stream {
            Ok(stream) => {
                let clients = Arc::clone(&clients);
                let log_file = Arc::clone(&log_file);
                thread::spawn(move || handle_client(stream, clients, log_file));
            }
            Err(e) => {
                let err_msg = format!("‌[Error] Failed to accept connection‌: {}\n", e);
                log_message(&log_file, &err_msg);
            }
        }
    }
    Ok(())
}

// use mod.rs 


use std::path::Path;
use std::fs;

/// Start the socket chat server.
///
/// # Examples
///
/// 
/// ```
/// use corn::start_chat_server;
///
/// fn main() {
///     println!("Hello, world!");
///     start_chat_server()
/// }
/// ```
pub fn start_chat_server(){
    let path_text = "./runtime/chat/";
    let dir_path = Path::new(&path_text);
    if dir_path.exists() && dir_path.is_dir() {
        // println!("Directory already exists: {}", path_text);
    } else {
        // println!("Directory does not exist or path is invalid: {}", path_text);
        if let Err(e) = fs::create_dir_all(dir_path) {
            eprintln!("Failed to create directory: {}", e);
        } else {
            // println!("Directory created successfully: {}", path_text);
        }
    }
    let _ = start(&(path_text.to_owned() + "rust_chat_server.log"));
}