Skip to content
Snippets Groups Projects
request.rs 1.95 KiB
Newer Older
insert's avatar
insert committed
use std::collections::HashMap;

use mime::Mime;
insert's avatar
insert committed
use reqwest::{header::CONTENT_TYPE, Client, ClientBuilder, Response};
insert's avatar
insert committed
use scraper::{Html, Selector};

use super::result::Error;

insert's avatar
insert committed
lazy_static! {
    static ref CLIENT: Client = reqwest::Client::builder()
        .user_agent(
            "Mozilla/5.0 (compatible; January/1.0; +https://gitlab.insrt.uk/revolt/january)"
        )
        .build()
        .unwrap();
}

insert's avatar
insert committed
pub async fn fetch(url: &str) -> Result<(Response, Mime), Error> {
insert's avatar
insert committed
    let resp = CLIENT
        .get(url)
        .send()
        .await
        .map_err(|_| Error::ReqwestFailed)?;
    
insert's avatar
insert committed
    if !resp.status().is_success() {
insert's avatar
insert committed
        return Err(Error::RequestFailed);
insert's avatar
insert committed
    }

    let content_type = resp
        .headers()
        .get(CONTENT_TYPE)
insert's avatar
insert committed
        .ok_or_else(|| Error::MissingContentType)?
insert's avatar
insert committed
        .to_str()
insert's avatar
insert committed
        .map_err(|_| Error::ConversionFailed)?;
insert's avatar
insert committed

    let mime: mime::Mime = content_type
        .parse()
        .map_err(|_| Error::FailedToParseContentType)?;
insert's avatar
insert committed
    Ok((resp, mime))
}

pub async fn consume_metatags(resp: Response) -> Result<HashMap<String, String>, Error> {
insert's avatar
insert committed
    let body = resp.text().await.map_err(|_| Error::FailedToConsumeText)?;
insert's avatar
insert committed
    let fragment = Html::parse_document(&body);
insert's avatar
insert committed
    let selector = Selector::parse("meta").map_err(|_| Error::MetaSelectionFailed)?;
insert's avatar
insert committed

    let mut properties = HashMap::new();
    for el in fragment.select(&selector) {
        let node = el.value();

        if let (Some(property), Some(content)) = (node.attr("property"), node.attr("content")) {
            properties.insert(property.to_string(), content.to_string());
        }
    }

    Ok(properties)
}

pub async fn consume_size(resp: Response) -> Result<(isize, isize), Error> {
insert's avatar
insert committed
    let bytes = resp
        .bytes()
        .await
        .map_err(|_| Error::FailedToConsumeBytes)?;
insert's avatar
insert committed
    if let Ok(size) = imagesize::blob_size(&bytes) {
        Ok((size.width as isize, size.height as isize))
    } else {
insert's avatar
insert committed
        Err(Error::CouldNotDetermineImageSize)
insert's avatar
insert committed
    }
}