Add xkcd retrievel functions

Signed-off-by: trivernis <trivernis@protonmail.com>
main
trivernis 4 years ago
commit 5683137f7d
Signed by: Trivernis
GPG Key ID: DFFFCC2C7A02DB45

3
.gitignore vendored

@ -0,0 +1,3 @@
/target
Cargo.lock
.idea

@ -0,0 +1,17 @@
[package]
name = "xkcd-search"
version = "0.1.0"
authors = ["trivernis <trivernis@protonmail.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
reqwest = {version="0.11.3", features = ["json", "serde_json"]}
scraper = "0.12.0"
thiserror = "1.0.24"
lazy_static = "1.4.0"
serde = {version = "1.0.125", features = ["serde_derive"]}
[dev-dependencies]
tokio = {version = "1.5.0", features = ["macros", "rt-multi-thread"]}

@ -0,0 +1,32 @@
use crate::error::XKCDResult;
use scraper::{Html, Selector};
use std::collections::HashMap;
use std::iter::FromIterator;
static ARCHIVE_URL: &str = "https://xkcd.com/archive/";
/// Returns the xkcd archive list
pub async fn get_archive() -> XKCDResult<HashMap<String, u32>> {
let response = reqwest::get(ARCHIVE_URL).await?;
let html = response.text().await?;
parse_archive_list(html)
}
fn parse_archive_list(html: String) -> XKCDResult<HashMap<String, u32>> {
let document = Html::parse_document(&html);
let archive_selector = Selector::parse(r#"#middleContainer > a"#).unwrap();
let archive = HashMap::from_iter(document.select(&archive_selector).filter_map(|element| {
Some((
element.inner_html(),
element
.value()
.attr("href")?
.replace("/", "")
.parse::<u32>()
.ok()?,
))
}));
Ok(archive)
}

@ -0,0 +1,36 @@
use crate::error::XKCDResult;
use serde::Deserialize;
static INFO_JSON: &str = "info.0.json";
static BASE_URL: &str = "https://xkcd.com";
static LATEST_URL: &str = "https://xkcd.com/info.0.json";
/// Returns the latest comic
pub async fn get_latest_comic() -> XKCDResult<Comic> {
retrieve_comic(LATEST_URL.to_string()).await
}
/// Returns a comic for an ID
pub async fn get_comic(id: u32) -> XKCDResult<Comic> {
retrieve_comic(format!("{}/{}/{}", BASE_URL, id, INFO_JSON)).await
}
async fn retrieve_comic(url: String) -> XKCDResult<Comic> {
let response = reqwest::get(url).await?;
let response = response.json::<Comic>().await?;
Ok(response)
}
#[derive(Deserialize)]
pub struct Comic {
pub day: String,
pub month: String,
pub year: String,
pub num: u32,
pub safe_title: String,
pub transcript: String,
pub alt: String,
pub img: String,
pub title: String,
}

@ -0,0 +1,18 @@
use thiserror::Error;
pub type XKCDResult<T> = Result<T, XKCDError>;
#[derive(Debug, Error)]
pub enum XKCDError {
#[error(transparent)]
Reqwest(#[from] reqwest::Error),
#[error("Parse Error: {0}")]
ParseError(String),
}
impl From<&str> for XKCDError {
fn from(s: &str) -> Self {
Self::ParseError(s.to_string())
}
}

@ -0,0 +1,9 @@
mod archive;
mod comic;
mod error;
#[cfg(test)]
mod tests;
pub use archive::*;
pub use comic::*;

@ -0,0 +1,17 @@
use crate::{get_archive, get_comic, get_latest_comic};
#[tokio::test]
async fn it_retrieves_the_archive() {
let archive = get_archive().await.unwrap();
assert!(archive.get("Password Strength").is_some());
}
#[tokio::test]
async fn it_retrieves_a_comic() {
assert!(get_comic(1000).await.is_ok())
}
#[tokio::test]
async fn it_retrieves_the_latest_comic() {
assert!(get_latest_comic().await.is_ok())
}
Loading…
Cancel
Save