【Rust】フレームワーク自作: Postメソッドを実装する

getとpostを分ける

use std::fs;
use std::io;
use std::io::prelude::*;
use std::net::{TcpListener, TcpStream};
use std::collections::HashMap;
use crate::main;

pub struct Router {
    get_routes: HashMap<String, String>,
    post_routes: HashMap<String, fn(String) -> String>,
}

impl Router {

    pub fn new() -> Self {
        Router {
            get_routes: HashMap::new(),
            post_routes: HashMap::new(),
        }
    }

    pub fn add_get(&mut self, path: &str, f: fn()-> Option<HashMap<&'static str, &'static str>>){
        let mut content: Option<HashMap<&str, &str>> = Some(HashMap::new());
        content = f();

        let temp_path = format!("./templates/{}.html", path);
        let mut html = fs::read_to_string(temp_path).unwrap();

        match content {
            Some(data) => {
                for (key, value) in data {
                    let k = format!("{{{{ {} }}}}", key);
                    html = html.replace(&k, value);
                }
            },
            None => {},
        }
        let route_path = format!("/{}", path);
        self.get_routes.insert(route_path, html);
    }

    pub fn add_post(&mut self, path: &str, handler: fn(String) -> String) {
        self.post_routes.insert(format!("/{}", path), handler);
    }

    pub fn up(&self, ip: &str) {
        let listenr = TcpListener::bind(ip).unwrap();
        for stream in listenr.incoming() {
            match stream {
                Ok(stream) => {
                    let _ = handle_connection(
                        stream, 
                        self.get_routes.clone(),
                        self.post_routes.clone(),
                    );
                }
                Err(e) => {
                    println!("Connection failed: {}", e);
                }
            }
        }
    }
}

fn handle_connection(mut stream: TcpStream, get_routes: HashMap<String, String>, post_routes: HashMap<String, fn(String) -> String>)  {
    let mut buffer = [0; 1024];
    stream.read(&mut buffer).unwrap();
 
    let request = String::from_utf8_lossy(&buffer);
    let request_line = request.lines().next().unwrap_or("");
    let mut parts = request_line.split_whitespace();
    let method = parts.next().unwrap_or("");
    let path = parts.next().unwrap_or("/");

    println!("Received {} request for {}", method, path);
 

    let response_body = match method {
        "GET" => get_routes.get(path).cloned().unwrap_or_else(|| "<h1>404 Not Found</h1>".to_string()),
        "POST" => {
            let body = request.split("\r\n\r\n").nth(1).unwrap_or("").to_string();
            match post_routes.get(path) {
                Some(handler) => handler(body),
                None => "<h1>404 Not Found</h1>".to_string(),
            }
        }
        _ => "<h1>405 Method Not Allowed</h1>".to_string(),
    };
 
    let response = http_response(200, "text/html", &response_body);
 
    stream.write_all(response.as_bytes()).unwrap();
    stream.flush().unwrap();
 }
 
 fn http_response(status_code: u16, content_type: &str, body: &str) -> String {
    format!(
        "HTTP/1.1 {} {}\r\nContent-Type: {}\r\nContent-Length: {}\r\n\r\n{}",
        status_code,
        get_status_text(status_code),
        content_type,
        body.len(),
        body
    )
 }
 
 fn get_status_text(code: u16) -> &'static str {
    match code {
        200 => "OK",
        404 => "Not Found",
        _ => "Unknown",
    }
 }

### 利用側

use std::collections::HashMap;
mod ares;

fn main() {
    let mut router = ares::Router::new();

    router.add_get("index", handle_index);
    router.add_get("hello", handle_hello);
    router.add_get("world", handle_world);
    router.add_post("submit", handle_post);

    router.up("192.168.33.10:8000");
}


fn handle_index() -> Option<HashMap<&'static str, &'static str>> {

    let mut content1 = HashMap::new();
    content1.insert(
        "title",
        "index page",
    );
    return Some(content1);
}

fn handle_hello() -> Option<HashMap<&'static str, &'static str>> {

    return None;
}

fn handle_world() -> Option<HashMap<&'static str, &'static str>> {

    return None;
}

fn handle_post(body: String) -> String {
    println!("{}", body);
    format!("<h1>POST 受信しました</h1><p>内容: {}</p>", body)
}

ほう…