fn parse_http_request(raw: &[u8]) -> Result<HttpRequest, ProtocolError> {
let request_str = String::from_utf8_lossy(raw);
let lines: Vec<&str> = request_str.lines().collect();
validate_request_lines(&lines)?;
let (method, path) = parse_request_line(lines[0])?;
let (headers, body_start) = parse_headers(&lines);
let body = parse_body(&lines, body_start);
Ok(HttpRequest {
method,
path,
headers,
body,
})
}
fn validate_request_lines(lines: &[&str]) -> Result<(), ProtocolError> {
if lines.is_empty() {
Err(ProtocolError::InvalidParams("Empty request".to_string()))
} else {
Ok(())
}
}
fn parse_request_line(line: &str) -> Result<(String, String), ProtocolError> {
let request_line: Vec<&str> = line.split_whitespace().collect();
if request_line.len() < 2 {
return Err(ProtocolError::InvalidParams(
"Invalid request line".to_string(),
));
}
Ok((request_line[0].to_string(), request_line[1].to_string()))
}
fn parse_headers(lines: &[&str]) -> (HashMap<String, String>, usize) {
let mut headers = HashMap::new();
let mut body_start = 0;
for (i, line) in lines.iter().enumerate().skip(1) {
if line.is_empty() {
body_start = i + 1;
break;
}
if let Some((key, value)) = line.split_once(": ") {
headers.insert(key.to_string(), value.to_string());
}
}
(headers, body_start)
}
fn parse_body(lines: &[&str], body_start: usize) -> Value {
if body_start >= lines.len() {
return Value::Null;
}
let body_str = lines[body_start..].join("\n");
serde_json::from_str(&body_str).unwrap_or(Value::Null)
}
fn route_to_operation(path: &str, method: &str) -> Result<Operation, ProtocolError> {
match (method, path) {
("GET" | "POST", "/analyze/complexity") => {
Ok(Operation::AnalyzeComplexity(ComplexityParams {
file_path: None,
max_cyclomatic: None,
max_cognitive: None,
}))
}
("GET" | "POST", "/analyze/satd") => Ok(Operation::AnalyzeSatd(SatdParams {
file_path: None,
strict: false,
})),
("GET" | "POST", "/analyze/dead-code") => Ok(Operation::AnalyzeDeadCode(DeadCodeParams {
file_path: None,
include_tests: false,
})),
("POST", "/quality/gate") => Ok(Operation::QualityGate(QualityGateParams {
file_path: None,
fail_on_violation: false,
})),
_ => Err(ProtocolError::UnknownMethod(format!("{method} {path}"))),
}
}