1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
use crate::errors::*; use crate::autonoscope::{Autonoscope, IntoRule, AutoRule, RulePrecision}; use crate::models::*; use std::convert::TryFrom; #[derive(Debug, PartialEq)] pub struct DomainRule { value: String, fragments: Vec<String>, } impl ToString for DomainRule { fn to_string(&self) -> String { self.value.clone() } } impl TryFrom<&str> for DomainRule { type Error = Error; fn try_from(rule: &str) -> Result<DomainRule> { let mut fragments = rule.split('.') .filter(|x| !x.is_empty()) .map(String::from) .collect::<Vec<_>>(); fragments.reverse(); Ok(DomainRule { value: rule.to_string(), fragments, }) } } impl TryFrom<Autonoscope> for DomainRule { type Error = Error; #[inline] fn try_from(rule: Autonoscope) -> Result<DomainRule> { DomainRule::try_from(rule.value.as_str()) } } impl AutoRule<NewDomain> for DomainRule { #[inline] fn matches(&self, domain: &NewDomain) -> Result<bool> { self.matches(domain.value.as_str()) } } impl AutoRule<NewSubdomain> for DomainRule { #[inline] fn matches(&self, domain: &NewSubdomain) -> Result<bool> { self.matches(domain.value.as_str()) } } impl AutoRule<NewUrl> for DomainRule { #[inline] fn matches(&self, url: &NewUrl) -> Result<bool> { let url = url.value.parse::<url::Url>()?; if let Some(domain) = url.domain() { self.matches(domain) } else { Ok(false) } } } impl AutoRule<str> for DomainRule { fn matches(&self, domain: &str) -> Result<bool> { let frags = domain.split('.') .filter(|x| !x.is_empty()) .collect::<Vec<_>>(); if self.fragments.len() > frags.len() { return Ok(false); } for (rule, domain) in self.fragments.iter().zip(frags.iter().rev()) { if rule != domain { return Ok(false); } } Ok(true) } } impl RulePrecision for DomainRule { #[inline] fn precision(&self) -> usize { self.fragments.len() } } impl IntoRule for DomainRule { fn into_rule(&self) -> (&'static str, String) { ("domain", self.to_string()) } } #[cfg(test)] mod tests { use super::*; use std::convert::TryFrom; #[test] fn test_domain_rule_root() { let rule = DomainRule::try_from(".").unwrap(); assert!(rule.matches("example.com").unwrap()); assert_eq!(rule.precision(), 0); } #[test] fn test_domain_rule_com() { let rule = DomainRule::try_from("com").unwrap(); assert!(rule.matches("example.com").unwrap()); assert_eq!(rule.precision(), 1); } #[test] fn test_domain_rule_equals() { let rule = DomainRule::try_from("example.com").unwrap(); assert!(rule.matches("example.com").unwrap()); assert_eq!(rule.precision(), 2); } #[test] fn test_domain_rule_mismatch() { let rule = DomainRule::try_from("foo.example.com").unwrap(); assert!(!rule.matches("example.com").unwrap()); assert_eq!(rule.precision(), 3); } }