extern crate reqwest; #[macro_use] extern crate lazy_static; use reqwest::header::{HeaderMap, ACCEPT}; use reqwest::StatusCode; use std::collections::HashMap; use std::error::Error; use std::sync::{Arc, RwLock}; #[derive(Clone, Debug, PartialEq)] pub struct LinkResult { pub code: Option, /// Whether the HTTP request didn't make it to getting a HTTP code pub error: Option, } impl LinkResult { pub fn is_valid(&self) -> bool { if self.error.is_some() { return false; } if let Some(c) = self.code { return c.is_success(); } true } pub fn message(&self) -> String { if let Some(ref e) = self.error { return e.clone(); } if let Some(c) = self.code { return format!("{}", c); } "Unknown error".to_string() } } lazy_static! { // Keep history of link checks so a rebuild doesn't have to check again static ref LINKS: Arc>> = Arc::new(RwLock::new(HashMap::new())); } pub fn check_url(url: &str) -> LinkResult { { let guard = LINKS.read().unwrap(); if let Some(res) = guard.get(url) { return res.clone(); } } let mut headers = HeaderMap::new(); headers.insert(ACCEPT, "text/html".parse().unwrap()); headers.append(ACCEPT, "*/*".parse().unwrap()); let client = reqwest::Client::new(); // Need to actually do the link checking let res = match client.get(url).headers(headers).send() { Ok(response) => LinkResult { code: Some(response.status()), error: None }, Err(e) => LinkResult { code: None, error: Some(e.description().to_string()) }, }; LINKS.write().unwrap().insert(url.to_string(), res.clone()); res } #[cfg(test)] mod tests { use super::{check_url, LINKS}; #[test] fn can_validate_ok_links() { let url = "https://google.com"; let res = check_url(url); assert!(res.is_valid()); assert!(LINKS.read().unwrap().get(url).is_some()); let res = check_url(url); assert!(res.is_valid()); } #[test] fn can_fail_404_links() { let res = check_url("https://google.comys"); assert_eq!(res.is_valid(), false); assert!(res.code.is_none()); assert!(res.error.is_some()); } }