1
//! ASN.1 `IA5String` support.
2

            
3
use crate::{asn1::AnyRef, FixedTag, Result, StrRef, Tag};
4
use core::{fmt, ops::Deref};
5

            
6
macro_rules! impl_ia5_string {
7
    ($type: ty) => {
8
        impl_ia5_string!($type,);
9
    };
10
    ($type: ty, $($li: lifetime)?) => {
11
        impl_string_type!($type, $($li),*);
12

            
13
        impl<$($li),*> FixedTag for $type {
14
            const TAG: Tag = Tag::Ia5String;
15
        }
16

            
17
        impl<$($li),*> fmt::Debug for $type {
18
            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
19
                write!(f, "Ia5String({:?})", self.as_str())
20
            }
21
        }
22
    };
23
}
24

            
25
/// ASN.1 `IA5String` type.
26
///
27
/// Supports the [International Alphabet No. 5 (IA5)] character encoding, i.e.
28
/// the lower 128 characters of the ASCII alphabet. (Note: IA5 is now
29
/// technically known as the International Reference Alphabet or IRA as
30
/// specified in the ITU-T's T.50 recommendation).
31
///
32
/// For UTF-8, use [`Utf8StringRef`][`crate::asn1::Utf8StringRef`].
33
///
34
/// This is a zero-copy reference type which borrows from the input data.
35
///
36
/// [International Alphabet No. 5 (IA5)]: https://en.wikipedia.org/wiki/T.50_%28standard%29
37
#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord)]
38
pub struct Ia5StringRef<'a> {
39
    /// Inner value
40
    inner: StrRef<'a>,
41
}
42

            
43
impl<'a> Ia5StringRef<'a> {
44
    /// Create a new `IA5String`.
45
    pub fn new<T>(input: &'a T) -> Result<Self>
46
    where
47
        T: AsRef<[u8]> + ?Sized,
48
    {
49
        let input = input.as_ref();
50

            
51
        // Validate all characters are within IA5String's allowed set
52
        if input.iter().any(|&c| c > 0x7F) {
53
            return Err(Self::TAG.value_error());
54
        }
55

            
56
        StrRef::from_bytes(input)
57
            .map(|inner| Self { inner })
58
            .map_err(|_| Self::TAG.value_error())
59
    }
60
}
61

            
62
impl_ia5_string!(Ia5StringRef<'a>, 'a);
63

            
64
impl<'a> Deref for Ia5StringRef<'a> {
65
    type Target = StrRef<'a>;
66

            
67
    fn deref(&self) -> &Self::Target {
68
        &self.inner
69
    }
70
}
71

            
72
impl<'a> From<&Ia5StringRef<'a>> for Ia5StringRef<'a> {
73
    fn from(value: &Ia5StringRef<'a>) -> Ia5StringRef<'a> {
74
        *value
75
    }
76
}
77

            
78
impl<'a> From<Ia5StringRef<'a>> for AnyRef<'a> {
79
    fn from(internationalized_string: Ia5StringRef<'a>) -> AnyRef<'a> {
80
        AnyRef::from_tag_and_value(Tag::Ia5String, internationalized_string.inner.into())
81
    }
82
}
83

            
84
#[cfg(feature = "alloc")]
85
pub use self::allocation::Ia5String;
86

            
87
#[cfg(feature = "alloc")]
88
mod allocation {
89
    use super::Ia5StringRef;
90
    use crate::{
91
        asn1::AnyRef,
92
        referenced::{OwnedToRef, RefToOwned},
93
        Error, FixedTag, Result, StrOwned, Tag,
94
    };
95
    use alloc::string::String;
96
    use core::{fmt, ops::Deref};
97

            
98
    /// ASN.1 `IA5String` type.
99
    ///
100
    /// Supports the [International Alphabet No. 5 (IA5)] character encoding, i.e.
101
    /// the lower 128 characters of the ASCII alphabet. (Note: IA5 is now
102
    /// technically known as the International Reference Alphabet or IRA as
103
    /// specified in the ITU-T's T.50 recommendation).
104
    ///
105
    /// For UTF-8, use [`String`][`alloc::string::String`].
106
    ///
107
    /// [International Alphabet No. 5 (IA5)]: https://en.wikipedia.org/wiki/T.50_%28standard%29
108
    #[derive(Clone, Eq, PartialEq, PartialOrd, Ord)]
109
    pub struct Ia5String {
110
        /// Inner value
111
        inner: StrOwned,
112
    }
113

            
114
    impl Ia5String {
115
        /// Create a new `IA5String`.
116
        pub fn new<T>(input: &T) -> Result<Self>
117
        where
118
            T: AsRef<[u8]> + ?Sized,
119
        {
120
            let input = input.as_ref();
121
            Ia5StringRef::new(input)?;
122

            
123
            StrOwned::from_bytes(input)
124
                .map(|inner| Self { inner })
125
                .map_err(|_| Self::TAG.value_error())
126
        }
127
    }
128

            
129
    impl_ia5_string!(Ia5String);
130

            
131
    impl Deref for Ia5String {
132
        type Target = StrOwned;
133

            
134
        fn deref(&self) -> &Self::Target {
135
            &self.inner
136
        }
137
    }
138

            
139
    impl<'a> From<Ia5StringRef<'a>> for Ia5String {
140
        fn from(international_string: Ia5StringRef<'a>) -> Ia5String {
141
            let inner = international_string.inner.into();
142
            Self { inner }
143
        }
144
    }
145

            
146
    impl<'a> From<&'a Ia5String> for AnyRef<'a> {
147
        fn from(international_string: &'a Ia5String) -> AnyRef<'a> {
148
            AnyRef::from_tag_and_value(Tag::Ia5String, (&international_string.inner).into())
149
        }
150
    }
151

            
152
    impl<'a> RefToOwned<'a> for Ia5StringRef<'a> {
153
        type Owned = Ia5String;
154
        fn ref_to_owned(&self) -> Self::Owned {
155
            Ia5String {
156
                inner: self.inner.ref_to_owned(),
157
            }
158
        }
159
    }
160

            
161
    impl OwnedToRef for Ia5String {
162
        type Borrowed<'a> = Ia5StringRef<'a>;
163
        fn owned_to_ref(&self) -> Self::Borrowed<'_> {
164
            Ia5StringRef {
165
                inner: self.inner.owned_to_ref(),
166
            }
167
        }
168
    }
169

            
170
    impl TryFrom<String> for Ia5String {
171
        type Error = Error;
172

            
173
        fn try_from(input: String) -> Result<Self> {
174
            Ia5StringRef::new(&input)?;
175

            
176
            StrOwned::new(input)
177
                .map(|inner| Self { inner })
178
                .map_err(|_| Self::TAG.value_error())
179
        }
180
    }
181
}
182

            
183
#[cfg(test)]
184
mod tests {
185
    use super::Ia5StringRef;
186
    use crate::Decode;
187
    use hex_literal::hex;
188

            
189
    #[test]
190
    fn parse_bytes() {
191
        let example_bytes = hex!("16 0d 74 65 73 74 31 40 72 73 61 2e 63 6f 6d");
192
        let internationalized_string = Ia5StringRef::from_der(&example_bytes).unwrap();
193
        assert_eq!(internationalized_string.as_str(), "test1@rsa.com");
194
    }
195
}