You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

95 lines
2.3KB

  1. extern crate reqwest;
  2. #[macro_use]
  3. extern crate lazy_static;
  4. use reqwest::header::{HeaderMap, ACCEPT};
  5. use reqwest::StatusCode;
  6. use std::collections::HashMap;
  7. use std::error::Error;
  8. use std::sync::{Arc, RwLock};
  9. #[derive(Clone, Debug, PartialEq)]
  10. pub struct LinkResult {
  11. pub code: Option<StatusCode>,
  12. /// Whether the HTTP request didn't make it to getting a HTTP code
  13. pub error: Option<String>,
  14. }
  15. impl LinkResult {
  16. pub fn is_valid(&self) -> bool {
  17. if self.error.is_some() {
  18. return false;
  19. }
  20. if let Some(c) = self.code {
  21. return c.is_success();
  22. }
  23. true
  24. }
  25. pub fn message(&self) -> String {
  26. if let Some(ref e) = self.error {
  27. return e.clone();
  28. }
  29. if let Some(c) = self.code {
  30. return format!("{}", c);
  31. }
  32. "Unknown error".to_string()
  33. }
  34. }
  35. lazy_static! {
  36. // Keep history of link checks so a rebuild doesn't have to check again
  37. static ref LINKS: Arc<RwLock<HashMap<String, LinkResult>>> = Arc::new(RwLock::new(HashMap::new()));
  38. }
  39. pub fn check_url(url: &str) -> LinkResult {
  40. {
  41. let guard = LINKS.read().unwrap();
  42. if let Some(res) = guard.get(url) {
  43. return res.clone();
  44. }
  45. }
  46. let mut headers = HeaderMap::new();
  47. headers.insert(ACCEPT, "text/html".parse().unwrap());
  48. headers.append(ACCEPT, "*/*".parse().unwrap());
  49. let client = reqwest::Client::new();
  50. // Need to actually do the link checking
  51. let res = match client.get(url).headers(headers).send() {
  52. Ok(response) => LinkResult { code: Some(response.status()), error: None },
  53. Err(e) => LinkResult { code: None, error: Some(e.description().to_string()) },
  54. };
  55. LINKS.write().unwrap().insert(url.to_string(), res.clone());
  56. res
  57. }
  58. #[cfg(test)]
  59. mod tests {
  60. use super::{check_url, LINKS};
  61. #[test]
  62. fn can_validate_ok_links() {
  63. let url = "https://google.com";
  64. let res = check_url(url);
  65. assert!(res.is_valid());
  66. assert!(LINKS.read().unwrap().get(url).is_some());
  67. let res = check_url(url);
  68. assert!(res.is_valid());
  69. }
  70. #[test]
  71. fn can_fail_404_links() {
  72. let res = check_url("https://google.comys");
  73. assert_eq!(res.is_valid(), false);
  74. assert!(res.code.is_none());
  75. assert!(res.error.is_some());
  76. }
  77. }