icu_capi/
locale.rs

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#[diplomat::bridge]
6pub mod ffi {
7    use crate::errors::ffi::ICU4XError;
8    use alloc::boxed::Box;
9    use core::str;
10    use icu_locid::extensions::unicode::Key;
11    use icu_locid::subtags::{Language, Region, Script};
12    use icu_locid::Locale;
13    use writeable::Writeable;
14
15    use crate::common::ffi::ICU4XOrdering;
16
17    #[diplomat::opaque]
18    /// An ICU4X Locale, capable of representing strings like `"en-US"`.
19    #[diplomat::rust_link(icu::locid::Locale, Struct)]
20    pub struct ICU4XLocale(pub Locale);
21
22    impl ICU4XLocale {
23        /// Construct an [`ICU4XLocale`] from an locale identifier.
24        ///
25        /// This will run the complete locale parsing algorithm. If code size and
26        /// performance are critical and the locale is of a known shape (such as
27        /// `aa-BB`) use `create_und`, `set_language`, `set_script`, and `set_region`.
28        #[diplomat::rust_link(icu::locid::Locale::try_from_bytes, FnInStruct)]
29        #[diplomat::rust_link(icu::locid::Locale::from_str, FnInStruct, hidden)]
30        #[diplomat::attr(all(supports = constructors, supports = fallible_constructors, supports = named_constructors), named_constructor = "from_string")]
31        pub fn create_from_string(name: &DiplomatStr) -> Result<Box<ICU4XLocale>, ICU4XError> {
32            Ok(Box::new(ICU4XLocale(Locale::try_from_bytes(name)?)))
33        }
34
35        /// Construct a default undefined [`ICU4XLocale`] "und".
36        #[diplomat::rust_link(icu::locid::Locale::UND, AssociatedConstantInStruct)]
37        #[diplomat::attr(all(supports = constructors, supports = fallible_constructors, supports = named_constructors), named_constructor = "und")]
38        pub fn create_und() -> Box<ICU4XLocale> {
39            Box::new(ICU4XLocale(Locale::UND))
40        }
41
42        /// Clones the [`ICU4XLocale`].
43        #[diplomat::rust_link(icu::locid::Locale, Struct)]
44        pub fn clone(&self) -> Box<ICU4XLocale> {
45            Box::new(ICU4XLocale(self.0.clone()))
46        }
47
48        /// Write a string representation of the `LanguageIdentifier` part of
49        /// [`ICU4XLocale`] to `write`.
50        #[diplomat::rust_link(icu::locid::Locale::id, StructField)]
51        #[diplomat::attr(supports = accessors, getter)]
52        pub fn basename(
53            &self,
54            write: &mut diplomat_runtime::DiplomatWriteable,
55        ) -> Result<(), ICU4XError> {
56            self.0.id.write_to(write)?;
57            Ok(())
58        }
59
60        /// Write a string representation of the unicode extension to `write`
61        #[diplomat::rust_link(icu::locid::Locale::extensions, StructField)]
62        pub fn get_unicode_extension(
63            &self,
64            bytes: &DiplomatStr,
65            write: &mut diplomat_runtime::DiplomatWriteable,
66        ) -> Result<(), ICU4XError> {
67            self.0
68                .extensions
69                .unicode
70                .keywords
71                .get(&Key::try_from_bytes(bytes)?)
72                .ok_or(ICU4XError::LocaleUndefinedSubtagError)?
73                .write_to(write)?;
74            Ok(())
75        }
76
77        /// Write a string representation of [`ICU4XLocale`] language to `write`
78        #[diplomat::rust_link(icu::locid::Locale::id, StructField)]
79        #[diplomat::attr(supports = accessors, getter)]
80        pub fn language(
81            &self,
82            write: &mut diplomat_runtime::DiplomatWriteable,
83        ) -> Result<(), ICU4XError> {
84            self.0.id.language.write_to(write)?;
85            Ok(())
86        }
87
88        /// Set the language part of the [`ICU4XLocale`].
89        #[diplomat::rust_link(icu::locid::Locale::try_from_bytes, FnInStruct)]
90        #[diplomat::attr(supports = accessors, setter = "language")]
91        pub fn set_language(&mut self, bytes: &DiplomatStr) -> Result<(), ICU4XError> {
92            self.0.id.language = if bytes.is_empty() {
93                Language::UND
94            } else {
95                Language::try_from_bytes(bytes)?
96            };
97            Ok(())
98        }
99
100        /// Write a string representation of [`ICU4XLocale`] region to `write`
101        #[diplomat::rust_link(icu::locid::Locale::id, StructField)]
102        #[diplomat::attr(supports = accessors, getter)]
103        pub fn region(
104            &self,
105            write: &mut diplomat_runtime::DiplomatWriteable,
106        ) -> Result<(), ICU4XError> {
107            if let Some(region) = self.0.id.region {
108                region.write_to(write)?;
109                Ok(())
110            } else {
111                Err(ICU4XError::LocaleUndefinedSubtagError)
112            }
113        }
114
115        /// Set the region part of the [`ICU4XLocale`].
116        #[diplomat::rust_link(icu::locid::Locale::try_from_bytes, FnInStruct)]
117        #[diplomat::attr(supports = accessors, setter = "region")]
118        pub fn set_region(&mut self, bytes: &DiplomatStr) -> Result<(), ICU4XError> {
119            self.0.id.region = if bytes.is_empty() {
120                None
121            } else {
122                Some(Region::try_from_bytes(bytes)?)
123            };
124            Ok(())
125        }
126
127        /// Write a string representation of [`ICU4XLocale`] script to `write`
128        #[diplomat::rust_link(icu::locid::Locale::id, StructField)]
129        #[diplomat::attr(supports = accessors, getter)]
130        pub fn script(
131            &self,
132            write: &mut diplomat_runtime::DiplomatWriteable,
133        ) -> Result<(), ICU4XError> {
134            if let Some(script) = self.0.id.script {
135                script.write_to(write)?;
136                Ok(())
137            } else {
138                Err(ICU4XError::LocaleUndefinedSubtagError)
139            }
140        }
141
142        /// Set the script part of the [`ICU4XLocale`]. Pass an empty string to remove the script.
143        #[diplomat::rust_link(icu::locid::Locale::try_from_bytes, FnInStruct)]
144        #[diplomat::attr(supports = accessors, setter = "script")]
145        pub fn set_script(&mut self, bytes: &DiplomatStr) -> Result<(), ICU4XError> {
146            self.0.id.script = if bytes.is_empty() {
147                None
148            } else {
149                Some(Script::try_from_bytes(bytes)?)
150            };
151            Ok(())
152        }
153
154        /// Best effort locale canonicalizer that doesn't need any data
155        ///
156        /// Use ICU4XLocaleCanonicalizer for better control and functionality
157        #[diplomat::rust_link(icu::locid::Locale::canonicalize, FnInStruct)]
158        pub fn canonicalize(
159            bytes: &DiplomatStr,
160            write: &mut DiplomatWriteable,
161        ) -> Result<(), ICU4XError> {
162            Locale::canonicalize(bytes)?.write_to(write)?;
163            Ok(())
164        }
165        /// Write a string representation of [`ICU4XLocale`] to `write`
166        #[diplomat::rust_link(icu::locid::Locale::write_to, FnInStruct)]
167        #[diplomat::attr(supports = stringifiers, stringifier)]
168        pub fn to_string(
169            &self,
170            write: &mut diplomat_runtime::DiplomatWriteable,
171        ) -> Result<(), ICU4XError> {
172            self.0.write_to(write)?;
173            Ok(())
174        }
175
176        #[diplomat::rust_link(icu::locid::Locale::normalizing_eq, FnInStruct)]
177        pub fn normalizing_eq(&self, other: &DiplomatStr) -> bool {
178            if let Ok(other) = str::from_utf8(other) {
179                self.0.normalizing_eq(other)
180            } else {
181                // invalid UTF8 won't be allowed in locales anyway
182                false
183            }
184        }
185
186        #[diplomat::rust_link(icu::locid::Locale::strict_cmp, FnInStruct)]
187        #[diplomat::attr(*, disable)]
188        pub fn strict_cmp(&self, other: &DiplomatStr) -> ICU4XOrdering {
189            self.0.strict_cmp(other).into()
190        }
191
192        #[diplomat::rust_link(icu::locid::Locale::strict_cmp, FnInStruct)]
193        #[diplomat::skip_if_ast]
194        #[diplomat::attr(dart, rename = "compareToString")]
195        pub fn strict_cmp_(&self, other: &DiplomatStr) -> core::cmp::Ordering {
196            self.0.strict_cmp(other)
197        }
198
199        #[diplomat::rust_link(icu::locid::Locale::total_cmp, FnInStruct)]
200        #[diplomat::attr(*, disable)]
201        pub fn total_cmp(&self, other: &Self) -> ICU4XOrdering {
202            self.0.total_cmp(&other.0).into()
203        }
204
205        #[diplomat::rust_link(icu::locid::Locale::total_cmp, FnInStruct)]
206        #[diplomat::skip_if_ast]
207        #[diplomat::attr(supports = comparators, comparison)]
208        pub fn total_cmp_(&self, other: &Self) -> core::cmp::Ordering {
209            self.0.total_cmp(&other.0)
210        }
211
212        /// Deprecated
213        ///
214        /// Use `create_from_string("en").
215        #[cfg(feature = "provider_test")]
216        #[diplomat::attr(supports = constructors, disable)]
217        pub fn create_en() -> Box<ICU4XLocale> {
218            Box::new(ICU4XLocale(icu_locid::locale!("en")))
219        }
220
221        /// Deprecated
222        ///
223        /// Use `create_from_string("bn").
224        #[cfg(feature = "provider_test")]
225        #[diplomat::attr(supports = constructors, disable)]
226        pub fn create_bn() -> Box<ICU4XLocale> {
227            Box::new(ICU4XLocale(icu_locid::locale!("bn")))
228        }
229    }
230}
231
232impl ffi::ICU4XLocale {
233    pub fn to_datalocale(&self) -> icu_provider::DataLocale {
234        (&self.0).into()
235    }
236}