rustybook 0.2.1

An ergonomic Facebook client in Rust
Documentation
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 {
    /// Gets posts from `url`.
    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)
    }

    /// Gets the first post from `url`.
    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())
    }

    /// Gets videos from `url`.
    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)
    }

    /// Gets the first video from `url`.
    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())
    }

    /// Gets attachments from `url`.
    pub async fn get_attachments(&self, url: &str) -> Result<Vec<Attachment>, Error> {
        self.collect_extracted(url, extract_attachments, "failed to extract attachments")
            .await
    }

    /// Gets extracted raw module payloads from `url`.
    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)
    }
}