1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
use std::cmp::PartialEq;
use std::fmt;
use std::ops::Deref;

macro_rules! def_static_names {
    ($($(#[$attr:meta])* $n:ident = $t:tt;)*) => ($(
        $(#[$attr])*
        pub const $n: Name = Name { source: $t };
    )*);
}

//the main types
def_static_names! {
    APPLICATION = "application";
    AUDIO = "audio";
    FONT = "font";
    IMAGE = "image";
    MESSAGE = "message";
    MODEL = "model";
    MULTIPART = "multipart";
    TEXT = "text";
    VIDEO = "video";
}

// some sub types
def_static_names! {
    PLAIN = "plain";
    JAVASCRIPT = "javascript";
    PNG = "png";
    SVG_XML = "svg+xml";
    OCTET_STREAM = "octet-stream";
    RELATED = "related";
    MIXED = "mixed";
    ALTERNATIVE = "alternative";
    //TODO more of them
}

// some fields
def_static_names! {
    CHARSET = "charset";
    BOUNDARY = "boundary";
}



//TODO add Spec :=/
/// A name section of a `Mime`.
///
/// For instance, for the Mime `image/svg+xml`, it contains 3 `Name`s,
/// `image`, `svg`, and `xml`.
///
/// In all cases, `Name`s are compared case insensitive.
#[derive(Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Hash)]
pub struct Name<'a> {
    // TODO: optimize with an Atom-like thing
    // There a `const` Names, and so it is possible for the static strings
    // to have a different memory address. Additionally, when used in match
    // statements, the strings are compared with a memcmp, possibly even
    // if the address and length are the same.
    //
    // Being an enum with an Atom variant that is a usize (and without a
    // string pointer and boolean) would allow for faster comparisons.
    /// the underlying str slice, which is _required to be lowercase_.
    /// Comparisons between two Name instances expect this, as they
    /// have to use `derive(PartialEq)` to be usable in a pattern
    source: &'a str,
}

impl<'a> Name<'a> {

    #[inline]
    pub(crate) fn new_unchecked(source: &'a str) -> Name<'a> {
        Name { source }
    }
}


impl<'a> Name<'a> {
    /// Get the value of this `Name` as a string.
    ///
    /// Note that the borrow is not tied to `&self` but the `'a` lifetime, allowing the
    /// string to outlive `Name`. Alternately, there is an `impl<'a> From<Name<'a>> for &'a str`
    /// which isn't rendered by Rustdoc, that can be accessed using `str::from(name)` or `name.into()`.
    pub fn as_str(&self) -> &'a str {
        self.source
    }
}

impl<'a> Deref for Name<'a> {
    type Target = str;
    fn deref(&self) -> &str {
        self.source
    }
}

impl<'a> PartialEq<str> for Name<'a> {
    #[inline]
    fn eq(&self, other: &str) -> bool {
        self.source.eq_ignore_ascii_case(other)
    }
}

impl<'a, 'b> PartialEq<&'b str> for Name<'a> {
    #[inline]
    fn eq(&self, other: & &'b str) -> bool {
        self == *other
    }
}

impl<'a> PartialEq<Name<'a>> for str {
    #[inline]
    fn eq(&self, other: &Name<'a>) -> bool {
        other == self
    }
}

impl<'a, 'b> PartialEq<Name<'a>> for &'b str {
    #[inline]
    fn eq(&self, other: &Name<'a>) -> bool {
        other == self
    }
}

impl<'a> AsRef<str> for Name<'a> {
    #[inline]
    fn as_ref(&self) -> &str {
        self.source
    }
}

impl<'a> From<Name<'a>> for &'a str {
    #[inline]
    fn from(name: Name<'a>) -> &'a str {
        name.source
    }
}

impl<'a> fmt::Debug for Name<'a> {
    #[inline]
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Debug::fmt(self.source, f)
    }
}

impl<'a> fmt::Display for Name<'a> {
    #[inline]
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Display::fmt(self.source, f)
    }
}


#[cfg(test)]
mod test {
    use super::Name;

    #[test]
    fn test_name_eq_str() {
        let param = Name { source: "abc" };

        assert_eq!(param, param);
        assert_eq!(param, "ABC");
        assert_eq!("ABC", param);
        assert_eq!(param, "abc");
        assert_eq!("abc", param);
    }

    #[test]
    fn test_name_eq_name() {
        let n1 = Name::new_unchecked("abc");
        let n2 = Name::new_unchecked("abc");
        assert_eq!(n1, n2);

        let n3 = Name::new_unchecked("aBc");
        assert_ne!(n1, n3, concat!(
            "while Name is case insensitive it needs to derive(PartialEq) to be usable in match\n",
            "as such names can only be constructed from lowercase strings"
        ));

        assert_eq!(n1, n3.as_str());
        assert_eq!(n3, n1.as_str());
    }


}