Coverage Report

Created: 2025-07-11 06:39

/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
}