Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
robertbastian committed May 23, 2022
1 parent 326c2f2 commit b2796ef
Show file tree
Hide file tree
Showing 15 changed files with 462 additions and 1,699 deletions.
6 changes: 3 additions & 3 deletions components/locale_canonicalizer/src/locale_canonicalizer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use icu_locid::{
LanguageIdentifier, Locale,
};
use icu_provider::prelude::*;
use tinystr::{tinystr, TinyAsciiStr};
use tinystr::TinyAsciiStr;

/// Used to track the result of a canonicalization operation that potentially modifies its argument in place.
#[derive(Debug, PartialEq)]
Expand Down Expand Up @@ -305,8 +305,8 @@ impl LocaleCanonicalizer {
// The `rg` region override and `sd` regional subdivision keys may contain
// language codes that require canonicalization.
let extension_keys = vec![
Key::from_tinystr_unchecked(tinystr!(2, "rg")),
Key::from_tinystr_unchecked(tinystr!(2, "sd")),
icu_locid::unicode_ext_key!("rg"),
icu_locid::unicode_ext_key!("sd"),
];
let aliases: DataPayload<AliasesV1Marker> = provider
.load_resource(&DataRequest::default())?
Expand Down
98 changes: 20 additions & 78 deletions components/locid/src/extensions/other/key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,95 +2,37 @@
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).

use core::ops::RangeInclusive;
use core::str::FromStr;

use crate::parser::errors::ParserError;
use tinystr::TinyAsciiStr;

/// A single item used in a list of [`Other`](super::Other) extensions.
///
/// The key has to be an ASCII alphanumerical string no shorter than
/// two characters and no longer than eight.
///
/// # Examples
///
/// ```
/// use icu::locid::extensions::other::Key;
///
/// let key1: Key = "Foo".parse().expect("Failed to parse a Key.");
///
/// assert_eq!(key1.as_str(), "foo");
/// ```
#[derive(Debug, PartialEq, Eq, Clone, Hash, PartialOrd, Ord, Copy)]
pub struct Key(TinyAsciiStr<{ *KEY_LENGTH.end() }>);

const KEY_LENGTH: RangeInclusive<usize> = 2..=8;

impl Key {
#[allow(missing_docs)] // TODO(#1028) - Add missing docs.
pub fn valid_key(v: &[u8]) -> bool {
KEY_LENGTH.contains(&v.len())
}

/// A constructor which takes a utf8 slice, parses it and
/// produces a well-formed [`Key`].
impl_tinystr_subtag!(
/// A single item used in a list of [`Other`](super::Other) extensions.
///
/// # Examples
///
/// ```
/// use icu::locid::extensions::other::Key;
///
/// let key = Key::from_bytes(b"foobar").expect("Parsing failed.");
///
/// assert_eq!(key.as_str(), "foobar");
/// ```
pub fn from_bytes(v: &[u8]) -> Result<Self, ParserError> {
if !Key::valid_key(v) {
return Err(ParserError::InvalidExtension);
}

let s = TinyAsciiStr::from_bytes(v).map_err(|_| ParserError::InvalidExtension)?;

if !s.is_ascii_alphanumeric() {
return Err(ParserError::InvalidExtension);
}

Ok(Self(s.to_ascii_lowercase()))
}

/// A helper function for displaying
/// a [`Key`] as a `&`[`str`].
/// The key has to be an ASCII alphanumerical string no shorter than
/// two characters and no longer than eight.
///
/// # Examples
///
/// ```
/// use icu::locid::extensions::other::Key;
///
/// let key = Key::from_bytes(b"foobar").expect("Parsing failed.");
/// let key1: Key = "Foo".parse().expect("Failed to parse a Key.");
///
/// assert_eq!(key.as_str(), "foobar");
/// assert_eq!(key1.as_str(), "foo");
/// ```
///
/// `Notice`: For many use cases, such as comparison,
/// [`Key`] implements [`PartialEq`]`<&`[`str`]`>` which allows for direct comparisons.
pub fn as_str(&self) -> &str {
self.0.as_str()
}
}
Key,
extensions::other::Key,
2..=8,
TinyAsciiStr::is_ascii_alphanumeric,
TinyAsciiStr::to_ascii_lowercase,
TinyAsciiStr::is_ascii_lowercase,
InvalidExtension,
["foo"],
["12"],
);

