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