use serde::{Serialize, Serializer};
use crate::error::Error;
#[doc(hidden)]
pub fn serialize_comma_separated_opt<S, T>(v: &Option<Vec<T>>, s: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
T: Serialize,
{
match v {
Some(values) => serialize_comma_separated(values, s),
None => s.serialize_none(),
}
}
#[doc(hidden)]
pub fn serialize_comma_separated<S, T>(values: &[T], s: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
T: Serialize,
{
let mut parts: Vec<String> = Vec::with_capacity(values.len());
for value in values {
let json = serde_json::to_string(value).map_err(serde::ser::Error::custom)?;
let trimmed = json
.strip_prefix('"')
.and_then(|t| t.strip_suffix('"'))
.unwrap_or(&json);
parts.push(trimmed.to_string());
}
s.serialize_str(&parts.join(","))
}
pub fn encode_query<P: Serialize>(params: &P) -> Result<String, Error> {
let value = serde_json::to_value(params)
.map_err(|e| Error::Builder(format!("query encode failed: {e}")))?;
let mut out = String::new();
encode_value("", &value, &mut out);
Ok(out)
}
fn encode_value(key: &str, value: &serde_json::Value, out: &mut String) {
match value {
serde_json::Value::Null => {}
serde_json::Value::Object(map) => {
for (k, v) in map {
encode_value(k, v, out);
}
}
serde_json::Value::Array(items) => {
for item in items {
encode_value(key, item, out);
}
}
serde_json::Value::String(s) => append_pair(out, key, s),
serde_json::Value::Number(n) => append_pair(out, key, &n.to_string()),
serde_json::Value::Bool(b) => append_pair(out, key, if *b { "true" } else { "false" }),
}
}
fn append_pair(out: &mut String, key: &str, value: &str) {
if !out.is_empty() {
out.push('&');
}
out.push_str(&percent_encode_query(key));
out.push('=');
out.push_str(&percent_encode_query(value));
}
fn percent_encode_query(s: &str) -> String {
let mut out = String::with_capacity(s.len());
for &b in s.as_bytes() {
match b {
b'A'..=b'Z' | b'a'..=b'z' | b'0'..=b'9' | b'-' | b'.' | b'_' | b'~' => {
out.push(b as char);
}
b' ' => out.push('+'),
_ => out.push_str(&format!("%{b:02X}")),
}
}
out
}