Przeglądaj źródła

Merge commit 'refs/pull/484/head' of github.com:servo/rust-url into 2.0

Simon Sapin 7 lat temu
rodzic
commit
da2b1af4b0
3 zmienionych plików z 121 dodań i 84 usunięć
  1. 1 1
      idna/Cargo.toml
  2. 13 17
      idna/src/lib.rs
  3. 107 66
      idna/src/uts46.rs

+ 1 - 1
idna/Cargo.toml

@@ -1,6 +1,6 @@
 [package]
 name = "idna"
-version = "0.1.5"
+version = "0.1.6"
 authors = ["The rust-url developers"]
 description = "IDNA (Internationalizing Domain Names in Applications) and Punycode."
 repository = "https://github.com/servo/rust-url/"

+ 13 - 17
idna/src/lib.rs

@@ -48,14 +48,12 @@ pub mod uts46;
 ///
 /// This process may fail.
 pub fn domain_to_ascii(domain: &str) -> Result<String, uts46::Errors> {
-    uts46::to_ascii(
-        domain,
-        uts46::Flags {
-            use_std3_ascii_rules: false,
-            transitional_processing: false,
-            verify_dns_length: false,
-        },
-    )
+    let flags = uts46::Flags {
+        use_std3_ascii_rules: false,
+        transitional_processing: false,
+        verify_dns_length: false,
+    };
+    uts46::Config::from(flags).check_hyphens(false).to_ascii(domain)
 }
 
 /// The [domain to Unicode](https://url.spec.whatwg.org/#concept-domain-to-unicode) algorithm.
