grscraper/
request_builder.rs1use crate::{
2 errors::ScraperError,
3 goodreads_id_fetcher::{
4 fetch_id_from_isbn, fetch_id_from_title, fetch_id_from_title_and_author, verify_id_exists,
5 },
6 metadata_fetcher::{fetch_metadata, BookMetadata},
7};
8
9pub trait RequestState {}
10pub struct EmptyState;
11pub struct IdState(String);
12pub struct IsbnState(String);
13pub struct TitleState(String);
14pub struct TitleWithAuthorState(String, String);
15
16impl RequestState for EmptyState {}
17impl RequestState for IdState {}
18impl RequestState for IsbnState {}
19impl RequestState for TitleState {}
20impl RequestState for TitleWithAuthorState {}
21
22pub struct MetadataRequestBuilder<T: RequestState> {
24 state: T,
25}
26
27impl Default for MetadataRequestBuilder<EmptyState> {
28 fn default() -> Self {
29 MetadataRequestBuilder::new()
30 }
31}
32
33impl MetadataRequestBuilder<EmptyState> {
34 fn new() -> Self {
35 MetadataRequestBuilder { state: EmptyState }
36 }
37
38 pub fn with_id(self, id: &str) -> MetadataRequestBuilder<IdState> {
39 MetadataRequestBuilder {
40 state: IdState(id.to_string()),
41 }
42 }
43
44 pub fn with_isbn(self, isbn: &str) -> MetadataRequestBuilder<IsbnState> {
45 MetadataRequestBuilder {
46 state: IsbnState(isbn.to_string()),
47 }
48 }
49
50 pub fn with_title(self, title: &str) -> MetadataRequestBuilder<TitleState> {
51 MetadataRequestBuilder {
52 state: TitleState(title.to_string()),
53 }
54 }
55}
56
57impl MetadataRequestBuilder<TitleState> {
58 pub fn with_author(self, author: &str) -> MetadataRequestBuilder<TitleWithAuthorState> {
59 MetadataRequestBuilder {
60 state: TitleWithAuthorState(self.state.0, author.to_string()),
61 }
62 }
63
64 pub async fn execute(&self) -> Result<Option<BookMetadata>, ScraperError> {
65 let title = &self.state.0;
66 let goodreads_id = fetch_id_from_title(title).await?;
67 match goodreads_id {
68 Some(id) => Ok(Some(fetch_metadata(&id).await?)),
69 None => Ok(None),
70 }
71 }
72}
73
74impl MetadataRequestBuilder<IdState> {
75 pub async fn execute(&self) -> Result<Option<BookMetadata>, ScraperError> {
76 let id = &self.state.0;
77 if !verify_id_exists(id).await {
78 return Ok(None);
79 }
80 Ok(Some(fetch_metadata(id).await?))
81 }
82}
83
84impl MetadataRequestBuilder<IsbnState> {
85 pub async fn execute(&self) -> Result<Option<BookMetadata>, ScraperError> {
86 let isbn = &self.state.0;
87 let goodreads_id = fetch_id_from_isbn(isbn).await?;
88 match goodreads_id {
89 Some(id) => Ok(Some(fetch_metadata(&id).await?)),
90 None => Ok(None),
91 }
92 }
93}
94
95impl MetadataRequestBuilder<TitleWithAuthorState> {
96 pub async fn execute(&self) -> Result<Option<BookMetadata>, ScraperError> {
97 let title = &self.state.0;
98 let author = &self.state.1;
99 let goodreads_id = fetch_id_from_title_and_author(title, author).await?;
100 match goodreads_id {
101 Some(id) => Ok(Some(fetch_metadata(&id).await?)),
102 None => Ok(None),
103 }
104 }
105}