|
2 | 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this |
3 | 3 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. |
4 | 4 |
|
5 | | -use std::{cmp::max, collections::VecDeque, iter::repeat}; |
| 5 | +use std::{cmp::max, collections::VecDeque, iter::repeat, str::FromStr}; |
6 | 6 |
|
7 | 7 | use small_string::SmallString; |
| 8 | +use unicode_normalization::{ |
| 9 | + is_nfc_quick, is_nfd_quick, is_nfkc_quick, is_nfkd_quick, IsNormalized, UnicodeNormalization, |
| 10 | +}; |
8 | 11 |
|
9 | 12 | use crate::{ |
10 | 13 | ecmascript::{ |
@@ -567,8 +570,44 @@ impl StringPrototype { |
567 | 570 | todo!() |
568 | 571 | } |
569 | 572 |
|
570 | | - fn normalize(_agent: &mut Agent, _this_value: Value, _: ArgumentsList) -> JsResult<Value> { |
571 | | - todo!() |
| 573 | + /// ### [22.1.3.15 String.prototype.normalize ( \[ form \] )](https://tc39.es/ecma262/#sec-string.prototype.normalize) |
| 574 | + fn normalize( |
| 575 | + agent: &mut Agent, |
| 576 | + this_value: Value, |
| 577 | + arguments: ArgumentsList, |
| 578 | + ) -> JsResult<Value> { |
| 579 | + // 1. Let O be ? RequireObjectCoercible(this value). |
| 580 | + let o = require_object_coercible(agent, this_value)?; |
| 581 | + |
| 582 | + // 2. Let S be ? ToString(O). |
| 583 | + let s = to_string(agent, o)?; |
| 584 | + |
| 585 | + // 3. If form is undefined, let f be "NFC". |
| 586 | + let form = arguments.get(0); |
| 587 | + let f = if form.is_undefined() { |
| 588 | + NormalizeForm::Nfc |
| 589 | + } else { |
| 590 | + // 4. Else, let f be ? ToString(form). |
| 591 | + let f = to_string(agent, form)?; |
| 592 | + let form_result = NormalizeForm::from_str(f.as_str(agent)); |
| 593 | + match form_result { |
| 594 | + Ok(form) => form, |
| 595 | + // 5. If f is not one of "NFC", "NFD", "NFKC", or "NFKD", throw a RangeError exception. |
| 596 | + Err(()) => { |
| 597 | + return Err(agent.throw_exception_with_static_message( |
| 598 | + ExceptionType::RangeError, |
| 599 | + "The normalization form should be one of NFC, NFD, NFKC, NFKD.", |
| 600 | + )) |
| 601 | + } |
| 602 | + } |
| 603 | + }; |
| 604 | + |
| 605 | + // 6. Let ns be the String value that is the result of normalizing S into the normalization form named by f as specified in the latest Unicode Standard, Normalization Forms. |
| 606 | + match unicode_normalize(s.as_str(agent), f) { |
| 607 | + // 7. Return ns. |
| 608 | + None => Ok(s.into_value()), |
| 609 | + Some(ns) => Ok(Value::from_string(agent, ns).into_value()), |
| 610 | + } |
572 | 611 | } |
573 | 612 |
|
574 | 613 | /// ### [22.1.3.16 String.prototype.padEnd ( maxLength \[ , fillString \] )](https://tc39.es/ecma262/#sec-string.prototype.padend) |
@@ -1478,3 +1517,45 @@ enum TrimWhere { |
1478 | 1517 | End, |
1479 | 1518 | StartAndEnd, |
1480 | 1519 | } |
| 1520 | + |
| 1521 | +enum NormalizeForm { |
| 1522 | + Nfc, |
| 1523 | + Nfd, |
| 1524 | + Nfkc, |
| 1525 | + Nfkd, |
| 1526 | +} |
| 1527 | + |
| 1528 | +impl FromStr for NormalizeForm { |
| 1529 | + type Err = (); |
| 1530 | + |
| 1531 | + fn from_str(input: &str) -> Result<NormalizeForm, Self::Err> { |
| 1532 | + match input { |
| 1533 | + "NFC" => Ok(NormalizeForm::Nfc), |
| 1534 | + "NFD" => Ok(NormalizeForm::Nfd), |
| 1535 | + "NFKC" => Ok(NormalizeForm::Nfkc), |
| 1536 | + "NFKD" => Ok(NormalizeForm::Nfkd), |
| 1537 | + _ => Err(()), |
| 1538 | + } |
| 1539 | + } |
| 1540 | +} |
| 1541 | + |
| 1542 | +fn unicode_normalize(s: &str, f: NormalizeForm) -> Option<std::string::String> { |
| 1543 | + match f { |
| 1544 | + NormalizeForm::Nfc => match is_nfc_quick(s.chars()) { |
| 1545 | + IsNormalized::Yes => None, |
| 1546 | + _ => Some(s.nfc().collect::<std::string::String>()), |
| 1547 | + }, |
| 1548 | + NormalizeForm::Nfd => match is_nfd_quick(s.chars()) { |
| 1549 | + IsNormalized::Yes => None, |
| 1550 | + _ => Some(s.nfd().collect::<std::string::String>()), |
| 1551 | + }, |
| 1552 | + NormalizeForm::Nfkc => match is_nfkc_quick(s.chars()) { |
| 1553 | + IsNormalized::Yes => None, |
| 1554 | + _ => Some(s.nfkc().collect::<std::string::String>()), |
| 1555 | + }, |
| 1556 | + NormalizeForm::Nfkd => match is_nfkd_quick(s.chars()) { |
| 1557 | + IsNormalized::Yes => None, |
| 1558 | + _ => Some(s.nfkd().collect::<std::string::String>()), |
| 1559 | + }, |
| 1560 | + } |
| 1561 | +} |
0 commit comments