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.

100 lines
2.4KB

  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. let client = reqwest::Client::new();
  49. // Need to actually do the link checking
  50. let res = match client.get(url).headers(headers).send() {
  51. Ok(response) => LinkResult {
  52. code: Some(response.status()),
  53. error: None,
  54. },
  55. Err(e) => LinkResult {
  56. code: None,
  57. error: Some(e.description().to_string()),
  58. },
  59. };
  60. LINKS.write().unwrap().insert(url.to_string(), res.clone());
  61. res
  62. }
  63. #[cfg(test)]
  64. mod tests {
  65. use super::{check_url, LINKS};
  66. #[test]
  67. fn can_validate_ok_links() {
  68. let url = "https://google.com";
  69. let res = check_url(url);
  70. assert!(res.is_valid());
  71. assert!(LINKS.read().unwrap().get(url).is_some());
  72. let res = check_url(url);
  73. assert!(res.is_valid());
  74. }
  75. #[test]
  76. fn can_fail_404_links() {
  77. let res = check_url("https://google.comys");
  78. assert_eq!(res.is_valid(), false);
  79. assert!(res.code.is_none());
  80. assert!(res.error.is_some());
  81. }
  82. }