Skip to content

Commit

Permalink
Move generic Subtag to subtags (#4932)
Browse files Browse the repository at this point in the history
First patch from the #1833 patchset.

It moves the `Subtag` to `locid::subtags::Subtag` - this is a generic
subtag (2..=8, separate from private::Subtag which is 1..=8) which will
be used to iterate over multi-part subtags like
`extensions::unicode::Value` to retrieve specific items without relying
on the underlying `TinyStr` storage.

It's a breaking change because we are removing
`extensions::other::Subtag`. I assume this is ok as we're working on 2.0
so I don't need to add alias and deprecate it, but please confirm.
  • Loading branch information
zbraniecki authored May 29, 2024
1 parent 418ae99 commit de39054
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 43 deletions.
11 changes: 5 additions & 6 deletions components/locid/src/extensions/other/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,11 @@
//! let mut loc: Locale = "en-US-a-foo-faa".parse().expect("Parsing failed.");
//! ```
mod subtag;

use crate::parser::ParserError;
use crate::parser::SubtagIterator;
use crate::shortvec::ShortBoxSlice;
use crate::subtags::Subtag;
use alloc::vec::Vec;
#[doc(inline)]
pub use subtag::{subtag, Subtag};

/// A list of [`Other Use Extensions`] as defined in [`Unicode Locale
/// Identifier`] specification.
Expand All @@ -37,7 +34,8 @@ pub use subtag::{subtag, Subtag};
/// # Examples
///
/// ```
/// use icu::locid::extensions::other::{Other, Subtag};
/// use icu::locid::extensions::other::Other;
/// use icu::locid::subtags::Subtag;
///
/// let subtag1: Subtag = "foo".parse().expect("Failed to parse a Subtag.");
/// let subtag2: Subtag = "bar".parse().expect("Failed to parse a Subtag.");
Expand All @@ -64,7 +62,8 @@ impl Other {
/// # Examples
///
/// ```
/// use icu::locid::extensions::other::{Other, Subtag};
/// use icu::locid::extensions::other::Other;
/// use icu::locid::subtags::Subtag;
///
/// let subtag1: Subtag = "foo".parse().expect("Failed to parse a Subtag.");
/// let subtag2: Subtag = "bar".parse().expect("Failed to parse a Subtag.");
Expand Down
36 changes: 0 additions & 36 deletions components/locid/src/extensions/other/subtag.rs

This file was deleted.

17 changes: 16 additions & 1 deletion components/locid/src/extensions/private/other.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,25 @@ impl_tinystr_subtag!(
/// ```
/// use icu::locid::extensions::private::Subtag;
///
/// let subtag1: Subtag = "Foo".parse().expect("Failed to parse a Subtag.");
/// let subtag1: Subtag = "Foo".parse()
/// .expect("Failed to parse a Subtag.");
///
/// assert_eq!(subtag1.as_str(), "foo");
/// ```
///
/// Notice: This is different from the generic [`Subtag`](crate::subtags::Subtag)
/// which is between two and eight characters.
///
/// ```
/// use icu::locid::extensions::private;
/// use icu::locid::subtags;
///
/// let subtag: Result<private::Subtag, _> = "f".parse();
/// assert!(subtag.is_ok());
///
/// let subtag: Result<subtags::Subtag, _> = "f".parse();
/// assert!(subtag.is_err());
/// ```
Subtag,
extensions::private,
subtag,
Expand Down
65 changes: 65 additions & 0 deletions components/locid/src/subtags/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,68 @@ pub use script::{script, Script};
#[doc(inline)]
pub use variant::{variant, Variant};
pub use variants::Variants;

impl_tinystr_subtag!(
/// A generic subtag.
///
/// The subtag has to be an ASCII alphanumerical string no shorter than
/// two characters and no longer than eight.
///
/// # Examples
///
/// ```
/// use icu::locid::subtags::Subtag;
///
/// let subtag1: Subtag = "Foo".parse()
/// .expect("Failed to parse a Subtag.");
///
/// assert_eq!(subtag1.as_str(), "foo");
/// ```
Subtag,
subtags,
subtag,
subtags_subtag,
2..=8,
s,
s.is_ascii_alphanumeric(),
s.to_ascii_lowercase(),
s.is_ascii_alphanumeric() && s.is_ascii_lowercase(),
InvalidSubtag,
["foo12"],
["f", "toolooong"],
);

impl Subtag {
pub(crate) const fn valid_key(v: &[u8]) -> bool {
2 <= v.len() && v.len() <= 8
}
}

impl<const N: usize> TryFrom<tinystr::TinyAsciiStr<N>> for Subtag {
type Error = crate::parser::errors::ParserError;

fn try_from(value: tinystr::TinyAsciiStr<N>) -> Result<Self, Self::Error> {
Self::try_from_bytes(value.as_bytes())
}
}

#[cfg(test)]
mod tests {
use super::*;
use tinystr::tinystr;

#[test]
fn test_subtag() {
let subtag = subtag!("foo");
assert_eq!(subtag.as_str(), "foo");
}

#[test]
fn test_subtag_from_tinystr() {
let subtag = Subtag::try_from(tinystr!(3, "foo"));
assert!(subtag.is_ok());

let subtag = Subtag::try_from(tinystr!(1, "f"));
assert!(subtag.is_err());
}
}

0 comments on commit de39054

Please sign in to comment.