mail_headers/data/
inner_item.rs

1use std::ops::Deref;
2use std::sync::Arc;
3use std::borrow::ToOwned;
4
5use owning_ref::OwningRef;
6use soft_ascii_string::{SoftAsciiString, SoftAsciiStr};
7
8#[cfg(feature="serde")]
9use serde::{Serialize, Deserialize, Serializer, Deserializer, de::Error as __Error};
10
11
12/// InnerAscii is string data container which can contain either a
13/// owned `SoftAsciiString` or a `SoftAsciiStr` reference into a shared
14/// string buffer.
15#[derive(Debug, Clone, Hash, Eq)]
16pub enum InnerAscii {
17    Owned(SoftAsciiString),
18    //by using String+SoftAsciiStr we can eliminate unessesary copies
19    Shared(OwningRef<Arc<String>, SoftAsciiStr>)
20}
21
22impl InnerAscii {
23
24    /// converts this container into on which uses underlying shared data
25    ///
26    /// if the data is already shared nothing is done.
27    /// If not the owned data is converted into the underlying string buffer
28    /// and `OwningRef` is used to enable the shared reference
29    ///
30    /// Note that the underlying buffer is no an `SoftAsciiString` but a
31    /// `String` (from which we happend to know that it fulfills the "is
32    ///  us-ascii" soft constraint). This allows us to have an `InnerAscii`
33    /// share data with a possible non us-ascii string buffer as long as
34    /// the part accessable through the `SoftAsciiStr` is ascii. Or at last
35    /// should be ascii as it's a soft constraint.
36    pub fn into_shared(self) -> Self {
37        match self {
38            InnerAscii::Owned(value) => {
39                let buffer: Arc<String> = Arc::new(value.into());
40                let orf = OwningRef::new(buffer).map(|data: &String| {
41                    // we got it from a SoftAsciiString so no check here
42                    SoftAsciiStr::from_unchecked(&**data)
43                });
44                InnerAscii::Shared(orf)
45            }
46            v => v
47        }
48    }
49}
50
51/// InnerUtf8 is string data container which can contain either a
52/// owned `String` or a `str` reference into a shared
53/// string buffer.
54#[derive(Debug, Clone, Hash, Eq)]
55pub enum InnerUtf8 {
56    Owned(String),
57    //by using String+SoftAsciiStr we can eliminate unessesary copies
58    Shared(OwningRef<Arc<String>, str>)
59}
60
61impl InnerUtf8 {
62
63    /// converts this container into on which uses underlying shared data
64    ///
65    /// if the data is already shared nothing is done.
66    /// If not the owned data is converted into the underlying string buffer
67    /// and `OwningRef` is used to enable the shared reference
68    pub fn into_shared(self) -> Self {
69        match self {
70            InnerUtf8::Owned(value) => {
71                let buffer = Arc::new(value);
72                let orf = OwningRef::new(buffer)
73                    .map(|rced| &**rced);
74                InnerUtf8::Shared(orf)
75            }
76            v => v
77        }
78    }
79}
80
81
82macro_rules! inner_impl {
83    ($name:ident, $owned_form:ty, $borrowed_form:ty) => (
84        impl $name {
85            pub fn new<S: Into<$owned_form>>( data: S ) -> Self {
86                $name::Owned( data.into() )
87            }
88        }
89        impl From<$owned_form> for $name {
90            fn from( data: $owned_form ) -> Self {
91                Self::new( data )
92            }
93        }
94
95        impl Into<$owned_form> for $name {
96            fn into(self) -> $owned_form {
97                match self {
98                    $name::Owned( owned ) => owned,
99                    $name::Shared( shared ) => {
100                        let as_ref: &$borrowed_form = &*shared;
101                        as_ref.to_owned()
102                    }
103                }
104            }
105        }
106
107        impl Deref for $name {
108            type Target = $borrowed_form;
109
110            fn deref( &self ) -> &$borrowed_form{
111                match *self {
112                    $name::Owned( ref string ) => &*string,
113                    $name::Shared( ref owning_ref ) => &*owning_ref
114                }
115            }
116        }
117
118        #[cfg(feature="serde")]
119        impl Serialize for $name {
120            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
121                where S: Serializer
122            {
123                let borrowed: &$borrowed_form = &*self;
124                let as_ref: &str = borrowed.as_ref();
125                serializer.serialize_str( as_ref )
126            }
127        }
128
129        impl PartialEq for $name {
130            fn eq(&self, other: &$name) -> bool {
131                let me: &$borrowed_form = &*self;
132                let other: &$borrowed_form = &*other;
133                me == other
134            }
135        }
136
137        impl AsRef<str> for $name {
138            fn as_ref(&self) -> &str {
139                self.as_str()
140            }
141        }
142    );
143}
144
145inner_impl!{ InnerAscii, SoftAsciiString,  SoftAsciiStr }
146inner_impl!{ InnerUtf8, String, str }
147//inner_impl!{ InnerOtherItem, OtherString, OtherStr }
148
149impl InnerAscii {
150    pub fn as_str( &self ) -> &str {
151        match *self {
152            InnerAscii::Owned( ref owned ) => owned.as_str(),
153            InnerAscii::Shared( ref shared ) => shared.as_str()
154        }
155    }
156}
157
158#[cfg(feature="serde")]
159impl<'de> Deserialize<'de> for InnerAscii {
160    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
161        where D: Deserializer<'de>
162    {
163        let content = String::deserialize(deserializer)
164            .map_err(|err| D::Error::custom(err))?;
165        let content = SoftAsciiString::from_string(content)
166            .map_err(|err| D::Error::custom(err))?;
167        Ok(InnerAscii::from(content))
168    }
169}
170
171impl InnerUtf8 {
172    pub fn as_str( &self ) -> &str {
173        match *self {
174            InnerUtf8::Owned( ref owned ) => owned.as_str(),
175            InnerUtf8::Shared( ref shared ) => &**shared
176        }
177    }
178}
179
180#[cfg(feature="serde")]
181impl<'de> Deserialize<'de> for InnerUtf8 {
182    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
183        where D: Deserializer<'de>
184    {
185        let content = String::deserialize(deserializer)
186            .map_err(|err| D::Error::custom(err))?;
187        Ok(InnerUtf8::from(content))
188    }
189}
190
191
192#[cfg(test)]
193mod test {
194    use super::*;
195
196    #[test]
197    fn inner_ascii_item_eq() {
198        let a = InnerAscii::Owned( SoftAsciiString::from_string( "same" ).unwrap() );
199        let b = InnerAscii::Shared(
200            OwningRef::new(Arc::new("same".to_owned()))
201                .map(|v| SoftAsciiStr::from_unchecked(&**v))
202        );
203        assert_eq!( a, b );
204    }
205
206    #[test]
207    fn inner_ascii_item_neq() {
208        let a = InnerAscii::Owned( SoftAsciiString::from_string( "same" ).unwrap() );
209        let b = InnerAscii::Shared(
210            OwningRef::new(Arc::new("not same".to_owned()))
211                .map(|v| SoftAsciiStr::from_unchecked(&**v))
212        );
213        assert_ne!( a, b );
214    }
215
216    #[test]
217    fn inner_utf8_item_eq() {
218        let a = InnerUtf8::Owned( String::from( "same" ) );
219        let b = InnerUtf8::Shared(
220            OwningRef::new(
221                Arc::new( String::from( "same" ) ) )
222                .map(|v| &**v)
223        );
224        assert_eq!( a, b );
225    }
226
227    #[test]
228    fn inner_utf8_item_neq() {
229        let a = InnerUtf8::Owned( String::from( "same" ) );
230        let b = InnerUtf8::Shared(
231            OwningRef::new(
232                Arc::new( String::from( "not same" ) ) )
233                .map(|v| &**v)
234        );
235        assert_ne!( a, b );
236    }
237
238    #[test]
239    fn has_as_str() {
240        use std::borrow::ToOwned;
241
242        assert_eq!(
243            "hy",
244            InnerAscii::Owned( SoftAsciiStr::from_unchecked("hy").to_owned() ).as_str()
245        );
246        assert_eq!(
247            "hy",
248            InnerUtf8::Owned( "hy".into() ).as_str()
249        );
250    }
251}