trakt_core/
url.rs

1use percent_encoding::{AsciiSet, CONTROLS};
2use serde::{ser, Serialize};
3
4use crate::error::{IntoHttpError, UrlError};
5
6/// Constructs a complete URL from a base URL, an endpoint, and parameters.
7///
8/// - The `base_url` is the base URL of the API: `https://api.trakt.tv`.
9/// - The `endpoint` is the path of the specific endpoint with parameters enclosed in curly braces:
10/// `/shows/{id}/seasons/{season}/episodes/{episode}`.
11/// - The `params` is a struct that will be serialized into the parameters of the `endpoint`.
12/// - The `query` is a struct that will be serialized into the query parameters of the URL.
13///
14/// # Errors
15///
16/// Returns an [`IntoHttpError`] if the URL cannot be constructed.
17pub fn construct_url(
18    base_url: &str,
19    endpoint: &str,
20    params: &impl Serialize,
21    query: &impl Serialize,
22) -> Result<String, IntoHttpError> {
23    // Serialize the url parameters
24    let url = to_string(base_url, endpoint, params)?;
25
26    // Serialize the query parameters
27    let query = serde_urlencoded::to_string(query)?;
28
29    // If there are query parameters, append them to the URL
30    let url = if query.is_empty() {
31        url
32    } else {
33        format!("{url}?{query}")
34    };
35
36    Ok(url)
37}
38
39struct UrlSerializer<'a> {
40    /// The URL being built
41    url: String,
42    /// The parts of the URL endpoint
43    parts: Vec<Part<'a>>,
44}
45
46#[derive(Debug, Clone, PartialEq, Eq)]
47enum Part<'a> {
48    /// A raw string that should be appended to the URL
49    Raw(&'a str),
50    /// A parameter that should be URL encoded and appended to the URL.
51    Param(Param<'a>),
52}
53
54#[derive(Debug, Clone, PartialEq, Eq)]
55enum Param<'a> {
56    /// The key of the parameter
57    Key(&'a str),
58    /// The serialized value of the parameter
59    Value(String),
60}
61
62fn to_string<T: Serialize>(base_url: &str, endpoint: &str, value: &T) -> Result<String, UrlError> {
63    let mut serializer = UrlSerializer {
64        url: base_url.to_owned(),
65        parts: parse_endpoint(endpoint)?,
66    };
67    value.serialize(&mut serializer)?;
68    serializer.end()
69}
70
71/// Parses the endpoint into parts
72///
73/// Example endpoint: `/shows/{id}/seasons/{season}/episodes/{episode}`
74///
75/// Example parts:
76/// - `Raw("/shows/")`
77/// - `Param("id")`
78/// - `Raw("/seasons/")`
79/// - `Param("season")`
80/// - `Raw("/episodes/")`
81/// - `Param("episode")`
82fn parse_endpoint(s: &str) -> Result<Vec<Part>, UrlError> {
83    let mut parts = Vec::new();
84    let mut start = 0;
85    let mut in_param = false;
86    for (i, c) in s.char_indices() {
87        // Find the start of a parameter
88        if c == '{' {
89            // If we're already in a parameter, this is an error
90            if in_param {
91                return Err(UrlError::InvalidEndpoint);
92            }
93
94            // Mark that we're in a parameter
95            in_param = true;
96
97            // If there's a string before this parameter, add it to the parts
98            if start != i {
99                parts.push(Part::Raw(&s[start..i]));
100            }
101
102            // Move the start to the beginning of the parameter
103            start = i + 1;
104        } else if c == '}' {
105            // If we're not in a parameter, this is an error
106            if !in_param {
107                return Err(UrlError::InvalidEndpoint);
108            }
109
110            // Mark that we're no longer in a parameter
111            in_param = false;
112
113            // Add the parameter to the parts
114            if start != i {
115                parts.push(Part::Param(Param::Key(&s[start..i])));
116            }
117
118            // Move the start to the end of the parameter
119            start = i + 1;
120        }
121    }
122
123    // If we're still in a parameter at end of endpoint, this is an error
124    if in_param {
125        return Err(UrlError::InvalidEndpoint);
126    }
127
128    // Add the last part of the string to the parts
129    if start != s.len() {
130        parts.push(Part::Raw(&s[start..]));
131    }
132
133    Ok(parts)
134}
135
136impl<'a> UrlSerializer<'a> {
137    pub fn end(self) -> Result<String, UrlError> {
138        let mut url = self.url;
139        for part in self.parts {
140            match part {
141                Part::Raw(s) => url.push_str(s),
142                Part::Param(p) => match p {
143                    Param::Key(k) => return Err(UrlError::UnfilledField(k.to_owned())),
144                    Param::Value(v) => url.push_str(&v),
145                },
146            }
147        }
148        Ok(url)
149    }
150}
151
152impl<'a, 'b> ser::Serializer for &'a mut UrlSerializer<'b> {
153    type Ok = ();
154
155    type Error = UrlError;
156    type SerializeSeq = ErrorSerializer;
157    type SerializeTuple = ErrorSerializer;
158    type SerializeTupleStruct = ErrorSerializer;
159    type SerializeTupleVariant = ErrorSerializer;
160    type SerializeMap = ErrorSerializer;
161    type SerializeStruct = Self;
162    type SerializeStructVariant = ErrorSerializer;
163
164    fn serialize_bool(self, _v: bool) -> Result<Self::Ok, Self::Error> {
165        Err(UrlError::TopLevel)
166    }
167
168    fn serialize_i8(self, _v: i8) -> Result<Self::Ok, Self::Error> {
169        Err(UrlError::TopLevel)
170    }
171
172    fn serialize_i16(self, _v: i16) -> Result<Self::Ok, Self::Error> {
173        Err(UrlError::TopLevel)
174    }
175
176    fn serialize_i32(self, _v: i32) -> Result<Self::Ok, Self::Error> {
177        Err(UrlError::TopLevel)
178    }
179
180    fn serialize_i64(self, _v: i64) -> Result<Self::Ok, Self::Error> {
181        Err(UrlError::TopLevel)
182    }
183
184    fn serialize_u8(self, _v: u8) -> Result<Self::Ok, Self::Error> {
185        Err(UrlError::TopLevel)
186    }
187
188    fn serialize_u16(self, _v: u16) -> Result<Self::Ok, Self::Error> {
189        Err(UrlError::TopLevel)
190    }
191
192    fn serialize_u32(self, _v: u32) -> Result<Self::Ok, Self::Error> {
193        Err(UrlError::TopLevel)
194    }
195
196    fn serialize_u64(self, _v: u64) -> Result<Self::Ok, Self::Error> {
197        Err(UrlError::TopLevel)
198    }
199
200    fn serialize_f32(self, _v: f32) -> Result<Self::Ok, Self::Error> {
201        Err(UrlError::TopLevel)
202    }
203
204    fn serialize_f64(self, _v: f64) -> Result<Self::Ok, Self::Error> {
205        Err(UrlError::TopLevel)
206    }
207
208    fn serialize_char(self, _v: char) -> Result<Self::Ok, Self::Error> {
209        Err(UrlError::TopLevel)
210    }
211
212    fn serialize_str(self, _v: &str) -> Result<Self::Ok, Self::Error> {
213        Err(UrlError::TopLevel)
214    }
215
216    fn serialize_bytes(self, _v: &[u8]) -> Result<Self::Ok, Self::Error> {
217        Err(UrlError::TopLevel)
218    }
219
220    fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
221        Ok(())
222    }
223
224    fn serialize_some<T: ?Sized + Serialize>(self, value: &T) -> Result<Self::Ok, Self::Error> {
225        value.serialize(self)
226    }
227
228    fn serialize_unit(self) -> Result<Self::Ok, Self::Error> {
229        Ok(())
230    }
231
232    fn serialize_unit_struct(self, _name: &'static str) -> Result<Self::Ok, Self::Error> {
233        Ok(())
234    }
235
236    fn serialize_unit_variant(
237        self,
238        _name: &'static str,
239        _variant_index: u32,
240        _variant: &'static str,
241    ) -> Result<Self::Ok, Self::Error> {
242        Err(UrlError::TopLevel)
243    }
244
245    fn serialize_newtype_struct<T: ?Sized + Serialize>(
246        self,
247        _name: &'static str,
248        value: &T,
249    ) -> Result<Self::Ok, Self::Error> {
250        value.serialize(self)
251    }
252
253    fn serialize_newtype_variant<T: ?Sized + Serialize>(
254        self,
255        _name: &'static str,
256        _variant_index: u32,
257        _variant: &'static str,
258        _value: &T,
259    ) -> Result<Self::Ok, Self::Error> {
260        Err(UrlError::TopLevel)
261    }
262
263    fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
264        Err(UrlError::ValueNotSupported)
265    }
266
267    fn serialize_tuple(self, _len: usize) -> Result<Self::SerializeTuple, Self::Error> {
268        Err(UrlError::ValueNotSupported)
269    }
270
271    fn serialize_tuple_struct(
272        self,
273        _name: &'static str,
274        _len: usize,
275    ) -> Result<Self::SerializeTupleStruct, Self::Error> {
276        Err(UrlError::ValueNotSupported)
277    }
278
279    fn serialize_tuple_variant(
280        self,
281        _name: &'static str,
282        _variant_index: u32,
283        _variant: &'static str,
284        _len: usize,
285    ) -> Result<Self::SerializeTupleVariant, Self::Error> {
286        Err(UrlError::ValueNotSupported)
287    }
288
289    fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
290        Err(UrlError::ValueNotSupported)
291    }
292
293    fn serialize_struct(
294        self,
295        _name: &'static str,
296        _len: usize,
297    ) -> Result<Self::SerializeStruct, Self::Error> {
298        Ok(self)
299    }
300
301    fn serialize_struct_variant(
302        self,
303        _name: &'static str,
304        _variant_index: u32,
305        _variant: &'static str,
306        _len: usize,
307    ) -> Result<Self::SerializeStructVariant, Self::Error> {
308        Err(UrlError::ValueNotSupported)
309    }
310}
311
312impl<'a, 'b> ser::SerializeStruct for &'a mut UrlSerializer<'b> {
313    type Ok = ();
314    type Error = UrlError;
315
316    fn serialize_field<T: ?Sized + Serialize>(
317        &mut self,
318        key: &'static str,
319        value: &T,
320    ) -> Result<(), Self::Error> {
321        // Search for the key in the parts
322        let mut part = None;
323        for p in &mut self.parts {
324            match p {
325                Part::Param(p) => match p {
326                    Param::Key(k) if *k == key => {
327                        part = Some(p);
328                        break;
329                    }
330                    _ => {}
331                },
332                Part::Raw(_) => {}
333            }
334        }
335
336        // If the key was not found, this is an error
337        let part = part.ok_or(UrlError::KeyNotFound(key))?;
338
339        // Serialize the value into the part
340        let mut serializer = UrlValueSerializer::default();
341        value.serialize(&mut serializer)?;
342        let value = serializer.value;
343
344        *part = Param::Value(value);
345
346        Ok(())
347    }
348
349    fn end(self) -> Result<Self::Ok, Self::Error> {
350        Ok(())
351    }
352}
353
354#[derive(Debug, Clone, Default)]
355struct UrlValueSerializer {
356    value: String,
357}
358
359const PATH_SET: &AsciiSet = &CONTROLS
360    .add(b'~')
361    .add(b' ')
362    .add(b'"')
363    .add(b'#')
364    .add(b'<')
365    .add(b'>')
366    .add(b'?')
367    .add(b'`')
368    .add(b'{')
369    .add(b'}');
370
371impl<'a> ser::Serializer for &'a mut UrlValueSerializer {
372    type Ok = ();
373    type Error = UrlError;
374
375    type SerializeSeq = ErrorSerializer;
376    type SerializeTuple = ErrorSerializer;
377    type SerializeTupleStruct = ErrorSerializer;
378    type SerializeTupleVariant = ErrorSerializer;
379    type SerializeMap = ErrorSerializer;
380    type SerializeStruct = ErrorSerializer;
381    type SerializeStructVariant = ErrorSerializer;
382
383    fn serialize_bool(self, v: bool) -> Result<Self::Ok, Self::Error> {
384        self.value = utf8_percent_encode(if v { "true" } else { "false" });
385        Ok(())
386    }
387
388    fn serialize_i8(self, v: i8) -> Result<Self::Ok, Self::Error> {
389        let mut buffer = itoa::Buffer::new();
390        self.value = utf8_percent_encode(buffer.format(v));
391        Ok(())
392    }
393
394    fn serialize_i16(self, v: i16) -> Result<Self::Ok, Self::Error> {
395        let mut buffer = itoa::Buffer::new();
396        self.value = utf8_percent_encode(buffer.format(v));
397        Ok(())
398    }
399
400    fn serialize_i32(self, v: i32) -> Result<Self::Ok, Self::Error> {
401        let mut buffer = itoa::Buffer::new();
402        self.value = utf8_percent_encode(buffer.format(v));
403        Ok(())
404    }
405
406    fn serialize_i64(self, v: i64) -> Result<Self::Ok, Self::Error> {
407        let mut buffer = itoa::Buffer::new();
408        self.value = utf8_percent_encode(buffer.format(v));
409        Ok(())
410    }
411
412    fn serialize_u8(self, v: u8) -> Result<Self::Ok, Self::Error> {
413        let mut buffer = itoa::Buffer::new();
414        self.value = utf8_percent_encode(buffer.format(v));
415        Ok(())
416    }
417
418    fn serialize_u16(self, v: u16) -> Result<Self::Ok, Self::Error> {
419        let mut buffer = itoa::Buffer::new();
420        self.value = utf8_percent_encode(buffer.format(v));
421        Ok(())
422    }
423
424    fn serialize_u32(self, v: u32) -> Result<Self::Ok, Self::Error> {
425        let mut buffer = itoa::Buffer::new();
426        self.value = utf8_percent_encode(buffer.format(v));
427        Ok(())
428    }
429
430    fn serialize_u64(self, v: u64) -> Result<Self::Ok, Self::Error> {
431        let mut buffer = itoa::Buffer::new();
432        self.value = utf8_percent_encode(buffer.format(v));
433        Ok(())
434    }
435
436    fn serialize_f32(self, v: f32) -> Result<Self::Ok, Self::Error> {
437        let mut buf = ryu::Buffer::new();
438        self.value = utf8_percent_encode(buf.format(v));
439        Ok(())
440    }
441
442    fn serialize_f64(self, v: f64) -> Result<Self::Ok, Self::Error> {
443        let mut buf = ryu::Buffer::new();
444        self.value = utf8_percent_encode(buf.format(v));
445        Ok(())
446    }
447
448    fn serialize_char(self, v: char) -> Result<Self::Ok, Self::Error> {
449        let mut buf = [0; 4];
450        self.value = utf8_percent_encode(v.encode_utf8(&mut buf));
451        Ok(())
452    }
453
454    fn serialize_str(self, v: &str) -> Result<Self::Ok, Self::Error> {
455        self.value = utf8_percent_encode(v);
456        Ok(())
457    }
458
459    fn serialize_bytes(self, v: &[u8]) -> Result<Self::Ok, Self::Error> {
460        self.value = percent_encoding::percent_encode(v, PATH_SET).to_string();
461        Ok(())
462    }
463
464    fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
465        self.serialize_unit()
466    }
467
468    fn serialize_some<T: ?Sized + Serialize>(self, value: &T) -> Result<Self::Ok, Self::Error> {
469        value.serialize(self)
470    }
471
472    fn serialize_unit(self) -> Result<Self::Ok, Self::Error> {
473        self.value = utf8_percent_encode("");
474        Ok(())
475    }
476
477    fn serialize_unit_struct(self, _name: &'static str) -> Result<Self::Ok, Self::Error> {
478        self.serialize_unit()
479    }
480
481    fn serialize_unit_variant(
482        self,
483        _name: &'static str,
484        _variant_index: u32,
485        variant: &'static str,
486    ) -> Result<Self::Ok, Self::Error> {
487        self.value = utf8_percent_encode(variant);
488        Ok(())
489    }
490
491    fn serialize_newtype_struct<T: ?Sized + Serialize>(
492        self,
493        _name: &'static str,
494        value: &T,
495    ) -> Result<Self::Ok, Self::Error> {
496        value.serialize(self)
497    }
498
499    fn serialize_newtype_variant<T: ?Sized + Serialize>(
500        self,
501        _name: &'static str,
502        _variant_index: u32,
503        _variant: &'static str,
504        value: &T,
505    ) -> Result<Self::Ok, Self::Error> {
506        value.serialize(self)
507    }
508
509    fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
510        Err(UrlError::ValueNotSupported)
511    }
512
513    fn serialize_tuple(self, _len: usize) -> Result<Self::SerializeTuple, Self::Error> {
514        Err(UrlError::ValueNotSupported)
515    }
516
517    fn serialize_tuple_struct(
518        self,
519        _name: &'static str,
520        _len: usize,
521    ) -> Result<Self::SerializeTupleStruct, Self::Error> {
522        Err(UrlError::ValueNotSupported)
523    }
524
525    fn serialize_tuple_variant(
526        self,
527        _name: &'static str,
528        _variant_index: u32,
529        _variant: &'static str,
530        _len: usize,
531    ) -> Result<Self::SerializeTupleVariant, Self::Error> {
532        Err(UrlError::ValueNotSupported)
533    }
534
535    fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
536        Err(UrlError::ValueNotSupported)
537    }
538
539    fn serialize_struct(
540        self,
541        _name: &'static str,
542        _len: usize,
543    ) -> Result<Self::SerializeStruct, Self::Error> {
544        Err(UrlError::ValueNotSupported)
545    }
546
547    fn serialize_struct_variant(
548        self,
549        _name: &'static str,
550        _variant_index: u32,
551        _variant: &'static str,
552        _len: usize,
553    ) -> Result<Self::SerializeStructVariant, Self::Error> {
554        Err(UrlError::ValueNotSupported)
555    }
556}
557
558#[derive(Debug)]
559struct ErrorSerializer;
560
561impl ser::SerializeSeq for ErrorSerializer {
562    type Ok = ();
563    type Error = UrlError;
564
565    fn serialize_element<T: ?Sized + Serialize>(&mut self, _value: &T) -> Result<(), Self::Error> {
566        Err(UrlError::ValueNotSupported)
567    }
568
569    fn end(self) -> Result<Self::Ok, Self::Error> {
570        Err(UrlError::ValueNotSupported)
571    }
572}
573
574impl ser::SerializeTuple for ErrorSerializer {
575    type Ok = ();
576    type Error = UrlError;
577
578    fn serialize_element<T: ?Sized + Serialize>(&mut self, _value: &T) -> Result<(), Self::Error> {
579        Err(UrlError::ValueNotSupported)
580    }
581
582    fn end(self) -> Result<Self::Ok, Self::Error> {
583        Err(UrlError::ValueNotSupported)
584    }
585}
586
587impl ser::SerializeTupleStruct for ErrorSerializer {
588    type Ok = ();
589    type Error = UrlError;
590
591    fn serialize_field<T: ?Sized + Serialize>(&mut self, _value: &T) -> Result<(), Self::Error> {
592        Err(UrlError::ValueNotSupported)
593    }
594
595    fn end(self) -> Result<Self::Ok, Self::Error> {
596        Err(UrlError::ValueNotSupported)
597    }
598}
599
600impl ser::SerializeTupleVariant for ErrorSerializer {
601    type Ok = ();
602    type Error = UrlError;
603
604    fn serialize_field<T: ?Sized + Serialize>(&mut self, _value: &T) -> Result<(), Self::Error> {
605        Err(UrlError::ValueNotSupported)
606    }
607
608    fn end(self) -> Result<Self::Ok, Self::Error> {
609        Err(UrlError::ValueNotSupported)
610    }
611}
612
613impl ser::SerializeMap for ErrorSerializer {
614    type Ok = ();
615    type Error = UrlError;
616
617    fn serialize_key<T: ?Sized + Serialize>(&mut self, _key: &T) -> Result<(), Self::Error> {
618        Err(UrlError::ValueNotSupported)
619    }
620
621    fn serialize_value<T: ?Sized + Serialize>(&mut self, _value: &T) -> Result<(), Self::Error> {
622        Err(UrlError::ValueNotSupported)
623    }
624
625    fn end(self) -> Result<Self::Ok, Self::Error> {
626        Err(UrlError::ValueNotSupported)
627    }
628}
629
630impl ser::SerializeStruct for ErrorSerializer {
631    type Ok = ();
632    type Error = UrlError;
633
634    fn serialize_field<T: ?Sized + Serialize>(
635        &mut self,
636        _key: &'static str,
637        _value: &T,
638    ) -> Result<(), Self::Error> {
639        Err(UrlError::ValueNotSupported)
640    }
641
642    fn end(self) -> Result<Self::Ok, Self::Error> {
643        Err(UrlError::ValueNotSupported)
644    }
645}
646
647impl ser::SerializeStructVariant for ErrorSerializer {
648    type Ok = ();
649    type Error = UrlError;
650
651    fn serialize_field<T: ?Sized + Serialize>(
652        &mut self,
653        _key: &'static str,
654        _value: &T,
655    ) -> Result<(), Self::Error> {
656        Err(UrlError::ValueNotSupported)
657    }
658
659    fn end(self) -> Result<Self::Ok, Self::Error> {
660        Err(UrlError::ValueNotSupported)
661    }
662}
663
664fn utf8_percent_encode(input: &str) -> String {
665    percent_encoding::utf8_percent_encode(input, PATH_SET).to_string()
666}
667
668#[cfg(test)]
669mod tests {
670    use serde::Serializer;
671
672    use super::*;
673
674    #[test]
675    fn test_parse_endpoint() {
676        let endpoint = "/shows/{id}/seasons/{season}/episodes/{episode}";
677        let parts = parse_endpoint(endpoint).unwrap();
678        assert_eq!(
679            parts,
680            vec![
681                Part::Raw("/shows/"),
682                Part::Param(Param::Key("id")),
683                Part::Raw("/seasons/"),
684                Part::Param(Param::Key("season")),
685                Part::Raw("/episodes/"),
686                Part::Param(Param::Key("episode")),
687            ]
688        );
689
690        let endpoint = "/shows/{id}/seasons/{season}/episodes/{episode}/";
691        let parts = parse_endpoint(endpoint).unwrap();
692        assert_eq!(
693            parts,
694            vec![
695                Part::Raw("/shows/"),
696                Part::Param(Param::Key("id")),
697                Part::Raw("/seasons/"),
698                Part::Param(Param::Key("season")),
699                Part::Raw("/episodes/"),
700                Part::Param(Param::Key("episode")),
701                Part::Raw("/"),
702            ]
703        );
704
705        assert_eq!(
706            parse_endpoint("/shows/{{id}}").unwrap_err(),
707            UrlError::InvalidEndpoint
708        );
709        assert_eq!(
710            parse_endpoint("/shows/{id}}").unwrap_err(),
711            UrlError::InvalidEndpoint
712        );
713        assert_eq!(
714            parse_endpoint("/shows/{id").unwrap_err(),
715            UrlError::InvalidEndpoint
716        );
717    }
718
719    #[test]
720    fn construct_url_normal() {
721        #[derive(Serialize)]
722        struct Params {
723            id: i32,
724        }
725        #[derive(Serialize)]
726        struct Query {
727            page: i32,
728            limit: Option<i32>,
729        }
730
731        let base_url = "https://example.com";
732        let endpoint = "/shows/{id}";
733        let params = Params { id: 1 };
734        let query = Query {
735            page: 1,
736            limit: None,
737        };
738
739        let url = construct_url(base_url, endpoint, &params, &query).unwrap();
740        assert_eq!(url, "https://example.com/shows/1?page=1");
741    }
742
743    #[test]
744    fn construct_url_no_query() {
745        #[derive(Serialize)]
746        struct Params {
747            id: i32,
748        }
749        #[derive(Serialize)]
750        struct Query;
751
752        let base_url = "https://example.com";
753        let endpoint = "/shows/{id}";
754        let params = Params { id: 1 };
755        let query = Query;
756
757        let url = construct_url(base_url, endpoint, &params, &query).unwrap();
758        assert_eq!(url, "https://example.com/shows/1");
759    }
760
761    #[test]
762    fn construct_url_unfilled() {
763        #[derive(Serialize)]
764        struct Params {
765            id: i32,
766        }
767        #[derive(Serialize)]
768        struct Query;
769
770        let base_url = "https://example.com";
771        let endpoint = "/shows/{id}/{unfilled}";
772        let params = Params { id: 1 };
773        let query = Query;
774
775        let res = construct_url(base_url, endpoint, &params, &query).unwrap_err();
776        assert_eq!(
777            res.to_string(),
778            "Url params error: Unfilled field: unfilled"
779        );
780    }
781
782    #[test]
783    fn construct_url_invalid_endpoint() {
784        #[derive(Serialize)]
785        struct Params {
786            id: i32,
787        }
788        #[derive(Serialize)]
789        struct Query;
790
791        let base_url = "https://example.com";
792        let endpoint = "/shows/{{id}";
793        let params = Params { id: 1 };
794        let query = Query;
795
796        let res = construct_url(base_url, endpoint, &params, &query).unwrap_err();
797        assert_eq!(res.to_string(), "Url params error: Invalid endpoint");
798    }
799
800    #[test]
801    fn construct_url_empty() {
802        #[derive(Serialize)]
803        struct Params;
804        #[derive(Serialize)]
805        struct Query;
806
807        let base_url = "https://example.com";
808        let endpoint = "/shows";
809        let params = Params;
810        let query = Query;
811
812        let url = construct_url(base_url, endpoint, &params, &query).unwrap();
813        assert_eq!(url, "https://example.com/shows");
814    }
815
816    #[allow(clippy::too_many_lines, clippy::cognitive_complexity)]
817    #[test]
818    fn url_value_serializer() {
819        let mut serializer = UrlValueSerializer::default();
820        serializer.serialize_bool(true).unwrap();
821        assert_eq!(serializer.value, "true");
822
823        let mut serializer = UrlValueSerializer::default();
824        serializer.serialize_bool(false).unwrap();
825        assert_eq!(serializer.value, "false");
826
827        let mut serializer = UrlValueSerializer::default();
828        serializer.serialize_i8(1).unwrap();
829        assert_eq!(serializer.value, "1");
830
831        let mut serializer = UrlValueSerializer::default();
832        serializer.serialize_i16(-1).unwrap();
833        assert_eq!(serializer.value, "-1");
834
835        let mut serializer = UrlValueSerializer::default();
836        serializer.serialize_i32(1024).unwrap();
837        assert_eq!(serializer.value, "1024");
838
839        let mut serializer = UrlValueSerializer::default();
840        serializer.serialize_i64(1024).unwrap();
841        assert_eq!(serializer.value, "1024");
842
843        let mut serializer = UrlValueSerializer::default();
844        serializer.serialize_u8(1).unwrap();
845        assert_eq!(serializer.value, "1");
846
847        let mut serializer = UrlValueSerializer::default();
848        serializer.serialize_u16(256).unwrap();
849        assert_eq!(serializer.value, "256");
850
851        let mut serializer = UrlValueSerializer::default();
852        serializer.serialize_u32(1024).unwrap();
853        assert_eq!(serializer.value, "1024");
854
855        let mut serializer = UrlValueSerializer::default();
856        serializer.serialize_u64(1024).unwrap();
857        assert_eq!(serializer.value, "1024");
858
859        let mut serializer = UrlValueSerializer::default();
860        serializer.serialize_f32(2.5).unwrap();
861        assert_eq!(serializer.value, "2.5");
862
863        let mut serializer = UrlValueSerializer::default();
864        serializer.serialize_f64(-2.5).unwrap();
865        assert_eq!(serializer.value, "-2.5");
866
867        let mut serializer = UrlValueSerializer::default();
868        serializer.serialize_char('a').unwrap();
869        assert_eq!(serializer.value, "a");
870
871        let mut serializer = UrlValueSerializer::default();
872        serializer.serialize_char('🚀').unwrap();
873        assert_eq!(serializer.value, "%F0%9F%9A%80");
874
875        let mut serializer = UrlValueSerializer::default();
876        serializer.serialize_str("hello?").unwrap();
877        assert_eq!(serializer.value, "hello%3F");
878
879        let mut serializer = UrlValueSerializer::default();
880        serializer.serialize_bytes(b"hello?\xc3\x28\x00").unwrap();
881        assert_eq!(serializer.value, "hello%3F%C3(%00");
882
883        let mut serializer = UrlValueSerializer::default();
884        serializer.serialize_none().unwrap();
885        assert_eq!(serializer.value, "");
886
887        let mut serializer = UrlValueSerializer::default();
888        serializer.serialize_some(&true).unwrap();
889        assert_eq!(serializer.value, "true");
890
891        let mut serializer = UrlValueSerializer::default();
892        serializer.serialize_unit().unwrap();
893        assert_eq!(serializer.value, "");
894
895        let mut serializer = UrlValueSerializer::default();
896        serializer.serialize_unit_struct("name").unwrap();
897        assert_eq!(serializer.value, "");
898
899        let mut serializer = UrlValueSerializer::default();
900        serializer
901            .serialize_unit_variant("name", 0, "variant")
902            .unwrap();
903        assert_eq!(serializer.value, "variant");
904
905        let mut serializer = UrlValueSerializer::default();
906        serializer.serialize_newtype_struct("name", &true).unwrap();
907        assert_eq!(serializer.value, "true");
908
909        let mut serializer = UrlValueSerializer::default();
910        serializer
911            .serialize_newtype_variant("name", 0, "variant", &true)
912            .unwrap();
913        assert_eq!(serializer.value, "true");
914
915        let mut serializer = UrlValueSerializer::default();
916        assert_eq!(
917            serializer.serialize_seq(None).unwrap_err(),
918            UrlError::ValueNotSupported
919        );
920
921        let mut serializer = UrlValueSerializer::default();
922        assert_eq!(
923            serializer.serialize_tuple(0).unwrap_err(),
924            UrlError::ValueNotSupported
925        );
926
927        let mut serializer = UrlValueSerializer::default();
928        assert_eq!(
929            serializer.serialize_tuple_struct("name", 0).unwrap_err(),
930            UrlError::ValueNotSupported
931        );
932
933        let mut serializer = UrlValueSerializer::default();
934        assert_eq!(
935            serializer
936                .serialize_tuple_variant("name", 0, "variant", 0)
937                .unwrap_err(),
938            UrlError::ValueNotSupported
939        );
940
941        let mut serializer = UrlValueSerializer::default();
942        assert_eq!(
943            serializer.serialize_map(None).unwrap_err(),
944            UrlError::ValueNotSupported
945        );
946
947        let mut serializer = UrlValueSerializer::default();
948        assert_eq!(
949            serializer.serialize_struct("name", 0).unwrap_err(),
950            UrlError::ValueNotSupported
951        );
952
953        let mut serializer = UrlValueSerializer::default();
954        assert_eq!(
955            serializer
956                .serialize_struct_variant("name", 0, "variant", 0)
957                .unwrap_err(),
958            UrlError::ValueNotSupported
959        );
960    }
961
962    #[allow(clippy::too_many_lines)]
963    #[test]
964    fn url_serializer() {
965        let mut serializer = UrlSerializer {
966            url: String::new(),
967            parts: vec![],
968        };
969
970        assert_eq!(
971            true.serialize(&mut serializer).unwrap_err(),
972            UrlError::TopLevel
973        );
974        assert_eq!(
975            1i8.serialize(&mut serializer).unwrap_err(),
976            UrlError::TopLevel
977        );
978        assert_eq!(
979            1i16.serialize(&mut serializer).unwrap_err(),
980            UrlError::TopLevel
981        );
982        assert_eq!(
983            1i32.serialize(&mut serializer).unwrap_err(),
984            UrlError::TopLevel
985        );
986        assert_eq!(
987            1i64.serialize(&mut serializer).unwrap_err(),
988            UrlError::TopLevel
989        );
990        assert_eq!(
991            1u8.serialize(&mut serializer).unwrap_err(),
992            UrlError::TopLevel
993        );
994        assert_eq!(
995            1u16.serialize(&mut serializer).unwrap_err(),
996            UrlError::TopLevel
997        );
998        assert_eq!(
999            1u32.serialize(&mut serializer).unwrap_err(),
1000            UrlError::TopLevel
1001        );
1002        assert_eq!(
1003            1u64.serialize(&mut serializer).unwrap_err(),
1004            UrlError::TopLevel
1005        );
1006        assert_eq!(
1007            2.5f32.serialize(&mut serializer).unwrap_err(),
1008            UrlError::TopLevel
1009        );
1010        assert_eq!(
1011            (-2.5f64).serialize(&mut serializer).unwrap_err(),
1012            UrlError::TopLevel
1013        );
1014        assert_eq!(
1015            'a'.serialize(&mut serializer).unwrap_err(),
1016            UrlError::TopLevel
1017        );
1018        assert_eq!(
1019            "hello?".serialize(&mut serializer).unwrap_err(),
1020            UrlError::TopLevel
1021        );
1022        assert_eq!(
1023            serializer
1024                .serialize_bytes(b"hello?\xc3\x28\x00")
1025                .unwrap_err(),
1026            UrlError::TopLevel
1027        );
1028        assert!(None::<bool>.serialize(&mut serializer).is_ok());
1029        assert_eq!(
1030            Some(true).serialize(&mut serializer).unwrap_err(),
1031            UrlError::TopLevel
1032        );
1033        assert!(().serialize(&mut serializer).is_ok());
1034        assert_eq!(
1035            "name".serialize(&mut serializer).unwrap_err(),
1036            UrlError::TopLevel
1037        );
1038        assert_eq!(
1039            (0, "variant").serialize(&mut serializer).unwrap_err(),
1040            UrlError::ValueNotSupported
1041        );
1042        assert_eq!(
1043            Ok::<_, bool>(true).serialize(&mut serializer).unwrap_err(),
1044            UrlError::TopLevel
1045        );
1046
1047        #[allow(clippy::items_after_statements)]
1048        #[derive(Serialize)]
1049        struct Params {
1050            id: i32,
1051        }
1052        let params = Params { id: 1 };
1053
1054        let mut serializer = UrlSerializer {
1055            url: String::new(),
1056            parts: vec![
1057                Part::Param(Param::Value("raw".to_owned())),
1058                Part::Param(Param::Key("id")),
1059            ],
1060        };
1061
1062        params.serialize(&mut serializer).unwrap();
1063        assert_eq!(
1064            serializer.parts,
1065            vec![
1066                Part::Param(Param::Value("raw".to_owned())),
1067                Part::Param(Param::Value("1".to_owned())),
1068            ]
1069        );
1070
1071        let params = Params { id: 1 };
1072
1073        let mut serializer = UrlSerializer {
1074            url: String::new(),
1075            parts: vec![
1076                Part::Param(Param::Value("raw".to_owned())),
1077                Part::Param(Param::Key("i")),
1078            ],
1079        };
1080
1081        assert_eq!(
1082            params.serialize(&mut serializer).unwrap_err(),
1083            UrlError::KeyNotFound("id")
1084        );
1085    }
1086}