kotoba 0.1.3

GP2-based Graph Rewriting Language - ISO GQL-compliant queries, MVCC+Merkle persistence, and distributed execution
Documentation
//! Kotoba HTTP Server
//!
//! このバイナリは.kotoba.jsonまたは.kotobaファイル(Jsonnet形式)で設定されたHTTPサーバーを起動します。

use clap::{Arg, Command};
use kotoba::http::{HttpConfig, ServerConfig, HttpRoute, HttpMiddleware, HttpMethod};
use kotoba::http::server::{HttpServer, ServerBuilder};
use kotoba::storage::{MVCCManager, MerkleDAG};
use kotoba::rewrite::RewriteEngine;
use std::sync::Arc;
use std::path::Path;
use tokio;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    env_logger::init();

    let matches = Command::new("kotoba-server")
        .version("0.1.0")
        .author("Kotoba Team")
        .about("Kotoba GP2 Graph Rewrite HTTP Server")
        .arg(
            Arg::new("config")
                .short('c')
                .long("config")
                .value_name("FILE")
                .help("Sets the configuration file (.kotoba.json or .kotoba - Jsonnet format)"),
        )
        .arg(
            Arg::new("host")
                .long("host")
                .value_name("HOST")
                .help("Sets the server host (default: 127.0.0.1)")
                .default_value("127.0.0.1"),
        )
        .arg(
            Arg::new("port")
                .short('p')
                .long("port")
                .value_name("PORT")
                .help("Sets the server port (default: 8080)")
                .default_value("8080"),
        )
        .arg(
            Arg::new("example")
                .long("example")
                .value_name("TYPE")
                .help("Creates and runs an example server (ping|api)"),
        )
        .get_matches();

    // ストレージとエンジンの初期化
    let mvcc = Arc::new(MVCCManager::new());
    let merkle = Arc::new(MerkleDAG::new());
    let rewrite_engine = Arc::new(RewriteEngine::new());

    let mut server = if let Some(example) = matches.get_one::<String>("example") {
        // 例のサーバーを作成
        create_example_server(example, mvcc, merkle, rewrite_engine).await?
    } else if let Some(config_path) = matches.get_one::<String>("config") {
        // 設定ファイルからサーバーを作成
        println!("Loading configuration from: {}", config_path);
        ServerBuilder::new()
            .with_config_file(config_path)
            .build(mvcc, merkle, rewrite_engine)
            .await?
    } else {
        eprintln!("Error: Either --config <FILE> or --example <TYPE> must be specified");
        eprintln!();
        eprintln!("For more information, try: {} --help", env!("CARGO_PKG_NAME"));
        std::process::exit(1);
    };

    // サーバーを起動
    println!("🚀 Starting Kotoba HTTP Server...");
    server.start().await?;
    println!("✅ Server started successfully!");

    // サーバーの情報を表示
    let status = server.get_status().await;
    println!("📡 Listening on {}:{}", status.host, status.port);
    println!("📊 Routes: {}, Middlewares: {}", status.routes_count, status.middlewares_count);

    // メインループを実行
    println!("🔄 Ready to accept connections...");
    server.run().await?;

    Ok(())
}

/// 例のサーバーを作成
async fn create_example_server(
    example_type: &str,
    mvcc: Arc<MVCCManager>,
    merkle: Arc<MerkleDAG>,
    rewrite_engine: Arc<RewriteEngine>,
) -> Result<HttpServer, Box<dyn std::error::Error>> {
    match example_type {
        "ping" => create_ping_server(mvcc, merkle, rewrite_engine).await,
        "api" => create_api_server(mvcc, merkle, rewrite_engine).await,
        _ => Err("Unknown example type".into()),
    }
}

