1// This file is part of ICU4X. For terms of use, please see the file
2// called LICENSE at the top level of the ICU4X source tree
3// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
4
5//! Private Use Extensions is a list of extensions intended for
6//! private use.
7//!
8//! Those extensions are treated as a pass-through, and no Unicode related
9//! behavior depends on them.
10//!
11//! The main struct for this extension is [`Private`] which is a list of [`Subtag`]s.
12//!
13//! # Examples
14//!
15//! ```
16//! use icu::locid::extensions::private::subtag;
17//! use icu::locid::{locale, Locale};
18//!
19//! let mut loc: Locale = "en-US-x-foo-faa".parse().expect("Parsing failed.");
20//!
21//! assert!(loc.extensions.private.contains(&subtag!("foo")));
22//! assert_eq!(loc.extensions.private.iter().next(), Some(&subtag!("foo")));
23//!
24//! loc.extensions.private.clear();
25//!
26//! assert!(loc.extensions.private.is_empty());
27//! assert_eq!(loc, locale!("en-US"));
28//! ```
29
30mod other;
31
32use alloc::vec::Vec;
33use core::ops::Deref;
34
35#[doc(inline)]
36pub use other::{subtag, Subtag};
37
38use crate::helpers::ShortSlice;
39use crate::parser::ParserError;
40use crate::parser::SubtagIterator;
41
42/// A list of [`Private Use Extensions`] as defined in [`Unicode Locale
43/// Identifier`] specification.
44///
45/// Those extensions are treated as a pass-through, and no Unicode related
46/// behavior depends on them.
47///
48/// # Examples
49///
50/// ```
51/// use icu::locid::extensions::private::{Private, Subtag};
52///
53/// let subtag1: Subtag = "foo".parse().expect("Failed to parse a Subtag.");
54/// let subtag2: Subtag = "bar".parse().expect("Failed to parse a Subtag.");
55///
56/// let private = Private::from_vec_unchecked(vec![subtag1, subtag2]);
57/// assert_eq!(&private.to_string(), "x-foo-bar");
58/// ```
59///
60/// [`Private Use Extensions`]: https://unicode.org/reports/tr35/#pu_extensions
61/// [`Unicode Locale Identifier`]: https://unicode.org/reports/tr35/#Unicode_locale_identifier
62#[derive(Clone, PartialEq, Eq, Debug, Default, Hash, PartialOrd, Ord)]
63pub struct Private(ShortSlice<Subtag>);
64
65impl Private {
66 /// Returns a new empty list of private-use extensions. Same as [`default()`](Default::default()), but is `const`.
67 ///
68 /// # Examples
69 ///
70 /// ```
71 /// use icu::locid::extensions::private::Private;
72 ///
73 /// assert_eq!(Private::new(), Private::default());
74 /// ```
75 #[inline]
76 pub const fn new() -> Self {
77 Self(ShortSlice::new())
78 }
79
80 /// A constructor which takes a pre-sorted list of [`Subtag`].
81 ///
82 /// # Examples
83 ///
84 /// ```
85 /// use icu::locid::extensions::private::{Private, Subtag};
86 ///
87 /// let subtag1: Subtag = "foo".parse().expect("Failed to parse a Subtag.");
88 /// let subtag2: Subtag = "bar".parse().expect("Failed to parse a Subtag.");
89 ///
90 /// let private = Private::from_vec_unchecked(vec![subtag1, subtag2]);
91 /// assert_eq!(&private.to_string(), "x-foo-bar");
92 /// ```
93 pub fn from_vec_unchecked(input: Vec<Subtag>) -> Self {
94 Self(input.into())
95 }
96
97 /// Empties the [`Private`] list.
98 ///
99 /// # Examples
100 ///
101 /// ```
102 /// use icu::locid::extensions::private::{Private, Subtag};
103 ///
104 /// let subtag1: Subtag = "foo".parse().expect("Failed to parse a Subtag.");
105 /// let subtag2: Subtag = "bar".parse().expect("Failed to parse a Subtag.");
106 /// let mut private = Private::from_vec_unchecked(vec![subtag1, subtag2]);
107 ///
108 /// assert_eq!(&private.to_string(), "x-foo-bar");
109 ///
110 /// private.clear();
111 ///
112 /// assert_eq!(private, Private::new());
113 /// ```
114 pub fn clear(&mut self) {
115 self.0.clear();
116 }
117
118 pub(crate) fn try_from_iter(iter: &mut SubtagIterator) -> Result<Self, ParserError> {
119 let keys = iter
120 .map(Subtag::try_from_bytes)
121 .collect::<Result<ShortSlice<_>, _>>()?;
122
123 Ok(Self(keys))
124 }
125
126 pub(crate) fn for_each_subtag_str<E, F>(&self, f: &mut F) -> Result<(), E>
127 where
128 F: FnMut(&str) -> Result<(), E>,
129 {
130 if self.is_empty() {
131 return Ok(());
132 }
133 f("x")?;
134 self.deref().iter().map(|t| t.as_str()).try_for_each(f)
135 }
136}
137
138writeable::impl_display_with_writeable!(Private);
139
140impl writeable::Writeable for Private {
141 fn write_to<W: core::fmt::Write + ?Sized>(&self, sink: &mut W) -> core::fmt::Result {
142 if self.is_empty() {
143 return Ok(());
144 }
145 sink.write_str("x")?;
146 for key: &Subtag in self.iter() {
147 sink.write_char('-')?;
148 writeable::Writeable::write_to(self:key, sink)?;
149 }
150 Ok(())
151 }
152
153 fn writeable_length_hint(&self) -> writeable::LengthHint {
154 if self.is_empty() {
155 return writeable::LengthHint::exact(0);
156 }
157 let mut result: LengthHint = writeable::LengthHint::exact(1);
158 for key: &Subtag in self.iter() {
159 result += writeable::Writeable::writeable_length_hint(self:key) + 1;
160 }
161 result
162 }
163}
164
165impl Deref for Private {
166 type Target = [Subtag];
167
168 fn deref(&self) -> &Self::Target {
169 self.0.deref()
170 }
171}
172