use std::borrow::Cow;
use std::ops::{Deref, DerefMut};
use serde::ser::{Error as SerError, SerializeSeq};
use serde::{Serialize, Serializer};
pub trait QueryItem {
fn query_item(&self) -> Result<(&str, Cow<str>), crate::Error>;
}
#[derive(Debug, Clone)]
pub struct Query<T>(pub Vec<T>);
impl<T> Default for Query<T> {
fn default() -> Query<T> {
Query(Vec::new())
}
}
impl<T> Query<T> {
#[inline]
pub fn with(mut self, item: T) -> Self {
self.0.push(item);
self
}
}
impl<T> Deref for Query<T> {
type Target = Vec<T>;
fn deref(&self) -> &Vec<T> {
&self.0
}
}
impl<T> DerefMut for Query<T> {
fn deref_mut(&mut self) -> &mut Vec<T> {
&mut self.0
}
}
impl<T> Serialize for Query<T>
where
T: QueryItem,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut seq = serializer.serialize_seq(Some(self.len()))?;
for e in &self.0 {
let item = e.query_item().map_err(SerError::custom)?;
seq.serialize_element(&item)?;
}
seq.end()
}
}
#[cfg(test)]
pub mod test {
use super::*;
use crate::Error;
#[derive(Debug)]
#[allow(dead_code)]
enum MyQueryItem {
Foo(String),
Bar(bool),
Baz(String),
}
impl QueryItem for MyQueryItem {
fn query_item(&self) -> Result<(&str, Cow<str>), Error> {
Ok(match self {
MyQueryItem::Foo(s) => ("foo", Cow::Borrowed(s)),
MyQueryItem::Bar(b) => ("bar", b.to_string().into()),
_ => unreachable!(),
})
}
}
#[test]
fn test_query() {
let mut q = Query::default();
let _ = q.push(MyQueryItem::Bar(true));
let _ = q.push(MyQueryItem::Foo("foo1".into()));
let _ = q.push(MyQueryItem::Foo("foo2".into()));
let s = serde_urlencoded::to_string(q).unwrap();
assert_eq!(&s, "bar=true&foo=foo1&foo=foo2");
}
#[test]
fn test_query_empty() {
let q: Query<MyQueryItem> = Query::default();
let s = serde_urlencoded::to_string(q).unwrap();
assert_eq!(&s, "");
}
}