use axum::http::StatusCode;
pub const MAX_CONTENT_BYTES: usize = 256 * 1024;
pub const MAX_QUERY_BYTES: usize = 16 * 1024;
pub const MAX_IDENT_BYTES: usize = 256;
pub const MAX_TOP_K: usize = 1000;
fn bad(reason: impl Into<String>) -> (StatusCode, String) {
(StatusCode::BAD_REQUEST, reason.into())
}
pub fn check_content(content: &str) -> Result<(), (StatusCode, String)> {
if content.is_empty() {
return Err(bad("content must not be empty"));
}
if content.len() > MAX_CONTENT_BYTES {
return Err(bad(format!(
"content exceeds {MAX_CONTENT_BYTES} bytes ({} given)",
content.len()
)));
}
Ok(())
}
pub fn check_query(q: &str) -> Result<(), (StatusCode, String)> {
if q.is_empty() {
return Err(bad("query must not be empty"));
}
if q.len() > MAX_QUERY_BYTES {
return Err(bad(format!(
"query exceeds {MAX_QUERY_BYTES} bytes ({} given)",
q.len()
)));
}
Ok(())
}
pub fn check_short_ident(name: &str, value: &str) -> Result<(), (StatusCode, String)> {
if value.is_empty() {
return Err(bad(format!("{name} must not be empty")));
}
if value.len() > MAX_IDENT_BYTES {
return Err(bad(format!(
"{name} exceeds {MAX_IDENT_BYTES} bytes ({} given)",
value.len()
)));
}
if value.bytes().any(|b| b.is_ascii_control()) {
return Err(bad(format!("{name} contains control characters")));
}
Ok(())
}
pub fn check_top_k(top_k: Option<usize>) -> Result<(), (StatusCode, String)> {
match top_k {
None => Ok(()),
Some(0) => Err(bad("top_k must be ≥ 1")),
Some(k) if k > MAX_TOP_K => Err(bad(format!("top_k exceeds {MAX_TOP_K} ({k} given)"))),
Some(_) => Ok(()),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn content_bounds() {
assert!(check_content("").is_err());
assert!(check_content("hi").is_ok());
let big = "x".repeat(MAX_CONTENT_BYTES + 1);
assert!(check_content(&big).is_err());
}
#[test]
fn query_bounds() {
assert!(check_query("").is_err());
assert!(check_query("rust").is_ok());
let big = "q".repeat(MAX_QUERY_BYTES + 1);
assert!(check_query(&big).is_err());
}
#[test]
fn short_ident_rejects_control_chars() {
assert!(check_short_ident("namespace", "personal").is_ok());
assert!(check_short_ident("namespace", "").is_err());
assert!(check_short_ident("namespace", "bad\nname").is_err());
let big = "n".repeat(MAX_IDENT_BYTES + 1);
assert!(check_short_ident("namespace", &big).is_err());
}
#[test]
fn top_k_bounds() {
assert!(check_top_k(None).is_ok());
assert!(check_top_k(Some(0)).is_err());
assert!(check_top_k(Some(10)).is_ok());
assert!(check_top_k(Some(MAX_TOP_K)).is_ok());
assert!(check_top_k(Some(MAX_TOP_K + 1)).is_err());
}
}