mail_headers_ng/data/
inner_item.rs

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