impl FromStr for Key {
type Err = ParserError;

fn from_str(source: &str) -> Result<Self, Self::Err> {
Self::from_bytes(source.as_bytes())
}
}

impl_writeable_for_single_subtag!(Key, "foobar");

impl PartialEq<&str> for Key {
fn eq(&self, other: &&str) -> bool {
self.as_str() == *other
impl Key {
#[allow(missing_docs)] // TODO(#1028) - Add missing docs.
pub const fn valid_key(v: &[u8]) -> bool {
2 <= v.len() && v.len() <= 8
}
}
86 changes: 16 additions & 70 deletions components/locid/src/extensions/private/key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,84 +2,30 @@
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).

use core::ops::RangeInclusive;
use core::str::FromStr;

use crate::parser::errors::ParserError;
use tinystr::TinyAsciiStr;

/// A single item used in a list of [`Private`](super::Private) extensions.
///
/// The key has to be an ASCII alphanumerical string no shorter than
/// one character and no longer than eight.
///
/// # Examples
///
/// ```
/// use icu::locid::extensions::private::Key;
///
/// let key1: Key = "Foo".parse().expect("Failed to parse a Key.");
///
/// assert_eq!(key1.as_str(), "foo");
/// ```
#[derive(Debug, PartialEq, Eq, Clone, Hash, PartialOrd, Ord, Copy)]
pub struct Key(TinyAsciiStr<{ *KEY_LENGTH.end() }>);

const KEY_LENGTH: RangeInclusive<usize> = 1..=8;

impl Key {
/// A constructor which takes a utf8 slice, parses it and
/// produces a well-formed [`Key`].
///
/// # Examples
///
/// ```
/// use icu::locid::extensions::private::Key;
///
/// let key = Key::from_bytes(b"foobar").expect("Parsing failed.");
impl_tinystr_subtag!(
/// A single item used in a list of [`Private`](super::Private) extensions.
///
/// assert_eq!(key.as_str(), "foobar");
/// ```
pub fn from_bytes(v: &[u8]) -> Result<Self, ParserError> {
if !KEY_LENGTH.contains(&v.len()) {
return Err(ParserError::InvalidExtension);
}

let s = TinyAsciiStr::from_bytes(v).map_err(|_| ParserError::InvalidExtension)?;

if !s.is_ascii_alphanumeric() {
return Err(ParserError::InvalidExtension);
}

Ok(Self(s.to_ascii_lowercase()))
}

/// A helper function for displaying
/// a [`Key`] as a `&`[`str`].
/// The key has to be an ASCII alphanumerical string no shorter than
/// one character and no longer than eight.
///
/// # Examples
///
/// ```
/// use icu::locid::extensions::private::Key;
///
/// let key = Key::from_bytes(b"foobar").expect("Parsing failed.");
/// let key1: Key = "Foo".parse().expect("Failed to parse a Key.");
///
/// assert_eq!(key.as_str(), "foobar");
/// assert_eq!(key1.as_str(), "foo");
/// ```
///
/// `Notice`: For many use cases, such as comparison,
/// [`Key`] implements [`PartialEq`]`<&`[`str`]`>` which allows for direct comparisons.
pub fn as_str(&self) -> &str {
self.0.as_str()
}
}

impl FromStr for Key {
type Err = ParserError;

fn from_str(source: &str) -> Result<Self, Self::Err> {
Self::from_bytes(source.as_bytes())
}
}

impl_writeable_for_single_subtag!(Key, "foobar");
Key,
extensions::private::Key,
1..=8,
TinyAsciiStr::is_ascii_alphanumeric,
TinyAsciiStr::to_ascii_lowercase,
TinyAsciiStr::is_ascii_lowercase,
InvalidExtension,
["foobar"],
["foobarfoo"],
);
98 changes: 21 additions & 77 deletions components/locid/src/extensions/transform/key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,93 +2,37 @@
// 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::errors::ParserError;
use core::str::FromStr;
use tinystr::TinyAsciiStr;

/// A key used in a list of [`Fields`](super::Fields).
///
/// The key has to be a two ASCII characters long, with the first
/// character being alphabetic, and the second being a number.
///
///
/// # Examples
///
/// ```
/// use icu::locid::extensions::transform::Key;
///
/// let key1: Key = "k0".parse().expect("Failed to parse a Key.");
///
/// assert_eq!(key1.as_str(), "k0");
/// ```
#[derive(Debug, PartialEq, Eq, Clone, Hash, PartialOrd, Ord, Copy)]
pub struct Key(TinyAsciiStr<KEY_LENGTH>);

const KEY_LENGTH: usize = 2;
#[inline]
const fn validate(s: &TinyAsciiStr<2>) -> bool {
s.all_bytes()[0].is_ascii_alphabetic() && s.all_bytes()[1].is_ascii_digit()
}

impl Key {
/// A constructor which takes a utf8 slice, parses it and
/// produces a well-formed [`Key`].
///
/// # Examples
///
/// ```
/// use icu::locid::extensions::transform::Key;
impl_tinystr_subtag!(
/// A key used in a list of [`Fields`](super::Fields).
///
/// let key = Key::from_bytes(b"i0").expect("Parsing failed.");
/// The key has to be a two ASCII characters long, with the first
/// character being alphabetic, and the second being a number.
///
/// assert_eq!(key, "i0");
/// ```
pub const fn from_bytes(key: &[u8]) -> Result<Self, ParserError> {
#[allow(clippy::indexing_slicing)] // TODO(#1668) Clippy exceptions need docs or fixing.
if key.len() != KEY_LENGTH || !key[0].is_ascii_alphabetic() || !key[1].is_ascii_digit() {
return Err(ParserError::InvalidExtension);
}
let tkey = match TinyAsciiStr::from_bytes(key) {
Ok(k) => k,
Err(_) => return Err(ParserError::InvalidSubtag),
};
Ok(Self(tkey.to_ascii_lowercase()))
}

/// A helper function for displaying
/// a [`Key`] as a `&`[`str`].
///
/// # Examples
///
/// ```
/// use icu::locid::extensions::transform::Key;
///
/// let key: Key = "s0".parse().expect("Parsing failed.");
/// let key1: Key = "k0".parse().expect("Failed to parse a Key.");
///
/// assert_eq!(key.as_str(), "s0");
/// assert_eq!(key1.as_str(), "k0");
/// ```
///
/// `Notice`: For many use cases, such as comparison,
/// [`Key`] implements [`PartialEq`]`<&`[`str`]`>` which allows for direct comparisons.
pub fn as_str(&self) -> &str {
self.0.as_str()
}
}

impl FromStr for Key {
type Err = ParserError;

fn from_str(source: &str) -> Result<Self, Self::Err> {
Self::from_bytes(source.as_bytes())
}
}

impl_writeable_for_single_subtag!(Key, "k0");

impl PartialEq<str> for Key {
fn eq(&self, other: &str) -> bool {
self.as_str() == other
}
}

impl PartialEq<&str> for Key {
fn eq(&self, other: &&str) -> bool {
self.as_str() == *other
}
}
Key,
extensions::transform::Key,
transform_ext_key,
2..=2,
validate,
TinyAsciiStr::to_ascii_lowercase,
TinyAsciiStr::is_ascii_lowercase,
InvalidExtension,
["i0"],
[""],
);
Loading

0 comments on commit b2796ef

Please sign in to comment.