/// シンプルなpingサーバーを作成
async fn create_ping_server(
    mvcc: Arc<MVCCManager>,
    merkle: Arc<MerkleDAG>,
    rewrite_engine: Arc<RewriteEngine>,
) -> Result<HttpServer, Box<dyn std::error::Error>> {
    println!("Creating ping example server...");

    let mut config = HttpConfig::new(ServerConfig {
        host: "127.0.0.1".to_string(),
        port: 8080,
        max_connections: Some(100),
        timeout_ms: Some(30000),
        tls: None,
    });

    // pingルートを追加
    let ping_route = HttpRoute::new(
        "ping_get".to_string(),
        HttpMethod::GET,
        "/ping".to_string(),
        kotoba::types::ContentHash::sha256([1; 32]), // ダミーハッシュ
    );
    config.routes.push(ping_route);

    // ミドルウェアを追加
    let logger_middleware = HttpMiddleware::new(
        "logger".to_string(),
        "request_logger".to_string(),
        100,
        kotoba::types::ContentHash::sha256([2; 32]), // ダミーハッシュ
    );
    config.middlewares.push(logger_middleware);

    HttpServer::new(config, mvcc, merkle, rewrite_engine).await
        .map_err(|e| e.into())
}

/// APIサーバーを作成
async fn create_api_server(
    mvcc: Arc<MVCCManager>,
    merkle: Arc<MerkleDAG>,
    rewrite_engine: Arc<RewriteEngine>,
) -> Result<HttpServer, Box<dyn std::error::Error>> {
    println!("Creating API example server...");

    let mut config = HttpConfig::new(ServerConfig {
        host: "127.0.0.1".to_string(),
        port: 8080,
        max_connections: Some(1000),
        timeout_ms: Some(30000),
        tls: None,
    });

    // 複数のルートを追加
    let routes = vec![
        (HttpMethod::GET, "/health", "health_check"),
        (HttpMethod::GET, "/api/v1/status", "api_status"),
        (HttpMethod::POST, "/api/v1/echo", "echo_handler"),
        (HttpMethod::GET, "/api/v1/users/{id}", "get_user"),
    ];

    for (method, pattern, handler) in routes {
        let route_id = format!("{}_{}", method, pattern.replace('/', "_").replace('{', "").replace('}', ""));
        let route = HttpRoute::new(
            route_id,
            method,
            pattern.to_string(),
            kotoba::types::ContentHash::sha256([3; 32]), // ダミーハッシュ
        );
        config.routes.push(route);
    }

    // 複数のミドルウェアを追加
    let middlewares = vec![
        ("cors", "cors_middleware", 50),
        ("auth", "auth_middleware", 75),
        ("logger", "request_logger", 100),
        ("rate_limit", "rate_limiter", 25),
    ];

    for (name, function, order) in middlewares {
        let middleware = HttpMiddleware::new(
            name.to_string(),
            function.to_string(),
            order,
            kotoba::types::ContentHash::sha256([4; 32]), // ダミーハッシュ
        );
        config.middlewares.push(middleware);
    }

    HttpServer::new(config, mvcc, merkle, rewrite_engine).await
        .map_err(|e| e.into())
}

#[cfg(test)]
mod tests {
    use super::*;

    #[tokio::test]
    async fn test_create_ping_server() {
        let mvcc = Arc::new(MVCCManager::new());
        let merkle = Arc::new(MerkleDAG::new());
        let rewrite_engine = Arc::new(RewriteEngine::new());

        let server = create_ping_server(mvcc, merkle, rewrite_engine).await.unwrap();
        let status = server.get_status().await;

        assert_eq!(status.routes_count, 1);
        assert_eq!(status.middlewares_count, 1);
        assert_eq!(status.port, 8080);
    }

    #[tokio::test]
    async fn test_create_api_server() {
        let mvcc = Arc::new(MVCCManager::new());
        let merkle = Arc::new(MerkleDAG::new());
        let rewrite_engine = Arc::new(RewriteEngine::new());

        let server = create_api_server(mvcc, merkle, rewrite_engine).await.unwrap();
        let status = server.get_status().await;

        assert_eq!(status.routes_count, 4);
        assert_eq!(status.middlewares_count, 4);
        assert_eq!(status.port, 8080);
    }
}