use rustybook_extractor::payload::ModuleInvocation;
use rustybook_extractor::payload::extract_modules;
use rustybook_extractor::{
Attachment,
ExtractionError,
Post,
Video,
extract_attachments,
extract_post,
extract_video,
};
use serde_json::Value;
use tracing::trace;
use crate::{
Error,
Rustybook,
};
impl Rustybook {
pub async fn get_posts(&self, url: &str) -> Result<Vec<Post>, Error> {
let posts = self
.collect_extracted(url, extract_post, "failed to extract post")
.await?;
trace!("posts: {}", posts.len());
Ok(posts)
}
pub async fn get_first_post(&self, url: &str) -> Result<Option<Post>, Error> {
let posts = self.get_posts(url).await?;
Ok(posts.into_iter().next())
}
pub async fn get_videos(&self, url: &str) -> Result<Vec<Video>, Error> {
let videos = self
.collect_extracted(url, extract_video, "failed to extract video info")
.await?;
trace!("videos: {}", videos.len());
Ok(videos)
}
pub async fn get_first_video(&self, url: &str) -> Result<Option<Video>, Error> {
let videos = self.get_videos(url).await?;
Ok(videos.into_iter().next())
}
pub async fn get_attachments(&self, url: &str) -> Result<Vec<Attachment>, Error> {
self.collect_extracted(url, extract_attachments, "failed to extract attachments")
.await
}
pub async fn get_raw_payloads(&self, url: &str) -> Result<Vec<ModuleInvocation>, Error> {
let response = self.inner.http.send(url).await?;
Ok(extract_modules(&response.text)?)
}
async fn collect_extracted<T>(
&self,
url: &str,
extractor: fn(&Value) -> Result<Vec<T>, ExtractionError>,
failure_message: &'static str,
) -> Result<Vec<T>, Error> {
let modules = self.get_raw_payloads(url).await?;
let items = modules
.into_iter()
.filter_map(|module| module.body)
.filter_map(|payload| match extractor(&payload) {
Ok(items) => Some(items),
Err(error) => {
tracing::warn!(?error, message = failure_message);
None
}
})
.flatten()
.collect();
Ok(items)
}
}