use kotoba_core::types::{ContentHash, Result, KotobaError};
use crate::http::ir::*;
use std::fs;
use std::path::Path;
use std::io::Write;
use sha2::{Sha256, Digest};
use tempfile::NamedTempFile;
pub struct HttpConfigParser;
impl HttpConfigParser {
pub fn parse<P: AsRef<Path>>(path: P) -> Result<HttpConfig> {
let path = path.as_ref();
if let Some(ext) = path.extension() {
match ext.to_str() {
Some("json") => Self::parse_kotoba_json(path),
Some("kotoba") => Self::parse_kotoba_file(path),
_ => Err(KotobaError::InvalidArgument(
"Unsupported file extension. Use .json or .kotoba".to_string()
)),
}
} else {
Err(KotobaError::InvalidArgument(
"File must have .json or .kotoba extension".to_string()
))
}
}
pub fn parse_kotoba_json<P: AsRef<Path>>(path: P) -> Result<HttpConfig> {
let content = fs::read_to_string(path)
.map_err(|e| KotobaError::IoError(format!("Failed to read config file: {}", e)))?;
Ok(HttpConfig::new(ServerConfig::default()))
}
pub fn parse_kotoba_file<P: AsRef<Path>>(path: P) -> Result<HttpConfig> {
Ok(HttpConfig::new(ServerConfig::default()))
}
fn convert_from_kotobanet_config(_kotobanet_config: serde_json::Value) -> Result<HttpConfig> {
Ok(HttpConfig::new(ServerConfig::default()))
}
fn convert_server_config(kotobanet_config: &serde_json::Value) -> Result<ServerConfig> {
let host = kotobanet_config
.get("host")
.and_then(|v| v.as_str())
.unwrap_or("127.0.0.1")
.to_string();
let port = kotobanet_config
.get("port")
.and_then(|v| v.as_u64())
.unwrap_or(8080) as u16;
let max_connections = kotobanet_config
.get("max_connections")
.and_then(|v| v.as_u64())
.map(|v| v as usize);
let timeout_ms = kotobanet_config
.get("timeout_ms")
.and_then(|v| v.as_u64());
let tls = None;
let graphql_enabled = kotobanet_config
.get("graphql_enabled")
.and_then(|v| v.as_bool());
Ok(ServerConfig {
host,
port,
max_connections,
timeout_ms,
tls,
graphql_enabled,
})
}
fn convert_route(_route: serde_json::Value) -> Result<HttpRoute> {
Ok(HttpRoute::new(
"default".to_string(),
HttpMethod::GET,
"/".to_string(),
ContentHash::sha256([0u8; 32]),
))
}
fn hash_function(function_def: &str) -> ContentHash {
let mut hasher = Sha256::new();
hasher.update(function_def.as_bytes());
let result = hasher.finalize();
ContentHash(hex::encode(result))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_kotoba_json() {
let config_str = r#"
{
"server": {
"host": "0.0.0.0",
"port": 3000,
"graphql_enabled": true
},
"routes": [
{
"method": "GET",
"path": "/ping",
"handler": "ping_handler",
"authRequired": false,
"corsEnabled": true
}
]
}
"#;
let mut temp_file = NamedTempFile::new().unwrap();
temp_file.write_all(config_str.as_bytes()).unwrap();
let config = HttpConfigParser::parse_kotoba_json(temp_file.path()).unwrap();
assert_eq!(config.server.host, "0.0.0.0");
assert_eq!(config.server.port, 3000);
assert_eq!(config.server.graphql_enabled, Some(true));
assert_eq!(config.routes.len(), 1);
assert_eq!(config.routes[0].method, HttpMethod::GET);
assert_eq!(config.routes[0].pattern, "/ping");
}
#[test]
fn test_parse_kotoba_file() {
let config_str = r#"
{
server: {
host: "127.0.0.1",
port: 4000,
graphql_enabled: false
},
routes: [
{
method: "GET",
path: "/health",
handler: "health_check",
authRequired: false,
corsEnabled: true
}
]
}
"#;
let mut temp_file = NamedTempFile::with_suffix(".kotoba").unwrap();
temp_file.write_all(config_str.as_bytes()).unwrap();
let config = HttpConfigParser::parse_kotoba_file(temp_file.path()).unwrap();
assert_eq!(config.server.host, "127.0.0.1");
assert_eq!(config.server.port, 4000);
assert_eq!(config.server.graphql_enabled, Some(false));
assert_eq!(config.routes.len(), 1);
assert_eq!(config.routes[0].method, HttpMethod::GET);
assert_eq!(config.routes[0].pattern, "/health");
}
}