use serde_json::Value;
pub fn required_str(params: &Value, key: &str) -> Result<String, String> {
params
.get(key)
.and_then(|v| v.as_str())
.map(String::from)
.ok_or_else(|| format!("missing required param '{key}'"))
}
pub fn required_nonempty_str(params: &Value, key: &str) -> Result<String, String> {
let v = required_str(params, key)?;
if v.is_empty() {
return Err(format!("param '{key}' must not be empty"));
}
Ok(v)
}
pub fn optional_bool(params: &Value, key: &str, default: bool) -> bool {
params.get(key).and_then(|v| v.as_bool()).unwrap_or(default)
}
pub fn optional_u64(params: &Value, key: &str) -> Option<u64> {
params.get(key).and_then(|v| v.as_u64())
}
pub fn optional_positive_usize(params: &Value, key: &str) -> Result<Option<usize>, String> {
match optional_u64(params, key) {
Some(0) => Err(format!("param '{key}' must be >= 1")),
Some(v) => usize::try_from(v)
.map(Some)
.map_err(|_| format!("param '{key}' is too large")),
None => Ok(None),
}
}
pub fn optional_string_array(params: &Value, key: &str) -> Result<Vec<String>, String> {
let Some(v) = params.get(key) else {
return Ok(vec![]);
};
let arr = v
.as_array()
.ok_or_else(|| format!("param '{key}' must be an array of strings"))?;
let mut out = Vec::with_capacity(arr.len());
for item in arr {
let s = item
.as_str()
.ok_or_else(|| format!("param '{key}' must be an array of strings"))?;
if !s.is_empty() {
out.push(s.to_string());
}
}
Ok(out)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn required_nonempty_str_errors_on_empty() {
let r = required_nonempty_str(&serde_json::json!({"cid": ""}), "cid");
assert!(r.is_err());
}
#[test]
fn optional_string_array_accepts_missing() {
let r = optional_string_array(&serde_json::json!({}), "images").unwrap();
assert!(r.is_empty());
}
#[test]
fn optional_positive_usize_rejects_zero() {
let r = optional_positive_usize(&serde_json::json!({"max": 0}), "max");
assert!(r.is_err());
}
#[test]
fn optional_positive_usize_accepts_missing() {
let r = optional_positive_usize(&serde_json::json!({}), "max").unwrap();
assert!(r.is_none());
}
}