1#[diplomat::bridge]
6pub 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 #[diplomat::rust_link(icu::properties::bidi::BidiClassAdapter, Struct)]
30 pub struct ICU4XBidi(pub maps::CodePointMapData<BidiClass>);
32
33 impl ICU4XBidi {
34 #[diplomat::rust_link(icu::properties::bidi::BidiClassAdapter::new, FnInStruct)]
36 #[diplomat::attr(all(supports = constructors, supports = fallible_constructors), constructor)]
37 pub fn create(provider: &ICU4XDataProvider) -> Result<Box<ICU4XBidi>, ICU4XError> {
38 Ok(Box::new(ICU4XBidi(call_constructor_unstable!(
39 maps::bidi_class [m => Ok(m.static_to_owned())],
40 maps::load_bidi_class,
41 provider,
42 )?)))
43 }
44
45 #[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 pub fn for_text<'text>(
58 &self,
59 text: &'text DiplomatStr,
60 default_level: u8,
61 ) -> Option<Box<ICU4XBidiInfo<'text>>> {
62 let text = core::str::from_utf8(text).ok()?;
63
64 let data = self.0.as_borrowed();
65 let adapter = BidiClassAdapter::new(data);
66
67 Some(Box::new(ICU4XBidiInfo(BidiInfo::new_with_data_source(
68 &adapter,
69 text,
70 Level::new(default_level).ok(),
71 ))))
72 }
73
74 #[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 pub fn for_text_valid_utf8<'text>(
87 &self,
88 text: &'text str,
89 default_level: u8,
90 ) -> Box<ICU4XBidiInfo<'text>> {
91 let data = self.0.as_borrowed();
92 let adapter = BidiClassAdapter::new(data);
93
94 Box::new(ICU4XBidiInfo(BidiInfo::new_with_data_source(
95 &adapter,
96 text,
97 Level::new(default_level).ok(),
98 )))
99 }
100
101 #[diplomat::rust_link(unicode_bidi::BidiInfo::reorder_visual, FnInStruct)]
110 pub fn reorder_visual(&self, levels: &[u8]) -> Box<ICU4XReorderedIndexMap> {
111 let levels = Level::from_slice_unchecked(levels);
112 Box::new(ICU4XReorderedIndexMap(BidiInfo::reorder_visual(levels)))
113 }
114
115 #[diplomat::rust_link(unicode_bidi::Level::is_rtl, FnInStruct)]
119 pub fn level_is_rtl(level: u8) -> bool {
120 Level::new(level).unwrap_or_else(|_| Level::ltr()).is_rtl()
121 }
122
123 #[diplomat::rust_link(unicode_bidi::Level::is_ltr, FnInStruct)]
127 pub fn level_is_ltr(level: u8) -> bool {
128 Level::new(level).unwrap_or_else(|_| Level::ltr()).is_ltr()
129 }
130
131 #[diplomat::rust_link(unicode_bidi::Level::rtl, FnInStruct)]
133 pub fn level_rtl() -> u8 {
134 Level::rtl().number()
135 }
136
137 #[diplomat::rust_link(unicode_bidi::Level::ltr, FnInStruct)]
139 pub fn level_ltr() -> u8 {
140 Level::ltr().number()
141 }
142 }
143
144 #[diplomat::opaque]
150 pub struct ICU4XReorderedIndexMap(pub Vec<usize>);
151
152 impl ICU4XReorderedIndexMap {
153 #[diplomat::attr(supports = accessors, getter)]
155 pub fn as_slice<'a>(&'a self) -> &'a [usize] {
156 &self.0
157 }
158
159 #[diplomat::attr(supports = accessors, getter = "length")]
161 pub fn len(&self) -> usize {
162 self.0.len()
163 }
164
165 #[diplomat::attr(supports = accessors, getter)]
167 pub fn is_empty(&self) -> bool {
168 self.0.is_empty()
169 }
170
171 #[diplomat::attr(supports = indexing, indexer)]
175 pub fn get(&self, index: usize) -> usize {
176 self.0.get(index).copied().unwrap_or(0)
177 }
178 }
179
180 #[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 #[diplomat::attr(supports = accessors, getter)]
188 pub fn paragraph_count(&self) -> usize {
189 self.0.paragraphs.len()
190 }
191
192 pub fn paragraph_at(&'text self, n: usize) -> Option<Box<ICU4XBidiParagraph<'text>>> {
194 self.0
195 .paragraphs
196 .get(n)
197 .map(|p| Box::new(ICU4XBidiParagraph(Paragraph::new(&self.0, p))))
198 }
199
200 #[diplomat::attr(supports = accessors, getter)]
202 pub fn size(&self) -> usize {
203 self.0.levels.len()
204 }
205
206 pub fn level_at(&self, pos: usize) -> u8 {
212 if let Some(l) = self.0.levels.get(pos) {
213 l.number()
214 } else {
215 0
216 }
217 }
218 }
219
220 #[diplomat::opaque]
222 pub struct ICU4XBidiParagraph<'info>(pub Paragraph<'info, 'info>);
223
224 impl<'info> ICU4XBidiParagraph<'info> {
225 pub fn set_paragraph_in_text(&mut self, n: usize) -> Result<(), ICU4XError> {
231 let para = self
232 .0
233 .info
234 .paragraphs
235 .get(n)
236 .ok_or(ICU4XError::OutOfBoundsError)?;
237 self.0 = Paragraph::new(self.0.info, para);
238 Ok(())
239 }
240 #[diplomat::rust_link(unicode_bidi::Paragraph::level_at, FnInStruct)]
241 #[diplomat::attr(supports = accessors, getter)]
242 pub fn direction(&self) -> ICU4XBidiDirection {
244 self.0.direction().into()
245 }
246
247 #[diplomat::rust_link(unicode_bidi::ParagraphInfo::len, FnInStruct)]
249 #[diplomat::attr(supports = accessors, getter)]
250 pub fn size(&self) -> usize {
251 self.0.para.len()
252 }
253
254 #[diplomat::attr(supports = accessors, getter)]
256 pub fn range_start(&self) -> usize {
257 self.0.para.range.start
258 }
259
260 #[diplomat::attr(supports = accessors, getter)]
262 pub fn range_end(&self) -> usize {
263 self.0.para.range.end
264 }
265
266 #[diplomat::rust_link(unicode_bidi::Paragraph::level_at, FnInStruct)]
269 pub fn reorder_line(
270 &self,
271 range_start: usize,
272 range_end: usize,
273 out: &mut DiplomatWriteable,
274 ) -> Result<(), ICU4XError> {
275 if range_start < self.range_start() || range_end > self.range_end() {
276 return Err(ICU4XError::OutOfBoundsError);
277 }
278
279 let info = self.0.info;
280 let para = self.0.para;
281
282 let reordered = info.reorder_line(para, range_start..range_end);
283
284 Ok(out.write_str(&reordered)?)
285 }
286
287 #[diplomat::rust_link(unicode_bidi::Paragraph::level_at, FnInStruct)]
293 pub fn level_at(&self, pos: usize) -> u8 {
294 if pos >= self.size() {
295 return 0;
296 }
297
298 self.0.level_at(pos).number()
299 }
300 }
301}
302
303use unicode_bidi::Direction;
304
305impl From<Direction> for ffi::ICU4XBidiDirection {
306 fn from(other: Direction) -> Self {
307 match other {
308 Direction::Ltr => Self::Ltr,
309 Direction::Rtl => Self::Rtl,
310 Direction::Mixed => Self::Mixed,
311 }
312 }
313}