pub fn coerce_value(raw: &str) -> serde_json::Value {
if raw.is_empty() {
return serde_json::Value::Null;
}
if raw.eq_ignore_ascii_case("true") {
return serde_json::Value::Bool(true);
}
if raw.eq_ignore_ascii_case("false") {
return serde_json::Value::Bool(false);
}
if raw.eq_ignore_ascii_case("null") {
return serde_json::Value::Null;
}
if let Ok(n) = raw.parse::<i64>() {
return serde_json::Value::Number(n.into());
}
if let Ok(f) = raw.parse::<f64>() {
if f.is_finite() {
if let Some(n) = serde_json::Number::from_f64(f) {
return serde_json::Value::Number(n);
}
}
}
serde_json::Value::String(raw.to_string())
}
pub fn parse_query_string(query: &str) -> serde_json::Map<String, serde_json::Value> {
let mut map = serde_json::Map::new();
if query.is_empty() {
return map;
}
for pair in query.split('&') {
if pair.is_empty() {
continue;
}
let (key, raw_value) = match pair.split_once('=') {
Some((k, v)) => (k, v),
None => (pair, ""),
};
let key = url_decode(key);
let raw_value = url_decode(raw_value);
map.insert(key, coerce_value(&raw_value));
}
map
}
pub fn query_string_to_json(query: &str) -> serde_json::Value {
serde_json::Value::Object(parse_query_string(query))
}
pub fn cookies_to_json(cookie_header: &str) -> serde_json::Value {
let mut map = serde_json::Map::new();
if cookie_header.is_empty() {
return serde_json::Value::Object(map);
}
for cookie in cookie_header.split(';') {
let cookie = cookie.trim();
if cookie.is_empty() {
continue;
}
let (name, value) = match cookie.split_once('=') {
Some((n, v)) => (n.trim(), v.trim()),
None => (cookie.trim(), ""),
};
map.insert(name.to_string(), coerce_value(value));
}
serde_json::Value::Object(map)
}
use serde::Serialize;
vld::schema! {
#[derive(Debug, Clone, Serialize)]
pub struct ErrorBody {
pub error: String => vld::string(),
}
}
vld::schema! {
#[derive(Debug, Clone, Serialize)]
pub struct ErrorWithMessage {
pub error: String => vld::string(),
pub message: String => vld::string(),
}
}
vld::schema! {
#[derive(Debug, Clone, Serialize)]
pub struct ValidationIssue {
pub path: String => vld::string(),
pub message: String => vld::string(),
}
}
vld::schema! {
#[derive(Debug, Clone, Serialize)]
pub struct ValidationIssueWithCode {
pub path: String => vld::string(),
pub message: String => vld::string(),
pub code: String => vld::string(),
}
}
vld::schema! {
#[derive(Debug, Clone, Serialize)]
pub struct ValidationErrorBody {
pub error: String => vld::string(),
pub issues: Vec<ValidationIssue> => vld::array(vld::nested(ValidationIssue::parse_value)),
}
}
pub fn format_issues(err: &vld::error::VldError) -> Vec<ValidationIssue> {
err.issues
.iter()
.map(|i| {
let path: String = i
.path
.iter()
.map(|p| p.to_string())
.collect::<Vec<_>>()
.join(".");
ValidationIssue {
path,
message: i.message.clone(),
}
})
.collect()
}
pub fn format_vld_error(err: &vld::error::VldError) -> serde_json::Value {
let body = ValidationErrorBody {
error: "Validation failed".into(),
issues: format_issues(err),
};
serde_json::to_value(body).expect("ValidationErrorBody serialization cannot fail")
}
pub fn format_issues_with_code(err: &vld::error::VldError) -> Vec<ValidationIssueWithCode> {
err.issues
.iter()
.map(|issue| {
let path: String = issue
.path
.iter()
.map(|p| p.to_string())
.collect::<Vec<_>>()
.join("");
ValidationIssueWithCode {
path,
message: issue.message.clone(),
code: issue.code.key().to_string(),
}
})
.collect()
}
pub fn url_decode(input: &str) -> String {
let s = input.replace('+', " ");
let mut result = String::with_capacity(s.len());
let mut chars = s.chars();
while let Some(c) = chars.next() {
if c == '%' {
let hex: String = chars.by_ref().take(2).collect();
if let Ok(byte) = u8::from_str_radix(&hex, 16) {
result.push(byte as char);
} else {
result.push('%');
result.push_str(&hex);
}
} else {
result.push(c);
}
}
result
}
pub fn format_json_parse_error(message: &str) -> serde_json::Value {
serde_json::to_value(ErrorWithMessage {
error: "Invalid JSON".into(),
message: message.into(),
})
.expect("ErrorWithMessage serialization cannot fail")
}
pub fn format_utf8_error() -> serde_json::Value {
serde_json::to_value(ErrorBody {
error: "Invalid UTF-8".into(),
})
.expect("ErrorBody serialization cannot fail")
}
pub fn format_payload_too_large() -> serde_json::Value {
serde_json::to_value(ErrorBody {
error: "Payload too large".into(),
})
.expect("ErrorBody serialization cannot fail")
}
pub fn format_generic_error(error: &str) -> serde_json::Value {
serde_json::to_value(ErrorBody {
error: error.into(),
})
.expect("ErrorBody serialization cannot fail")
}
pub fn extract_path_param_names(pattern: &str) -> Vec<String> {
let mut names = Vec::new();
let mut chars = pattern.chars().peekable();
while let Some(c) = chars.next() {
if c == '{' {
let name: String = chars.by_ref().take_while(|&c| c != '}').collect();
if !name.is_empty() {
names.push(name);
}
}
}
names
}