1use std::collections::HashMap;
2
3use crate::client::Client;
4use crate::constants;
5use crate::error;
6use crate::types::search::SearchOptions;
7use crate::types::{PostsResponse, SearchMode};
8use crate::validation;
9
10impl Client {
11 pub async fn keyword_search(
13 &self,
14 query: &str,
15 opts: Option<&SearchOptions>,
16 ) -> crate::Result<PostsResponse> {
17 if query.is_empty() {
18 return Err(error::new_validation_error(
19 0,
20 constants::ERR_EMPTY_SEARCH_QUERY,
21 "",
22 "query",
23 ));
24 }
25
26 if let Some(opts) = opts {
27 validation::validate_search_options(opts)?;
28 }
29
30 let token = self.access_token().await;
31 let mut params = HashMap::new();
32 params.insert("q".into(), query.to_owned());
33 params.insert("fields".into(), constants::POST_EXTENDED_FIELDS.into());
34
35 if let Some(opts) = opts {
36 if let Some(ref search_type) = opts.search_type {
37 params.insert(
38 "search_type".into(),
39 serde_json::to_string(search_type)
40 .unwrap_or_default()
41 .trim_matches('"')
42 .to_owned(),
43 );
44 }
45 if let Some(ref search_mode) = opts.search_mode {
46 params.insert(
47 "search_mode".into(),
48 serde_json::to_string(search_mode)
49 .unwrap_or_default()
50 .trim_matches('"')
51 .to_owned(),
52 );
53 }
54 if let Some(ref media_type) = opts.media_type {
55 params.insert("media_type".into(), media_type.clone());
56 }
57 if let Some(ref author) = opts.author_username {
58 params.insert("author_username".into(), author.clone());
59 }
60 if let Some(limit) = opts.limit {
61 params.insert("limit".into(), limit.to_string());
62 }
63 if let Some(since) = opts.since {
64 params.insert("since".into(), since.to_string());
65 }
66 if let Some(until) = opts.until {
67 params.insert("until".into(), until.to_string());
68 }
69 if let Some(ref before) = opts.before {
70 params.insert("before".into(), before.clone());
71 }
72 if let Some(ref after) = opts.after {
73 params.insert("after".into(), after.clone());
74 }
75 }
76
77 let resp = self
78 .http_client
79 .get("/keyword_search", params, &token)
80 .await?;
81 resp.json()
82 }
83
84 pub async fn hashtag_search(
88 &self,
89 tag: &str,
90 opts: Option<&SearchOptions>,
91 ) -> crate::Result<PostsResponse> {
92 let mut tag_opts = opts.cloned().unwrap_or_default();
93 tag_opts.search_mode = Some(SearchMode::Tag);
94 self.keyword_search(tag, Some(&tag_opts)).await
95 }
96}