Bläddra i källkod

idna: refactor mapping as an iterator API

Dirkjan Ochtman 6 år sedan
förälder
incheckning
75fe25cee5
1 ändrade filer med 65 tillägg och 35 borttagningar
  1. 65 35
      idna/src/uts46.rs

+ 65 - 35
idna/src/uts46.rs

@@ -82,38 +82,65 @@ fn find_char(codepoint: char) -> &'static Mapping {
         .unwrap()
 }
 
-fn map_char(codepoint: char, config: Config, output: &mut String, errors: &mut Errors) {
-    if let '.' | '-' | 'a'..='z' | '0'..='9' = codepoint {
-        output.push(codepoint);
-        return;
-    }
+struct Mapper<'a> {
+    chars: std::str::Chars<'a>,
+    config: Config,
+    errors: &'a mut Errors,
+    slice: Option<std::str::Chars<'static>>,
+}
 
-    match *find_char(codepoint) {
-        Mapping::Valid => output.push(codepoint),
-        Mapping::Ignored => {}
-        Mapping::Mapped(ref slice) => output.push_str(decode_slice(slice)),
-        Mapping::Deviation(ref slice) => {
-            if config.transitional_processing {
-                output.push_str(decode_slice(slice))
-            } else {
-                output.push(codepoint)
-            }
-        }
-        Mapping::Disallowed => {
-            errors.disallowed_character = true;
-            output.push(codepoint);
-        }
-        Mapping::DisallowedStd3Valid => {
-            if config.use_std3_ascii_rules {
-                errors.disallowed_by_std3_ascii_rules = true;
+impl<'a> Iterator for Mapper<'a> {
+    type Item = char;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        loop {
+            if let Some(s) = &mut self.slice {
+                match s.next() {
+                    Some(c) => return Some(c),
+                    None => {
+                        self.slice = None;
+                    }
+                }
             }
-            output.push(codepoint)
-        }
-        Mapping::DisallowedStd3Mapped(ref slice) => {
-            if config.use_std3_ascii_rules {
-                errors.disallowed_mapped_in_std3 = true;
+
+            let codepoint = self.chars.next()?;
+            if let '.' | '-' | 'a'..='z' | '0'..='9' = codepoint {
+                return Some(codepoint);
             }
-            output.push_str(decode_slice(slice))
+
+            return Some(match *find_char(codepoint) {
+                Mapping::Valid => codepoint,
+                Mapping::Ignored => continue,
+                Mapping::Mapped(ref slice) => {
+                    self.slice = Some(decode_slice(slice).chars());
+                    continue;
+                }
+                Mapping::Deviation(ref slice) => {
+                    if self.config.transitional_processing {
+                        self.slice = Some(decode_slice(slice).chars());
+                        continue;
+                    } else {
+                        codepoint
+                    }
+                }
+                Mapping::Disallowed => {
+                    self.errors.disallowed_character = true;
+                    codepoint
+                }
+                Mapping::DisallowedStd3Valid => {
+                    if self.config.use_std3_ascii_rules {
+                        self.errors.disallowed_by_std3_ascii_rules = true;
+                    };
+                    codepoint
+                }
+                Mapping::DisallowedStd3Mapped(ref slice) => {
+                    if self.config.use_std3_ascii_rules {
+                        self.errors.disallowed_mapped_in_std3 = true;
+                    };
+                    self.slice = Some(decode_slice(slice).chars());
+                    continue;
+                }
+            });
         }
     }
 }
@@ -336,12 +363,15 @@ fn processing(domain: &str, config: Config) -> (String, Errors) {
     }
 
     let mut errors = Errors::default();
-    let mut mapped = String::with_capacity(domain.len());
-    for c in domain.chars() {
-        map_char(c, config, &mut mapped, &mut errors)
-    }
-    let mut normalized = String::with_capacity(mapped.len());
-    normalized.extend(mapped.nfc());
+    let iter = Mapper {
+        chars: domain.chars(),
+        config,
+        errors: &mut errors,
+        slice: None,
+    };
+
+    let mut normalized = String::with_capacity(domain.len());
+    normalized.extend(iter.nfc());
 
     let mut validated = String::new();
     let non_transitional = config.transitional_processing(false);