mailparsing/
strings.rs

1use crate::MessageConformance;
2use bstr::{BStr, BString};
3use std::borrow::Cow;
4use std::str::Utf8Error;
5use std::sync::Arc;
6
7/// Helper for holding either an owned or borrowed string,
8/// and where the slice method is aware of that borrowing,
9/// allowing for efficient copying and slicing without
10/// making extraneous additional copies
11pub enum SharedString<'a> {
12    Owned(Arc<Vec<u8>>),
13    Borrowed(&'a [u8]),
14    Sliced {
15        other: Arc<Vec<u8>>,
16        range: std::ops::Range<usize>,
17    },
18}
19
20impl std::cmp::PartialEq<Self> for SharedString<'_> {
21    fn eq(&self, other: &Self) -> bool {
22        self.as_bytes().eq(other.as_bytes())
23    }
24}
25
26impl std::cmp::PartialEq<&str> for SharedString<'_> {
27    fn eq(&self, other: &&str) -> bool {
28        self.as_bytes().eq(other.as_bytes())
29    }
30}
31
32impl std::fmt::Display for SharedString<'_> {
33    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
34        let s = BStr::new(self.as_bytes());
35        (&s as &dyn std::fmt::Display).fmt(fmt)
36    }
37}
38
39impl std::fmt::Debug for SharedString<'_> {
40    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
41        let s = BStr::new(self.as_bytes());
42        (&s as &dyn std::fmt::Debug).fmt(fmt)
43    }
44}
45
46impl std::ops::Index<usize> for SharedString<'_> {
47    type Output = u8;
48    fn index(&self, index: usize) -> &u8 {
49        &self.as_bytes()[index]
50    }
51}
52
53impl Clone for SharedString<'_> {
54    fn clone(&self) -> Self {
55        match self {
56            Self::Owned(s) => Self::Sliced {
57                other: Arc::clone(s),
58                range: 0..s.len(),
59            },
60            Self::Borrowed(s) => Self::Borrowed(s),
61            Self::Sliced { other, range } => Self::Sliced {
62                other: Arc::clone(other),
63                range: range.clone(),
64            },
65        }
66    }
67}
68
69impl SharedString<'_> {
70    pub fn slice(&self, slice_range: std::ops::Range<usize>) -> Self {
71        self.assert_slice(slice_range.clone());
72        match self {
73            Self::Owned(s) => Self::Sliced {
74                other: Arc::clone(s),
75                range: slice_range,
76            },
77            Self::Borrowed(s) => Self::Borrowed(s.get(slice_range).unwrap()),
78            Self::Sliced { other, range } => {
79                let len = slice_range.end - slice_range.start;
80                Self::Sliced {
81                    other: Arc::clone(other),
82                    range: range.start + slice_range.start..range.start + slice_range.start + len,
83                }
84            }
85        }
86    }
87
88    fn assert_slice(&self, slice_range: std::ops::Range<usize>) {
89        if self.as_bytes().get(slice_range.clone()).is_none() {
90            panic!("slice range {slice_range:?} is invalid for {self:?}");
91        }
92    }
93
94    pub fn as_bytes(&self) -> &[u8] {
95        match self {
96            Self::Owned(s) => s,
97            Self::Borrowed(s) => s,
98            Self::Sliced { other, range } => other.get(range.clone()).unwrap(),
99        }
100    }
101
102    pub fn to_str(&self) -> Result<&str, Utf8Error> {
103        std::str::from_utf8(self.as_bytes())
104    }
105
106    pub fn to_str_lossy(&self) -> Cow<'_, str> {
107        String::from_utf8_lossy(self.as_bytes())
108    }
109
110    pub fn len(&self) -> usize {
111        match self {
112            Self::Owned(s) => s.len(),
113            Self::Borrowed(s) => s.len(),
114            Self::Sliced { range, .. } => range.len(),
115        }
116    }
117
118    pub fn to_owned(&'_ self) -> SharedString<'static> {
119        match self {
120            SharedString::Owned(s) => SharedString::Owned(Arc::clone(s)),
121            SharedString::Borrowed(s) => SharedString::Owned(Arc::new(s.to_vec())),
122            SharedString::Sliced { other, range } => SharedString::Sliced {
123                other: other.clone(),
124                range: range.clone(),
125            },
126        }
127    }
128}
129
130impl From<BString> for SharedString<'_> {
131    fn from(s: BString) -> Self {
132        let v: Vec<u8> = s.into();
133        v.into()
134    }
135}
136
137impl From<String> for SharedString<'_> {
138    fn from(s: String) -> Self {
139        Self::Owned(Arc::new(s.into_bytes()))
140    }
141}
142
143impl From<Vec<u8>> for SharedString<'_> {
144    fn from(s: Vec<u8>) -> Self {
145        Self::Owned(Arc::new(s))
146    }
147}
148
149impl<'a> From<&'a str> for SharedString<'a> {
150    fn from(s: &'a str) -> Self {
151        Self::Borrowed(s.as_bytes())
152    }
153}
154
155impl<'a> From<&'a [u8]> for SharedString<'a> {
156    fn from(s: &'a [u8]) -> Self {
157        Self::Borrowed(s)
158    }
159}
160
161pub trait IntoSharedString<'a> {
162    fn into_shared_string(self) -> (SharedString<'a>, MessageConformance);
163}
164
165impl<'a> IntoSharedString<'a> for SharedString<'a> {
166    fn into_shared_string(self) -> (SharedString<'a>, MessageConformance) {
167        (self, MessageConformance::default())
168    }
169}
170
171impl<'a> IntoSharedString<'a> for String {
172    fn into_shared_string(self) -> (SharedString<'a>, MessageConformance) {
173        (
174            SharedString::Owned(Arc::new(self.into_bytes())),
175            MessageConformance::default(),
176        )
177    }
178}
179
180impl<'a> IntoSharedString<'a> for &'a str {
181    fn into_shared_string(self) -> (SharedString<'a>, MessageConformance) {
182        (
183            SharedString::Borrowed(self.as_bytes()),
184            MessageConformance::default(),
185        )
186    }
187}
188
189impl<'a> IntoSharedString<'a> for &'a [u8] {
190    fn into_shared_string(self) -> (SharedString<'a>, MessageConformance) {
191        match std::str::from_utf8(self) {
192            Ok(s) => (
193                SharedString::Borrowed(s.as_bytes()),
194                MessageConformance::default(),
195            ),
196            Err(_) => (
197                SharedString::Borrowed(self),
198                MessageConformance::NEEDS_TRANSFER_ENCODING,
199            ),
200        }
201    }
202}
203
204impl<'a> IntoSharedString<'a> for BString {
205    fn into_shared_string(self) -> (SharedString<'a>, MessageConformance) {
206        let bytes: Vec<u8> = self.into();
207        match std::str::from_utf8(&bytes) {
208            Ok(_) => (bytes.into(), MessageConformance::default()),
209            Err(_) => (bytes.into(), MessageConformance::NEEDS_TRANSFER_ENCODING),
210        }
211    }
212}