pexels_sdk/videos/
search.rs

1use crate::{
2    Locale, Orientation, Pexels, PexelsError, Size, VideoResponse, PEXELS_API, PEXELS_VIDEO_PATH,
3};
4use url::Url;
5
6/// The path for the search endpoint.
7const PEXELS_VIDEO_SEARCH_PATH: &str = "search";
8
9/// Represents a search request to the Pexels API for videos.
10pub struct Search<'a> {
11    query: &'a str,
12    page: Option<usize>,
13    per_page: Option<usize>,
14    orientation: Option<Orientation>,
15    size: Option<Size>,
16    locale: Option<Locale>,
17}
18
19impl<'a> Search<'a> {
20    /// Creates [`SearchBuilder`] for building URI's.
21    pub fn builder() -> SearchBuilder<'a> {
22        SearchBuilder::default()
23    }
24
25    /// Creates a URI from the provided parameters.
26    pub fn create_uri(&self) -> crate::BuilderResult {
27        let uri = format!("{PEXELS_API}/{PEXELS_VIDEO_PATH}/{PEXELS_VIDEO_SEARCH_PATH}");
28
29        let mut url = Url::parse(uri.as_str())?;
30
31        url.query_pairs_mut().append_pair("query", self.query);
32
33        if let Some(page) = &self.page {
34            url.query_pairs_mut()
35                .append_pair("page", page.to_string().as_str());
36        }
37
38        if let Some(per_page) = &self.per_page {
39            url.query_pairs_mut()
40                .append_pair("per_page", per_page.to_string().as_str());
41        }
42
43        if let Some(orientation) = &self.orientation {
44            url.query_pairs_mut()
45                .append_pair("orientation", orientation.as_str());
46        }
47
48        if let Some(size) = &self.size {
49            url.query_pairs_mut().append_pair("size", size.as_str());
50        }
51
52        if let Some(locale) = &self.locale {
53            url.query_pairs_mut().append_pair("locale", locale.as_str());
54        }
55
56        Ok(url.into())
57    }
58
59    /// Fetches the list of videos based on the search query from the Pexels API.
60    pub async fn fetch(&self, client: &Pexels) -> Result<VideoResponse, PexelsError> {
61        let url = self.create_uri()?;
62        let response = client.make_request(url.as_str()).await?;
63        let response_video: VideoResponse = serde_json::from_value(response)?;
64        Ok(response_video)
65    }
66}
67
68/// Builder for [`Search`].
69#[derive(Default)]
70pub struct SearchBuilder<'a> {
71    query: &'a str,
72    page: Option<usize>,
73    per_page: Option<usize>,
74    orientation: Option<Orientation>,
75    size: Option<Size>,
76    locale: Option<Locale>,
77}
78
79impl<'a> SearchBuilder<'a> {
80    /// Create a new [`SearchBuilder`].
81    pub fn new() -> Self {
82        Self {
83            query: "",
84            page: None,
85            per_page: None,
86            orientation: None,
87            size: None,
88            locale: None,
89        }
90    }
91
92    /// Sets the search query.
93    pub fn query(mut self, query: &'a str) -> Self {
94        self.query = query;
95        self
96    }
97
98    /// Sets the page number for the request.
99    pub fn page(mut self, page: usize) -> Self {
100        self.page = Some(page);
101        self
102    }
103
104    /// Sets the number of results per page for the request.
105    pub fn per_page(mut self, per_page: usize) -> Self {
106        self.per_page = Some(per_page);
107        self
108    }
109
110    /// Sets the desired video orientation.
111    pub fn orientation(mut self, orientation: Orientation) -> Self {
112        self.orientation = Some(orientation);
113        self
114    }
115
116    /// Sets the minimum video size.
117    pub fn size(mut self, size: Size) -> Self {
118        self.size = Some(size);
119        self
120    }
121
122    /// Sets the locale of the search.
123    pub fn locale(mut self, locale: Locale) -> Self {
124        self.locale = Some(locale);
125        self
126    }
127
128    /// Builds a `Search` instance from the `SearchBuilder`.
129    pub fn build(self) -> Search<'a> {
130        Search {
131            query: self.query,
132            page: self.page,
133            per_page: self.per_page,
134            orientation: self.orientation,
135            size: self.size,
136            locale: self.locale,
137        }
138    }
139}
140
141#[cfg(test)]
142mod tests {
143    use crate::videos::search::SearchBuilder;
144    use crate::{Locale, Orientation, Size};
145
146    #[test]
147    fn test_query() {
148        let uri = SearchBuilder::new().query("bar").build();
149        assert_eq!(
150            "https://api.pexels.com/videos/search?query=bar",
151            uri.create_uri().unwrap()
152        );
153    }
154
155    #[test]
156    fn test_page() {
157        let uri = SearchBuilder::new().page(1).build();
158        assert_eq!(
159            "https://api.pexels.com/videos/search?query=&page=1",
160            uri.create_uri().unwrap()
161        );
162    }
163
164    #[test]
165    fn test_per_page() {
166        let uri = SearchBuilder::new().per_page(1).build();
167        assert_eq!(
168            "https://api.pexels.com/videos/search?query=&per_page=1",
169            uri.create_uri().unwrap()
170        );
171    }
172
173    #[test]
174    fn test_orientation() {
175        let uri = SearchBuilder::new()
176            .orientation(Orientation::Landscape)
177            .build();
178        assert_eq!(
179            "https://api.pexels.com/videos/search?query=&orientation=landscape",
180            uri.create_uri().unwrap()
181        );
182    }
183
184    #[test]
185    fn test_size() {
186        let uri = SearchBuilder::new().size(Size::Small).build();
187        assert_eq!(
188            "https://api.pexels.com/videos/search?query=&size=small",
189            uri.create_uri().unwrap()
190        );
191    }
192
193    #[test]
194    fn test_locale() {
195        let uri = SearchBuilder::new().locale(Locale::sv_SE).build();
196        assert_eq!(
197            "https://api.pexels.com/videos/search?query=&locale=sv-SE",
198            uri.create_uri().unwrap()
199        );
200    }
201}