1use std::fmt::Display;
2
3use derive_builder::Builder;
4
5use crate::OpenlibraryRequest;
6
7#[derive(Default, Clone, Debug)]
8pub enum SearchType {
9 #[default]
10 Books,
11 Authors,
12 Subjects,
13 Lists,
14}
15
16impl Display for SearchType {
17 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
18 write!(
19 f,
20 "{}",
21 match self {
22 Self::Books => "",
23 Self::Authors => "/authors",
24 Self::Subjects => "/subjects",
25 Self::Lists => "/lists",
26 }
27 )
28 }
29}
30
31#[derive(Builder, Default, Debug)]
36#[builder(setter(into), default)]
37pub struct Search {
38 #[builder(setter(strip_option))]
39 query: Option<String>,
40 search_type: SearchType,
41 #[builder(default = "1")]
42 page: u32,
43 #[builder(default = "10")]
44 limit: u32,
45 #[builder(default = "vec![]")]
46 fields: Vec<String>,
47}
48
49impl OpenlibraryRequest for Search {
50 fn path(&self) -> String {
51 format!("/search{}.json", self.search_type)
52 }
53
54 fn query(&self) -> Vec<(&'static str, String)> {
55 vec![
56 ("page", self.page.to_string()),
57 ("limit", self.limit.to_string()),
58 ("q", self.query.as_deref().unwrap_or_default().to_string()),
59 ("fields", self.fields.join(",")),
60 ]
61 }
62}
63
64#[cfg(test)]
65mod tests {
66 use mockito::mock;
67 use serde_json::json;
68
69 use crate::OpenlibraryRequest;
70
71 use super::SearchBuilder;
72
73 #[test]
74 fn test_search_execute() {
75 let search = SearchBuilder::default()
76 .query("test")
77 .fields(
78 ["key", "title"]
79 .into_iter()
80 .map(String::from)
81 .collect::<Vec<String>>(),
82 )
83 .build()
84 .unwrap();
85
86 let json = json!({
87 "numFound": 1,
88 "start": 0,
89 "numFoundExact": true,
90 "docs": [
91 {
92 "key": "/works/43242",
93 "title": "test",
94 }
95 ]
96 });
97
98 let _m = mock(
99 "GET",
100 format!("{}?{}", search.url().path(), search.url().query().unwrap()).as_str(),
101 )
102 .with_header("content-type", "application/json")
103 .with_body(json.to_string())
104 .create();
105
106 let search_result = search.execute();
107
108 assert_eq!(search_result["numFound"], 1);
109 assert_eq!(search_result["start"], 0);
110 assert_eq!(search_result["numFoundExact"], true);
111 assert_eq!(search_result["docs"].as_array().unwrap().len(), 1);
112
113 let doc = &search_result["docs"][0];
114
115 assert_eq!(doc.get("key").unwrap().as_str().unwrap(), "/works/43242");
116 assert_eq!(doc.get("title").unwrap().as_str().unwrap(), "test");
117 }
118}