tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

bidi.rs (11970B)


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