offset_string/
conversions.rs

1// Copyright 2019 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::OffsetString;
6use anyhow::{format_err, Error};
7use char_collection::{CharCollection, MultiCharRange};
8use char_set::CharSet;
9use unic_char_range::{chars, CharRange};
10
11impl TryFrom<String> for OffsetString {
12    type Error = anyhow::Error;
13
14    fn try_from(source: String) -> Result<OffsetString, Error> {
15        OffsetString::new(source)
16    }
17}
18
19impl TryFrom<&str> for OffsetString {
20    type Error = anyhow::Error;
21
22    fn try_from(source: &str) -> Result<OffsetString, Error> {
23        OffsetString::new(source)
24    }
25}
26
27impl From<OffsetString> for String {
28    fn from(value: OffsetString) -> String {
29        value.0
30    }
31}
32
33impl From<CharCollection> for OffsetString {
34    fn from(value: CharCollection) -> OffsetString {
35        OffsetString::from(&value)
36    }
37}
38
39impl From<&CharCollection> for OffsetString {
40    fn from(value: &CharCollection) -> OffsetString {
41        let mut prev_high: u32 = 0;
42        let mut segments: Vec<String> = Vec::with_capacity(value.range_count());
43        for range in value.iter_ranges() {
44            let relative_low = range.low as u32 - prev_high;
45            let range_size = range.len();
46            let segment = if range_size == 1 {
47                relative_low.to_string()
48            } else {
49                format!("{}+{}", relative_low, range_size - 1)
50            };
51            segments.push(segment);
52            prev_high = range.high as u32;
53        }
54        OffsetString(segments.join(","))
55    }
56}
57
58impl TryFrom<OffsetString> for CharCollection {
59    type Error = anyhow::Error;
60
61    fn try_from(value: OffsetString) -> Result<Self, Self::Error> {
62        CharCollection::try_from(&value)
63    }
64}
65
66impl TryFrom<&OffsetString> for CharCollection {
67    type Error = anyhow::Error;
68
69    fn try_from(value: &OffsetString) -> Result<Self, Self::Error> {
70        let ranges: Result<Vec<CharRange>, Error> = value
71            .iter_ranges()
72            .map(|range| {
73                let low: u32 = *range.start();
74                let high: u32 = *range.end();
75                let low_char =
76                    std::char::from_u32(low).ok_or_else(|| format_err!("Bad char: {}", low))?;
77                let high_char =
78                    std::char::from_u32(high).ok_or_else(|| format_err!("Bad char: {}", high))?;
79                Ok(chars!(low_char..=high_char))
80            })
81            .collect();
82        return CharCollection::from_sorted_ranges(ranges?);
83    }
84}
85
86impl From<CharSet> for OffsetString {
87    fn from(value: CharSet) -> OffsetString {
88        OffsetString::from(&value)
89    }
90}
91
92impl From<&CharSet> for OffsetString {
93    fn from(char_set: &CharSet) -> OffsetString {
94        let collection: CharCollection = char_set.into();
95        collection.into()
96    }
97}
98
99impl TryFrom<OffsetString> for CharSet {
100    type Error = anyhow::Error;
101
102    fn try_from(value: OffsetString) -> Result<Self, Self::Error> {
103        CharSet::try_from(&value)
104    }
105}
106
107impl TryFrom<&OffsetString> for CharSet {
108    type Error = anyhow::Error;
109
110    fn try_from(value: &OffsetString) -> Result<Self, Self::Error> {
111        Ok(CharSet::new(value.iter().collect()))
112    }
113}
114
115#[cfg(test)]
116mod tests {
117    use super::*;
118    use char_collection::char_collect;
119
120    #[test]
121    fn test_char_collection_to_offset_string() -> Result<(), Error> {
122        let collection = char_collect!(0..=4, 9..=9, 20..=28);
123        let offset_string: OffsetString = collection.into();
124        assert_eq!(offset_string, OffsetString::new("0+4,5,11+8")?);
125
126        let collection = char_collect!(3..=4, 9..=9, 20..=28);
127        let offset_string: OffsetString = collection.into();
128        assert_eq!(offset_string, OffsetString("3+1,5,11+8".to_string()));
129
130        Ok(())
131    }
132
133    #[test]
134    fn test_char_collection_from_offset_string() -> Result<(), Error> {
135        assert_eq!(
136            char_collect!(0..=4, 9..=9, 20..=28),
137            CharCollection::try_from(OffsetString::new("0+4,5,11+8")?)?
138        );
139
140        assert_eq!(
141            char_collect!(3..=4, 9..=9, 20..=28),
142            CharCollection::try_from(OffsetString::new("3+1,5,11+8")?)?
143        );
144
145        Ok(())
146    }
147}