use std::fmt::{Display, Write};
use error::{Error, top_level_must_be_object};
use json::{JsonSerializer, KeySerializerNoQuotes};
use serde::Serialize;
use write::PercentEncoding;
pub mod error;
mod json;
mod write;
#[inline]
pub fn to_writer<W, T>(writer: W, value: &T) -> Result<(), Error>
where
W: Write,
T: ?Sized + Serialize,
{
let ser = Serializer::new(writer);
value.serialize(ser)
}
#[inline]
pub fn to_string<T>(value: &T) -> Result<String, Error>
where
T: ?Sized + Serialize,
{
let mut writer = String::with_capacity(128);
to_writer(&mut writer, value)?;
Ok(writer)
}
#[inline]
pub fn to_vec<T>(value: &T) -> Result<Vec<u8>, Error>
where
T: ?Sized + Serialize,
{
let string = to_string(value)?;
Ok(string.into())
}
#[inline]
pub fn display<T>(value: &T) -> DisplaySerializer<'_, T>
where
T: ?Sized + Serialize,
{
DisplaySerializer { value }
}
#[derive(Debug)]
pub struct DisplaySerializer<'a, V: ?Sized> {
value: &'a V,
}
impl<V> Display for DisplaySerializer<'_, V>
where
V: ?Sized + Serialize,
{
#[inline]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let ser = Serializer::new(f);
self.value.serialize(ser).map_err(|_| std::fmt::Error)
}
}
pub struct Serializer<W> {
output: W,
is_first: bool,
}
impl<W: Write> Serializer<W> {
#[inline]
pub fn new(writer: W) -> Self {
Self {
output: writer,
is_first: true,
}
}
pub fn into_inner(self) -> W {
self.output
}
}
impl<W: Write> serde::Serializer for Serializer<W> {
type Ok = ();
type Error = Error;
type SerializeMap = MapSerializer<W>;
type SerializeStruct = StructSerializer<W>;
type SerializeTupleVariant = TupleVariantSerializer<W>;
type SerializeStructVariant = StructVariantSerializer<W>;
#[inline]
fn serialize_some<T>(self, value: &T) -> Result<Self::Ok, Self::Error>
where
T: ?Sized + Serialize,
{
value.serialize(self)
}
#[inline]
fn serialize_newtype_struct<T>(
self,
_name: &'static str,
value: &T,
) -> Result<Self::Ok, Self::Error>
where
T: ?Sized + Serialize,
{
value.serialize(self)
}
#[inline]
fn serialize_unit(mut self) -> Result<Self::Ok, Self::Error> {
Ok(self.output.write_str("")?)
}
#[inline]
fn serialize_unit_struct(self, _name: &'static str) -> Result<Self::Ok, Self::Error> {
self.serialize_unit()
}
#[inline]
fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
self.serialize_unit()
}
#[inline]
fn serialize_newtype_variant<T>(
mut self,
_name: &'static str,
_variant_index: u32,
variant: &'static str,
value: &T,
) -> Result<Self::Ok, Self::Error>
where
T: ?Sized + Serialize,
{
use serde::ser::SerializeMap as _;
self.serialize_entry(variant, value)
}
#[inline]
fn serialize_tuple_variant(
mut self,
_name: &'static str,
_variant_index: u32,
variant: &'static str,
len: usize,
) -> Result<Self::SerializeTupleVariant, Self::Error> {
use serde::ser::SerializeMap as _;
self.serialize_key(variant)?;
let seq = json::SeqSerializer::new(PercentEncoding::new(self.output), Some(len))?;
Ok(TupleVariantSerializer { inner: seq })
}
#[inline]
fn serialize_struct_variant(
mut self,
_name: &'static str,
_variant_index: u32,
variant: &'static str,
len: usize,
) -> Result<Self::SerializeStructVariant, Self::Error> {
use serde::ser::SerializeMap as _;
self.serialize_key(variant)?;
let object = json::StructSerializer::new(PercentEncoding::new(self.output), Some(len))?;
Ok(StructVariantSerializer { inner: object })
}
#[inline]
fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
Ok(self)
}
#[inline]
fn serialize_struct(
self,
_name: &'static str,
_len: usize,
) -> Result<Self::SerializeStruct, Self::Error> {
Ok(self)
}
error_unsupported! {
top_level_must_be_object, [bool integers char str bytes array]
}
}
pub type MapSerializer<W> = Serializer<W>;
impl<W: Write> serde::ser::SerializeMap for MapSerializer<W> {
type Ok = ();
type Error = Error;
#[inline]
fn serialize_key<T>(&mut self, key: &T) -> Result<(), Self::Error>
where
T: ?Sized + Serialize,
{
if !self.is_first {
self.output.write_str("&")?;
}
key.serialize(KeySerializerNoQuotes {
output: PercentEncoding::new(&mut self.output),
})?;
self.output.write_str("=")?;
Ok(())
}
#[inline]
fn serialize_value<T>(&mut self, value: &T) -> Result<(), Self::Error>
where
T: ?Sized + Serialize,
{
value.serialize(JsonSerializer {
output: PercentEncoding::new(&mut self.output),
is_top_level_value: true,
})?;
self.is_first = false;
Ok(())
}
#[inline]
fn end(self) -> Result<Self::Ok, Self::Error> {
Ok(())
}
}
pub type StructSerializer<W> = Serializer<W>;
impl<W: Write> serde::ser::SerializeStruct for StructSerializer<W> {
type Ok = ();
type Error = Error;
#[inline]
fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error>
where
T: ?Sized + Serialize,
{
use serde::ser::SerializeMap as _;
self.serialize_entry(key, value)
}
#[inline]
fn end(self) -> Result<Self::Ok, Self::Error> {
Ok(())
}
}
pub struct TupleVariantSerializer<W: Write> {
inner: json::SeqSerializer<PercentEncoding<W>>,
}
impl<W: Write> serde::ser::SerializeTupleVariant for TupleVariantSerializer<W> {
type Ok = ();
type Error = Error;
#[inline]
fn serialize_field<T>(&mut self, value: &T) -> Result<(), Self::Error>
where
T: ?Sized + Serialize,
{
use serde::ser::SerializeSeq as _;
self.inner.serialize_element(value)
}
#[inline]
fn end(self) -> Result<Self::Ok, Self::Error> {
use serde::ser::SerializeSeq as _;
self.inner.end()
}
}
pub struct StructVariantSerializer<W: Write> {
inner: json::StructSerializer<PercentEncoding<W>>,
}
impl<W: Write> serde::ser::SerializeStructVariant for StructVariantSerializer<W> {
type Ok = ();
type Error = Error;
#[inline]
fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error>
where
T: ?Sized + serde::Serialize,
{
use serde::ser::SerializeStruct as _;
self.inner.serialize_field(key, value)
}
#[inline]
fn end(self) -> Result<Self::Ok, Self::Error> {
use serde::ser::SerializeStruct as _;
self.inner.end()
}
}
#[cfg(test)]
mod tests {
use crate::error::ErrorInner;
use super::*;
use serde::Serialize;
use std::collections::BTreeMap;
#[derive(Debug, Serialize)]
struct SimplePayload {
id: u64,
name: String,
}
#[derive(Debug, Serialize)]
struct ComplexPayloadFieldValue {
recipient: String,
amount: u32,
}
#[derive(Debug, Serialize)]
struct ComplexPayload {
field: ComplexPayloadFieldValue,
id: u64,
#[serde(skip_serializing_if = "Option::is_none")]
key: Option<String>,
is_active: bool,
}
#[test]
fn test_simple_struct() {
let payload = SimplePayload {
id: 123,
name: "John Doe".to_string(),
};
let result = to_string(&payload).unwrap();
assert_eq!(result, "id=123&name=John%20Doe");
}
#[test]
fn test_special_chars() {
let mut map = BTreeMap::new();
map.insert("key w/ spaces & symbols", "value w/ spaces & symbols");
map.insert("another key", "a=b&c=d");
let result = to_string(&map).unwrap();
assert_eq!(
result,
"another%20key=a%3Db%26c%3Dd&key%20w%2F%20spaces%20%26%20symbols=value%20w%2F%20spaces%20%26%20symbols"
);
}
#[test]
fn test_empty_struct() {
#[derive(Serialize)]
struct Empty;
let result = to_string(&Empty).unwrap();
assert_eq!(result, "");
}
#[test]
fn test_complex_field_json_encoding() {
let payload = ComplexPayload {
field: ComplexPayloadFieldValue {
recipient: "Victor + Sons".to_string(),
amount: 100,
},
id: 12345,
key: Some("api_key_123".to_string()),
is_active: true,
};
let result = to_string(&payload).unwrap();
let expected_field_value =
"%7B%22recipient%22%3A%22Victor%20%2B%20Sons%22%2C%22amount%22%3A100%7D";
let expected = format!(
"field={}&id=12345&key=api_key_123&is_active=true",
expected_field_value
);
assert_eq!(result, expected);
}
#[test]
fn test_top_level_enum_variants() {
#[derive(Serialize)]
enum Status {
Pending,
Complete(u32),
Error { code: u32, message: String },
List(Vec<String>),
}
let pending = Status::Pending;
assert_eq!(
to_string(&pending).unwrap_err().inner,
ErrorInner::NotAnObject("UnitVariant")
);
let complete = Status::Complete(404);
assert_eq!(to_string(&complete).unwrap(), "Complete=404");
let error = Status::Error {
code: 500,
message: "Server Issue".to_string(),
};
let expected_error_val = "%7B%22code%22%3A500%2C%22message%22%3A%22Server%20Issue%22%7D";
assert_eq!(
to_string(&error).unwrap(),
format!("Error={}", expected_error_val)
);
let list = Status::List(vec!["item 1".to_string(), "item/2".to_string()]);
let expected_list_val = "%5B%22item%201%22%2C%22item%2F2%22%5D";
assert_eq!(
to_string(&list).unwrap(),
format!("List={}", expected_list_val)
);
}
#[test]
fn test_skip_none_field() {
let payload = ComplexPayload {
field: ComplexPayloadFieldValue {
recipient: "test".to_string(),
amount: 50,
},
id: 99,
key: None,
is_active: false,
};
let result = to_string(&payload).unwrap();
let expected_field_value = "%7B%22recipient%22%3A%22test%22%2C%22amount%22%3A50%7D";
let expected = format!("field={}&id=99&is_active=false", expected_field_value);
assert_eq!(result, expected);
}
#[test]
fn test_top_level_errors() {
let err_int = to_string(&123).unwrap_err();
assert_eq!(err_int.inner, ErrorInner::NotAnObject("i32"));
let err_seq = to_string(&vec![1, 2, 3]).unwrap_err();
assert_eq!(err_seq.inner, ErrorInner::NotAnObject("Seq"));
#[derive(Serialize)]
enum MyEnum {
Unit,
}
let err_enum = to_string(&MyEnum::Unit).unwrap_err();
assert_eq!(err_enum.inner, ErrorInner::NotAnObject("UnitVariant"));
assert_eq!(
to_string(&123).unwrap_err().inner,
ErrorInner::NotAnObject("i32")
);
assert_eq!(
to_string("a str").unwrap_err().inner,
ErrorInner::NotAnObject("str")
);
assert_eq!(
to_string(&vec![1, 2]).unwrap_err().inner,
ErrorInner::NotAnObject("Seq")
);
assert_eq!(
to_string(&(1, 4)).unwrap_err().inner,
ErrorInner::NotAnObject("Tuple")
);
}
}