orly 0.1.7

Download O'Reilly books as EPUB
Documentation
use reqwest::Url;
use serde::{de::Error, Deserialize, Deserializer};
use std::result::Result as StdResult;

fn parse_url<'de, D>(deserializer: D) -> StdResult<Url, D::Error>
where
    D: Deserializer<'de>,
{
    let s: &str = Deserialize::deserialize(deserializer)?;
    Url::parse(s).map_err(D::Error::custom)
}

fn parse_vec_url<'de, D>(deserializer: D) -> StdResult<Vec<Url>, D::Error>
where
    D: Deserializer<'de>,
{
    let vec: Vec<&str> = Deserialize::deserialize(deserializer)?;
    vec.into_iter()
        .map(Url::parse)
        .collect::<std::result::Result<_, _>>()
        .map_err(D::Error::custom)
}

fn to_xhtml<'de, D>(deserializer: D) -> StdResult<String, D::Error>
where
    D: Deserializer<'de>,
{
    let s: String = Deserialize::deserialize(deserializer)?;
    Ok(s.replace(".html", ".xhtml"))
}

#[derive(Deserialize, Debug)]
pub(crate) struct BillingInfo {
    pub subscription: SubscriptionInfo,
    pub trial: TrialInfo,
}

#[derive(Deserialize, Debug, Default)]
pub(crate) struct SubscriptionInfo {
    pub cancellation_date: Option<String>,
}

#[derive(Deserialize, Debug, Default)]
pub(crate) struct TrialInfo {
    pub trial_expiration_date: Option<String>,
}

#[derive(Deserialize, Debug)]
pub(crate) struct Credentials {
    pub logged_in: bool,
}

#[derive(Deserialize, Debug)]
pub(crate) struct LoginLookup {
    pub password_login_allowed: bool,
}

#[derive(Deserialize, Debug)]
pub struct Author {
    pub name: String,
}

#[derive(Deserialize, Debug)]
pub struct Subject {
    pub name: String,
}

#[derive(Deserialize, Debug)]
pub struct Publisher {
    pub name: String,
}

#[derive(Deserialize, Debug)]
pub struct Book {
    pub identifier: String,
    pub isbn: String,
    #[serde(deserialize_with = "parse_url")]
    pub cover: Url,
    pub chapter_list: String,
    pub toc: String,
    pub flat_toc: String,
    pub title: String,
    pub source: String,
    pub pagecount: usize,
    pub authors: Vec<Author>,
    pub subjects: Vec<Subject>,
    pub publishers: Vec<Publisher>,
    pub description: String,
    pub issued: String,
    #[serde(default)]
    pub rights: String,
    pub language: String,
}

#[derive(Deserialize, Debug)]
pub struct Stylesheet {
    pub full_path: String,
    #[serde(deserialize_with = "parse_url")]
    pub url: Url,
    pub original_url: String,
}

#[derive(Deserialize, Debug)]
pub struct ChapterNode {
    pub url: String,
    pub web_url: String,
    pub title: String,
}

#[derive(Deserialize, Debug)]
pub struct ChapterMeta {
    #[serde(deserialize_with = "parse_url")]
    pub asset_base_url: Url,
    pub title: String,
    #[serde(deserialize_with = "to_xhtml")]
    pub filename: String,
    pub images: Vec<String>,
    pub stylesheets: Vec<Stylesheet>,
    #[serde(deserialize_with = "parse_vec_url")]
    pub site_styles: Vec<Url>,
    #[serde(deserialize_with = "parse_url", rename = "content")]
    pub content_url: Url,
    #[serde(default)]
    pub position: usize,
}

#[derive(Debug)]
pub struct Chapter {
    pub meta: ChapterMeta,
    pub content: String,
}

#[derive(Deserialize, Debug)]
pub(crate) struct ChaptersResponse {
    pub count: usize,
    pub results: Vec<ChapterMeta>,
}

#[derive(Deserialize, Debug)]
pub struct TocElement {
    pub depth: usize,
    pub url: String,
    pub minutes_required: f64,
    pub fragment: String,
    pub filename: String,
    pub natural_key: Vec<String>,
    pub label: String,
    pub full_path: String,
    #[serde(deserialize_with = "to_xhtml")]
    pub href: String,
    pub id: String,
    pub media_type: String,
    pub children: Vec<TocElement>,
}