/rust/registry/src/index.crates.io-6f17d22bba15001f/icu_capi-1.5.1/src/bidi.rs
Line | Count | Source (jump to first uncovered line) |
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 | 0 | #[diplomat::bridge] Unexecuted instantiation: ICU4XBidi_create Unexecuted instantiation: ICU4XBidi_for_text Unexecuted instantiation: ICU4XBidi_for_text_valid_utf8 Unexecuted instantiation: ICU4XBidi_reorder_visual Unexecuted instantiation: ICU4XBidi_level_is_rtl Unexecuted instantiation: ICU4XBidi_level_is_ltr Unexecuted instantiation: ICU4XBidi_level_rtl Unexecuted instantiation: ICU4XBidi_level_ltr Unexecuted instantiation: ICU4XBidi_destroy Unexecuted instantiation: ICU4XBidiDirection_destroy Unexecuted instantiation: ICU4XBidiInfo_paragraph_count Unexecuted instantiation: ICU4XBidiInfo_paragraph_at Unexecuted instantiation: ICU4XBidiInfo_size Unexecuted instantiation: ICU4XBidiInfo_level_at Unexecuted instantiation: ICU4XBidiInfo_destroy Unexecuted instantiation: ICU4XBidiParagraph_set_paragraph_in_text Unexecuted instantiation: ICU4XBidiParagraph_direction Unexecuted instantiation: ICU4XBidiParagraph_size Unexecuted instantiation: ICU4XBidiParagraph_range_start Unexecuted instantiation: ICU4XBidiParagraph_range_end Unexecuted instantiation: ICU4XBidiParagraph_reorder_line Unexecuted instantiation: ICU4XBidiParagraph_level_at Unexecuted instantiation: ICU4XBidiParagraph_destroy Unexecuted instantiation: ICU4XReorderedIndexMap_as_slice Unexecuted instantiation: ICU4XReorderedIndexMap_len Unexecuted instantiation: ICU4XReorderedIndexMap_is_empty Unexecuted instantiation: ICU4XReorderedIndexMap_get Unexecuted instantiation: ICU4XReorderedIndexMap_destroy |
6 | | pub mod ffi { |
7 | | use alloc::boxed::Box; |
8 | | use alloc::vec::Vec; |
9 | | |
10 | | use core::fmt::Write; |
11 | | use icu_properties::bidi::BidiClassAdapter; |
12 | | use icu_properties::maps; |
13 | | use icu_properties::BidiClass; |
14 | | use unicode_bidi::BidiInfo; |
15 | | use unicode_bidi::Level; |
16 | | use unicode_bidi::Paragraph; |
17 | | |
18 | | use crate::errors::ffi::ICU4XError; |
19 | | use crate::provider::ffi::ICU4XDataProvider; |
20 | | |
21 | | pub enum ICU4XBidiDirection { |
22 | | Ltr, |
23 | | Rtl, |
24 | | Mixed, |
25 | | } |
26 | | |
27 | | #[diplomat::opaque] |
28 | | /// An ICU4X Bidi object, containing loaded bidi data |
29 | | #[diplomat::rust_link(icu::properties::bidi::BidiClassAdapter, Struct)] |
30 | | // #[diplomat::rust_link(icu::properties::maps::load_bidi_class, Struct)] |
31 | | pub struct ICU4XBidi(pub maps::CodePointMapData<BidiClass>); |
32 | | |
33 | | impl ICU4XBidi { |
34 | | /// Creates a new [`ICU4XBidi`] from locale data. |
35 | | #[diplomat::rust_link(icu::properties::bidi::BidiClassAdapter::new, FnInStruct)] |
36 | | #[diplomat::attr(all(supports = constructors, supports = fallible_constructors), constructor)] |
37 | 0 | pub fn create(provider: &ICU4XDataProvider) -> Result<Box<ICU4XBidi>, ICU4XError> { |
38 | 0 | Ok(Box::new(ICU4XBidi(call_constructor_unstable!( |
39 | 0 | maps::bidi_class [m => Ok(m.static_to_owned())], |
40 | | maps::load_bidi_class, |
41 | | provider, |
42 | 0 | )?))) |
43 | 0 | } |
44 | | |
45 | | /// Use the data loaded in this object to process a string and calculate bidi information |
46 | | /// |
47 | | /// Takes in a Level for the default level, if it is an invalid value it will default to LTR |
48 | | /// |
49 | | /// Returns nothing if `text` is invalid UTF-8. |
50 | | #[diplomat::rust_link(unicode_bidi::BidiInfo::new_with_data_source, FnInStruct)] |
51 | | #[diplomat::rust_link( |
52 | | icu::properties::bidi::BidiClassAdapter::bidi_class, |
53 | | FnInStruct, |
54 | | hidden |
55 | | )] |
56 | | #[diplomat::attr(dart, disable)] |
57 | 0 | pub fn for_text<'text>( |
58 | 0 | &self, |
59 | 0 | text: &'text DiplomatStr, |
60 | 0 | default_level: u8, |
61 | 0 | ) -> Option<Box<ICU4XBidiInfo<'text>>> { |
62 | 0 | let text = core::str::from_utf8(text).ok()?; |
63 | | |
64 | 0 | let data = self.0.as_borrowed(); |
65 | 0 | let adapter = BidiClassAdapter::new(data); |
66 | 0 |
|
67 | 0 | Some(Box::new(ICU4XBidiInfo(BidiInfo::new_with_data_source( |
68 | 0 | &adapter, |
69 | 0 | text, |
70 | 0 | Level::new(default_level).ok(), |
71 | 0 | )))) |
72 | 0 | } |
73 | | |
74 | | /// Use the data loaded in this object to process a string and calculate bidi information |
75 | | /// |
76 | | /// Takes in a Level for the default level, if it is an invalid value it will default to LTR |
77 | | #[diplomat::rust_link(unicode_bidi::BidiInfo::new_with_data_source, FnInStruct)] |
78 | | #[diplomat::rust_link( |
79 | | icu::properties::bidi::BidiClassAdapter::bidi_class, |
80 | | FnInStruct, |
81 | | hidden |
82 | | )] |
83 | | #[diplomat::attr(not(dart), disable)] |
84 | | #[diplomat::attr(*, rename = "for_text")] |
85 | | #[diplomat::skip_if_ast] |
86 | 0 | pub fn for_text_valid_utf8<'text>( |
87 | 0 | &self, |
88 | 0 | text: &'text str, |
89 | 0 | default_level: u8, |
90 | 0 | ) -> Box<ICU4XBidiInfo<'text>> { |
91 | 0 | let data = self.0.as_borrowed(); |
92 | 0 | let adapter = BidiClassAdapter::new(data); |
93 | 0 |
|
94 | 0 | Box::new(ICU4XBidiInfo(BidiInfo::new_with_data_source( |
95 | 0 | &adapter, |
96 | 0 | text, |
97 | 0 | Level::new(default_level).ok(), |
98 | 0 | ))) |
99 | 0 | } |
100 | | |
101 | | /// Utility function for producing reorderings given a list of levels |
102 | | /// |
103 | | /// Produces a map saying which visual index maps to which source index. |
104 | | /// |
105 | | /// The levels array must not have values greater than 126 (this is the |
106 | | /// Bidi maximum explicit depth plus one). |
107 | | /// Failure to follow this invariant may lead to incorrect results, |
108 | | /// but is still safe. |
109 | | #[diplomat::rust_link(unicode_bidi::BidiInfo::reorder_visual, FnInStruct)] |
110 | 0 | pub fn reorder_visual(&self, levels: &[u8]) -> Box<ICU4XReorderedIndexMap> { |
111 | 0 | let levels = Level::from_slice_unchecked(levels); |
112 | 0 | Box::new(ICU4XReorderedIndexMap(BidiInfo::reorder_visual(levels))) |
113 | 0 | } |
114 | | |
115 | | /// Check if a Level returned by level_at is an RTL level. |
116 | | /// |
117 | | /// Invalid levels (numbers greater than 125) will be assumed LTR |
118 | | #[diplomat::rust_link(unicode_bidi::Level::is_rtl, FnInStruct)] |
119 | 0 | pub fn level_is_rtl(level: u8) -> bool { |
120 | 0 | Level::new(level).unwrap_or_else(|_| Level::ltr()).is_rtl() |
121 | 0 | } |
122 | | |
123 | | /// Check if a Level returned by level_at is an LTR level. |
124 | | /// |
125 | | /// Invalid levels (numbers greater than 125) will be assumed LTR |
126 | | #[diplomat::rust_link(unicode_bidi::Level::is_ltr, FnInStruct)] |
127 | 0 | pub fn level_is_ltr(level: u8) -> bool { |
128 | 0 | Level::new(level).unwrap_or_else(|_| Level::ltr()).is_ltr() |
129 | 0 | } |
130 | | |
131 | | /// Get a basic RTL Level value |
132 | | #[diplomat::rust_link(unicode_bidi::Level::rtl, FnInStruct)] |
133 | 0 | pub fn level_rtl() -> u8 { |
134 | 0 | Level::rtl().number() |
135 | 0 | } |
136 | | |
137 | | /// Get a simple LTR Level value |
138 | | #[diplomat::rust_link(unicode_bidi::Level::ltr, FnInStruct)] |
139 | 0 | pub fn level_ltr() -> u8 { |
140 | 0 | Level::ltr().number() |
141 | 0 | } |
142 | | } |
143 | | |
144 | | /// Thin wrapper around a vector that maps visual indices to source indices |
145 | | /// |
146 | | /// `map[visualIndex] = sourceIndex` |
147 | | /// |
148 | | /// Produced by `reorder_visual()` on [`ICU4XBidi`]. |
149 | | #[diplomat::opaque] |
150 | | pub struct ICU4XReorderedIndexMap(pub Vec<usize>); |
151 | | |
152 | | impl ICU4XReorderedIndexMap { |
153 | | /// Get this as a slice/array of indices |
154 | | #[diplomat::attr(supports = accessors, getter)] |
155 | 0 | pub fn as_slice<'a>(&'a self) -> &'a [usize] { |
156 | 0 | &self.0 |
157 | 0 | } |
158 | | |
159 | | /// The length of this map |
160 | | #[diplomat::attr(supports = accessors, getter = "length")] |
161 | 0 | pub fn len(&self) -> usize { |
162 | 0 | self.0.len() |
163 | 0 | } |
164 | | |
165 | | /// Whether this map is empty |
166 | | #[diplomat::attr(supports = accessors, getter)] |
167 | 0 | pub fn is_empty(&self) -> bool { |
168 | 0 | self.0.is_empty() |
169 | 0 | } |
170 | | |
171 | | /// Get element at `index`. Returns 0 when out of bounds |
172 | | /// (note that 0 is also a valid in-bounds value, please use `len()` |
173 | | /// to avoid out-of-bounds) |
174 | | #[diplomat::attr(supports = indexing, indexer)] |
175 | 0 | pub fn get(&self, index: usize) -> usize { |
176 | 0 | self.0.get(index).copied().unwrap_or(0) |
177 | 0 | } |
178 | | } |
179 | | |
180 | | /// An object containing bidi information for a given string, produced by `for_text()` on `ICU4XBidi` |
181 | | #[diplomat::rust_link(unicode_bidi::BidiInfo, Struct)] |
182 | | #[diplomat::opaque] |
183 | | pub struct ICU4XBidiInfo<'text>(pub BidiInfo<'text>); |
184 | | |
185 | | impl<'text> ICU4XBidiInfo<'text> { |
186 | | /// The number of paragraphs contained here |
187 | | #[diplomat::attr(supports = accessors, getter)] |
188 | 0 | pub fn paragraph_count(&self) -> usize { |
189 | 0 | self.0.paragraphs.len() |
190 | 0 | } |
191 | | |
192 | | /// Get the nth paragraph, returning `None` if out of bounds |
193 | 0 | pub fn paragraph_at(&'text self, n: usize) -> Option<Box<ICU4XBidiParagraph<'text>>> { |
194 | 0 | self.0 |
195 | 0 | .paragraphs |
196 | 0 | .get(n) |
197 | 0 | .map(|p| Box::new(ICU4XBidiParagraph(Paragraph::new(&self.0, p)))) |
198 | 0 | } |
199 | | |
200 | | /// The number of bytes in this full text |
201 | | #[diplomat::attr(supports = accessors, getter)] |
202 | 0 | pub fn size(&self) -> usize { |
203 | 0 | self.0.levels.len() |
204 | 0 | } |
205 | | |
206 | | /// Get the BIDI level at a particular byte index in the full text. |
207 | | /// This integer is conceptually a `unicode_bidi::Level`, |
208 | | /// and can be further inspected using the static methods on ICU4XBidi. |
209 | | /// |
210 | | /// Returns 0 (equivalent to `Level::ltr()`) on error |
211 | 0 | pub fn level_at(&self, pos: usize) -> u8 { |
212 | 0 | if let Some(l) = self.0.levels.get(pos) { |
213 | 0 | l.number() |
214 | | } else { |
215 | 0 | 0 |
216 | | } |
217 | 0 | } |
218 | | } |
219 | | |
220 | | /// Bidi information for a single processed paragraph |
221 | | #[diplomat::opaque] |
222 | | pub struct ICU4XBidiParagraph<'info>(pub Paragraph<'info, 'info>); |
223 | | |
224 | | impl<'info> ICU4XBidiParagraph<'info> { |
225 | | /// Given a paragraph index `n` within the surrounding text, this sets this |
226 | | /// object to the paragraph at that index. Returns `ICU4XError::OutOfBoundsError` when out of bounds. |
227 | | /// |
228 | | /// This is equivalent to calling `paragraph_at()` on `ICU4XBidiInfo` but doesn't |
229 | | /// create a new object |
230 | 0 | pub fn set_paragraph_in_text(&mut self, n: usize) -> Result<(), ICU4XError> { |
231 | 0 | let para = self |
232 | 0 | .0 |
233 | 0 | .info |
234 | 0 | .paragraphs |
235 | 0 | .get(n) |
236 | 0 | .ok_or(ICU4XError::OutOfBoundsError)?; |
237 | 0 | self.0 = Paragraph::new(self.0.info, para); |
238 | 0 | Ok(()) |
239 | 0 | } |
240 | | #[diplomat::rust_link(unicode_bidi::Paragraph::level_at, FnInStruct)] |
241 | | #[diplomat::attr(supports = accessors, getter)] |
242 | | /// The primary direction of this paragraph |
243 | 0 | pub fn direction(&self) -> ICU4XBidiDirection { |
244 | 0 | self.0.direction().into() |
245 | 0 | } |
246 | | |
247 | | /// The number of bytes in this paragraph |
248 | | #[diplomat::rust_link(unicode_bidi::ParagraphInfo::len, FnInStruct)] |
249 | | #[diplomat::attr(supports = accessors, getter)] |
250 | 0 | pub fn size(&self) -> usize { |
251 | 0 | self.0.para.len() |
252 | 0 | } |
253 | | |
254 | | /// The start index of this paragraph within the source text |
255 | | #[diplomat::attr(supports = accessors, getter)] |
256 | 0 | pub fn range_start(&self) -> usize { |
257 | 0 | self.0.para.range.start |
258 | 0 | } |
259 | | |
260 | | /// The end index of this paragraph within the source text |
261 | | #[diplomat::attr(supports = accessors, getter)] |
262 | 0 | pub fn range_end(&self) -> usize { |
263 | 0 | self.0.para.range.end |
264 | 0 | } |
265 | | |
266 | | /// Reorder a line based on display order. The ranges are specified relative to the source text and must be contained |
267 | | /// within this paragraph's range. |
268 | | #[diplomat::rust_link(unicode_bidi::Paragraph::level_at, FnInStruct)] |
269 | 0 | pub fn reorder_line( |
270 | 0 | &self, |
271 | 0 | range_start: usize, |
272 | 0 | range_end: usize, |
273 | 0 | out: &mut DiplomatWriteable, |
274 | 0 | ) -> Result<(), ICU4XError> { |
275 | 0 | if range_start < self.range_start() || range_end > self.range_end() { |
276 | 0 | return Err(ICU4XError::OutOfBoundsError); |
277 | 0 | } |
278 | 0 |
|
279 | 0 | let info = self.0.info; |
280 | 0 | let para = self.0.para; |
281 | 0 |
|
282 | 0 | let reordered = info.reorder_line(para, range_start..range_end); |
283 | 0 |
|
284 | 0 | Ok(out.write_str(&reordered)?) |
285 | 0 | } |
286 | | |
287 | | /// Get the BIDI level at a particular byte index in this paragraph. |
288 | | /// This integer is conceptually a `unicode_bidi::Level`, |
289 | | /// and can be further inspected using the static methods on ICU4XBidi. |
290 | | /// |
291 | | /// Returns 0 (equivalent to `Level::ltr()`) on error |
292 | | #[diplomat::rust_link(unicode_bidi::Paragraph::level_at, FnInStruct)] |
293 | 0 | pub fn level_at(&self, pos: usize) -> u8 { |
294 | 0 | if pos >= self.size() { |
295 | 0 | return 0; |
296 | 0 | } |
297 | 0 |
|
298 | 0 | self.0.level_at(pos).number() |
299 | 0 | } |
300 | | } |
301 | | } |
302 | | |
303 | | use unicode_bidi::Direction; |
304 | | |
305 | | impl From<Direction> for ffi::ICU4XBidiDirection { |
306 | 0 | fn from(other: Direction) -> Self { |
307 | 0 | match other { |
308 | 0 | Direction::Ltr => Self::Ltr, |
309 | 0 | Direction::Rtl => Self::Rtl, |
310 | 0 | Direction::Mixed => Self::Mixed, |
311 | | } |
312 | 0 | } |
313 | | } |