icu_collections/codepointinvlist/
conversions.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 core::iter::FromIterator;
6use core::{
7    convert::TryFrom,
8    ops::{Range, RangeBounds, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive},
9};
10
11use super::RangeError;
12use crate::codepointinvlist::utils::deconstruct_range;
13use crate::codepointinvlist::CodePointInversionList;
14use crate::codepointinvlist::CodePointInversionListBuilder;
15use potential_utf::PotentialCodePoint;
16use zerovec::ZeroVec;
17
18fn try_from_range<'data>(
19    range: impl RangeBounds<char>,
20) -> Result<CodePointInversionList<'data>, RangeError> {
21    let (from, till) = deconstruct_range(range);
22    if from < till {
23        let set = [
24            PotentialCodePoint::from_u24(from),
25            PotentialCodePoint::from_u24(till),
26        ];
27        let inv_list: ZeroVec<PotentialCodePoint> = ZeroVec::alloc_from_slice(&set);
28        #[allow(clippy::unwrap_used)] // valid
29        Ok(CodePointInversionList::try_from_inversion_list(inv_list).unwrap())
30    } else {
31        Err(RangeError(from, till))
32    }
33}
34
35impl TryFrom<Range<char>> for CodePointInversionList<'_> {
36    type Error = RangeError;
37
38    fn try_from(range: Range<char>) -> Result<Self, Self::Error> {
39        try_from_range(range)
40    }
41}
42
43impl TryFrom<RangeFrom<char>> for CodePointInversionList<'_> {
44    type Error = RangeError;
45
46    fn try_from(range: RangeFrom<char>) -> Result<Self, Self::Error> {
47        try_from_range(range)
48    }
49}
50
51impl TryFrom<RangeFull> for CodePointInversionList<'_> {
52    type Error = RangeError;
53
54    fn try_from(_: RangeFull) -> Result<Self, Self::Error> {
55        Ok(Self::all())
56    }
57}
58
59impl TryFrom<RangeInclusive<char>> for CodePointInversionList<'_> {
60    type Error = RangeError;
61
62    fn try_from(range: RangeInclusive<char>) -> Result<Self, Self::Error> {
63        try_from_range(range)
64    }
65}
66
67impl TryFrom<RangeTo<char>> for CodePointInversionList<'_> {
68    type Error = RangeError;
69
70    fn try_from(range: RangeTo<char>) -> Result<Self, Self::Error> {
71        try_from_range(range)
72    }
73}
74
75impl TryFrom<RangeToInclusive<char>> for CodePointInversionList<'_> {
76    type Error = RangeError;
77
78    fn try_from(range: RangeToInclusive<char>) -> Result<Self, Self::Error> {
79        try_from_range(range)
80    }
81}
82
83impl FromIterator<RangeInclusive<u32>> for CodePointInversionList<'_> {
84    fn from_iter<I: IntoIterator<Item = RangeInclusive<u32>>>(iter: I) -> Self {
85        let mut builder = CodePointInversionListBuilder::new();
86        for range in iter {
87            builder.add_range32(range);
88        }
89        builder.build()
90    }
91}
92
93#[cfg(test)]
94mod tests {
95    use super::*;
96    use crate::codepointinvlist::CodePointInversionList;
97    use core::{char, convert::TryFrom};
98
99    #[test]
100    fn test_try_from_range() {
101        let check: Vec<char> = CodePointInversionList::try_from('A'..'B')
102            .unwrap()
103            .iter_chars()
104            .collect();
105        assert_eq!(vec!['A'], check);
106    }
107
108    #[test]
109    fn test_try_from_range_error() {
110        let check = CodePointInversionList::try_from('A'..'A');
111        assert!(matches!(check, Err(RangeError(65, 65))));
112    }
113
114    #[test]
115    fn test_try_from_range_inclusive() {
116        let check: Vec<char> = CodePointInversionList::try_from('A'..='A')
117            .unwrap()
118            .iter_chars()
119            .collect();
120        assert_eq!(vec!['A'], check);
121    }
122
123    #[test]
124    fn test_try_from_range_inclusive_err() {
125        let check = CodePointInversionList::try_from('B'..'A');
126        assert!(matches!(check, Err(RangeError(66, 65))));
127    }
128
129    #[test]
130    fn test_try_from_range_from() {
131        let uset = CodePointInversionList::try_from('A'..).unwrap();
132        let check: usize = uset.size();
133        let expected: usize = (char::MAX as usize) + 1 - 65;
134        assert_eq!(expected, check);
135    }
136
137    #[test]
138    fn test_try_from_range_to() {
139        let uset = CodePointInversionList::try_from(..'A').unwrap();
140        let check: usize = uset.size();
141        let expected: usize = 65;
142        assert_eq!(expected, check);
143    }
144
145    #[test]
146    fn test_try_from_range_to_err() {
147        let check = CodePointInversionList::try_from(..(0x0 as char));
148        assert!(matches!(check, Err(RangeError(0, 0))));
149    }
150
151    #[test]
152    fn test_try_from_range_to_inclusive() {
153        let uset = CodePointInversionList::try_from(..='A').unwrap();
154        let check: usize = uset.size();
155        let expected: usize = 66;
156        assert_eq!(expected, check);
157    }
158
159    #[test]
160    fn test_try_from_range_full() {
161        let uset = CodePointInversionList::try_from(..).unwrap();
162        let check: usize = uset.size();
163        let expected: usize = (char::MAX as usize) + 1;
164        assert_eq!(expected, check);
165    }
166
167    #[test]
168    fn test_from_range_iterator() {
169        let ranges = [
170            0..=0x3FFF,
171            0x4000..=0x7FFF,
172            0x8000..=0xBFFF,
173            0xC000..=0xFFFF,
174        ];
175        let expected =
176            CodePointInversionList::try_from_u32_inversion_list_slice(&[0x0, 0x1_0000]).unwrap();
177        let actual = CodePointInversionList::from_iter(ranges);
178        assert_eq!(expected, actual);
179    }
180}