|
@@ -82,38 +82,65 @@ fn find_char(codepoint: char) -> &'static Mapping {
|
|
|
.unwrap()
|
|
.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 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 mut validated = String::new();
|
|
|
let non_transitional = config.transitional_processing(false);
|
|
let non_transitional = config.transitional_processing(false);
|