icu_provider_source/properties/
bidi.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
5use std::collections::HashSet;
6
7use crate::SourceDataProvider;
8use icu::properties::provider::PropertyEnumBidiMirroringGlyphV1;
9use icu_provider::prelude::*;
10
11#[cfg(any(feature = "use_wasm", feature = "use_icu4c"))]
12impl SourceDataProvider {
13    fn get_code_point_prop_map<'a>(
14        &'a self,
15        key: &str,
16    ) -> Result<&'a super::uprops_serde::code_point_prop::CodePointPropertyMap, DataError> {
17        self.icuexport()?
18            .read_and_parse_toml::<super::uprops_serde::code_point_prop::Main>(&format!(
19                "uprops/{}/{}.toml",
20                self.trie_type(),
21                key
22            ))?
23            .enum_property
24            .first()
25            .ok_or_else(|| DataErrorKind::MarkerNotFound.into_error())
26    }
27}
28
29// implement data provider 2 different ways, based on whether or not
30// features exist that enable the use of CPT Builder (ex: `use_wasm` or `use_icu4c`)
31impl DataProvider<PropertyEnumBidiMirroringGlyphV1> for SourceDataProvider {
32    #[cfg(any(feature = "use_wasm", feature = "use_icu4c"))]
33    fn load(
34        &self,
35        req: DataRequest,
36    ) -> Result<DataResponse<PropertyEnumBidiMirroringGlyphV1>, DataError> {
37        use icu::collections::codepointinvlist::CodePointInversionListBuilder;
38        use icu::collections::codepointtrie::CodePointTrie;
39        use icu::collections::codepointtrie::TrieType;
40        use icu::properties::props::BidiMirroringGlyph;
41        use icu::properties::props::BidiPairedBracketType;
42        use icu_codepointtrie_builder::{CodePointTrieBuilder, CodePointTrieBuilderData};
43        use icu_collections::codepointtrie::TrieValue;
44
45        self.check_req::<PropertyEnumBidiMirroringGlyphV1>(req)?;
46
47        // Bidi_M / Bidi_Mirrored
48        let bidi_m_data = self.get_binary_prop_for_code_point_set("Bidi_M")?;
49        let mut bidi_m_builder = CodePointInversionListBuilder::new();
50        for (start, end) in &bidi_m_data.ranges {
51            bidi_m_builder.add_range32(start..=end);
52        }
53        let bidi_m_cpinvlist = bidi_m_builder.build();
54
55        // bmg / Bidi_Mirroring_Glyph
56        let bmg_data = &self.get_code_point_prop_map("bmg")?.code_point_trie;
57        let bmg_trie = CodePointTrie::try_from(bmg_data).map_err(|e| {
58            DataError::custom("Could not parse CodePointTrie TOML").with_display_context(&e)
59        })?;
60
61        // bpt / Bidi_Paired_Bracket_Type
62        let bpt_data = &self.get_enumerated_prop("bpt")?.code_point_trie;
63        let bpt_trie = CodePointTrie::try_from(bpt_data).map_err(|e| {
64            DataError::custom("Could not parse CodePointTrie TOML").with_display_context(&e)
65        })?;
66
67        let trie_vals = (0..=(char::MAX as u32)).map(|cp| {
68            let mut r = BidiMirroringGlyph::default();
69            r.mirrored = bidi_m_cpinvlist.contains32(cp);
70            r.mirroring_glyph = r
71                .mirrored
72                .then_some(bmg_trie.get32(cp))
73                .filter(|&cp| cp as u32 != 0);
74            r.paired_bracket_type = match bpt_trie.get32(cp) {
75                1 => BidiPairedBracketType::Open,
76                2 => BidiPairedBracketType::Close,
77                _ => BidiPairedBracketType::None,
78            };
79            if r.mirrored && r.mirroring_glyph.is_none() {
80                log::trace!(
81                    "Missing mirroring glyph: U+{cp:X}: {}",
82                    char::try_from_u32(cp).unwrap()
83                );
84            }
85            r
86        });
87
88        Ok(DataResponse {
89            metadata: Default::default(),
90            payload: DataPayload::from_owned(
91                icu::properties::provider::PropertyCodePointMap::CodePointTrie(
92                    CodePointTrieBuilder {
93                        data: CodePointTrieBuilderData::ValuesByCodePoint(
94                            &trie_vals.collect::<Vec<_>>(),
95                        ),
96                        default_value: BidiMirroringGlyph::default(),
97                        error_value: BidiMirroringGlyph::default(),
98                        trie_type: TrieType::Small,
99                    }
100                    .build(),
101                ),
102            ),
103        })
104    }
105
106    #[cfg(not(any(feature = "use_wasm", feature = "use_icu4c")))]
107    fn load(
108        &self,
109        req: DataRequest,
110    ) -> Result<DataResponse<PropertyEnumBidiMirroringGlyphV1>, DataError> {
111        self.check_req::<PropertyEnumBidiMirroringGlyphV1>(req)?;
112        return Err(DataError::custom(
113            "icu_provider_source must be built with use_icu4c or use_wasm to build Bidi auxiliary properties data",
114        ));
115    }
116}
117
118impl crate::IterableDataProviderCached<PropertyEnumBidiMirroringGlyphV1> for SourceDataProvider {
119    fn iter_ids_cached(&self) -> Result<HashSet<DataIdentifierCow<'static>>, DataError> {
120        Ok(HashSet::from_iter([Default::default()]))
121    }
122}
123
124#[cfg(test)]
125mod tests {
126    use super::*;
127    use icu::properties::props::{BidiMirroringGlyph, BidiPairedBracketType};
128
129    #[test]
130    fn test_bidi_data_provider() {
131        let provider = SourceDataProvider::new_testing();
132
133        let bidi_data =
134            icu::properties::CodePointMapData::<BidiMirroringGlyph>::try_new_unstable(&provider)
135                .unwrap();
136        let bidi_data = bidi_data.as_borrowed();
137
138        let close_paren = bidi_data.get(')');
139        assert_eq!(close_paren.mirroring_glyph, Some('('));
140        assert!(close_paren.mirrored);
141        let close_angle_bracket = bidi_data.get('>');
142        assert_eq!(close_angle_bracket.mirroring_glyph, Some('<'));
143        assert!(close_angle_bracket.mirrored);
144
145        let open_paren = bidi_data.get('(');
146        assert_eq!(open_paren.paired_bracket_type, BidiPairedBracketType::Open);
147        let open_angle_bracket = bidi_data.get('<');
148        assert_eq!(
149            open_angle_bracket.paired_bracket_type,
150            BidiPairedBracketType::None
151        );
152    }
153}