wp_mini/endpoints/
story.rs

1use crate::client::WattpadRequestBuilder;
2use crate::field::{PartField, StoryField};
3use crate::types::{PartContentResponse, PartResponse, StoryResponse};
4use crate::WattpadError;
5use std::sync::atomic::AtomicBool;
6use std::sync::Arc;
7use bytes::Bytes;
8
9/// Contains methods for story-related API endpoints.
10///
11/// This client provides access to fetching information about stories, story parts,
12/// and their content in various formats.
13pub struct StoryClient {
14    /// The shared `reqwest` client for making HTTP requests.
15    pub(crate) http: reqwest::Client,
16    /// A flag indicating whether the main client is authenticated.
17    pub(crate) is_authenticated: Arc<AtomicBool>,
18}
19
20impl StoryClient {
21    /// Returns detailed information about a story.
22    ///
23    /// # Arguments
24    /// * `story_id` - The unique identifier of the story to fetch.
25    /// * `fields` - An optional slice of `StoryField` specifying which fields to retrieve.
26    ///   If `None`, a comprehensive list of all known fields will be requested by default.
27    ///
28    /// # Returns
29    /// A `Result` containing a `StoryResponse` struct with the story's metadata on success.
30    ///
31    /// # Errors
32    /// Returns a `WattpadError` if the network request fails or the API returns an error.
33    ///
34    /// # Examples
35    /// ```no_run
36    /// # use wattpad::{WattpadClient, field::StoryField};
37    /// # #[tokio::main]
38    /// # async fn main() -> Result<(), wattpad::WattpadError> {
39    /// let client = WattpadClient::new();
40    /// let story_id = 12345678; // Example story ID
41    /// let fields = &[StoryField::Title, StoryField::VoteCount];
42    ///
43    /// let story_info = client.story.get_story_info(story_id, Some(fields)).await?;
44    ///
45    /// println!("Title: {}", story_info.title);
46    /// println!("Votes: {}", story_info.vote_count);
47    /// # Ok(())
48    /// # }
49    /// ```
50    pub async fn get_story_info(
51        &self,
52        story_id: u64,
53        fields: Option<&[StoryField]>,
54    ) -> Result<StoryResponse, WattpadError> {
55        WattpadRequestBuilder::new(
56            &self.http,
57            &self.is_authenticated,
58            reqwest::Method::GET,
59            &format!("/api/v3/stories/{}", story_id),
60        )
61            .fields(fields)?
62            .execute()
63            .await
64    }
65
66    /// Returns detailed information about a single story part.
67    ///
68    /// # Arguments
69    /// * `part_id` - The unique identifier of the story part to fetch.
70    /// * `fields` - An optional slice of `PartField` specifying which fields to retrieve.
71    ///   If `None`, a default set of fields will be requested.
72    ///
73    /// # Returns
74    /// A `Result` containing a `PartResponse` struct with the part's metadata on success.
75    ///
76    /// # Errors
77    /// Returns a `WattpadError` if the network request fails or the API returns an error.
78    ///
79    /// # Examples
80    /// ```no_run
81    /// # use wattpad::{WattpadClient, field::PartField};
82    /// # #[tokio::main]
83    /// # async fn main() -> Result<(), wattpad::WattpadError> {
84    /// let client = WattpadClient::new();
85    /// let part_id = 87654321; // Example part ID
86    /// let fields = &[PartField::Title, PartField::WordCount];
87    ///
88    /// let part_info = client.story.get_part_info(part_id, Some(fields)).await?;
89    ///
90    /// println!("Part Title: {}", part_info.title);
91    /// println!("Word Count: {}", part_info.word_count);
92    /// # Ok(())
93    /// # }
94    /// ```
95    pub async fn get_part_info(
96        &self,
97        part_id: u64,
98        fields: Option<&[PartField]>,
99    ) -> Result<PartResponse, WattpadError> {
100        WattpadRequestBuilder::new(
101            &self.http,
102            &self.is_authenticated,
103            reqwest::Method::GET,
104            &format!("/api/v3/story_parts/{}", part_id),
105        )
106            .fields(fields)?
107            .execute()
108            .await
109    }
110
111    /// Fetches the raw text content of a single story part.
112    ///
113    /// This endpoint is useful for getting the plain story text without any metadata.
114    ///
115    /// # Arguments
116    /// * `part_id` - The unique identifier for the story part.
117    ///
118    /// # Returns
119    /// A `Result` containing a `String` with the raw story text on success.
120    ///
121    /// # Errors
122    /// Returns a `WattpadError` if the network request fails.
123    ///
124    /// # Examples
125    /// ```no_run
126    /// # use wattpad::WattpadClient;
127    /// # #[tokio::main]
128    /// # async fn main() -> Result<(), wattpad::WattpadError> {
129    /// let client = WattpadClient::new();
130    /// let part_id = 87654321;
131    ///
132    /// let content = client.story.get_part_content_raw(part_id).await?;
133    /// println!("Fetched content snippet: {}...", content.chars().take(100).collect::<String>());
134    /// # Ok(())
135    /// # }
136    /// ```
137    pub async fn get_part_content_raw(&self, part_id: u64) -> Result<String, WattpadError> {
138        WattpadRequestBuilder::new(
139            &self.http,
140            &self.is_authenticated,
141            reqwest::Method::GET,
142            "/apiv2/",
143        )
144            .param("m", Some("storytext"))
145            .param("id", Some(part_id))
146            .execute_raw_text()
147            .await
148    }
149
150    /// Fetches the content of a story part as a structured JSON object.
151    ///
152    /// # Arguments
153    /// * `part_id` - The unique identifier for the story part.
154    ///
155    /// # Returns
156    /// A `Result` containing a `PartContentResponse` struct with the parsed story content on success.
157    ///
158    /// # Errors
159    /// Returns a `WattpadError` if the network request fails or the JSON response cannot be parsed.
160    ///
161    /// # Examples
162    /// ```no_run
163    /// # use wattpad::WattpadClient;
164    /// # #[tokio::main]
165    /// # async fn main() -> Result<(), wattpad::WattpadError> {
166    /// let client = WattpadClient::new();
167    /// let part_id = 87654321;
168    ///
169    /// let content_json = client.story.get_part_content_json(part_id).await?;
170    /// println!("Text from JSON: {}...", content_json.text.chars().take(100).collect::<String>());
171    /// # Ok(())
172    /// # }
173    /// ```
174    pub async fn get_part_content_json(
175        &self,
176        part_id: u64,
177    ) -> Result<PartContentResponse, WattpadError> {
178        WattpadRequestBuilder::new(
179            &self.http,
180            &self.is_authenticated,
181            reqwest::Method::GET,
182            "/apiv2/",
183        )
184            .param("m", Some("storytext"))
185            .param("id", Some(part_id))
186            .param("output", Some("json"))
187            .execute()
188            .await
189    }
190
191    /// Downloads the text content of an entire story as a single ZIP archive.
192    ///
193    /// The archive contains the story text, typically organized by parts.
194    ///
195    /// # Arguments
196    /// * `story_id` - The unique identifier for the story (not a part).
197    ///
198    /// # Returns
199    /// A `Result` containing a `Bytes` object with the binary data of the ZIP file on success.
200    ///
201    /// # Errors
202    /// Returns a `WattpadError` if the network request or download fails.
203    ///
204    /// # Examples
205    /// ```no_run
206    /// # use wattpad::WattpadClient;
207    /// # use std::fs::File;
208    /// # use std::io::Write;
209    /// # #[tokio::main]
210    /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
211    /// let client = WattpadClient::new();
212    /// let story_id = 12345678;
213    ///
214    /// let zip_bytes = client.story.get_story_content_zip(story_id).await?;
215    ///
216    /// // Example: Save the ZIP file to disk
217    /// let mut file = File::create(format!("{}.zip", story_id))?;
218    /// file.write_all(&zip_bytes)?;
219    ///
220    /// println!("Successfully downloaded and saved {}.zip", story_id);
221    /// # Ok(())
222    /// # }
223    /// ```
224    pub async fn get_story_content_zip(&self, story_id: u64) -> Result<Bytes, WattpadError> {
225        WattpadRequestBuilder::new(
226            &self.http,
227            &self.is_authenticated,
228            reqwest::Method::GET,
229            "/apiv2/",
230        )
231            .param("m", Some("storytext"))
232            .param("group_id", Some(story_id))
233            .param("output", Some("zip"))
234            .execute_bytes()
235            .await
236    }
237}