Skip to main content

resourcespace_client/api/
search.rs

1use serde::{Serialize, Serializer};
2use serde_with::skip_serializing_none;
3
4use crate::client::Client;
5use crate::error::RsError;
6
7use super::{List, SortOrder};
8
9/// Sub-API for search endpoints.
10#[derive(Debug)]
11pub struct SearchApi<'a> {
12    client: &'a Client,
13}
14
15impl<'a> SearchApi<'a> {
16    pub(crate) fn new(client: &'a Client) -> Self {
17        Self { client }
18    }
19
20    /// Performs a search and returns matching resources.
21    ///
22    /// ## Arguments
23    /// * `request` - Parameters built via [`DoSearchRequest`]
24    ///
25    /// ## TODO: Errors
26    /// Returns [`RsError::OperationFailed`] if the search returns no results
27    /// or the user lacks search permissions.
28    ///
29    /// ## Examples
30    /// ```no_run
31    /// # use resourcespace_client::{Client, api::search::{DoSearchRequest}};
32    /// # use resourcespace_client::api::SortOrder;
33    /// # async fn example(client: Client) -> Result<(), Box<dyn std::error::Error>> {
34    /// let results = client.search()
35    ///     .do_search(DoSearchRequest::new("cat").sort(SortOrder::Desc))
36    ///     .await?;
37    ///
38    /// let specific_results = client.search()
39    ///     .do_search(
40    ///         DoSearchRequest::new("cat")
41    ///             .fetchrows("100")
42    ///             .offset(50)
43    ///             .archive(0)
44    ///     )
45    ///     .await?;
46    /// # Ok(())
47    /// # }
48    /// ```
49    pub async fn do_search(&self, request: DoSearchRequest) -> Result<serde_json::Value, RsError> {
50        self.client
51            .send_request("do_search", reqwest::Method::GET, request)
52            .await
53    }
54
55    /// Performs a search and returns matching resources including URLs for requested preview sizes.
56    ///
57    /// ## Arguments
58    /// * `request` - Parameters built via [`SearchGetPreviewsRequest`]
59    ///
60    /// ## TODO: Errors
61    /// Returns [`RsError::OperationFailed`] if the search returns no results
62    /// or the user lacks search permissions.
63    ///
64    /// ## Examples
65    /// ```no_run
66    /// # use resourcespace_client::{Client, api::search::SearchGetPreviewsRequest};
67    /// # use resourcespace_client::api::SortOrder;
68    /// # async fn example(client: Client) -> Result<(), Box<dyn std::error::Error>> {
69    /// let results = client.search()
70    ///     .search_get_previews(SearchGetPreviewsRequest::new("cat").getsizes("thm,scr"))
71    ///     .await?;
72    ///
73    /// let specific_results = client.search()
74    ///     .search_get_previews(
75    ///         SearchGetPreviewsRequest::new("cat")
76    ///             .getsizes("thm,scr,pre")
77    ///             .previewext("jpg")
78    ///             .sort(SortOrder::Desc)
79    ///             .fetchrows("0,50")
80    ///     )
81    ///     .await?;
82    /// # Ok(())
83    /// # }
84    /// ```
85    pub async fn search_get_previews(
86        &self,
87        request: SearchGetPreviewsRequest,
88    ) -> Result<serde_json::Value, RsError> {
89        self.client
90            .send_request("search_get_previews", reqwest::Method::GET, request)
91            .await
92    }
93}
94
95/// The row fetch mode for a search request.
96///
97/// Use [`FetchRows::limit`] to cap the number of results, or
98/// [`FetchRows::page`] to fetch a specific window with offset and limit.
99/// Note that these two modes return different response shapes from
100/// ResourceSpace — `page` returns a structured response with a `total`
101/// count alongside the results.
102///
103/// ```no_run
104/// FetchRows::limit(100)         // return up to 100 results
105/// FetchRows::page(0, 50)        // return results 0–50
106/// ```
107#[derive(Clone, Debug, PartialEq)]
108pub enum FetchRows {
109    /// Return up to N rows
110    Limit(u32),
111    /// Return rows with explicit offset and limit, enables paginated response
112    Page { offset: u32, limit: u32 },
113}
114
115impl FetchRows {
116    pub fn limit(n: u32) -> Self {
117        Self::Limit(n)
118    }
119
120    pub fn page(offset: u32, limit: u32) -> Self {
121        Self::Page { offset, limit }
122    }
123}
124
125impl Serialize for FetchRows {
126    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
127        match self {
128            Self::Limit(n) => n.serialize(serializer),
129            Self::Page { offset, limit } => format!("{},{}", offset, limit).serialize(serializer),
130        }
131    }
132}
133
134#[skip_serializing_none]
135#[derive(Clone, Debug, PartialEq, Serialize)]
136pub struct DoSearchRequest {
137    /// The search string to match resources against.
138    pub search: String,
139    /// Comma-separated list of resource type IDs to restrict results to.
140    pub restypes: Option<List<u32>>,
141    /// Field name to order results by.
142    pub order_by: Option<String>,
143    /// Archive status filter: 0 = live, 1 = archived, 2 = deleted.
144    pub archive: Option<i8>,
145    /// Number of rows to return, or `"offset,rows"` for paginated fetching.
146    pub fetchrows: Option<FetchRows>,
147    /// Sort direction for the results.
148    pub sort: Option<SortOrder>,
149    /// Number of results to skip, used for pagination.
150    pub offset: Option<u32>,
151}
152
153impl DoSearchRequest {
154    pub fn new(search: impl Into<String>) -> Self {
155        Self {
156            search: search.into(),
157            restypes: None,
158            order_by: None,
159            archive: None,
160            fetchrows: None,
161            sort: None,
162            offset: None,
163        }
164    }
165
166    pub fn restypes(mut self, restypes: impl Into<List<u32>>) -> Self {
167        self.restypes = Some(restypes.into());
168        self
169    }
170
171    pub fn order_by(mut self, order_by: impl Into<String>) -> Self {
172        self.order_by = Some(order_by.into());
173        self
174    }
175
176    pub fn archive(mut self, archive: i8) -> Self {
177        self.archive = Some(archive);
178        self
179    }
180
181    pub fn fetchrows(mut self, fetchrows: FetchRows) -> Self {
182        self.fetchrows = Some(fetchrows);
183        self
184    }
185
186    pub fn sort(mut self, sort: SortOrder) -> Self {
187        self.sort = Some(sort);
188        self
189    }
190
191    pub fn offset(mut self, offset: u32) -> Self {
192        self.offset = Some(offset);
193        self
194    }
195}
196
197#[skip_serializing_none]
198#[derive(Clone, Debug, PartialEq, Serialize)]
199pub struct SearchGetPreviewsRequest {
200    /// The search string to match resources against.
201    pub search: String,
202    /// Comma-separated list of resource type IDs to restrict results to.
203    pub restypes: Option<List<u32>>,
204    /// Field name to order results by.
205    pub order_by: Option<String>,
206    /// Archive status filter: 0 = live, 1 = archived, 2 = deleted.
207    pub archive: Option<i8>,
208    /// Number of rows to return, or `"offset,rows"` for paginated fetching.
209    pub fetchrows: Option<FetchRows>,
210    /// Sort direction for the results.
211    pub sort: Option<SortOrder>,
212    /// Only return resources modified within this many days.
213    pub recent_search_daylimit: Option<String>,
214    /// Comma-separated list of preview sizes to include URLs for (e.g. `"thm,scr,pre"`).
215    pub getsizes: Option<List<u32>>,
216    /// Override the preview file extension returned (e.g. `"jpg"`).
217    pub previewext: Option<String>,
218}
219
220impl SearchGetPreviewsRequest {
221    pub fn new(search: impl Into<String>) -> Self {
222        Self {
223            search: search.into(),
224            restypes: None,
225            order_by: None,
226            archive: None,
227            fetchrows: None,
228            sort: None,
229            recent_search_daylimit: None,
230            getsizes: None,
231            previewext: None,
232        }
233    }
234
235    pub fn restypes(mut self, restypes: impl Into<List<u32>>) -> Self {
236        self.restypes = Some(restypes.into());
237        self
238    }
239
240    pub fn order_by(mut self, order_by: impl Into<String>) -> Self {
241        self.order_by = Some(order_by.into());
242        self
243    }
244
245    pub fn archive(mut self, archive: i8) -> Self {
246        self.archive = Some(archive);
247        self
248    }
249
250    pub fn fetchrows(mut self, fetchrows: FetchRows) -> Self {
251        self.fetchrows = Some(fetchrows);
252        self
253    }
254
255    pub fn sort(mut self, sort: SortOrder) -> Self {
256        self.sort = Some(sort);
257        self
258    }
259
260    pub fn recent_search_daylimit(mut self, recent_search_daylimit: impl Into<String>) -> Self {
261        self.recent_search_daylimit = Some(recent_search_daylimit.into());
262        self
263    }
264
265    pub fn getsizes(mut self, getsizes: impl Into<List<u32>>) -> Self {
266        self.getsizes = Some(getsizes.into());
267        self
268    }
269
270    pub fn previewext(mut self, previewext: impl Into<String>) -> Self {
271        self.previewext = Some(previewext.into());
272        self
273    }
274}