summaryrefslogtreecommitdiffstats
path: root/vendor/icu_locid/src
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/icu_locid/src')
-rw-r--r--vendor/icu_locid/src/extensions/mod.rs29
-rw-r--r--vendor/icu_locid/src/extensions/other/mod.rs61
-rw-r--r--vendor/icu_locid/src/extensions/other/subtag.rs6
-rw-r--r--vendor/icu_locid/src/extensions/private/mod.rs26
-rw-r--r--vendor/icu_locid/src/extensions/transform/fields.rs30
-rw-r--r--vendor/icu_locid/src/extensions/transform/mod.rs6
-rw-r--r--vendor/icu_locid/src/extensions/transform/value.rs35
-rw-r--r--vendor/icu_locid/src/extensions/unicode/attributes.rs15
-rw-r--r--vendor/icu_locid/src/extensions/unicode/keywords.rs65
-rw-r--r--vendor/icu_locid/src/extensions/unicode/mod.rs46
-rw-r--r--vendor/icu_locid/src/extensions/unicode/value.rs33
-rw-r--r--vendor/icu_locid/src/helpers.rs46
-rw-r--r--vendor/icu_locid/src/langid.rs109
-rw-r--r--vendor/icu_locid/src/lib.rs32
-rw-r--r--vendor/icu_locid/src/locale.rs106
-rw-r--r--vendor/icu_locid/src/parser/errors.rs16
-rw-r--r--vendor/icu_locid/src/parser/langid.rs47
-rw-r--r--vendor/icu_locid/src/parser/locale.rs6
-rw-r--r--vendor/icu_locid/src/parser/mod.rs231
-rw-r--r--vendor/icu_locid/src/subtags/language.rs11
-rw-r--r--vendor/icu_locid/src/subtags/variants.rs28
21 files changed, 569 insertions, 415 deletions
diff --git a/vendor/icu_locid/src/extensions/mod.rs b/vendor/icu_locid/src/extensions/mod.rs
index 42bfcd3c9..a6a189b11 100644
--- a/vendor/icu_locid/src/extensions/mod.rs
+++ b/vendor/icu_locid/src/extensions/mod.rs
@@ -102,11 +102,11 @@ impl ExtensionType {
#[derive(Debug, Default, PartialEq, Eq, Clone, Hash)]
#[non_exhaustive]
pub struct Extensions {
- /// A representation of the data for a Unicode extension, when present in the locale identifer.
+ /// A representation of the data for a Unicode extension, when present in the locale identifier.
pub unicode: Unicode,
- /// A representation of the data for a transform extension, when present in the locale identifer.
+ /// A representation of the data for a transform extension, when present in the locale identifier.
pub transform: Transform,
- /// A representation of the data for a private-use extension, when present in the locale identifer.
+ /// A representation of the data for a private-use extension, when present in the locale identifier.
pub private: Private,
/// A sequence of any other extensions that are present in the locale identifier but are not formally
/// [defined](https://unicode.org/reports/tr35/) and represented explicitly as [`Unicode`], [`Transform`],
@@ -210,19 +210,33 @@ impl Extensions {
let mut private = None;
let mut other = Vec::new();
- let mut st = iter.next();
- while let Some(subtag) = st {
+ while let Some(subtag) = iter.next() {
+ if subtag.is_empty() {
+ return Err(ParserError::InvalidExtension);
+ }
match subtag.get(0).map(|b| ExtensionType::try_from_byte(*b)) {
Some(Ok(ExtensionType::Unicode)) => {
+ if unicode.is_some() {
+ return Err(ParserError::DuplicatedExtension);
+ }
unicode = Some(Unicode::try_from_iter(iter)?);
}
Some(Ok(ExtensionType::Transform)) => {
+ if transform.is_some() {
+ return Err(ParserError::DuplicatedExtension);
+ }
transform = Some(Transform::try_from_iter(iter)?);
}
Some(Ok(ExtensionType::Private)) => {
+ if private.is_some() {
+ return Err(ParserError::DuplicatedExtension);
+ }
private = Some(Private::try_from_iter(iter)?);
}
Some(Ok(ExtensionType::Other(ext))) => {
+ if other.iter().any(|o: &Other| o.get_ext_byte() == ext) {
+ return Err(ParserError::DuplicatedExtension);
+ }
let parsed = Other::try_from_iter(ext, iter)?;
if let Err(idx) = other.binary_search(&parsed) {
other.insert(idx, parsed);
@@ -230,11 +244,8 @@ impl Extensions {
return Err(ParserError::InvalidExtension);
}
}
- None => {}
_ => return Err(ParserError::InvalidExtension),
}
-
- st = iter.next();
}
Ok(Self {
@@ -283,7 +294,7 @@ impl_writeable_for_each_subtag_str_no_test!(Extensions);
fn test_writeable() {
use crate::Locale;
use writeable::assert_writeable_eq;
- assert_writeable_eq!(Extensions::new(), "",);
+ assert_writeable_eq!(Extensions::new(), "");
assert_writeable_eq!(
"my-t-my-d0-zawgyi".parse::<Locale>().unwrap().extensions,
"t-my-d0-zawgyi",
diff --git a/vendor/icu_locid/src/extensions/other/mod.rs b/vendor/icu_locid/src/extensions/other/mod.rs
index 36dbc49b6..44d5c9cf8 100644
--- a/vendor/icu_locid/src/extensions/other/mod.rs
+++ b/vendor/icu_locid/src/extensions/other/mod.rs
@@ -41,13 +41,16 @@ pub use subtag::Subtag;
/// let subtag2: Subtag = "bar".parse().expect("Failed to parse a Subtag.");
///
/// let other = Other::from_vec_unchecked(b'a', vec![subtag1, subtag2]);
-/// assert_eq!(&other.to_string(), "-a-foo-bar");
+/// assert_eq!(&other.to_string(), "a-foo-bar");
/// ```
///
/// [`Other Use Extensions`]: https://unicode.org/reports/tr35/#other_extensions
/// [`Unicode Locale Identifier`]: https://unicode.org/reports/tr35/#Unicode_locale_identifier
#[derive(Clone, PartialEq, Eq, Debug, Default, Hash, PartialOrd, Ord)]
-pub struct Other((u8, Vec<Subtag>));
+pub struct Other {
+ ext: u8,
+ keys: Vec<Subtag>,
+}
impl Other {
/// A constructor which takes a pre-sorted list of [`Subtag`].
@@ -65,11 +68,11 @@ impl Other {
/// let subtag2: Subtag = "bar".parse().expect("Failed to parse a Subtag.");
///
/// let other = Other::from_vec_unchecked(b'a', vec![subtag1, subtag2]);
- /// assert_eq!(&other.to_string(), "-a-foo-bar");
+ /// assert_eq!(&other.to_string(), "a-foo-bar");
/// ```
- pub fn from_vec_unchecked(ext: u8, input: Vec<Subtag>) -> Self {
+ pub fn from_vec_unchecked(ext: u8, keys: Vec<Subtag>) -> Self {
assert!(ext.is_ascii_alphabetic());
- Self((ext, input))
+ Self { ext, keys }
}
pub(crate) fn try_from_iter(ext: u8, iter: &mut SubtagIterator) -> Result<Self, ParserError> {
@@ -89,6 +92,22 @@ impl Other {
Ok(Self::from_vec_unchecked(ext, keys))
}
+ /// Gets the tag character for this extension as a &str.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use icu::locid::Locale;
+ ///
+ /// let loc: Locale = "und-a-hello-world".parse().unwrap();
+ /// let other_ext = &loc.extensions.other[0];
+ /// assert_eq!(other_ext.get_ext_str(), "a");
+ /// ```
+ pub fn get_ext_str(&self) -> &str {
+ debug_assert!(self.ext.is_ascii_alphabetic());
+ unsafe { core::str::from_utf8_unchecked(core::slice::from_ref(&self.ext)) }
+ }
+
/// Gets the tag character for this extension as a char.
///
/// # Examples
@@ -101,7 +120,7 @@ impl Other {
/// assert_eq!(other_ext.get_ext(), 'a');
/// ```
pub fn get_ext(&self) -> char {
- self.get_ext_byte() as char
+ self.ext as char
}
/// Gets the tag character for this extension as a byte.
@@ -116,19 +135,15 @@ impl Other {
/// assert_eq!(other_ext.get_ext_byte(), b'a');
/// ```
pub fn get_ext_byte(&self) -> u8 {
- self.0 .0
+ self.ext
}
pub(crate) fn for_each_subtag_str<E, F>(&self, f: &mut F) -> Result<(), E>
where
F: FnMut(&str) -> Result<(), E>,
{
- let (ext, keys) = &self.0;
- debug_assert!(ext.is_ascii_alphabetic());
- // Safety: ext is ascii_alphabetic, so it is valid UTF-8
- let ext_str = unsafe { core::str::from_utf8_unchecked(core::slice::from_ref(ext)) };
- f(ext_str)?;
- keys.iter().map(|t| t.as_str()).try_for_each(f)
+ f(self.get_ext_str())?;
+ self.keys.iter().map(|t| t.as_str()).try_for_each(f)
}
}
@@ -136,10 +151,8 @@ writeable::impl_display_with_writeable!(Other);
impl writeable::Writeable for Other {
fn write_to<W: core::fmt::Write + ?Sized>(&self, sink: &mut W) -> core::fmt::Result {
- let (ext, keys) = &self.0;
- sink.write_char('-')?;
- sink.write_char(*ext as char)?;
- for key in keys.iter() {
+ sink.write_str(self.get_ext_str())?;
+ for key in self.keys.iter() {
sink.write_char('-')?;
writeable::Writeable::write_to(key, sink)?;
}
@@ -148,10 +161,20 @@ impl writeable::Writeable for Other {
}
fn writeable_length_hint(&self) -> writeable::LengthHint {
- let mut result = writeable::LengthHint::exact(2);
- for key in self.0 .1.iter() {
+ let mut result = writeable::LengthHint::exact(1);
+ for key in self.keys.iter() {
result += writeable::Writeable::writeable_length_hint(key) + 1;
}
result
}
+
+ fn write_to_string(&self) -> alloc::borrow::Cow<str> {
+ if self.keys.is_empty() {
+ return alloc::borrow::Cow::Borrowed(self.get_ext_str());
+ }
+ let mut string =
+ alloc::string::String::with_capacity(self.writeable_length_hint().capacity());
+ let _ = self.write_to(&mut string);
+ alloc::borrow::Cow::Owned(string)
+ }
}
diff --git a/vendor/icu_locid/src/extensions/other/subtag.rs b/vendor/icu_locid/src/extensions/other/subtag.rs
index 60995c395..ad4d6a0f2 100644
--- a/vendor/icu_locid/src/extensions/other/subtag.rs
+++ b/vendor/icu_locid/src/extensions/other/subtag.rs
@@ -11,11 +11,9 @@ impl_tinystr_subtag!(
/// # Examples
///
/// ```
- /// use icu::locid::extensions::other::Subtag;
+ /// use icu::locid::extensions_other_subtag as subtag;
///
- /// let subtag: Subtag = "Foo".parse().expect("Failed to parse a Subtag.");
- ///
- /// assert_eq!(subtag.as_str(), "foo");
+ /// assert_eq!(subtag!("Foo").as_str(), "foo");
/// ```
Subtag,
extensions::other::Subtag,
diff --git a/vendor/icu_locid/src/extensions/private/mod.rs b/vendor/icu_locid/src/extensions/private/mod.rs
index 13090c94a..8382d166f 100644
--- a/vendor/icu_locid/src/extensions/private/mod.rs
+++ b/vendor/icu_locid/src/extensions/private/mod.rs
@@ -13,16 +13,18 @@
//! # Examples
//!
//! ```
-//! use icu::locid::extensions::private::{Private, Subtag};
-//! use icu::locid::Locale;
+//! use icu::locid::extensions_private_subtag as subtag;
+//! use icu::locid::{locale, Locale};
//!
//! let mut loc: Locale = "en-US-x-foo-faa".parse().expect("Parsing failed.");
//!
-//! let subtag: Subtag = "foo".parse().expect("Parsing subtag failed.");
-//! assert!(loc.extensions.private.contains(&subtag));
-//! assert_eq!(loc.extensions.private.iter().next(), Some(&subtag));
+//! assert!(loc.extensions.private.contains(&subtag!("foo")));
+//! assert_eq!(loc.extensions.private.iter().next(), Some(&subtag!("foo")));
+//!
//! loc.extensions.private.clear();
-//! assert_eq!(loc.to_string(), "en-US");
+//!
+//! assert!(loc.extensions.private.is_empty());
+//! assert_eq!(loc, locale!("en-US"));
//! ```
mod other;
@@ -50,7 +52,7 @@ use crate::parser::SubtagIterator;
/// let subtag2: Subtag = "bar".parse().expect("Failed to parse a Subtag.");
///
/// let private = Private::from_vec_unchecked(vec![subtag1, subtag2]);
-/// assert_eq!(&private.to_string(), "-x-foo-bar");
+/// assert_eq!(&private.to_string(), "x-foo-bar");
/// ```
///
/// [`Private Use Extensions`]: https://unicode.org/reports/tr35/#pu_extensions
@@ -84,7 +86,7 @@ impl Private {
/// let subtag2: Subtag = "bar".parse().expect("Failed to parse a Subtag.");
///
/// let private = Private::from_vec_unchecked(vec![subtag1, subtag2]);
- /// assert_eq!(&private.to_string(), "-x-foo-bar");
+ /// assert_eq!(&private.to_string(), "x-foo-bar");
/// ```
pub fn from_vec_unchecked(input: Vec<Subtag>) -> Self {
Self(input)
@@ -101,11 +103,11 @@ impl Private {
/// let subtag2: Subtag = "bar".parse().expect("Failed to parse a Subtag.");
/// let mut private = Private::from_vec_unchecked(vec![subtag1, subtag2]);
///
- /// assert_eq!(&private.to_string(), "-x-foo-bar");
+ /// assert_eq!(&private.to_string(), "x-foo-bar");
///
/// private.clear();
///
- /// assert_eq!(&private.to_string(), "");
+ /// assert_eq!(private, Private::new());
/// ```
pub fn clear(&mut self) {
self.0.clear();
@@ -138,7 +140,7 @@ impl writeable::Writeable for Private {
if self.is_empty() {
return Ok(());
}
- sink.write_str("-x")?;
+ sink.write_str("x")?;
for key in self.iter() {
sink.write_char('-')?;
writeable::Writeable::write_to(key, sink)?;
@@ -150,7 +152,7 @@ impl writeable::Writeable for Private {
if self.is_empty() {
return writeable::LengthHint::exact(0);
}
- let mut result = writeable::LengthHint::exact(2);
+ let mut result = writeable::LengthHint::exact(1);
for key in self.iter() {
result += writeable::Writeable::writeable_length_hint(key) + 1;
}
diff --git a/vendor/icu_locid/src/extensions/transform/fields.rs b/vendor/icu_locid/src/extensions/transform/fields.rs
index ca10000a7..f08581a87 100644
--- a/vendor/icu_locid/src/extensions/transform/fields.rs
+++ b/vendor/icu_locid/src/extensions/transform/fields.rs
@@ -25,10 +25,10 @@ use super::Value;
///
/// ```
/// use icu::locid::extensions::transform::{Fields, Key, Value};
+/// use icu::locid::extensions_transform_key as key;
///
-/// let key: Key = "h0".parse().expect("Failed to parse a Key.");
-/// let value: Value = "hybrid".parse().expect("Failed to parse a Value.");
-/// let fields: Fields = vec![(key, value)].into_iter().collect();
+/// let value = "hybrid".parse::<Value>().expect("Failed to parse a Value.");
+/// let fields = vec![(key!("h0"), value)].into_iter().collect::<Fields>();
///
/// assert_eq!(&fields.to_string(), "h0-hybrid");
/// ```
@@ -76,17 +76,17 @@ impl Fields {
/// # Examples
///
/// ```
- /// use icu::locid::extensions::transform::{Fields, Key, Value};
+ /// use icu::locid::extensions::transform::{Fields, Value};
+ /// use icu::locid::extensions_transform_key as key;
///
- /// let key: Key = "h0".parse().expect("Failed to parse a Key.");
- /// let value: Value = "hybrid".parse().expect("Failed to parse a Value.");
- /// let mut fields: Fields = vec![(key, value)].into_iter().collect();
+ /// let value = "hybrid".parse::<Value>().expect("Failed to parse a Value.");
+ /// let mut fields = vec![(key!("h0"), value)].into_iter().collect::<Fields>();
///
/// assert_eq!(&fields.to_string(), "h0-hybrid");
///
/// fields.clear();
///
- /// assert_eq!(&fields.to_string(), "");
+ /// assert_eq!(fields, Fields::new());
/// ```
pub fn clear(&mut self) -> Self {
core::mem::take(self)
@@ -122,16 +122,14 @@ impl Fields {
///
/// ```
/// use icu::locid::extensions::transform::{Fields, Key, Value};
+ /// use icu::locid::extensions_transform_key as key;
///
- /// let key: Key = "h0".parse().expect("Failed to parse a Key.");
- /// let value: Value = "hybrid".parse().expect("Failed to parse a Value.");
- /// let mut fields: Fields = vec![(key, value)].into_iter().collect();
+ /// let value = "hybrid".parse::<Value>().unwrap();
+ /// let fields = vec![(key!("h0"), value.clone())]
+ /// .into_iter()
+ /// .collect::<Fields>();
///
- /// let key: Key = "h0".parse().expect("Failed to parse a Key.");
- /// assert_eq!(
- /// fields.get(&key).map(|v| v.to_string()),
- /// Some("hybrid".to_string())
- /// );
+ /// assert_eq!(fields.get(&key!("h0")), Some(&value));
/// ```
pub fn get<Q>(&self, key: &Q) -> Option<&Value>
where
diff --git a/vendor/icu_locid/src/extensions/transform/mod.rs b/vendor/icu_locid/src/extensions/transform/mod.rs
index a8c605146..7b97d87f6 100644
--- a/vendor/icu_locid/src/extensions/transform/mod.rs
+++ b/vendor/icu_locid/src/extensions/transform/mod.rs
@@ -28,7 +28,7 @@
//! assert!(loc.extensions.transform.fields.contains_key(&key));
//! assert_eq!(loc.extensions.transform.fields.get(&key), Some(&value));
//!
-//! assert_eq!(&loc.extensions.transform.to_string(), "-t-es-AR-h0-hybrid");
+//! assert_eq!(&loc.extensions.transform.to_string(), "t-es-AR-h0-hybrid");
//! ```
mod fields;
mod key;
@@ -208,7 +208,7 @@ impl writeable::Writeable for Transform {
if self.is_empty() {
return Ok(());
}
- sink.write_str("-t")?;
+ sink.write_str("t")?;
if let Some(lang) = &self.lang {
sink.write_char('-')?;
writeable::Writeable::write_to(lang, sink)?;
@@ -224,7 +224,7 @@ impl writeable::Writeable for Transform {
if self.is_empty() {
return writeable::LengthHint::exact(0);
}
- let mut result = writeable::LengthHint::exact(2);
+ let mut result = writeable::LengthHint::exact(1);
if let Some(lang) = &self.lang {
result += writeable::Writeable::writeable_length_hint(lang) + 1;
}
diff --git a/vendor/icu_locid/src/extensions/transform/value.rs b/vendor/icu_locid/src/extensions/transform/value.rs
index 84468361a..f908b0208 100644
--- a/vendor/icu_locid/src/extensions/transform/value.rs
+++ b/vendor/icu_locid/src/extensions/transform/value.rs
@@ -2,7 +2,7 @@
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
-use crate::parser::{get_subtag_iterator, ParserError};
+use crate::parser::{ParserError, SubtagIterator};
use alloc::vec;
use alloc::vec::Vec;
use core::ops::RangeInclusive;
@@ -16,20 +16,18 @@ use tinystr::TinyAsciiStr;
/// Each part of the sequence has to be no shorter than three characters and no
/// longer than 8.
///
-///
/// # Examples
///
/// ```
/// use icu::locid::extensions::transform::Value;
///
-/// let value1: Value = "hybrid".parse().expect("Failed to parse a Value.");
-/// let value2: Value =
-/// "hybrid-foobar".parse().expect("Failed to parse a Value.");
+/// "hybrid".parse::<Value>().expect("Valid Value.");
+///
+/// "hybrid-foobar".parse::<Value>().expect("Valid Value.");
///
-/// assert_eq!(&value1.to_string(), "hybrid");
-/// assert_eq!(&value2.to_string(), "hybrid-foobar");
+/// "no".parse::<Value>().expect_err("Invalid Value.");
/// ```
-#[derive(Debug, PartialEq, Eq, Clone, Hash, PartialOrd, Ord)]
+#[derive(Debug, PartialEq, Eq, Clone, Hash, PartialOrd, Ord, Default)]
pub struct Value(Vec<TinyAsciiStr<{ *TYPE_LENGTH.end() }>>);
const TYPE_LENGTH: RangeInclusive<usize> = 3..=8;
@@ -45,14 +43,12 @@ impl Value {
/// use icu::locid::extensions::transform::Value;
///
/// let value = Value::try_from_bytes(b"hybrid").expect("Parsing failed.");
- ///
- /// assert_eq!(&value.to_string(), "hybrid");
/// ```
pub fn try_from_bytes(input: &[u8]) -> Result<Self, ParserError> {
let mut v = vec![];
let mut has_value = false;
- for subtag in get_subtag_iterator(input) {
+ for subtag in SubtagIterator::new(input) {
if !Self::is_type_subtag(subtag) {
return Err(ParserError::InvalidExtension);
}
@@ -116,4 +112,19 @@ impl FromStr for Value {
}
}
-impl_writeable_for_tinystr_list!(Value, "true", "hybrid", "foobar");
+impl_writeable_for_each_subtag_str_no_test!(Value, selff, selff.0.is_empty() => alloc::borrow::Cow::Borrowed("true"));
+
+#[test]
+fn test_writeable() {
+ use writeable::assert_writeable_eq;
+
+ let hybrid = "hybrid".parse().unwrap();
+ let foobar = "foobar".parse().unwrap();
+
+ assert_writeable_eq!(Value::default(), "true");
+ assert_writeable_eq!(Value::from_vec_unchecked(vec![hybrid]), "hybrid");
+ assert_writeable_eq!(
+ Value::from_vec_unchecked(vec![hybrid, foobar]),
+ "hybrid-foobar"
+ );
+}
diff --git a/vendor/icu_locid/src/extensions/unicode/attributes.rs b/vendor/icu_locid/src/extensions/unicode/attributes.rs
index 1f9536bfa..e58fb04da 100644
--- a/vendor/icu_locid/src/extensions/unicode/attributes.rs
+++ b/vendor/icu_locid/src/extensions/unicode/attributes.rs
@@ -79,18 +79,19 @@ impl Attributes {
///
/// ```
/// use icu::locid::extensions::unicode::{Attribute, Attributes};
+ /// use icu::locid::extensions_unicode_attribute as attribute;
+ /// use writeable::assert_writeable_eq;
///
- /// let attribute1: Attribute = "foobar".parse().expect("Parsing failed.");
- /// let attribute2: Attribute = "testing".parse().expect("Parsing failed.");
- /// let mut v = vec![attribute1, attribute2];
- ///
- /// let mut attributes: Attributes = Attributes::from_vec_unchecked(v);
+ /// let mut attributes = Attributes::from_vec_unchecked(vec![
+ /// attribute!("foobar"),
+ /// attribute!("testing"),
+ /// ]);
///
- /// assert_eq!(attributes.to_string(), "foobar-testing");
+ /// assert_writeable_eq!(attributes, "foobar-testing");
///
/// attributes.clear();
///
- /// assert_eq!(attributes.to_string(), "");
+ /// assert_writeable_eq!(attributes, "");
/// ```
pub fn clear(&mut self) -> Self {
core::mem::take(self)
diff --git a/vendor/icu_locid/src/extensions/unicode/keywords.rs b/vendor/icu_locid/src/extensions/unicode/keywords.rs
index dc9a15921..580cacaf1 100644
--- a/vendor/icu_locid/src/extensions/unicode/keywords.rs
+++ b/vendor/icu_locid/src/extensions/unicode/keywords.rs
@@ -29,11 +29,14 @@ use crate::ordering::SubtagOrderingResult;
/// Manually build up a [`Keywords`] object:
///
/// ```
-/// use icu::locid::extensions::unicode::{Key, Keywords, Value};
+/// use icu::locid::{
+/// extensions::unicode::Keywords, extensions_unicode_key as key,
+/// extensions_unicode_value as value, locale,
+/// };
///
-/// let key: Key = "hc".parse().expect("Failed to parse a Key.");
-/// let value: Value = "h23".parse().expect("Failed to parse a Value.");
-/// let keywords: Keywords = vec![(key, value)].into_iter().collect();
+/// let keywords = vec![(key!("hc"), value!("h23"))]
+/// .into_iter()
+/// .collect::<Keywords>();
///
/// assert_eq!(&keywords.to_string(), "hc-h23");
/// ```
@@ -113,15 +116,16 @@ impl Keywords {
/// # Examples
///
/// ```
- /// use icu::locid::extensions::unicode::{Key, Keywords, Value};
- /// use litemap::LiteMap;
+ /// use icu::locid::{
+ /// extensions::unicode::Keywords, extensions_unicode_key as key,
+ /// extensions_unicode_value as value,
+ /// };
///
- /// let key: Key = "ca".parse().expect("Failed to parse a Key.");
- /// let value: Value = "gregory".parse().expect("Failed to parse a Value.");
- /// let keywords: Keywords = vec![(key, value)].into_iter().collect();
+ /// let keywords = vec![(key!("ca"), value!("gregory"))]
+ /// .into_iter()
+ /// .collect::<Keywords>();
///
- /// let key: Key = "ca".parse().expect("Failed to parse a Key.");
- /// assert!(&keywords.contains_key(&key));
+ /// assert!(&keywords.contains_key(&key!("ca")));
/// ```
pub fn contains_key<Q>(&self, key: &Q) -> bool
where
@@ -137,17 +141,16 @@ impl Keywords {
/// # Examples
///
/// ```
- /// use icu::locid::extensions::unicode::{Key, Keywords, Value};
+ /// use icu::locid::{
+ /// extensions::unicode::Keywords, extensions_unicode_key as key,
+ /// extensions_unicode_value as value,
+ /// };
///
- /// let key: Key = "ca".parse().expect("Failed to parse a Key.");
- /// let value: Value = "buddhist".parse().expect("Failed to parse a Value.");
- /// let keywords: Keywords = vec![(key, value)].into_iter().collect();
+ /// let keywords = vec![(key!("ca"), value!("buddhist"))]
+ /// .into_iter()
+ /// .collect::<Keywords>();
///
- /// let key: Key = "ca".parse().expect("Failed to parse a Key.");
- /// assert_eq!(
- /// keywords.get(&key).map(|v| v.to_string()),
- /// Some("buddhist".to_string())
- /// );
+ /// assert_eq!(keywords.get(&key!("ca")), Some(&value!("buddhist")));
/// ```
pub fn get<Q>(&self, key: &Q) -> Option<&Value>
where
@@ -164,20 +167,19 @@ impl Keywords {
/// # Examples
///
/// ```
- /// use icu::locid::extensions::unicode::{Key, Keywords, Value};
+ /// use icu::locid::{
+ /// extensions::unicode::Keywords, extensions_unicode_key as key,
+ /// extensions_unicode_value as value,
+ /// };
///
- /// let key: Key = "ca".parse().expect("Failed to parse a Key.");
- /// let value: Value = "buddhist".parse().expect("Failed to parse a Value.");
- /// let mut keywords: Keywords = vec![(key, value)].into_iter().collect();
+ /// let mut keywords = vec![(key!("ca"), value!("buddhist"))]
+ /// .into_iter()
+ /// .collect::<Keywords>();
///
- /// let key: Key = "ca".parse().expect("Failed to parse a Key.");
- /// if let Some(value) = keywords.get_mut(&key) {
- /// *value = "gregory".parse().expect("Failed to parse a Value.");
+ /// if let Some(value) = keywords.get_mut(&key!("ca")) {
+ /// *value = value!("gregory");
/// }
- /// assert_eq!(
- /// keywords.get(&key).map(|v| v.to_string()),
- /// Some("gregory".to_string())
- /// );
+ /// assert_eq!(keywords.get(&key!("ca")), Some(&value!("gregory")));
/// ```
pub fn get_mut<Q>(&mut self, key: &Q) -> Option<&mut Value>
where
@@ -308,7 +310,6 @@ impl Keywords {
/// .extensions
/// .unicode
/// .keywords;
- /// assert_eq!(a, a_kwds.to_string());
/// assert!(a_kwds.strict_cmp(a.as_bytes()) == Ordering::Equal);
/// assert!(a_kwds.strict_cmp(b.as_bytes()) == Ordering::Less);
/// }
diff --git a/vendor/icu_locid/src/extensions/unicode/mod.rs b/vendor/icu_locid/src/extensions/unicode/mod.rs
index fabf1036c..687a8c383 100644
--- a/vendor/icu_locid/src/extensions/unicode/mod.rs
+++ b/vendor/icu_locid/src/extensions/unicode/mod.rs
@@ -11,21 +11,24 @@
//! # Examples
//!
//! ```
-//! use icu::locid::extensions::unicode::{Attribute, Key, Unicode, Value};
-//! use icu::locid::{LanguageIdentifier, Locale};
+//! use icu::locid::Locale;
+//! use icu::locid::{
+//! extensions::unicode::Unicode,
+//! extensions_unicode_attribute as attribute,
+//! extensions_unicode_key as key, extensions_unicode_value as value,
+//! };
//!
-//! let mut loc: Locale =
-//! "en-US-u-foobar-hc-h12".parse().expect("Parsing failed.");
+//! let loc: Locale = "en-US-u-foobar-hc-h12".parse().expect("Parsing failed.");
//!
-//! let key: Key = "hc".parse().expect("Parsing key failed.");
-//! let value: Value = "h12".parse().expect("Parsing value failed.");
-//! let attribute: Attribute =
-//! "foobar".parse().expect("Parsing attribute failed.");
-//!
-//! assert_eq!(loc.extensions.unicode.keywords.get(&key), Some(&value));
-//! assert!(loc.extensions.unicode.attributes.contains(&attribute));
-//!
-//! assert_eq!(&loc.extensions.unicode.to_string(), "-u-foobar-hc-h12");
+//! assert_eq!(
+//! loc.extensions.unicode.keywords.get(&key!("hc")),
+//! Some(&value!("h12"))
+//! );
+//! assert!(loc
+//! .extensions
+//! .unicode
+//! .attributes
+//! .contains(&attribute!("foobar")));
//! ```
mod attribute;
mod attributes;
@@ -60,15 +63,18 @@ use litemap::LiteMap;
/// # Examples
///
/// ```
-/// use icu::locid::extensions::unicode::{Key, Value};
/// use icu::locid::Locale;
+/// use icu::locid::{
+/// extensions_unicode_key as key, extensions_unicode_value as value,
+/// };
///
-/// let mut loc: Locale =
+/// let loc: Locale =
/// "de-u-hc-h12-ca-buddhist".parse().expect("Parsing failed.");
///
-/// let key: Key = "ca".parse().expect("Parsing key failed.");
-/// let value: Value = "buddhist".parse().expect("Parsing value failed.");
-/// assert_eq!(loc.extensions.unicode.keywords.get(&key), Some(&value));
+/// assert_eq!(
+/// loc.extensions.unicode.keywords.get(&key!("ca")),
+/// Some(&value!("buddhist"))
+/// );
/// ```
#[derive(Clone, PartialEq, Eq, Debug, Default, Hash, PartialOrd, Ord)]
#[allow(clippy::exhaustive_structs)] // spec-backed stable datastructure
@@ -205,7 +211,7 @@ impl writeable::Writeable for Unicode {
if self.is_empty() {
return Ok(());
}
- sink.write_str("-u")?;
+ sink.write_str("u")?;
if !self.attributes.is_empty() {
sink.write_char('-')?;
writeable::Writeable::write_to(&self.attributes, sink)?;
@@ -221,7 +227,7 @@ impl writeable::Writeable for Unicode {
if self.is_empty() {
return writeable::LengthHint::exact(0);
}
- let mut result = writeable::LengthHint::exact(2);
+ let mut result = writeable::LengthHint::exact(1);
if !self.attributes.is_empty() {
result += writeable::Writeable::writeable_length_hint(&self.attributes) + 1;
}
diff --git a/vendor/icu_locid/src/extensions/unicode/value.rs b/vendor/icu_locid/src/extensions/unicode/value.rs
index ce9982a4c..e6374372c 100644
--- a/vendor/icu_locid/src/extensions/unicode/value.rs
+++ b/vendor/icu_locid/src/extensions/unicode/value.rs
@@ -3,7 +3,7 @@
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
use crate::helpers::ShortVec;
-use crate::parser::{get_subtag_iterator, ParserError};
+use crate::parser::{ParserError, SubtagIterator};
use alloc::vec::Vec;
use core::ops::RangeInclusive;
use core::str::FromStr;
@@ -20,20 +20,21 @@ use tinystr::TinyAsciiStr;
/// # Examples
///
/// ```
-/// use icu::locid::extensions::unicode::Value;
-///
-/// let value1: Value = "gregory".parse().expect("Failed to parse a Value.");
-/// let value2: Value =
-/// "islamic-civil".parse().expect("Failed to parse a Value.");
-/// let value3: Value = "true".parse().expect("Failed to parse a Value.");
+/// use icu::locid::{
+/// extensions::unicode::Value, extensions_unicode_value as value,
+/// };
+/// use writeable::assert_writeable_eq;
///
-/// assert_eq!(&value1.to_string(), "gregory");
-/// assert_eq!(&value2.to_string(), "islamic-civil");
+/// assert_writeable_eq!(value!("gregory"), "gregory");
+/// assert_writeable_eq!(
+/// "islamic-civil".parse::<Value>().unwrap(),
+/// "islamic-civil"
+/// );
///
-/// // The value "true" is special-cased to an empty value
-/// assert_eq!(&value3.to_string(), "");
+/// // The value "true" has the special, empty string representation
+/// assert_eq!(value!("true").to_string(), "");
/// ```
-#[derive(Debug, PartialEq, Eq, Clone, Hash, PartialOrd, Ord)]
+#[derive(Debug, PartialEq, Eq, Clone, Hash, PartialOrd, Ord, Default)]
pub struct Value(ShortVec<TinyAsciiStr<{ *VALUE_LENGTH.end() }>>);
const VALUE_LENGTH: RangeInclusive<usize> = 3..=8;
@@ -48,15 +49,13 @@ impl Value {
/// ```
/// use icu::locid::extensions::unicode::Value;
///
- /// let value = Value::try_from_bytes(b"buddhist").expect("Parsing failed.");
- ///
- /// assert_eq!(&value.to_string(), "buddhist");
+ /// Value::try_from_bytes(b"buddhist").expect("Parsing failed.");
/// ```
pub fn try_from_bytes(input: &[u8]) -> Result<Self, ParserError> {
let mut v = ShortVec::new();
if !input.is_empty() {
- for subtag in get_subtag_iterator(input) {
+ for subtag in SubtagIterator::new(input) {
let val = Self::subtag_from_bytes(subtag)?;
if let Some(val) = val {
v.push(val);
@@ -153,7 +152,7 @@ impl FromStr for Value {
}
}
-impl_writeable_for_tinystr_list!(Value, "", "islamic", "civil");
+impl_writeable_for_subtag_list!(Value, "islamic", "civil");
/// A macro allowing for compile-time construction of valid Unicode [`Value`] subtag.
///
diff --git a/vendor/icu_locid/src/helpers.rs b/vendor/icu_locid/src/helpers.rs
index e617ded5d..e5889a7b0 100644
--- a/vendor/icu_locid/src/helpers.rs
+++ b/vendor/icu_locid/src/helpers.rs
@@ -115,7 +115,7 @@ impl<T> ShortVec<T> {
#[allow(clippy::unwrap_used)]
// we know that the vec has exactly one element left
1 => (ShortVec::Single(v.pop().unwrap()), removed_item),
- // v has atleast 2 elements, create a Multi variant
+ // v has at least 2 elements, create a Multi variant
_ => (ShortVec::Multi(v), removed_item),
}
}
@@ -387,6 +387,7 @@ macro_rules! impl_tinystr_subtag {
}
impl writeable::Writeable for $name {
+ #[inline]
fn write_to<W: core::fmt::Write + ?Sized>(&self, sink: &mut W) -> core::fmt::Result {
sink.write_str(self.as_str())
}
@@ -394,6 +395,10 @@ macro_rules! impl_tinystr_subtag {
fn writeable_length_hint(&self) -> writeable::LengthHint {
writeable::LengthHint::exact(self.0.len())
}
+ #[inline]
+ fn write_to_string(&self) -> alloc::borrow::Cow<str> {
+ alloc::borrow::Cow::Borrowed(self.0.as_str())
+ }
}
writeable::impl_display_with_writeable!($name);
@@ -546,7 +551,7 @@ macro_rules! impl_tinystr_subtag {
}
macro_rules! impl_writeable_for_each_subtag_str_no_test {
- ($type:tt) => {
+ ($type:tt $(, $self:ident, $borrow_cond:expr => $borrow:expr)?) => {
impl writeable::Writeable for $type {
fn write_to<W: core::fmt::Write + ?Sized>(&self, sink: &mut W) -> core::fmt::Result {
let mut initial = true;
@@ -576,6 +581,20 @@ macro_rules! impl_writeable_for_each_subtag_str_no_test {
.expect("infallible");
result
}
+
+ $(
+ fn write_to_string(&self) -> alloc::borrow::Cow<str> {
+ #[allow(clippy::unwrap_used)] // impl_writeable_for_subtag_list's $borrow uses unwrap
+ let $self = self;
+ if $borrow_cond {
+ $borrow
+ } else {
+ let mut output = alloc::string::String::with_capacity(self.writeable_length_hint().capacity());
+ let _ = self.write_to(&mut output);
+ alloc::borrow::Cow::Owned(output)
+ }
+ }
+ )?
}
writeable::impl_display_with_writeable!($type);
@@ -584,7 +603,7 @@ macro_rules! impl_writeable_for_each_subtag_str_no_test {
macro_rules! impl_writeable_for_subtag_list {
($type:tt, $sample1:literal, $sample2:literal) => {
- impl_writeable_for_each_subtag_str_no_test!($type);
+ impl_writeable_for_each_subtag_str_no_test!($type, selff, selff.0.len() == 1 => alloc::borrow::Cow::Borrowed(selff.0.as_slice().get(0).unwrap().as_str()));
#[test]
fn test_writeable() {
@@ -594,27 +613,6 @@ macro_rules! impl_writeable_for_subtag_list {
$sample1,
);
writeable::assert_writeable_eq!(
- &$type::from_vec_unchecked(alloc::vec![
- $sample1.parse().unwrap(),
- $sample2.parse().unwrap()
- ]),
- core::concat!($sample1, "-", $sample2),
- );
- }
- };
-}
-
-macro_rules! impl_writeable_for_tinystr_list {
- ($type:tt, $if_empty:literal, $sample1:literal, $sample2:literal) => {
- impl_writeable_for_each_subtag_str_no_test!($type);
-
- #[test]
- fn test_writeable() {
- writeable::assert_writeable_eq!(
- &$type::from_vec_unchecked(vec![$sample1.parse().unwrap()]),
- $sample1,
- );
- writeable::assert_writeable_eq!(
&$type::from_vec_unchecked(vec![
$sample1.parse().unwrap(),
$sample2.parse().unwrap()
diff --git a/vendor/icu_locid/src/langid.rs b/vendor/icu_locid/src/langid.rs
index fc5435766..b6858c91b 100644
--- a/vendor/icu_locid/src/langid.rs
+++ b/vendor/icu_locid/src/langid.rs
@@ -7,27 +7,28 @@ use core::str::FromStr;
use crate::ordering::SubtagOrderingResult;
use crate::parser::{
- get_subtag_iterator, parse_language_identifier, parse_language_identifier_with_single_variant,
- ParserError, ParserMode,
+ parse_language_identifier, parse_language_identifier_with_single_variant, ParserError,
+ ParserMode, SubtagIterator,
};
use crate::subtags;
use alloc::string::String;
-use alloc::string::ToString;
+use writeable::Writeable;
/// A core struct representing a [`Unicode BCP47 Language Identifier`].
///
/// # Examples
///
/// ```
-/// use icu::locid::{subtags::*, LanguageIdentifier};
+/// use icu::locid::{
+/// langid, subtags_language as language, subtags_region as region,
+/// };
///
-/// let li: LanguageIdentifier = "en-US".parse().expect("Failed to parse.");
+/// let li = langid!("en-US");
///
-/// assert_eq!(li.language, "en".parse::<Language>().unwrap());
+/// assert_eq!(li.language, language!("en"));
/// assert_eq!(li.script, None);
-/// assert_eq!(li.region.unwrap(), "US".parse::<Region>().unwrap());
+/// assert_eq!(li.region, Some(region!("US")));
/// assert_eq!(li.variants.len(), 0);
-/// assert_eq!(li.to_string(), "en-US");
/// ```
///
/// # Parsing
@@ -47,18 +48,17 @@ use alloc::string::ToString;
/// # Examples
///
/// ```
-/// use icu::locid::{subtags::*, LanguageIdentifier};
+/// use icu::locid::{
+/// langid, subtags_language as language, subtags_region as region,
+/// subtags_script as script, subtags_variant as variant,
+/// };
///
-/// let li: LanguageIdentifier =
-/// "eN_latn_Us-Valencia".parse().expect("Failed to parse.");
+/// let li = langid!("eN_latn_Us-Valencia");
///
-/// assert_eq!(li.language, "en".parse::<Language>().unwrap());
-/// assert_eq!(li.script, "Latn".parse::<Script>().ok());
-/// assert_eq!(li.region, "US".parse::<Region>().ok());
-/// assert_eq!(
-/// li.variants.get(0),
-/// "valencia".parse::<Variant>().ok().as_ref()
-/// );
+/// assert_eq!(li.language, language!("en"));
+/// assert_eq!(li.script, Some(script!("Latn")));
+/// assert_eq!(li.region, Some(region!("US")));
+/// assert_eq!(li.variants.get(0), Some(&variant!("valencia")));
/// ```
///
/// [`Unicode BCP47 Language Identifier`]: https://unicode.org/reports/tr35/tr35.html#Unicode_language_identifier
@@ -84,10 +84,7 @@ impl LanguageIdentifier {
/// ```
/// use icu::locid::LanguageIdentifier;
///
- /// let li =
- /// LanguageIdentifier::try_from_bytes(b"en-US").expect("Parsing failed.");
- ///
- /// assert_eq!(li.to_string(), "en-US");
+ /// LanguageIdentifier::try_from_bytes(b"en-US").expect("Parsing failed");
/// ```
pub fn try_from_bytes(v: &[u8]) -> Result<Self, ParserError> {
parse_language_identifier(v, ParserMode::LanguageIdentifier)
@@ -117,12 +114,12 @@ impl LanguageIdentifier {
/// # Examples
///
/// ```
- /// use icu::locid::LanguageIdentifier;
+ /// use icu::locid::{langid, LanguageIdentifier};
///
/// let li = LanguageIdentifier::try_from_locale_bytes(b"en-US-x-posix")
/// .expect("Parsing failed.");
///
- /// assert_eq!(li.to_string(), "en-US");
+ /// assert_eq!(li, langid!("en-US"));
/// ```
///
/// This method should be used for input that may be a locale identifier.
@@ -139,7 +136,6 @@ impl LanguageIdentifier {
/// use icu::locid::LanguageIdentifier;
///
/// assert_eq!(LanguageIdentifier::default(), LanguageIdentifier::UND);
- /// assert_eq!("und", LanguageIdentifier::UND.to_string());
/// ```
pub const UND: Self = Self {
language: subtags::Language::UND,
@@ -159,13 +155,13 @@ impl LanguageIdentifier {
/// use icu::locid::LanguageIdentifier;
///
/// assert_eq!(
- /// LanguageIdentifier::canonicalize("pL_latn_pl"),
- /// Ok("pl-Latn-PL".to_string())
+ /// LanguageIdentifier::canonicalize("pL_latn_pl").as_deref(),
+ /// Ok("pl-Latn-PL")
/// );
/// ```
pub fn canonicalize<S: AsRef<[u8]>>(input: S) -> Result<String, ParserError> {
let lang_id = Self::try_from_bytes(input.as_ref())?;
- Ok(lang_id.to_string())
+ Ok(lang_id.write_to_string().into_owned())
}
/// Compare this [`LanguageIdentifier`] with BCP-47 bytes.
@@ -197,7 +193,6 @@ impl LanguageIdentifier {
/// let b = ab[1];
/// assert!(a.cmp(b) == Ordering::Less);
/// let a_langid = a.parse::<LanguageIdentifier>().unwrap();
- /// assert_eq!(a, a_langid.to_string());
/// assert!(a_langid.strict_cmp(a.as_bytes()) == Ordering::Equal);
/// assert!(a_langid.strict_cmp(b.as_bytes()) == Ordering::Less);
/// }
@@ -293,7 +288,7 @@ impl LanguageIdentifier {
};
}
- let mut iter = get_subtag_iterator(other.as_bytes());
+ let mut iter = SubtagIterator::new(other.as_bytes());
if !subtag_matches!(subtags::Language, iter, self.language) {
return false;
}
@@ -359,7 +354,7 @@ impl FromStr for LanguageIdentifier {
}
}
-impl_writeable_for_each_subtag_str_no_test!(LanguageIdentifier);
+impl_writeable_for_each_subtag_str_no_test!(LanguageIdentifier, selff, selff.script.is_none() && selff.region.is_none() && selff.variants.is_empty() => selff.language.write_to_string());
#[test]
fn test_writeable() {
@@ -387,14 +382,11 @@ fn test_writeable() {
/// # Examples
///
/// ```
-/// use icu::locid::subtags_language as language;
-/// use icu::locid::LanguageIdentifier;
-///
-/// let language = language!("en");
-/// let li = LanguageIdentifier::from(language);
+/// use icu::locid::{
+/// langid, subtags_language as language, LanguageIdentifier,
+/// };
///
-/// assert_eq!(li.language, language);
-/// assert_eq!(li.to_string(), "en");
+/// assert_eq!(LanguageIdentifier::from(language!("en")), langid!("en"));
/// ```
impl From<subtags::Language> for LanguageIdentifier {
fn from(language: subtags::Language) -> Self {
@@ -408,14 +400,12 @@ impl From<subtags::Language> for LanguageIdentifier {
/// # Examples
///
/// ```
-/// use icu::locid::subtags_script as script;
-/// use icu::locid::LanguageIdentifier;
+/// use icu::locid::{langid, subtags_script as script, LanguageIdentifier};
///
-/// let script = script!("latn");
-/// let li = LanguageIdentifier::from(Some(script));
-///
-/// assert_eq!(li.script.unwrap(), script);
-/// assert_eq!(li.to_string(), "und-Latn");
+/// assert_eq!(
+/// LanguageIdentifier::from(Some(script!("latn"))),
+/// langid!("und-Latn")
+/// );
/// ```
impl From<Option<subtags::Script>> for LanguageIdentifier {
fn from(script: Option<subtags::Script>) -> Self {
@@ -429,14 +419,12 @@ impl From<Option<subtags::Script>> for LanguageIdentifier {
/// # Examples
///
/// ```
-/// use icu::locid::subtags_region as region;
-/// use icu::locid::LanguageIdentifier;
+/// use icu::locid::{langid, subtags_region as region, LanguageIdentifier};
///
-/// let region = region!("US");
-/// let li = LanguageIdentifier::from(Some(region));
-///
-/// assert_eq!(li.region.unwrap(), region);
-/// assert_eq!(li.to_string(), "und-US");
+/// assert_eq!(
+/// LanguageIdentifier::from(Some(region!("US"))),
+/// langid!("und-US")
+/// );
/// ```
impl From<Option<subtags::Region>> for LanguageIdentifier {
fn from(region: Option<subtags::Region>) -> Self {
@@ -452,22 +440,18 @@ impl From<Option<subtags::Region>> for LanguageIdentifier {
/// # Examples
///
/// ```
-/// use icu::locid::LanguageIdentifier;
/// use icu::locid::{
-/// subtags_language as language, subtags_region as region,
-/// subtags_script as script,
+/// langid, subtags_language as language, subtags_region as region,
+/// subtags_script as script, LanguageIdentifier,
/// };
///
/// let lang = language!("en");
/// let script = script!("Latn");
/// let region = region!("US");
-/// let li = LanguageIdentifier::from((lang, Some(script), Some(region)));
-///
-/// assert_eq!(li.language, lang);
-/// assert_eq!(li.script.unwrap(), script);
-/// assert_eq!(li.region.unwrap(), region);
-/// assert_eq!(li.variants.len(), 0);
-/// assert_eq!(li.to_string(), "en-Latn-US");
+/// assert_eq!(
+/// LanguageIdentifier::from((lang, Some(script), Some(region))),
+/// langid!("en-Latn-US")
+/// );
/// ```
impl
From<(
@@ -497,7 +481,6 @@ impl
/// # Examples
///
/// ```
-/// use icu::locid::LanguageIdentifier;
/// use icu::locid::{
/// langid, subtags_language as language, subtags_region as region,
/// subtags_script as script,
diff --git a/vendor/icu_locid/src/lib.rs b/vendor/icu_locid/src/lib.rs
index 885c4b743..226a8e53c 100644
--- a/vendor/icu_locid/src/lib.rs
+++ b/vendor/icu_locid/src/lib.rs
@@ -22,39 +22,23 @@
//! # Examples
//!
//! ```
-//! use icu::locid::subtags::{Language, Region};
//! use icu::locid::Locale;
+//! use icu::locid::{
+//! locale, subtags_language as language, subtags_region as region,
+//! };
//!
-//! let mut loc: Locale = "en-US".parse().expect("Parsing failed.");
-//!
-//! let lang: Language = "en".parse().expect("Parsing failed.");
-//! let region: Region = "US".parse().expect("Parsing failed.");
+//! let mut loc: Locale = locale!("en-US");
//!
-//! assert_eq!(loc.id.language, lang);
+//! assert_eq!(loc.id.language, language!("en"));
//! assert_eq!(loc.id.script, None);
-//! assert_eq!(loc.id.region, Some(region));
+//! assert_eq!(loc.id.region, Some(region!("US")));
//! assert_eq!(loc.id.variants.len(), 0);
//!
-//! let region: Region = "GB".parse().expect("Parsing failed.");
-//! loc.id.region = Some(region);
+//! loc.id.region = Some(region!("GB"));
//!
-//! assert_eq!(loc.to_string(), "en-GB");
+//! assert_eq!(loc, locale!("en-GB"));
//! ```
//!
-//! ## Macros
-//!
-//! ```rust
-//! use icu::locid::{
-//! langid, subtags_language as language, subtags_region as region,
-//! };
-//!
-//! let lid = langid!("EN_US");
-//!
-//! assert_eq!(lid.language, language!("en"));
-//! assert_eq!(lid.region, Some(region!("US")));
-//! ```
-
-//!
//! For more details, see [`Locale`] and [`LanguageIdentifier`].
//!
//! [`UTS #35: Unicode LDML 3. Unicode Language and Locale Identifiers`]: https://unicode.org/reports/tr35/tr35.html#Unicode_Language_and_Locale_Identifiers
diff --git a/vendor/icu_locid/src/locale.rs b/vendor/icu_locid/src/locale.rs
index d7040d31a..5d9109fee 100644
--- a/vendor/icu_locid/src/locale.rs
+++ b/vendor/icu_locid/src/locale.rs
@@ -4,16 +4,15 @@
use crate::ordering::SubtagOrderingResult;
use crate::parser::{
- get_subtag_iterator, parse_locale,
- parse_locale_with_single_variant_single_keyword_unicode_keyword_extension, ParserError,
- ParserMode,
+ parse_locale, parse_locale_with_single_variant_single_keyword_unicode_keyword_extension,
+ ParserError, ParserMode, SubtagIterator,
};
use crate::{extensions, subtags, LanguageIdentifier};
use alloc::string::String;
-use alloc::string::ToString;
use core::cmp::Ordering;
use core::str::FromStr;
use tinystr::TinyAsciiStr;
+use writeable::Writeable;
/// A core struct representing a [`Unicode Locale Identifier`].
///
@@ -28,20 +27,21 @@ use tinystr::TinyAsciiStr;
/// # Examples
///
/// ```
-/// use icu::locid::extensions::unicode::{Key, Value};
-/// use icu::locid::{subtags::*, Locale};
+/// use icu_locid::{
+/// extensions_unicode_key as key, extensions_unicode_value as value,
+/// locale, subtags_language as language, subtags_region as region,
+/// };
///
-/// let loc: Locale = "en-US-u-ca-buddhist".parse().expect("Failed to parse.");
+/// let loc = locale!("en-US-u-ca-buddhist");
///
-/// assert_eq!(loc.id.language, "en".parse::<Language>().unwrap());
+/// assert_eq!(loc.id.language, language!("en"));
/// assert_eq!(loc.id.script, None);
-/// assert_eq!(loc.id.region, "US".parse::<Region>().ok());
+/// assert_eq!(loc.id.region, Some(region!("US")));
/// assert_eq!(loc.id.variants.len(), 0);
-/// assert_eq!(loc.to_string(), "en-US-u-ca-buddhist");
-///
-/// let key: Key = "ca".parse().expect("Parsing key failed.");
-/// let value: Value = "buddhist".parse().expect("Parsing value failed.");
-/// assert_eq!(loc.extensions.unicode.keywords.get(&key), Some(&value));
+/// assert_eq!(
+/// loc.extensions.unicode.keywords.get(&key!("ca")),
+/// Some(&value!("buddhist"))
+/// );
/// ```
///
/// # Parsing
@@ -87,6 +87,8 @@ pub struct Locale {
#[test]
fn test_sizes() {
+ // Remove when we upgrade to a compiler where the new sizes are default
+ let forced_nightly = std::env::var("ICU4X_BUILDING_WITH_FORCED_NIGHTLY").is_ok();
assert_eq!(core::mem::size_of::<subtags::Language>(), 3);
assert_eq!(core::mem::size_of::<subtags::Script>(), 4);
assert_eq!(core::mem::size_of::<subtags::Region>(), 3);
@@ -99,12 +101,21 @@ fn test_sizes() {
assert_eq!(core::mem::size_of::<extensions::transform::Fields>(), 24);
assert_eq!(core::mem::size_of::<extensions::unicode::Attributes>(), 24);
- assert_eq!(core::mem::size_of::<extensions::unicode::Keywords>(), 48);
+ assert_eq!(
+ core::mem::size_of::<extensions::unicode::Keywords>(),
+ if forced_nightly { 40 } else { 48 }
+ );
assert_eq!(core::mem::size_of::<Vec<extensions::other::Other>>(), 24);
assert_eq!(core::mem::size_of::<extensions::private::Private>(), 24);
- assert_eq!(core::mem::size_of::<extensions::Extensions>(), 192);
+ assert_eq!(
+ core::mem::size_of::<extensions::Extensions>(),
+ if forced_nightly { 184 } else { 192 }
+ );
- assert_eq!(core::mem::size_of::<Locale>(), 240);
+ assert_eq!(
+ core::mem::size_of::<Locale>(),
+ if forced_nightly { 232 } else { 240 }
+ );
}
impl Locale {
@@ -116,10 +127,7 @@ impl Locale {
/// ```
/// use icu::locid::Locale;
///
- /// let loc = Locale::try_from_bytes("en-US-u-hc-h12".as_bytes())
- /// .expect("Parsing failed.");
- ///
- /// assert_eq!(loc.to_string(), "en-US-u-hc-h12");
+ /// Locale::try_from_bytes(b"en-US-u-hc-h12").unwrap();
/// ```
pub fn try_from_bytes(v: &[u8]) -> Result<Self, ParserError> {
parse_locale(v)
@@ -133,7 +141,6 @@ impl Locale {
/// use icu::locid::Locale;
///
/// assert_eq!(Locale::default(), Locale::UND);
- /// assert_eq!("und", Locale::UND.to_string());
/// ```
pub const UND: Self = Self {
id: LanguageIdentifier::UND,
@@ -151,13 +158,13 @@ impl Locale {
/// use icu::locid::Locale;
///
/// assert_eq!(
- /// Locale::canonicalize("pL_latn_pl-U-HC-H12"),
- /// Ok("pl-Latn-PL-u-hc-h12".to_string())
+ /// Locale::canonicalize("pL_latn_pl-U-HC-H12").as_deref(),
+ /// Ok("pl-Latn-PL-u-hc-h12")
/// );
/// ```
pub fn canonicalize<S: AsRef<[u8]>>(input: S) -> Result<String, ParserError> {
let locale = Self::try_from_bytes(input.as_ref())?;
- Ok(locale.to_string())
+ Ok(locale.write_to_string().into_owned())
}
/// Compare this [`Locale`] with BCP-47 bytes.
@@ -189,7 +196,6 @@ impl Locale {
/// let b = ab[1];
/// assert!(a.cmp(b) == Ordering::Less);
/// let a_loc = a.parse::<Locale>().unwrap();
- /// assert_eq!(a, a_loc.to_string());
/// assert!(a_loc.strict_cmp(a.as_bytes()) == Ordering::Equal);
/// assert!(a_loc.strict_cmp(b.as_bytes()) == Ordering::Less);
/// }
@@ -286,7 +292,7 @@ impl Locale {
};
}
- let mut iter = get_subtag_iterator(other.as_bytes());
+ let mut iter = SubtagIterator::new(other.as_bytes());
if !subtag_matches!(subtags::Language, iter, self.id.language) {
return false;
}
@@ -391,7 +397,7 @@ impl core::fmt::Debug for Locale {
}
}
-impl_writeable_for_each_subtag_str_no_test!(Locale);
+impl_writeable_for_each_subtag_str_no_test!(Locale, selff, selff.extensions.is_empty() => selff.id.write_to_string());
#[test]
fn test_writeable() {
@@ -426,14 +432,10 @@ fn test_writeable() {
/// # Examples
///
/// ```
-/// use icu::locid::subtags_language as language;
/// use icu::locid::Locale;
+/// use icu::locid::{locale, subtags_language as language};
///
-/// let language = language!("en");
-/// let loc = Locale::from(language);
-///
-/// assert_eq!(loc.id.language, language);
-/// assert_eq!(loc.to_string(), "en");
+/// assert_eq!(Locale::from(language!("en")), locale!("en"));
/// ```
impl From<subtags::Language> for Locale {
fn from(language: subtags::Language) -> Self {
@@ -447,14 +449,10 @@ impl From<subtags::Language> for Locale {
/// # Examples
///
/// ```
-/// use icu::locid::subtags_script as script;
/// use icu::locid::Locale;
+/// use icu::locid::{locale, subtags_script as script};
///
-/// let script = script!("latn");
-/// let loc = Locale::from(Some(script));
-///
-/// assert_eq!(loc.id.script.unwrap(), script);
-/// assert_eq!(loc.to_string(), "und-Latn");
+/// assert_eq!(Locale::from(Some(script!("latn"))), locale!("und-Latn"));
/// ```
impl From<Option<subtags::Script>> for Locale {
fn from(script: Option<subtags::Script>) -> Self {
@@ -468,14 +466,10 @@ impl From<Option<subtags::Script>> for Locale {
/// # Examples
///
/// ```
-/// use icu::locid::subtags_region as region;
/// use icu::locid::Locale;
+/// use icu::locid::{locale, subtags_region as region};
///
-/// let region = region!("US");
-/// let loc = Locale::from(Some(region));
-///
-/// assert_eq!(loc.id.region.unwrap(), region);
-/// assert_eq!(loc.to_string(), "und-US");
+/// assert_eq!(Locale::from(Some(region!("US"))), locale!("und-US"));
/// ```
impl From<Option<subtags::Region>> for Locale {
fn from(region: Option<subtags::Region>) -> Self {
@@ -491,20 +485,18 @@ impl From<Option<subtags::Region>> for Locale {
/// ```
/// use icu::locid::Locale;
/// use icu::locid::{
-/// subtags_language as language, subtags_region as region,
+/// locale, subtags_language as language, subtags_region as region,
/// subtags_script as script,
/// };
///
-/// let lang = language!("en");
-/// let script = script!("Latn");
-/// let region = region!("US");
-/// let loc = Locale::from((lang, Some(script), Some(region)));
-///
-/// assert_eq!(loc.id.language, lang);
-/// assert_eq!(loc.id.script.unwrap(), script);
-/// assert_eq!(loc.id.region.unwrap(), region);
-/// assert_eq!(loc.id.variants.len(), 0);
-/// assert_eq!(loc.to_string(), "en-Latn-US");
+/// assert_eq!(
+/// Locale::from((
+/// language!("en"),
+/// Some(script!("Latn")),
+/// Some(region!("US"))
+/// )),
+/// locale!("en-Latn-US")
+/// );
/// ```
impl
From<(
diff --git a/vendor/icu_locid/src/parser/errors.rs b/vendor/icu_locid/src/parser/errors.rs
index a989bcc60..5cbbb2bd4 100644
--- a/vendor/icu_locid/src/parser/errors.rs
+++ b/vendor/icu_locid/src/parser/errors.rs
@@ -48,6 +48,22 @@ pub enum ParserError {
/// ```
#[displaydoc("Invalid extension")]
InvalidExtension,
+
+ /// Duplicated extension.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use icu::locid::Locale;
+ /// use icu::locid::ParserError;
+ ///
+ /// assert_eq!(
+ /// "und-u-hc-h12-u-ca-calendar".parse::<Locale>(),
+ /// Err(ParserError::DuplicatedExtension)
+ /// );
+ /// ```
+ #[displaydoc("Duplicated extension")]
+ DuplicatedExtension,
}
#[cfg(feature = "std")]
diff --git a/vendor/icu_locid/src/parser/langid.rs b/vendor/icu_locid/src/parser/langid.rs
index 9efa078ac..653ca7e6e 100644
--- a/vendor/icu_locid/src/parser/langid.rs
+++ b/vendor/icu_locid/src/parser/langid.rs
@@ -5,7 +5,7 @@
pub use super::errors::ParserError;
use crate::extensions::unicode::{Attribute, Key, Value};
use crate::extensions::ExtensionType;
-use crate::parser::{get_subtag_iterator, SubtagIterator};
+use crate::parser::SubtagIterator;
use crate::LanguageIdentifier;
use crate::{extensions, subtags};
use alloc::vec::Vec;
@@ -103,7 +103,7 @@ pub fn parse_language_identifier(
t: &[u8],
mode: ParserMode,
) -> Result<LanguageIdentifier, ParserError> {
- let mut iter = get_subtag_iterator(t);
+ let mut iter = SubtagIterator::new(t);
parse_language_identifier_from_iter(&mut iter, mode)
}
@@ -127,9 +127,9 @@ pub const fn parse_locale_with_single_variant_single_keyword_unicode_extension_f
let mut variant = None;
let mut keyword = None;
- if let (i, Some((t, start, end))) = iter.next_manual() {
+ if let (i, Some((start, end))) = iter.next_manual() {
iter = i;
- match subtags::Language::try_from_bytes_manual_slice(t, start, end) {
+ match subtags::Language::try_from_bytes_manual_slice(iter.slice, start, end) {
Ok(l) => language = l,
Err(e) => return Err(e),
}
@@ -139,19 +139,23 @@ pub const fn parse_locale_with_single_variant_single_keyword_unicode_extension_f
let mut position = ParserPosition::Script;
- while let Some((t, start, end)) = iter.peek_manual() {
+ while let Some((start, end)) = iter.peek_manual() {
if !matches!(mode, ParserMode::LanguageIdentifier) && end - start == 1 {
break;
}
if matches!(position, ParserPosition::Script) {
- if let Ok(s) = subtags::Script::try_from_bytes_manual_slice(t, start, end) {
+ if let Ok(s) = subtags::Script::try_from_bytes_manual_slice(iter.slice, start, end) {
script = Some(s);
position = ParserPosition::Region;
- } else if let Ok(r) = subtags::Region::try_from_bytes_manual_slice(t, start, end) {
+ } else if let Ok(r) =
+ subtags::Region::try_from_bytes_manual_slice(iter.slice, start, end)
+ {
region = Some(r);
position = ParserPosition::Variant;
- } else if let Ok(v) = subtags::Variant::try_from_bytes_manual_slice(t, start, end) {
+ } else if let Ok(v) =
+ subtags::Variant::try_from_bytes_manual_slice(iter.slice, start, end)
+ {
// We cannot handle multiple variants in a const context
debug_assert!(variant.is_none());
variant = Some(v);
@@ -162,10 +166,12 @@ pub const fn parse_locale_with_single_variant_single_keyword_unicode_extension_f
return Err(ParserError::InvalidSubtag);
}
} else if matches!(position, ParserPosition::Region) {
- if let Ok(s) = subtags::Region::try_from_bytes_manual_slice(t, start, end) {
+ if let Ok(s) = subtags::Region::try_from_bytes_manual_slice(iter.slice, start, end) {
region = Some(s);
position = ParserPosition::Variant;
- } else if let Ok(v) = subtags::Variant::try_from_bytes_manual_slice(t, start, end) {
+ } else if let Ok(v) =
+ subtags::Variant::try_from_bytes_manual_slice(iter.slice, start, end)
+ {
// We cannot handle multiple variants in a const context
debug_assert!(variant.is_none());
variant = Some(v);
@@ -175,7 +181,8 @@ pub const fn parse_locale_with_single_variant_single_keyword_unicode_extension_f
} else {
return Err(ParserError::InvalidSubtag);
}
- } else if let Ok(v) = subtags::Variant::try_from_bytes_manual_slice(t, start, end) {
+ } else if let Ok(v) = subtags::Variant::try_from_bytes_manual_slice(iter.slice, start, end)
+ {
debug_assert!(matches!(position, ParserPosition::Variant));
if variant.is_some() {
// We cannot handle multiple variants in a const context
@@ -192,12 +199,12 @@ pub const fn parse_locale_with_single_variant_single_keyword_unicode_extension_f
}
if matches!(mode, ParserMode::Locale) {
- if let Some((bytes, start, end)) = iter.peek_manual() {
- match ExtensionType::try_from_bytes_manual_slice(bytes, start, end) {
+ if let Some((start, end)) = iter.peek_manual() {
+ match ExtensionType::try_from_bytes_manual_slice(iter.slice, start, end) {
Ok(ExtensionType::Unicode) => {
iter = iter.next_manual().0;
- if let Some((bytes, start, end)) = iter.peek_manual() {
- if Attribute::try_from_bytes_manual_slice(bytes, start, end).is_ok() {
+ if let Some((start, end)) = iter.peek_manual() {
+ if Attribute::try_from_bytes_manual_slice(iter.slice, start, end).is_ok() {
// We cannot handle Attributes in a const context
return Err(ParserError::InvalidSubtag);
}
@@ -206,19 +213,21 @@ pub const fn parse_locale_with_single_variant_single_keyword_unicode_extension_f
let mut key = None;
let mut current_type = None;
- while let Some((bytes, start, end)) = iter.peek_manual() {
+ while let Some((start, end)) = iter.peek_manual() {
let slen = end - start;
if slen == 2 {
if key.is_some() {
// We cannot handle more than one Key in a const context
return Err(ParserError::InvalidSubtag);
}
- match Key::try_from_bytes_manual_slice(bytes, start, end) {
+ match Key::try_from_bytes_manual_slice(iter.slice, start, end) {
Ok(k) => key = Some(k),
Err(e) => return Err(e),
};
} else if key.is_some() {
- match Value::parse_subtag_from_bytes_manual_slice(bytes, start, end) {
+ match Value::parse_subtag_from_bytes_manual_slice(
+ iter.slice, start, end,
+ ) {
Ok(Some(t)) => {
if current_type.is_some() {
// We cannot handle more than one type in a const context
@@ -261,7 +270,7 @@ pub const fn parse_language_identifier_with_single_variant(
),
ParserError,
> {
- let iter = get_subtag_iterator(t);
+ let iter = SubtagIterator::new(t);
match parse_locale_with_single_variant_single_keyword_unicode_extension_from_iter(iter, mode) {
Ok((l, s, r, v, _)) => Ok((l, s, r, v)),
Err(e) => Err(e),
diff --git a/vendor/icu_locid/src/parser/locale.rs b/vendor/icu_locid/src/parser/locale.rs
index 805b6c290..175fd3a05 100644
--- a/vendor/icu_locid/src/parser/locale.rs
+++ b/vendor/icu_locid/src/parser/locale.rs
@@ -6,13 +6,13 @@ use tinystr::TinyAsciiStr;
use crate::extensions::{self, Extensions};
use crate::parser::errors::ParserError;
-use crate::parser::{get_subtag_iterator, parse_language_identifier_from_iter, ParserMode};
+use crate::parser::{parse_language_identifier_from_iter, ParserMode, SubtagIterator};
use crate::{subtags, Locale};
use super::parse_locale_with_single_variant_single_keyword_unicode_extension_from_iter;
pub fn parse_locale(t: &[u8]) -> Result<Locale, ParserError> {
- let mut iter = get_subtag_iterator(t);
+ let mut iter = SubtagIterator::new(t);
let id = parse_language_identifier_from_iter(&mut iter, ParserMode::Locale)?;
let extensions = if iter.peek().is_some() {
@@ -37,6 +37,6 @@ pub const fn parse_locale_with_single_variant_single_keyword_unicode_keyword_ext
),
ParserError,
> {
- let iter = get_subtag_iterator(t);
+ let iter = SubtagIterator::new(t);
parse_locale_with_single_variant_single_keyword_unicode_extension_from_iter(iter, mode)
}
diff --git a/vendor/icu_locid/src/parser/mod.rs b/vendor/icu_locid/src/parser/mod.rs
index fef10b0ab..4b02f71c9 100644
--- a/vendor/icu_locid/src/parser/mod.rs
+++ b/vendor/icu_locid/src/parser/mod.rs
@@ -17,72 +17,93 @@ pub use locale::{
parse_locale, parse_locale_with_single_variant_single_keyword_unicode_keyword_extension,
};
-pub const fn get_subtag_iterator(slice: &[u8]) -> SubtagIterator {
- let mut current_start = 0;
+#[inline]
+const fn is_separator(slice: &[u8], idx: usize) -> bool {
#[allow(clippy::indexing_slicing)]
- while current_start < slice.len()
- && (slice[current_start] == b'-' || slice[current_start] == b'_')
- {
- current_start += 1;
- }
- let mut current_end = current_start;
- #[allow(clippy::indexing_slicing)]
- while current_end < slice.len() && slice[current_end] != b'-' && slice[current_end] != b'_' {
- current_end += 1;
- }
- SubtagIterator {
- slice,
- current_start,
- current_end,
+ let b = slice[idx];
+ b == b'-' || b == b'_'
+}
+
+const fn get_current_subtag(slice: &[u8], idx: usize) -> (usize, usize) {
+ debug_assert!(idx < slice.len());
+
+ // This function is called only on the idx == 0 or on a separator.
+ let (start, mut end) = if is_separator(slice, idx) {
+ // If it's a separator, set the start to idx+1 and advance the idx to the next char.
+ (idx + 1, idx + 1)
+ } else {
+ // If it's idx=0, start is 0 and end is set to 1
+ debug_assert!(idx == 0);
+ (0, 1)
+ };
+
+ while end < slice.len() && !is_separator(slice, end) {
+ // Advance until we reach end of slice or a separator.
+ end += 1;
}
+ // Notice: this slice may be empty (start == end) for cases like `"en-"` or `"en--US"`
+ (start, end)
}
+// `SubtagIterator` is a helper iterator for [`LanguageIdentifier`] and [`Locale`] parsing.
+//
+// It is quite extraordinary due to focus on performance and Rust limitations for `const`
+// functions.
+//
+// The iterator is eager and fallible allowing it to reject invalid slices such as `"-"`, `"-en"`,
+// `"en-"` etc.
+//
+// The iterator provides methods available for static users - `next_manual` and `peek_manual`,
+// as well as typical `Peekable` iterator APIs - `next` and `peek`.
+//
+// All methods return an `Option` of a `Result`.
#[derive(Copy, Clone, Debug)]
pub struct SubtagIterator<'a> {
- slice: &'a [u8],
- current_start: usize,
- current_end: usize,
+ pub slice: &'a [u8],
+ done: bool,
+ // done + subtag is faster than Option<(usize, usize)>
+ // at the time of writing.
+ subtag: (usize, usize),
}
-pub type ManualSlice<'a> = (&'a [u8], usize, usize);
-
impl<'a> SubtagIterator<'a> {
- pub const fn next_manual(mut self) -> (Self, Option<ManualSlice<'a>>) {
- if self.current_start == self.current_end {
- (self, None)
+ pub const fn new(slice: &'a [u8]) -> Self {
+ let subtag = if slice.is_empty() || is_separator(slice, 0) {
+ // This returns (0, 0) which returns Some(b"") for slices like `"-en"` or `"-"`
+ (0, 0)
} else {
- let r = (self.slice, self.current_start, self.current_end);
- self.current_start = self.current_end;
- #[allow(clippy::indexing_slicing)]
- while self.current_start < self.slice.len()
- && (self.slice[self.current_start] == b'-'
- || self.slice[self.current_start] == b'_')
- {
- self.current_start += 1;
- }
- self.current_end = self.current_start;
- #[allow(clippy::indexing_slicing)]
- while self.current_end < self.slice.len()
- && self.slice[self.current_end] != b'-'
- && self.slice[self.current_end] != b'_'
- {
- self.current_end += 1;
- }
- (self, Some(r))
+ get_current_subtag(slice, 0)
+ };
+ Self {
+ slice,
+ done: false,
+ subtag,
}
}
- pub const fn peek_manual(&self) -> Option<ManualSlice<'a>> {
- if self.current_start == self.current_end {
- None
+ pub const fn next_manual(mut self) -> (Self, Option<(usize, usize)>) {
+ if self.done {
+ return (self, None);
+ }
+ let result = self.subtag;
+ if result.1 < self.slice.len() {
+ self.subtag = get_current_subtag(self.slice, result.1);
} else {
- Some((self.slice, self.current_start, self.current_end))
+ self.done = true;
}
+ (self, Some(result))
+ }
+
+ pub const fn peek_manual(&self) -> Option<(usize, usize)> {
+ if self.done {
+ return None;
+ }
+ Some(self.subtag)
}
pub fn peek(&self) -> Option<&'a [u8]> {
#[allow(clippy::indexing_slicing)] // peek_manual returns valid indices
- self.peek_manual().map(|(t, s, e)| &t[s..e])
+ self.peek_manual().map(|(s, e)| &self.slice[s..e])
}
}
@@ -91,8 +112,120 @@ impl<'a> Iterator for SubtagIterator<'a> {
fn next(&mut self) -> Option<Self::Item> {
let (s, res) = self.next_manual();
- self.clone_from(&s);
+ *self = s;
#[allow(clippy::indexing_slicing)] // next_manual returns valid indices
- res.map(|(t, s, e)| &t[s..e])
+ res.map(|(s, e)| &self.slice[s..e])
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+
+ fn slice_to_str(input: &[u8]) -> &str {
+ std::str::from_utf8(input).unwrap()
+ }
+
+ #[test]
+ fn subtag_iterator_peek_test() {
+ let slice = "de_at-u-ca-foobar";
+ let mut si = SubtagIterator::new(slice.as_bytes());
+
+ assert_eq!(si.peek().map(slice_to_str), Some("de"));
+ assert_eq!(si.peek().map(slice_to_str), Some("de"));
+ assert_eq!(si.next().map(slice_to_str), Some("de"));
+
+ assert_eq!(si.peek().map(slice_to_str), Some("at"));
+ assert_eq!(si.peek().map(slice_to_str), Some("at"));
+ assert_eq!(si.next().map(slice_to_str), Some("at"));
+ }
+
+ #[test]
+ fn subtag_iterator_test() {
+ let slice = "";
+ let mut si = SubtagIterator::new(slice.as_bytes());
+ assert_eq!(si.next().map(slice_to_str), Some(""));
+
+ let slice = "-";
+ let mut si = SubtagIterator::new(slice.as_bytes());
+ assert_eq!(si.next().map(slice_to_str), Some(""));
+
+ let slice = "-en";
+ let mut si = SubtagIterator::new(slice.as_bytes());
+ assert_eq!(si.next().map(slice_to_str), Some(""));
+ assert_eq!(si.next().map(slice_to_str), Some("en"));
+ assert_eq!(si.next(), None);
+
+ let slice = "en";
+ let si = SubtagIterator::new(slice.as_bytes());
+ assert_eq!(si.map(slice_to_str).collect::<Vec<_>>(), vec!["en",]);
+
+ let slice = "en-";
+ let si = SubtagIterator::new(slice.as_bytes());
+ assert_eq!(si.map(slice_to_str).collect::<Vec<_>>(), vec!["en", "",]);
+
+ let slice = "--";
+ let mut si = SubtagIterator::new(slice.as_bytes());
+ assert_eq!(si.next().map(slice_to_str), Some(""));
+ assert_eq!(si.next().map(slice_to_str), Some(""));
+ assert_eq!(si.next().map(slice_to_str), Some(""));
+ assert_eq!(si.next(), None);
+
+ let slice = "-en-";
+ let mut si = SubtagIterator::new(slice.as_bytes());
+ assert_eq!(si.next().map(slice_to_str), Some(""));
+ assert_eq!(si.next().map(slice_to_str), Some("en"));
+ assert_eq!(si.next().map(slice_to_str), Some(""));
+ assert_eq!(si.next(), None);
+
+ let slice = "de_at-u-ca-foobar";
+ let si = SubtagIterator::new(slice.as_bytes());
+ assert_eq!(
+ si.map(slice_to_str).collect::<Vec<_>>(),
+ vec!["de", "at", "u", "ca", "foobar",]
+ );
+ }
+
+ #[test]
+ fn get_current_subtag_test() {
+ let slice = "-";
+ let current = get_current_subtag(slice.as_bytes(), 0);
+ assert_eq!(current, (1, 1));
+
+ let slice = "-en";
+ let current = get_current_subtag(slice.as_bytes(), 0);
+ assert_eq!(current, (1, 3));
+
+ let slice = "-en-";
+ let current = get_current_subtag(slice.as_bytes(), 3);
+ assert_eq!(current, (4, 4));
+
+ let slice = "en-";
+ let current = get_current_subtag(slice.as_bytes(), 0);
+ assert_eq!(current, (0, 2));
+
+ let current = get_current_subtag(slice.as_bytes(), 2);
+ assert_eq!(current, (3, 3));
+
+ let slice = "en--US";
+ let current = get_current_subtag(slice.as_bytes(), 0);
+ assert_eq!(current, (0, 2));
+
+ let current = get_current_subtag(slice.as_bytes(), 2);
+ assert_eq!(current, (3, 3));
+
+ let current = get_current_subtag(slice.as_bytes(), 3);
+ assert_eq!(current, (4, 6));
+
+ let slice = "--";
+ let current = get_current_subtag(slice.as_bytes(), 0);
+ assert_eq!(current, (1, 1));
+
+ let current = get_current_subtag(slice.as_bytes(), 1);
+ assert_eq!(current, (2, 2));
+
+ let slice = "-";
+ let current = get_current_subtag(slice.as_bytes(), 0);
+ assert_eq!(current, (1, 1));
}
}
diff --git a/vendor/icu_locid/src/subtags/language.rs b/vendor/icu_locid/src/subtags/language.rs
index a5ec8d76e..86b51b93a 100644
--- a/vendor/icu_locid/src/subtags/language.rs
+++ b/vendor/icu_locid/src/subtags/language.rs
@@ -55,7 +55,6 @@ impl Language {
/// use icu::locid::subtags::Language;
///
/// assert_eq!(Language::default(), Language::UND);
- /// assert_eq!("und", Language::UND.to_string());
/// ```
pub const UND: Self = unsafe { Self::from_raw_unchecked(*b"und") };
@@ -64,15 +63,15 @@ impl Language {
/// # Examples
///
/// ```
- /// use icu::locid::subtags::Language;
+ /// use icu::locid::{subtags::Language, subtags_language as language};
///
- /// let mut lang: Language = "csb".parse().expect("Parsing failed.");
+ /// let mut lang = language!("csb");
///
- /// assert_eq!(lang.as_str(), "csb");
+ /// assert_ne!(lang, Language::UND);
///
/// lang.clear();
///
- /// assert_eq!(lang.as_str(), "und");
+ /// assert_eq!(lang, Language::UND);
/// ```
#[inline]
pub fn clear(&mut self) {
@@ -86,7 +85,7 @@ impl Language {
/// ```
/// use icu::locid::subtags::Language;
///
- /// let mut lang: Language = "und".parse().expect("Parsing failed.");
+ /// let mut lang = Language::UND;
///
/// assert!(lang.is_empty());
///
diff --git a/vendor/icu_locid/src/subtags/variants.rs b/vendor/icu_locid/src/subtags/variants.rs
index bbff9ebac..3bd83f149 100644
--- a/vendor/icu_locid/src/subtags/variants.rs
+++ b/vendor/icu_locid/src/subtags/variants.rs
@@ -16,14 +16,9 @@ use core::ops::Deref;
/// # Examples
///
/// ```
-/// use icu::locid::subtags::{Variant, Variants};
+/// use icu::locid::{subtags::Variants, subtags_variant as variant};
///
-/// let variant1: Variant =
-/// "posix".parse().expect("Failed to parse a variant subtag.");
-///
-/// let variant2: Variant =
-/// "macos".parse().expect("Failed to parse a variant subtag.");
-/// let mut v = vec![variant1, variant2];
+/// let mut v = vec![variant!("posix"), variant!("macos")];
/// v.sort();
/// v.dedup();
///
@@ -53,10 +48,9 @@ impl Variants {
/// # Examples
///
/// ```
- /// use icu::locid::subtags::{Variant, Variants};
+ /// use icu::locid::{subtags::Variants, subtags_variant as variant};
///
- /// let variant: Variant = "posix".parse().expect("Parsing failed.");
- /// let variants = Variants::from_variant(variant);
+ /// let variants = Variants::from_variant(variant!("posix"));
/// ```
#[inline]
pub const fn from_variant(variant: Variant) -> Self {
@@ -70,11 +64,9 @@ impl Variants {
/// # Examples
///
/// ```
- /// use icu::locid::subtags::{Variant, Variants};
+ /// use icu::locid::{subtags::Variants, subtags_variant as variant};
///
- /// let variant1: Variant = "posix".parse().expect("Parsing failed.");
- /// let variant2: Variant = "macos".parse().expect("Parsing failed.");
- /// let mut v = vec![variant1, variant2];
+ /// let mut v = vec![variant!("posix"), variant!("macos")];
/// v.sort();
/// v.dedup();
///
@@ -95,11 +87,9 @@ impl Variants {
/// # Examples
///
/// ```
- /// use icu::locid::subtags::{Variant, Variants};
+ /// use icu::locid::{subtags::Variants, subtags_variant as variant};
///
- /// let variant1: Variant = "posix".parse().expect("Parsing failed.");
- /// let variant2: Variant = "macos".parse().expect("Parsing failed.");
- /// let mut v = vec![variant1, variant2];
+ /// let mut v = vec![variant!("posix"), variant!("macos")];
/// v.sort();
/// v.dedup();
///
@@ -109,7 +99,7 @@ impl Variants {
///
/// variants.clear();
///
- /// assert_eq!(variants.to_string(), "");
+ /// assert_eq!(variants, Variants::default());
/// ```
pub fn clear(&mut self) -> Self {
core::mem::take(self)