1use std::{borrow::Cow, fmt::Display};
83
84use itertools::Itertools;
85use percent_encoding::{AsciiSet, CONTROLS, PercentEncode};
86use serde::{
87 Serialize,
88 ser::{Impossible, SerializeMap, SerializeSeq, SerializeStruct, SerializeTuple},
89};
90use url::Url;
91
92#[derive(Debug, Clone, Copy, PartialEq, Eq)]
94pub enum QueryStyle {
95 Form { exploded: bool },
100
101 SpaceDelimited,
103
104 PipeDelimited,
106
107 DeepObject,
109}
110
111impl Default for QueryStyle {
112 fn default() -> Self {
113 Self::Form { exploded: true }
114 }
115}
116
117pub struct QuerySerializer<'a> {
120 url: &'a mut Url,
121 style: QueryStyle,
122}
123
124impl<'a> QuerySerializer<'a> {
125 pub fn new(url: &'a mut Url) -> Self {
127 Self {
128 url,
129 style: QueryStyle::default(),
130 }
131 }
132
133 pub fn style(mut self, style: QueryStyle) -> Self {
135 self.style = style;
136 self
137 }
138
139 pub fn append<T: Serialize>(self, name: &str, value: &T) -> Result<Self, QueryParamError> {
141 use ParamSerializerState::*;
142 let style = match self.style {
143 QueryStyle::DeepObject => DeepObject,
144 QueryStyle::Form { exploded: true } => ExplodedForm,
145 QueryStyle::Form { exploded: false } => NonExplodedForm(vec![]),
146 QueryStyle::PipeDelimited => Delimited("|", vec![]),
147 QueryStyle::SpaceDelimited => Delimited(" ", vec![]),
148 };
149 let mut path = KeyPath::new(name);
150 let mut serializer = QueryParamSerializer::new(self.url, &mut path, style);
151 value.serialize(&mut serializer)?;
152 serializer.flush();
153 Ok(self)
154 }
155}
156
157#[derive(Debug)]
158enum ParamSerializerState {
159 Delimited(&'static str, Vec<String>),
161 ExplodedForm,
163 NonExplodedForm(Vec<String>),
165 DeepObject,
167}
168
169#[derive(Clone, Debug)]
170struct KeyPath<'a>(Cow<'a, str>, Vec<Cow<'a, str>>);
171
172impl<'a> KeyPath<'a> {
173 fn new(head: impl Into<Cow<'a, str>>) -> Self {
174 Self(head.into(), vec![])
175 }
176
177 fn len(&self) -> usize {
178 self.1.len() + 1
179 }
180
181 fn push(&mut self, segment: impl Into<Cow<'a, str>>) {
182 self.1.push(segment.into());
183 }
184
185 fn pop(&mut self) -> Cow<'a, str> {
186 self.1.pop().unwrap_or_else(|| self.0.clone())
187 }
188
189 fn first(&self) -> &str {
190 &self.0
191 }
192
193 fn last(&self) -> &str {
194 self.1.last().unwrap_or(&self.0)
195 }
196
197 fn split_first(&self) -> (&str, &[Cow<'a, str>]) {
198 (&self.0, &self.1)
199 }
200}
201
202const COMPONENT: &AsciiSet = &CONTROLS
213 .add(b' ')
214 .add(b'"')
215 .add(b'#')
216 .add(b'<')
217 .add(b'>')
218 .add(b'?')
219 .add(b'`')
220 .add(b'^')
221 .add(b'{')
222 .add(b'}')
223 .add(b'/')
224 .add(b':')
225 .add(b';')
226 .add(b'=')
227 .add(b'@')
228 .add(b'[')
229 .add(b'\\')
230 .add(b']')
231 .add(b'|')
232 .add(b'$')
233 .add(b'%')
234 .add(b'&')
235 .add(b'+')
236 .add(b',');
237
238#[derive(Clone, Debug)]
239enum EncodedOrRaw<'a> {
240 Encoded(PercentEncode<'a>),
241 Raw(&'a str),
242}
243
244impl<'a> EncodedOrRaw<'a> {
245 fn encode(input: &'a str) -> Self {
246 Self::Encoded(percent_encoding::utf8_percent_encode(input, COMPONENT))
247 }
248}
249
250impl Display for EncodedOrRaw<'_> {
251 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
252 match self {
253 Self::Encoded(s) => write!(f, "{s}"),
254 Self::Raw(s) => f.write_str(s),
255 }
256 }
257}
258
259#[derive(Debug)]
260struct PercentEncodeDelimited<'a, T>(&'a [T], EncodedOrRaw<'a>);
261
262impl<T: AsRef<str>> Display for PercentEncodeDelimited<'_, T> {
263 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
264 write!(
265 f,
266 "{}",
267 Itertools::intersperse(
270 self.0
271 .iter()
272 .map(|input| input.as_ref())
273 .map(EncodedOrRaw::encode),
274 self.1.clone()
275 )
276 .format("")
277 )
278 }
279}
280
281#[derive(Debug)]
283struct QueryParamSerializer<'a> {
284 url: &'a mut url::Url,
286 path: &'a mut KeyPath<'a>,
290 state: ParamSerializerState,
291}
292
293impl<'a> QueryParamSerializer<'a> {
294 fn new(url: &'a mut url::Url, path: &'a mut KeyPath<'a>, state: ParamSerializerState) -> Self {
296 Self { url, path, state }
297 }
298
299 fn key(&self) -> Cow<'_, str> {
301 use ParamSerializerState::*;
302 match &self.state {
303 DeepObject => {
304 match self.path.split_first() {
306 (head, []) => head.into(),
307 (head, rest) => format!("{head}[{}]", rest.iter().format("][")).into(),
308 }
309 }
310 ExplodedForm => {
311 self.path.last().into()
313 }
314 NonExplodedForm(_) | Delimited(_, _) => {
315 self.path.first().into()
317 }
318 }
319 }
320
321 fn append<'b>(&mut self, value: impl Into<Cow<'b, str>>) {
323 use ParamSerializerState::*;
324 let value = value.into();
325 match &mut self.state {
326 NonExplodedForm(buf) | Delimited(_, buf) => {
327 buf.push(value.into_owned());
328 }
329 DeepObject | ExplodedForm => {
330 let key = self.key().into_owned();
334 self.url.query_pairs_mut().append_pair(&key, &value);
335 }
336 }
337 }
338
339 fn flush(&mut self) {
344 use ParamSerializerState::*;
345 let (delimiter, buf) = match &mut self.state {
346 NonExplodedForm(buf) => (
347 EncodedOrRaw::Raw(","),
349 std::mem::take(buf),
350 ),
351 Delimited(delimiter, buf) => (
352 EncodedOrRaw::encode(delimiter),
354 std::mem::take(buf),
355 ),
356 _ => return,
357 };
358 if buf.is_empty() {
359 return;
360 }
361
362 let key = self.key();
363 let key = EncodedOrRaw::encode(&key);
364 let value = PercentEncodeDelimited(&buf, delimiter);
365
366 let new_query = match self.url.query().map(|q| q.trim_end_matches('&')) {
370 Some(query) if !query.is_empty() => format!("{query}&{key}={value}"),
371 _ => format!("{key}={value}"),
372 };
373 self.url.set_query(Some(&new_query));
374 }
375}
376
377impl<'a, 'b> serde::Serializer for &'a mut QueryParamSerializer<'b> {
378 type Ok = ();
379 type Error = QueryParamError;
380
381 type SerializeSeq = QuerySeqSerializer<'a, 'b>;
382 type SerializeTuple = QuerySeqSerializer<'a, 'b>;
383 type SerializeTupleStruct = Impossible<(), QueryParamError>;
384 type SerializeTupleVariant = Impossible<(), QueryParamError>;
385 type SerializeMap = QueryStructSerializer<'a, 'b>;
386 type SerializeStruct = QueryStructSerializer<'a, 'b>;
387 type SerializeStructVariant = Impossible<(), QueryParamError>;
388
389 fn serialize_bool(self, v: bool) -> Result<Self::Ok, Self::Error> {
390 self.append(if v { "true" } else { "false" });
391 Ok(())
392 }
393
394 fn serialize_i8(self, v: i8) -> Result<Self::Ok, Self::Error> {
395 self.append(v.to_string());
396 Ok(())
397 }
398
399 fn serialize_i16(self, v: i16) -> Result<Self::Ok, Self::Error> {
400 self.append(v.to_string());
401 Ok(())
402 }
403
404 fn serialize_i32(self, v: i32) -> Result<Self::Ok, Self::Error> {
405 self.append(v.to_string());
406 Ok(())
407 }
408
409 fn serialize_i64(self, v: i64) -> Result<Self::Ok, Self::Error> {
410 self.append(v.to_string());
411 Ok(())
412 }
413
414 fn serialize_u8(self, v: u8) -> Result<Self::Ok, Self::Error> {
415 self.append(v.to_string());
416 Ok(())
417 }
418
419 fn serialize_u16(self, v: u16) -> Result<Self::Ok, Self::Error> {
420 self.append(v.to_string());
421 Ok(())
422 }
423
424 fn serialize_u32(self, v: u32) -> Result<Self::Ok, Self::Error> {
425 self.append(v.to_string());
426 Ok(())
427 }
428
429 fn serialize_u64(self, v: u64) -> Result<Self::Ok, Self::Error> {
430 self.append(v.to_string());
431 Ok(())
432 }
433
434 fn serialize_f32(self, v: f32) -> Result<Self::Ok, Self::Error> {
435 self.append(v.to_string());
436 Ok(())
437 }
438
439 fn serialize_f64(self, v: f64) -> Result<Self::Ok, Self::Error> {
440 self.append(v.to_string());
441 Ok(())
442 }
443
444 fn serialize_char(self, v: char) -> Result<Self::Ok, Self::Error> {
445 self.append(v.to_string());
446 Ok(())
447 }
448
449 fn serialize_str(self, v: &str) -> Result<Self::Ok, Self::Error> {
450 self.append(v);
451 Ok(())
452 }
453
454 fn serialize_bytes(self, _v: &[u8]) -> Result<Self::Ok, Self::Error> {
455 Err(UnsupportedTypeError::Bytes)?
456 }
457
458 fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
459 Ok(())
461 }
462
463 fn serialize_some<T: ?Sized + Serialize>(self, value: &T) -> Result<Self::Ok, Self::Error> {
464 value.serialize(self)
465 }
466
467 fn serialize_unit(self) -> Result<Self::Ok, Self::Error> {
468 Err(UnsupportedTypeError::Unit)?
469 }
470
471 fn serialize_unit_struct(self, name: &'static str) -> Result<Self::Ok, Self::Error> {
472 Err(UnsupportedTypeError::UnitStruct(name))?
473 }
474
475 fn serialize_unit_variant(
476 self,
477 _name: &'static str,
478 _index: u32,
479 variant: &'static str,
480 ) -> Result<Self::Ok, Self::Error> {
481 self.append(variant);
482 Ok(())
483 }
484
485 fn serialize_newtype_struct<T: ?Sized + Serialize>(
486 self,
487 _name: &'static str,
488 value: &T,
489 ) -> Result<Self::Ok, Self::Error> {
490 value.serialize(self)
491 }
492
493 fn serialize_newtype_variant<T: ?Sized + Serialize>(
494 self,
495 name: &'static str,
496 _index: u32,
497 variant: &'static str,
498 _value: &T,
499 ) -> Result<Self::Ok, Self::Error> {
500 Err(UnsupportedTypeError::NewtypeVariant(name, variant))?
501 }
502
503 fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
504 Ok(QuerySeqSerializer {
505 serializer: self,
506 index: 0,
507 })
508 }
509
510 fn serialize_tuple(self, _len: usize) -> Result<Self::SerializeTuple, Self::Error> {
511 Ok(QuerySeqSerializer {
512 serializer: self,
513 index: 0,
514 })
515 }
516
517 fn serialize_tuple_struct(
518 self,
519 name: &'static str,
520 _len: usize,
521 ) -> Result<Self::SerializeTupleStruct, Self::Error> {
522 Err(UnsupportedTypeError::TupleStruct(name))?
523 }
524
525 fn serialize_tuple_variant(
526 self,
527 name: &'static str,
528 _index: u32,
529 variant: &'static str,
530 _len: usize,
531 ) -> Result<Self::SerializeTupleVariant, Self::Error> {
532 Err(UnsupportedTypeError::TupleVariant(name, variant))?
533 }
534
535 fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
536 Ok(QueryStructSerializer { serializer: self })
537 }
538
539 fn serialize_struct(
540 self,
541 _name: &'static str,
542 _len: usize,
543 ) -> Result<Self::SerializeStruct, Self::Error> {
544 Ok(QueryStructSerializer { serializer: self })
545 }
546
547 fn serialize_struct_variant(
548 self,
549 name: &'static str,
550 _index: u32,
551 variant: &'static str,
552 _len: usize,
553 ) -> Result<Self::SerializeStructVariant, Self::Error> {
554 Err(UnsupportedTypeError::StructVariant(name, variant))?
555 }
556}
557
558pub struct QuerySeqSerializer<'a, 'b> {
560 serializer: &'a mut QueryParamSerializer<'b>,
561 index: usize,
562}
563
564impl<'a, 'b> SerializeSeq for QuerySeqSerializer<'a, 'b> {
565 type Ok = ();
566 type Error = QueryParamError;
567
568 fn serialize_element<T: ?Sized + Serialize>(&mut self, value: &T) -> Result<(), Self::Error> {
569 use ParamSerializerState::*;
570 match &mut self.serializer.state {
571 DeepObject if self.serializer.path.len() == 1 => {
572 return Err(QueryParamError::UnspecifiedStyleExploded);
575 }
576 DeepObject => {
577 self.serializer.path.push(self.index.to_string());
579 value.serialize(&mut *self.serializer)?;
580 self.serializer.path.pop();
581 }
582 _ => value.serialize(&mut *self.serializer)?,
583 }
584 self.index += 1;
585 Ok(())
586 }
587
588 fn end(self) -> Result<Self::Ok, Self::Error> {
589 self.serializer.flush();
590 Ok(())
591 }
592}
593
594impl<'a, 'b> SerializeTuple for QuerySeqSerializer<'a, 'b> {
595 type Ok = ();
596 type Error = QueryParamError;
597
598 fn serialize_element<T: ?Sized + Serialize>(&mut self, value: &T) -> Result<(), Self::Error> {
599 SerializeSeq::serialize_element(self, value)
600 }
601
602 fn end(self) -> Result<Self::Ok, Self::Error> {
603 SerializeSeq::end(self)
604 }
605}
606
607pub struct QueryStructSerializer<'a, 'b> {
609 serializer: &'a mut QueryParamSerializer<'b>,
610}
611
612impl<'a, 'b> SerializeStruct for QueryStructSerializer<'a, 'b> {
613 type Ok = ();
614 type Error = QueryParamError;
615
616 fn serialize_field<T: ?Sized + Serialize>(
617 &mut self,
618 key: &'static str,
619 value: &T,
620 ) -> Result<(), Self::Error> {
621 use ParamSerializerState::*;
622 if let NonExplodedForm(buf) | Delimited(_, buf) = &mut self.serializer.state {
623 buf.push(key.to_owned());
626 };
627
628 self.serializer.path.push(key);
629 value.serialize(&mut *self.serializer)?;
630 self.serializer.path.pop();
631 Ok(())
632 }
633
634 fn end(self) -> Result<Self::Ok, Self::Error> {
635 self.serializer.flush();
636 Ok(())
637 }
638}
639
640impl<'a, 'b> SerializeMap for QueryStructSerializer<'a, 'b> {
641 type Ok = ();
642 type Error = QueryParamError;
643
644 fn serialize_key<T: ?Sized + Serialize>(&mut self, key: &T) -> Result<(), Self::Error> {
645 let mut extractor = KeyExtractor { key: String::new() };
646 key.serialize(&mut extractor)?;
647 self.serializer.path.push(extractor.key);
648 Ok(())
649 }
650
651 fn serialize_value<T: ?Sized + Serialize>(&mut self, value: &T) -> Result<(), Self::Error> {
652 use ParamSerializerState::*;
653 if let NonExplodedForm(buf) | Delimited(_, buf) = &mut self.serializer.state {
654 buf.push(self.serializer.path.last().to_owned());
657 };
658
659 value.serialize(&mut *self.serializer)?;
660 self.serializer.path.pop();
661 Ok(())
662 }
663
664 fn end(self) -> Result<Self::Ok, Self::Error> {
665 SerializeStruct::end(self)
666 }
667}
668
669struct KeyExtractor {
672 key: String,
673}
674
675impl serde::Serializer for &mut KeyExtractor {
676 type Ok = ();
677 type Error = QueryParamError;
678
679 type SerializeSeq = Impossible<(), QueryParamError>;
680 type SerializeTuple = Impossible<(), QueryParamError>;
681 type SerializeTupleStruct = Impossible<(), QueryParamError>;
682 type SerializeTupleVariant = Impossible<(), QueryParamError>;
683 type SerializeMap = Impossible<(), QueryParamError>;
684 type SerializeStruct = Impossible<(), QueryParamError>;
685 type SerializeStructVariant = Impossible<(), QueryParamError>;
686
687 fn serialize_bool(self, _: bool) -> Result<Self::Ok, Self::Error> {
688 Err(QueryParamError::MapKeyNotString)
689 }
690
691 fn serialize_i8(self, _: i8) -> Result<Self::Ok, Self::Error> {
692 Err(QueryParamError::MapKeyNotString)
693 }
694
695 fn serialize_i16(self, _: i16) -> Result<Self::Ok, Self::Error> {
696 Err(QueryParamError::MapKeyNotString)
697 }
698
699 fn serialize_i32(self, _: i32) -> Result<Self::Ok, Self::Error> {
700 Err(QueryParamError::MapKeyNotString)
701 }
702
703 fn serialize_i64(self, _: i64) -> Result<Self::Ok, Self::Error> {
704 Err(QueryParamError::MapKeyNotString)
705 }
706
707 fn serialize_u8(self, _: u8) -> Result<Self::Ok, Self::Error> {
708 Err(QueryParamError::MapKeyNotString)
709 }
710
711 fn serialize_u16(self, _: u16) -> Result<Self::Ok, Self::Error> {
712 Err(QueryParamError::MapKeyNotString)
713 }
714
715 fn serialize_u32(self, _: u32) -> Result<Self::Ok, Self::Error> {
716 Err(QueryParamError::MapKeyNotString)
717 }
718
719 fn serialize_u64(self, _: u64) -> Result<Self::Ok, Self::Error> {
720 Err(QueryParamError::MapKeyNotString)
721 }
722
723 fn serialize_f32(self, _: f32) -> Result<Self::Ok, Self::Error> {
724 Err(QueryParamError::MapKeyNotString)
725 }
726
727 fn serialize_f64(self, _: f64) -> Result<Self::Ok, Self::Error> {
728 Err(QueryParamError::MapKeyNotString)
729 }
730
731 fn serialize_char(self, _: char) -> Result<Self::Ok, Self::Error> {
732 Err(QueryParamError::MapKeyNotString)
733 }
734
735 fn serialize_str(self, v: &str) -> Result<Self::Ok, Self::Error> {
736 self.key = v.to_owned();
737 Ok(())
738 }
739
740 fn serialize_bytes(self, _: &[u8]) -> Result<Self::Ok, Self::Error> {
741 Err(QueryParamError::MapKeyNotString)
742 }
743
744 fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
745 Err(QueryParamError::MapKeyNotString)
746 }
747
748 fn serialize_some<T: ?Sized + Serialize>(self, _: &T) -> Result<Self::Ok, Self::Error> {
749 Err(QueryParamError::MapKeyNotString)
750 }
751
752 fn serialize_unit(self) -> Result<Self::Ok, Self::Error> {
753 Err(QueryParamError::MapKeyNotString)
754 }
755
756 fn serialize_unit_struct(self, _: &'static str) -> Result<Self::Ok, Self::Error> {
757 Err(QueryParamError::MapKeyNotString)
758 }
759
760 fn serialize_unit_variant(
761 self,
762 _: &'static str,
763 _: u32,
764 _: &'static str,
765 ) -> Result<Self::Ok, Self::Error> {
766 Err(QueryParamError::MapKeyNotString)
767 }
768
769 fn serialize_newtype_struct<T: ?Sized + Serialize>(
770 self,
771 _: &'static str,
772 _: &T,
773 ) -> Result<Self::Ok, Self::Error> {
774 Err(QueryParamError::MapKeyNotString)
775 }
776
777 fn serialize_newtype_variant<T: ?Sized + Serialize>(
778 self,
779 _: &'static str,
780 _: u32,
781 _: &'static str,
782 _: &T,
783 ) -> Result<Self::Ok, Self::Error> {
784 Err(QueryParamError::MapKeyNotString)
785 }
786
787 fn serialize_seq(self, _: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
788 Err(QueryParamError::MapKeyNotString)
789 }
790
791 fn serialize_tuple(self, _: usize) -> Result<Self::SerializeTuple, Self::Error> {
792 Err(QueryParamError::MapKeyNotString)
793 }
794
795 fn serialize_tuple_struct(
796 self,
797 _: &'static str,
798 _: usize,
799 ) -> Result<Self::SerializeTupleStruct, Self::Error> {
800 Err(QueryParamError::MapKeyNotString)
801 }
802
803 fn serialize_tuple_variant(
804 self,
805 _: &'static str,
806 _: u32,
807 _: &'static str,
808 _: usize,
809 ) -> Result<Self::SerializeTupleVariant, Self::Error> {
810 Err(QueryParamError::MapKeyNotString)
811 }
812
813 fn serialize_map(self, _: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
814 Err(QueryParamError::MapKeyNotString)
815 }
816
817 fn serialize_struct(
818 self,
819 _: &'static str,
820 _: usize,
821 ) -> Result<Self::SerializeStruct, Self::Error> {
822 Err(QueryParamError::MapKeyNotString)
823 }
824
825 fn serialize_struct_variant(
826 self,
827 _: &'static str,
828 _: u32,
829 _: &'static str,
830 _: usize,
831 ) -> Result<Self::SerializeStructVariant, Self::Error> {
832 Err(QueryParamError::MapKeyNotString)
833 }
834}
835
836#[derive(Debug, thiserror::Error)]
838pub enum QueryParamError {
839 #[error("can't serialize {0} as query parameter")]
840 UnsupportedType(#[from] UnsupportedTypeError),
841 #[error("style-exploded combination not defined by OpenAPI")]
842 UnspecifiedStyleExploded,
843 #[error("map keys must be strings")]
844 MapKeyNotString,
845 #[error("{0}")]
846 Custom(String),
847}
848
849impl serde::ser::Error for QueryParamError {
850 fn custom<T: std::fmt::Display>(err: T) -> Self {
851 Self::Custom(err.to_string())
852 }
853}
854
855#[derive(Debug, thiserror::Error)]
856pub enum UnsupportedTypeError {
857 #[error("bytes")]
858 Bytes,
859 #[error("unit")]
860 Unit,
861 #[error("unit struct `{0}`")]
862 UnitStruct(&'static str),
863 #[error("tuple struct `{0}`")]
864 TupleStruct(&'static str),
865 #[error("newtype variant `{1}` of `{0}`")]
866 NewtypeVariant(&'static str, &'static str),
867 #[error("tuple variant `{1}` of `{0}`")]
868 TupleVariant(&'static str, &'static str),
869 #[error("struct variant `{1}` of `{0}`")]
870 StructVariant(&'static str, &'static str),
871}
872
873#[cfg(test)]
874mod tests {
875 use super::*;
876 use serde::Serialize;
877 use url::Url;
878
879 #[test]
880 fn test_integer() {
881 let mut url = Url::parse("http://example.com/").unwrap();
882 QuerySerializer::new(&mut url).append("limit", &42).unwrap();
883 assert_eq!(url.query(), Some("limit=42"));
884 }
885
886 #[test]
887 fn test_string() {
888 let mut url = Url::parse("http://example.com/").unwrap();
889 QuerySerializer::new(&mut url)
890 .append("name", &"Alice")
891 .unwrap();
892 assert_eq!(url.query(), Some("name=Alice"));
893 }
894
895 #[test]
896 fn test_bool() {
897 let mut url = Url::parse("http://example.com/").unwrap();
898 QuerySerializer::new(&mut url)
899 .append("active", &true)
900 .unwrap();
901 assert_eq!(url.query(), Some("active=true"));
902 }
903
904 #[test]
905 fn test_option_some() {
906 let mut url = Url::parse("http://example.com/").unwrap();
907 let value = Some(42);
908 QuerySerializer::new(&mut url)
909 .append("limit", &value)
910 .unwrap();
911 assert_eq!(url.query(), Some("limit=42"));
912 }
913
914 #[test]
915 fn test_option_none() {
916 let mut url = Url::parse("http://example.com/").unwrap();
917 let value: Option<i32> = None;
918 QuerySerializer::new(&mut url)
919 .append("limit", &value)
920 .unwrap();
921 assert_eq!(url.query(), None);
922 }
923
924 #[test]
925 fn test_array_form_exploded() {
926 let mut url = Url::parse("http://example.com/").unwrap();
927 let values = vec![1, 2, 3];
928 QuerySerializer::new(&mut url)
929 .append("ids", &values)
930 .unwrap();
931 assert_eq!(url.query(), Some("ids=1&ids=2&ids=3"));
932 }
933
934 #[test]
935 fn test_array_form_non_exploded() {
936 let mut url = Url::parse("http://example.com/").unwrap();
937 let values = vec![1, 2, 3];
938 QuerySerializer::new(&mut url)
939 .style(QueryStyle::Form { exploded: false })
940 .append("ids", &values)
941 .unwrap();
942 assert_eq!(url.query(), Some("ids=1,2,3"));
943 }
944
945 #[test]
946 fn test_array_space_delimited() {
947 let mut url = Url::parse("http://example.com/").unwrap();
948 let values = vec![1, 2, 3];
949 QuerySerializer::new(&mut url)
950 .style(QueryStyle::SpaceDelimited)
951 .append("ids", &values)
952 .unwrap();
953 assert_eq!(url.query(), Some("ids=1%202%203"));
954 }
955
956 #[test]
957 fn test_array_pipe_delimited() {
958 let mut url = Url::parse("http://example.com/").unwrap();
959 let values = vec![1, 2, 3];
960 QuerySerializer::new(&mut url)
961 .style(QueryStyle::PipeDelimited)
962 .append("ids", &values)
963 .unwrap();
964 assert_eq!(url.query(), Some("ids=1%7C2%7C3"));
965 }
966
967 #[test]
968 fn test_empty_array() {
969 let mut url = Url::parse("http://example.com/").unwrap();
970 let values: Vec<i32> = vec![];
971 QuerySerializer::new(&mut url)
972 .append("ids", &values)
973 .unwrap();
974 assert_eq!(url.query(), None);
975 }
976
977 #[test]
978 fn test_object_form_exploded() {
979 #[derive(Serialize)]
980 struct Person {
981 first_name: String,
982 last_name: String,
983 }
984
985 let mut url = Url::parse("http://example.com/").unwrap();
986 let person = Person {
987 first_name: "John".to_owned(),
988 last_name: "Doe".to_owned(),
989 };
990 QuerySerializer::new(&mut url)
991 .append("person", &person)
992 .unwrap();
993 assert_eq!(url.query(), Some("first_name=John&last_name=Doe"));
994 }
995
996 #[test]
997 fn test_object_form_non_exploded() {
998 #[derive(Serialize)]
999 struct Person {
1000 first_name: String,
1001 last_name: String,
1002 }
1003
1004 let mut url = Url::parse("http://example.com/").unwrap();
1005 let person = Person {
1006 first_name: "John".to_owned(),
1007 last_name: "Doe".to_owned(),
1008 };
1009 QuerySerializer::new(&mut url)
1010 .style(QueryStyle::Form { exploded: false })
1011 .append("person", &person)
1012 .unwrap();
1013 assert_eq!(url.query(), Some("person=first_name,John,last_name,Doe"));
1014 }
1015
1016 #[test]
1017 fn test_object_deep_object() {
1018 #[derive(Serialize)]
1019 struct Filter {
1020 #[serde(rename = "type")]
1021 type_field: String,
1022 location: String,
1023 }
1024
1025 let mut url = Url::parse("http://example.com/").unwrap();
1026 let filter = Filter {
1027 type_field: "cocktail".to_owned(),
1028 location: "bar".to_owned(),
1029 };
1030 QuerySerializer::new(&mut url)
1031 .style(QueryStyle::DeepObject)
1032 .append("filter", &filter)
1033 .unwrap();
1034 assert_eq!(
1035 url.query(),
1036 Some("filter%5Btype%5D=cocktail&filter%5Blocation%5D=bar")
1037 );
1038 }
1039
1040 #[test]
1041 fn test_multiple_params_chained() {
1042 let mut url = Url::parse("http://example.com/").unwrap();
1043 let tags = vec!["dog", "cat"];
1044 QuerySerializer::new(&mut url)
1045 .append("limit", &10)
1046 .unwrap()
1047 .append("tags", &tags)
1048 .unwrap();
1049 assert_eq!(url.query(), Some("limit=10&tags=dog&tags=cat"));
1050 }
1051
1052 #[test]
1053 fn test_string_with_special_chars() {
1054 let mut url = Url::parse("http://example.com/").unwrap();
1055 QuerySerializer::new(&mut url)
1056 .append("name", &"John Doe & Co.")
1057 .unwrap();
1058 assert_eq!(url.query(), Some("name=John+Doe+%26+Co."));
1059 }
1060
1061 #[test]
1062 fn test_array_of_strings_with_special_chars() {
1063 let mut url = Url::parse("http://example.com/").unwrap();
1064 let values = vec!["hello world", "foo&bar"];
1065 QuerySerializer::new(&mut url)
1066 .style(QueryStyle::Form { exploded: false })
1067 .append("tags", &values)
1068 .unwrap();
1069 assert_eq!(url.query(), Some("tags=hello%20world,foo%26bar"));
1070 }
1071
1072 #[test]
1073 fn test_nested_deep_object() {
1074 #[derive(Serialize)]
1075 struct Address {
1076 city: String,
1077 country: String,
1078 }
1079
1080 #[derive(Serialize)]
1081 struct Person {
1082 name: String,
1083 address: Address,
1084 }
1085
1086 let mut url = Url::parse("http://example.com/").unwrap();
1087 let person = Person {
1088 name: "Alice".to_owned(),
1089 address: Address {
1090 city: "Paris".to_owned(),
1091 country: "France".to_owned(),
1092 },
1093 };
1094 QuerySerializer::new(&mut url)
1095 .style(QueryStyle::DeepObject)
1096 .append("person", &person)
1097 .unwrap();
1098 assert_eq!(
1099 url.query(),
1100 Some(
1101 "person%5Bname%5D=Alice&person%5Baddress%5D%5Bcity%5D=Paris&person%5Baddress%5D%5Bcountry%5D=France"
1102 )
1103 );
1104 }
1105
1106 #[test]
1107 fn test_deep_object_with_array_field() {
1108 #[derive(Serialize)]
1109 struct Filter {
1110 category: String,
1111 tags: Vec<String>,
1112 }
1113
1114 let mut url = Url::parse("http://example.com/").unwrap();
1115 let filter = Filter {
1116 category: "electronics".to_owned(),
1117 tags: vec!["new".to_owned(), "sale".to_owned()],
1118 };
1119 QuerySerializer::new(&mut url)
1120 .style(QueryStyle::DeepObject)
1121 .append("filter", &filter)
1122 .unwrap();
1123 assert_eq!(
1124 url.query(),
1125 Some(
1126 "filter%5Bcategory%5D=electronics&filter%5Btags%5D%5B0%5D=new&filter%5Btags%5D%5B1%5D=sale"
1127 )
1128 );
1129 }
1130
1131 #[test]
1132 fn test_serde_skip_if() {
1133 #[derive(Serialize)]
1134 struct Params {
1135 required: i32,
1136 #[serde(skip_serializing_if = "Option::is_none")]
1137 optional: Option<String>,
1138 }
1139
1140 let mut url = Url::parse("http://example.com/").unwrap();
1141 let params = Params {
1142 required: 42,
1143 optional: None,
1144 };
1145 QuerySerializer::new(&mut url)
1146 .append("params", ¶ms)
1147 .unwrap();
1148 assert_eq!(url.query(), Some("required=42"));
1149 }
1150
1151 #[test]
1152 fn test_unit_variant_enum() {
1153 #[derive(Serialize)]
1154 #[allow(dead_code)]
1155 enum Status {
1156 Active,
1157 Inactive,
1158 }
1159
1160 let mut url = Url::parse("http://example.com/").unwrap();
1161 QuerySerializer::new(&mut url)
1162 .append("status", &Status::Active)
1163 .unwrap();
1164 assert_eq!(url.query(), Some("status=Active"));
1165 }
1166
1167 #[test]
1168 fn test_unicode_string() {
1169 let mut url = Url::parse("http://example.com/").unwrap();
1170 QuerySerializer::new(&mut url)
1171 .append("name", &"日本語")
1172 .unwrap();
1173 assert_eq!(url.query(), Some("name=%E6%97%A5%E6%9C%AC%E8%AA%9E"));
1174 }
1175
1176 #[test]
1177 fn test_deep_object_rejects_arrays() {
1178 let mut url = Url::parse("http://example.com/").unwrap();
1179 let values = vec![1, 2, 3];
1180 let result = QuerySerializer::new(&mut url)
1181 .style(QueryStyle::DeepObject)
1182 .append("ids", &values);
1183 assert!(matches!(
1184 result,
1185 Err(QueryParamError::UnspecifiedStyleExploded)
1186 ));
1187 }
1188
1189 #[test]
1190 fn test_space_delimited_object() {
1191 #[derive(Serialize)]
1192 struct Color {
1193 r: u32,
1194 g: u32,
1195 b: u32,
1196 }
1197
1198 let mut url = Url::parse("http://example.com/").unwrap();
1199 let color = Color {
1200 r: 100,
1201 g: 200,
1202 b: 150,
1203 };
1204 QuerySerializer::new(&mut url)
1205 .style(QueryStyle::SpaceDelimited)
1206 .append("color", &color)
1207 .unwrap();
1208
1209 assert_eq!(url.query(), Some("color=r%20100%20g%20200%20b%20150"));
1211 }
1212
1213 #[test]
1214 fn test_pipe_delimited_object() {
1215 #[derive(Serialize)]
1216 struct Color {
1217 r: u32,
1218 g: u32,
1219 b: u32,
1220 }
1221
1222 let mut url = Url::parse("http://example.com/").unwrap();
1223 let color = Color {
1224 r: 100,
1225 g: 200,
1226 b: 150,
1227 };
1228 QuerySerializer::new(&mut url)
1229 .style(QueryStyle::PipeDelimited)
1230 .append("color", &color)
1231 .unwrap();
1232
1233 assert_eq!(url.query(), Some("color=r%7C100%7Cg%7C200%7Cb%7C150"));
1235 }
1236
1237 #[test]
1238 fn test_tuple_form_exploded() {
1239 let mut url = Url::parse("http://example.com/").unwrap();
1240 let coords = (42, 24, 10);
1241 QuerySerializer::new(&mut url)
1242 .append("coords", &coords)
1243 .unwrap();
1244 assert_eq!(url.query(), Some("coords=42&coords=24&coords=10"));
1245 }
1246
1247 #[test]
1248 fn test_tuple_form_non_exploded() {
1249 let mut url = Url::parse("http://example.com/").unwrap();
1250 let coords = (42, 24, 10);
1251 QuerySerializer::new(&mut url)
1252 .style(QueryStyle::Form { exploded: false })
1253 .append("coords", &coords)
1254 .unwrap();
1255 assert_eq!(url.query(), Some("coords=42,24,10"));
1256 }
1257
1258 #[test]
1259 fn test_tuple_space_delimited() {
1260 let mut url = Url::parse("http://example.com/").unwrap();
1261 let coords = (42, 24, 10);
1262 QuerySerializer::new(&mut url)
1263 .style(QueryStyle::SpaceDelimited)
1264 .append("coords", &coords)
1265 .unwrap();
1266 assert_eq!(url.query(), Some("coords=42%2024%2010"));
1267 }
1268
1269 #[test]
1270 fn test_tuple_pipe_delimited() {
1271 let mut url = Url::parse("http://example.com/").unwrap();
1272 let coords = (42, 24, 10);
1273 QuerySerializer::new(&mut url)
1274 .style(QueryStyle::PipeDelimited)
1275 .append("coords", &coords)
1276 .unwrap();
1277 assert_eq!(url.query(), Some("coords=42%7C24%7C10"));
1278 }
1279}