aboutsummaryrefslogtreecommitdiff
path: root/src/orly.rs
blob: 0e34e5ce78aef02cb27604776bcc9eab9932cbdc (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
use crate::http_client::HttpClient;
use anyhow::{bail, Result};
use serde::Deserialize;

pub const PROFILE_URL: &str = "https://learning.oreilly.com/profile/";

/// Minimal subset of the book that we care about.
#[derive(Debug, Deserialize)]
pub struct BookInfo {
    pub title: String,
    pub web_url: String,
}

/// Check whether cookies keep us logged in by fetching the profile page.
/// Returns:
/// - Ok(true)  => HTTP 200 (assume logged in)
/// - Ok(false) => Redirect or 401/403 (assume not logged in)
/// - Err(..)   => Network/other error
pub async fn check_login(client: &HttpClient) -> Result<bool> {
    let res = client.client().get(PROFILE_URL).send().await?;
    let status = res.status();

    if status.is_redirection() {
        Ok(false)
    } else if status == 200 {
        Ok(true)
    } else {
        bail!("Profile request returned unexpected status {}", status)
    }
}

/// Build the v1 API URL for the book.
pub fn book_api_url(bookid: &str) -> String {
    format!("https://learning.oreilly.com/api/v1/book/{bookid}")
}

/// Fetch book metadata from the website.
pub async fn fetch_book_info(client: &HttpClient, bookid: &str) -> Result<BookInfo> {
    let url = book_api_url(bookid);
    let res = client.client().get(url).send().await?;
    let status = res.status();

    if status == 200 {
        let info = res.json::<BookInfo>().await?;
        return Ok(info);
    }
    if status == 404 {
        bail!("Book not found (HTTP 404). Please double-check the book ID provided")
    }
    bail!("Got status: {}", status)
}