@@ -67,14 +65,12 @@ pub fn domain_to_ascii(domain: &str) -> Result<String, uts46::Errors> {
 /// This may indicate [syntax violations](https://url.spec.whatwg.org/#syntax-violation)
 /// but always returns a string for the mapped domain.
 pub fn domain_to_unicode(domain: &str) -> (String, Result<(), uts46::Errors>) {
-    uts46::to_unicode(
-        domain,
-        uts46::Flags {
-            use_std3_ascii_rules: false,
+    let flags = uts46::Flags {
+        use_std3_ascii_rules: false,
 
-            // Unused:
-            transitional_processing: false,
-            verify_dns_length: false,
-        },
-    )
+        // Unused:
+        transitional_processing: false,
+        verify_dns_length: false,
+    };
+    uts46::Config::from(flags).check_hyphens(false).to_unicode(domain)
 }

+ 107 - 66
idna/src/uts46.rs

@@ -258,16 +258,16 @@ fn passes_bidi(label: &str, is_bidi_domain: bool) -> bool {
 }
 
 /// http://www.unicode.org/reports/tr46/#Validity_Criteria
-fn validate_full(label: &str, is_bidi_domain: bool, flags: Flags, errors: &mut Vec<Error>) {
+fn validate_full(label: &str, is_bidi_domain: bool, config: Config, errors: &mut Vec<Error>) {
     // V1: Must be in NFC form.
     if label.nfc().ne(label.chars()) {
         errors.push(Error::ValidityCriteria);
     } else {
-        validate(label, is_bidi_domain, flags, errors);
+        validate(label, is_bidi_domain, config, errors);
     }
 }
 
-fn validate(label: &str, is_bidi_domain: bool, flags: Flags, errors: &mut Vec<Error>) {
+fn validate(label: &str, is_bidi_domain: bool, config: Config, errors: &mut Vec<Error>) {
     let first_char = label.chars().next();
     if first_char == None {
         // Empty string, pass
@@ -277,11 +277,9 @@ fn validate(label: &str, is_bidi_domain: bool, flags: Flags, errors: &mut Vec<Er
     // NOTE: Spec says that the label must not contain a HYPHEN-MINUS character in both the
     // third and fourth positions. But nobody follows this criteria. See the spec issue below:
     // https://github.com/whatwg/url/issues/53
-    //
-    // TODO: Add *CheckHyphens* flag.
 
     // V3: neither begin nor end with a U+002D HYPHEN-MINUS
-    else if label.starts_with("-") || label.ends_with("-") {
+    else if config.check_hyphens && (label.starts_with("-") || label.ends_with("-")) {
         errors.push(Error::ValidityCriteria);
     }
     // V4: not contain a U+002E FULL STOP
@@ -295,8 +293,8 @@ fn validate(label: &str, is_bidi_domain: bool, flags: Flags, errors: &mut Vec<Er
     // V6: Check against Mapping Table
     else if label.chars().any(|c| match *find_char(c) {
         Mapping::Valid => false,
-        Mapping::Deviation(_) => flags.transitional_processing,
-        Mapping::DisallowedStd3Valid => flags.use_std3_ascii_rules,
+        Mapping::Deviation(_) => config.flags.transitional_processing,
+        Mapping::DisallowedStd3Valid => config.flags.use_std3_ascii_rules,
         _ => true,
     }) {
         errors.push(Error::ValidityCriteria);
@@ -314,10 +312,10 @@ fn validate(label: &str, is_bidi_domain: bool, flags: Flags, errors: &mut Vec<Er
 }
 
 /// http://www.unicode.org/reports/tr46/#Processing
-fn processing(domain: &str, flags: Flags, errors: &mut Vec<Error>) -> String {
+fn processing(domain: &str, config: Config, errors: &mut Vec<Error>) -> String {
     let mut mapped = String::with_capacity(domain.len());
     for c in domain.chars() {
-        map_char(c, flags, &mut mapped, errors)
+        map_char(c, config.flags, &mut mapped, errors)
     }
     let mut normalized = String::with_capacity(mapped.len());
     normalized.extend(mapped.nfc());
@@ -358,29 +356,117 @@ fn processing(domain: &str, flags: Flags, errors: &mut Vec<Error>) -> String {
         if label.starts_with(PUNYCODE_PREFIX) {
             match punycode::decode_to_string(&label[PUNYCODE_PREFIX.len()..]) {
                 Some(decoded_label) => {
-                    let flags = Flags {
-                        transitional_processing: false,
-                        ..flags
-                    };
-                    validate_full(&decoded_label, is_bidi_domain, flags, errors);
+                    let config = config.transitional_processing(false);
+                    validate_full(&decoded_label, is_bidi_domain, config, errors);
                     validated.push_str(&decoded_label)
                 }
                 None => errors.push(Error::PunycodeError),
             }
         } else {
             // `normalized` is already `NFC` so we can skip that check
-            validate(label, is_bidi_domain, flags, errors);
+            validate(label, is_bidi_domain, config, errors);
             validated.push_str(label)
         }
     }
     validated
 }
 
+#[derive(Clone, Copy)]
+pub struct Config {
+    flags: Flags,
+    check_hyphens: bool,
+}
+
+impl From<Flags> for Config {
+    #[inline]
+    fn from(flags: Flags) -> Self {
+        Self { flags, check_hyphens: true }
+    }
+}
+
+impl Config {
+    #[inline]
+    pub fn use_std3_ascii_rules(mut self, value: bool) -> Self {
+        self.flags.use_std3_ascii_rules = value;
+        self
+    }
+
+    #[inline]
+    pub fn transitional_processing(mut self, value: bool) -> Self {
+        self.flags.transitional_processing = value;
+        self
+    }
+
+    #[inline]
+    pub fn verify_dns_length(mut self, value: bool) -> Self {
+        self.flags.verify_dns_length = value;
+        self
+    }
+
+    #[inline]
+    pub fn check_hyphens(mut self, value: bool) -> Self {
+        self.check_hyphens = value;
+        self
+    }
+
+    /// http://www.unicode.org/reports/tr46/#ToASCII
+    pub fn to_ascii(self, domain: &str) -> Result<String, Errors> {
+        let mut errors = Vec::new();
+        let mut result = String::new();
+        let mut first = true;
+        for label in processing(domain, self, &mut errors).split('.') {
+            if !first {
+                result.push('.');
+            }
+            first = false;
+            if label.is_ascii() {
+                result.push_str(label);
+            } else {
+                match punycode::encode_str(label) {
+                    Some(x) => {
+                        result.push_str(PUNYCODE_PREFIX);
+                        result.push_str(&x);
+                    },
+                    None => errors.push(Error::PunycodeError)
+                }
+            }
+        }
+
+        if self.flags.verify_dns_length {
+            let domain = if result.ends_with(".") { &result[..result.len()-1] } else { &*result };
+            if domain.len() < 1 || domain.split('.').any(|label| label.len() < 1) {
+                errors.push(Error::TooShortForDns)
+            }
+            if domain.len() > 253 || domain.split('.').any(|label| label.len() > 63) {
+                errors.push(Error::TooLongForDns)
+            }
+        }
+        if errors.is_empty() {
+            Ok(result)
+        } else {
+            Err(Errors(errors))
+        }
+    }
+
+    /// http://www.unicode.org/reports/tr46/#ToUnicode
+    pub fn to_unicode(self, domain: &str) -> (String, Result<(), Errors>) {
+        let mut errors = Vec::new();
+        let domain = processing(domain, self, &mut errors);
+        let errors = if errors.is_empty() {
+            Ok(())
+        } else {
+            Err(Errors(errors))
+        };
+        (domain, errors)
+    }
+
+}
+
 #[derive(Copy, Clone)]
 pub struct Flags {
-    pub use_std3_ascii_rules: bool,
-    pub transitional_processing: bool,
-    pub verify_dns_length: bool,
+   pub use_std3_ascii_rules: bool,
+   pub transitional_processing: bool,
+   pub verify_dns_length: bool,
 }
 
 #[derive(PartialEq, Eq, Clone, Copy, Debug)]
@@ -403,45 +489,7 @@ pub struct Errors(Vec<Error>);
 
 /// http://www.unicode.org/reports/tr46/#ToASCII
 pub fn to_ascii(domain: &str, flags: Flags) -> Result<String, Errors> {
-    let mut errors = Vec::new();
-    let mut result = String::new();
-    let mut first = true;
-    for label in processing(domain, flags, &mut errors).split('.') {
-        if !first {
-            result.push('.');
-        }
-        first = false;
-        if label.is_ascii() {
-            result.push_str(label);
-        } else {
-            match punycode::encode_str(label) {
-                Some(x) => {
-                    result.push_str(PUNYCODE_PREFIX);
-                    result.push_str(&x);
-                }
-                None => errors.push(Error::PunycodeError),
-            }
-        }
-    }
-
-    if flags.verify_dns_length {
-        let domain = if result.ends_with(".") {
-            &result[..result.len() - 1]
-        } else {
-            &*result
-        };
-        if domain.len() < 1 || domain.split('.').any(|label| label.len() < 1) {
-            errors.push(Error::TooShortForDns)
-        }
-        if domain.len() > 253 || domain.split('.').any(|label| label.len() > 63) {
-            errors.push(Error::TooLongForDns)
-        }
-    }
-    if errors.is_empty() {
-        Ok(result)
-    } else {
-        Err(Errors(errors))
-    }
+    Config::from(flags).to_ascii(domain)
 }
 
 /// http://www.unicode.org/reports/tr46/#ToUnicode
@@ -449,12 +497,5 @@ pub fn to_ascii(domain: &str, flags: Flags) -> Result<String, Errors> {
 /// Only `use_std3_ascii_rules` is used in `flags`.
 pub fn to_unicode(domain: &str, mut flags: Flags) -> (String, Result<(), Errors>) {
     flags.transitional_processing = false;
-    let mut errors = Vec::new();
-    let domain = processing(domain, flags, &mut errors);
-    let errors = if errors.is_empty() {
-        Ok(())
-    } else {
-        Err(Errors(errors))
-    };
-    (domain, errors)
+    Config::from(flags).to_unicode(domain)
 }