lotr_api/
client.rs

1//! Client definition. This is the main entry point for the library.
2//! It is used to make requests to the API. It is created with a token, which is used to authenticate the requests.
3//! You can get a token from <https://the-one-api.dev/>.
4
5use crate::{
6    request::{Request, Requester},
7    Book, Chapter, Character, Error, Item, ItemType, Movie, Quote, Response,
8};
9
10/// The client for the one api to rule them all.
11/// It is used to make requests to the API.
12///
13/// # Examples
14/// ```rust, no_run
15/// use lotr_api::Client;
16///
17/// #[tokio::main]
18/// async fn main() {
19///     let client = Client::new("your_token".to_string());
20///     let books = client.get_books().await.unwrap();
21///     // ...
22/// }
23/// ```
24pub struct Client {
25    requester: Requester,
26}
27
28impl Client {
29    /// Creates a new client with the given token.
30    /// The token is used to authenticate the requests.
31    /// You can get a token from <https://the-one-api.dev/>.
32    pub fn new(token: String) -> Self {
33        Self {
34            requester: Requester::new(token),
35        }
36    }
37
38    async fn request_with_url<T>(&self, url: &str) -> Result<Response<T>, Error>
39    where
40        T: serde::de::DeserializeOwned,
41    {
42        let response = self.requester.get(url).await?;
43        let response: Response<T> = serde_json::from_str(&response).map_err(Error::from)?;
44        Ok(response)
45    }
46
47    async fn request<T>(&self, request: Request) -> Result<Response<T>, Error>
48    where
49        T: serde::de::DeserializeOwned,
50    {
51        let response = self.requester.get_from_request(request).await?;
52        let response: Response<T> = serde_json::from_str(&response).map_err(Error::from)?;
53        Ok(response)
54    }
55
56    /// Returns all books.
57    pub async fn get_books(&self) -> Result<Vec<Book>, Error> {
58        Ok(self.request_with_url::<Book>("book").await?.get_contents())
59    }
60
61    /// Returns all movies.
62    pub async fn get_movies(&self) -> Result<Vec<Movie>, Error> {
63        Ok(self
64            .request_with_url::<Movie>("movie")
65            .await?
66            .get_contents())
67    }
68
69    /// Returns all the quotes. Due to the API default limit of 1000,
70    /// this function has to set a hardcoded limit.
71    /// Currently there are  2384 quotes on the api, so we call it with a limit of 2400
72    /// to have a little bit of buffer in case the number of quotes increases
73    pub async fn get_quotes(&self) -> Result<Vec<Quote>, Error> {
74        Ok(self
75            .request_with_url::<Quote>("quote?limit=2400")
76            .await?
77            .get_contents())
78    }
79
80    // Returns all the characters.
81    pub async fn get_characters(&self) -> Result<Vec<Character>, Error> {
82        Ok(self
83            .request_with_url::<Character>("character")
84            .await?
85            .get_contents())
86    }
87
88    /// Returns all chapters.
89    pub async fn get_chapters(&self) -> Result<Vec<Chapter>, Error> {
90        Ok(self
91            .request_with_url::<Chapter>("chapter")
92            .await?
93            .get_contents())
94    }
95
96    /// Returns the book with the given id.
97    ///
98    /// # Errors
99    /// If there is no book with the given id, an error is returned.
100    pub async fn get_book_by_id(&self, id: &str) -> Result<Book, Error> {
101        let url = format!("book/{}", id);
102        let mut books = self.request_with_url::<Book>(&url).await?.get_contents();
103        books
104            .pop()
105            .ok_or(Error::Other("No book with id {} found".to_string()))
106    }
107
108    /// Returns the movie with the given id.
109    ///
110    /// # Errors
111    /// If there is no movie with the given id, an error is returned.
112    pub async fn get_movie_by_id(&self, id: &str) -> Result<Movie, Error> {
113        let url = format!("movie/{}", id);
114        let mut movies = self.request_with_url::<Movie>(&url).await?.get_contents();
115        movies
116            .pop()
117            .ok_or(Error::Other("No movie with id {} found".to_string()))
118    }
119
120    /// Returns the quote with the given id.
121    ///
122    /// # Errors
123    /// If there is no quote with the given id, an error is returned.
124    pub async fn get_quote_by_id(&self, id: &str) -> Result<Quote, Error> {
125        let url = format!("quote/{}", id);
126        let mut quotes = self.request_with_url::<Quote>(&url).await?.get_contents();
127        quotes
128            .pop()
129            .ok_or(Error::Other("No quote with id {} found".to_string()))
130    }
131
132    /// Returns the character with the given id.
133    ///
134    /// # Errors
135    /// If there is no character with the given id, an error is returned.
136    pub async fn get_character_by_id(&self, id: &str) -> Result<Character, Error> {
137        let url = format!("character/{}", id);
138        let mut characters = self
139            .request_with_url::<Character>(&url)
140            .await?
141            .get_contents();
142        characters
143            .pop()
144            .ok_or(Error::Other("No character with id {} found".to_string()))
145    }
146
147    /// Returns the chapter with the given id.
148    ///
149    /// # Errors
150    /// If there is no chapter with the given id, an error is returned.
151    pub async fn get_chapter_by_id(&self, id: &str) -> Result<Chapter, Error> {
152        let url = format!("chapter/{}", id);
153        let mut chapters = self.request_with_url::<Chapter>(&url).await?.get_contents();
154        chapters
155            .pop()
156            .ok_or(Error::Other("No chapter with id {} found".to_string()))
157    }
158
159    /// Returns the chapters of the given book.
160    pub async fn get_chapters_from_book(&self, book_id: &str) -> Result<Vec<Chapter>, Error> {
161        let url = format!("book/{}/chapter", book_id);
162        Ok(self.request_with_url::<Chapter>(&url).await?.get_contents())
163    }
164
165    /// Returns the quotes of the given book.
166    pub async fn get_quotes_from_movie(&self, movie_id: &str) -> Result<Vec<Quote>, Error> {
167        let url = format!("movie/{}/quote", movie_id);
168        Ok(self.request_with_url::<Quote>(&url).await?.get_contents())
169    }
170
171    /// Returns the quotes of the given book.
172    pub async fn get_quotes_from_character(&self, character_id: &str) -> Result<Vec<Quote>, Error> {
173        let url = format!("character/{}/quote", character_id);
174        Ok(self.request_with_url::<Quote>(&url).await?.get_contents())
175    }
176
177    /// returns the result of the given request.
178    /// You must specify the type of the result, if not
179    /// there is no way of deserialize the result.
180    ///
181    /// # Examples
182    ///
183    /// ```rust, no_run
184    /// use lotr_api::{Client, ItemType};
185    /// use lotr_api::Book;
186    ///
187    /// #[tokio::main]
188    /// async fn main() {
189    ///    let client = Client::new("your_token".to_string());
190    ///    let url= "book?page=2&limit=2";
191    ///    let books = client.get_from_url::<Book>(url).await.unwrap();
192    ///    // ...
193    /// }
194    /// ```
195    ///
196    pub async fn get_from_url<T>(&self, url: &str) -> Result<Vec<T>, Error>
197    where
198        T: serde::de::DeserializeOwned,
199    {
200        Ok(self.request_with_url::<T>(url).await?.get_contents())
201    }
202
203    /// Returns the items of the given custom request.
204    ///
205    /// # Examples
206    /// ```rust, no_run
207    /// use lotr_api::{
208    ///     attribute::{Attribute, BookAttribute},
209    ///     filter::{Filter, Operator},
210    ///     request::{
211    ///         sort::{Sort, SortOrder},
212    ///         RequestBuilder},
213    ///     Client, Item, ItemType};
214    ///
215    /// #[tokio::main]
216    /// async fn main() {
217    ///    let client = Client::new("your_token".to_string());
218    ///    let request = RequestBuilder::new(ItemType::Book)
219    ///         .filter(Filter::Match(
220    ///             Attribute::Book(BookAttribute::Name),
221    ///             Operator::Eq,
222    ///             vec!["The Fellowship of the Ring".to_string()])
223    ///         )
224    ///         .sort(Sort::new(SortOrder::Ascending, Attribute::Book(BookAttribute::Name)))
225    ///         .build()
226    ///        .expect("Failed to build request");
227    ///     let books = client.get(request).await.unwrap();
228    ///     // ...
229    /// }
230    ///     
231    pub async fn get(&self, request: Request) -> Result<Vec<Item>, Error> {
232        match request.get_item_type() {
233            ItemType::Book => {
234                let response = self.request::<Book>(request).await?;
235                Ok(response.into())
236            }
237
238            ItemType::Movie => {
239                let response = self.request::<Movie>(request).await?;
240                Ok(response.into())
241            }
242
243            ItemType::Quote => {
244                let response = self.request::<Quote>(request).await?;
245                Ok(response.into())
246            }
247
248            ItemType::Character => {
249                let response = self.request::<Character>(request).await?;
250                Ok(response.into())
251            }
252
253            ItemType::Chapter => {
254                let response = self.request::<Chapter>(request).await?;
255                Ok(response.into())
256            }
257        }
258    }
259}