grscraper/
request_builder.rs

1use 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
22/// Builder for constructing a metadata request.
23pub 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}