Skip to main content

der/asn1/
teletex_string.rs

1//! ASN.1 `TeletexString` support.
2//!
3use crate::{FixedTag, Result, StringRef, Tag, asn1::AnyRef};
4use core::{fmt, ops::Deref};
5
6macro_rules! impl_teletex_string {
7    ($type: ty) => {
8        impl_teletex_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::TeletexString;
15        }
16
17        impl<$($li),*> fmt::Debug for $type {
18            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
19                write!(f, "TeletexString({:?})", self.as_str())
20            }
21        }
22    };
23}
24
25/// ASN.1 `TeletexString` type.
26///
27/// Supports a subset the ASCII character set (described below).
28///
29/// For UTF-8, use [`Utf8StringRef`][`crate::asn1::Utf8StringRef`] instead.
30/// For the full ASCII character set, use
31/// [`Ia5StringRef`][`crate::asn1::Ia5StringRef`].
32///
33/// This is a zero-copy reference type which borrows from the input data.
34///
35/// # Supported characters
36///
37/// The standard defines a complex character set allowed in this type. However,
38/// [quoting the ASN.1 mailing list]:
39///
40/// > "a sizable volume of software in the world treats TeletexString (T61String) as
41/// > a simple 8-bit string with mostly Windows Latin 1 (superset of iso-8859-1) encoding".
42///
43/// [quoting the ASN.1 mailing list]: https://www.mail-archive.com/asn1@asn1.org/msg00460.html
44#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord)]
45pub struct TeletexStringRef<'a> {
46    /// Inner value
47    inner: &'a StringRef,
48}
49
50impl<'a> TeletexStringRef<'a> {
51    /// Create a new ASN.1 `TeletexString`.
52    ///
53    /// # Errors
54    /// If `input` contains characters outside the allowed range.
55    pub fn new<T>(input: &'a T) -> Result<Self>
56    where
57        T: AsRef<[u8]> + ?Sized,
58    {
59        let input = input.as_ref();
60
61        // FIXME: support higher part of the charset
62        if input.iter().any(|&c| c > 0x7F) {
63            return Err(Self::TAG.value_error().into());
64        }
65
66        StringRef::from_bytes(input)
67            .map(|inner| Self { inner })
68            .map_err(|_| Self::TAG.value_error().into())
69    }
70
71    /// Borrow the inner `str`.
72    #[must_use]
73    pub fn as_str(&self) -> &'a str {
74        self.inner.as_str()
75    }
76}
77
78impl_teletex_string!(TeletexStringRef<'a>, 'a);
79
80impl<'a> Deref for TeletexStringRef<'a> {
81    type Target = StringRef;
82
83    fn deref(&self) -> &Self::Target {
84        self.inner
85    }
86}
87
88impl<'a> From<&TeletexStringRef<'a>> for TeletexStringRef<'a> {
89    fn from(value: &TeletexStringRef<'a>) -> TeletexStringRef<'a> {
90        *value
91    }
92}
93
94impl<'a> From<TeletexStringRef<'a>> for AnyRef<'a> {
95    fn from(teletex_string: TeletexStringRef<'a>) -> AnyRef<'a> {
96        AnyRef::from_tag_and_value(Tag::TeletexString, teletex_string.inner.as_ref())
97    }
98}
99
100#[cfg(feature = "alloc")]
101pub use self::allocation::TeletexString;
102
103#[cfg(feature = "alloc")]
104mod allocation {
105    use super::TeletexStringRef;
106
107    use crate::{
108        BytesRef, Error, FixedTag, Result, StringOwned, Tag,
109        asn1::AnyRef,
110        referenced::{OwnedToRef, RefToOwned},
111    };
112    use alloc::{borrow::ToOwned, string::String};
113    use core::{fmt, ops::Deref};
114
115    /// ASN.1 `TeletexString` type.
116    ///
117    /// Supports a subset the ASCII character set (described below).
118    ///
119    /// For UTF-8, use [`Utf8StringRef`][`crate::asn1::Utf8StringRef`] instead.
120    /// For the full ASCII character set, use
121    /// [`Ia5StringRef`][`crate::asn1::Ia5StringRef`].
122    ///
123    /// # Supported characters
124    ///
125    /// The standard defines a complex character set allowed in this type. However, quoting the ASN.1
126    /// mailing list, "a sizable volume of software in the world treats `TeletexString` (`T61String`) as a
127    /// simple 8-bit string with mostly Windows Latin 1 (superset of iso-8859-1) encoding".
128    ///
129    #[derive(Clone, Eq, PartialEq, PartialOrd, Ord)]
130    pub struct TeletexString {
131        /// Inner value
132        inner: StringOwned,
133    }
134
135    impl TeletexString {
136        /// Create a new ASN.1 `TeletexString`.
137        ///
138        /// # Errors
139        /// If characters are out-of-range.
140        pub fn new<T>(input: &T) -> Result<Self>
141        where
142            T: AsRef<[u8]> + ?Sized,
143        {
144            let input = input.as_ref();
145
146            TeletexStringRef::new(input)?;
147
148            StringOwned::from_bytes(input)
149                .map(|inner| Self { inner })
150                .map_err(|_| Self::TAG.value_error().into())
151        }
152    }
153
154    impl_teletex_string!(TeletexString);
155
156    impl Deref for TeletexString {
157        type Target = StringOwned;
158
159        fn deref(&self) -> &Self::Target {
160            &self.inner
161        }
162    }
163
164    impl<'a> From<TeletexStringRef<'a>> for TeletexString {
165        fn from(value: TeletexStringRef<'a>) -> TeletexString {
166            let inner =
167                StringOwned::from_bytes(value.inner.as_bytes()).expect("Invalid TeletexString");
168            Self { inner }
169        }
170    }
171
172    impl<'a> From<&'a TeletexString> for AnyRef<'a> {
173        fn from(teletex_string: &'a TeletexString) -> AnyRef<'a> {
174            AnyRef::from_tag_and_value(
175                Tag::TeletexString,
176                BytesRef::new(teletex_string.inner.as_bytes()).expect("Invalid TeletexString"),
177            )
178        }
179    }
180
181    impl<'a> From<&'a TeletexString> for TeletexStringRef<'a> {
182        fn from(teletex_string: &'a TeletexString) -> TeletexStringRef<'a> {
183            teletex_string.owned_to_ref()
184        }
185    }
186
187    impl<'a> RefToOwned<'a> for TeletexStringRef<'a> {
188        type Owned = TeletexString;
189        fn ref_to_owned(&self) -> Self::Owned {
190            TeletexString {
191                inner: self.inner.to_owned(),
192            }
193        }
194    }
195
196    impl OwnedToRef for TeletexString {
197        type Borrowed<'a> = TeletexStringRef<'a>;
198        fn owned_to_ref(&self) -> Self::Borrowed<'_> {
199            TeletexStringRef {
200                inner: self.inner.as_ref(),
201            }
202        }
203    }
204
205    impl TryFrom<String> for TeletexString {
206        type Error = Error;
207
208        fn try_from(input: String) -> Result<Self> {
209            TeletexStringRef::new(&input)?;
210
211            StringOwned::new(input)
212                .map(|inner| Self { inner })
213                .map_err(|_| Self::TAG.value_error().into())
214        }
215    }
216}
217
218#[cfg(test)]
219#[allow(clippy::unwrap_used)]
220mod tests {
221    use super::TeletexStringRef;
222    use crate::Decode;
223    use crate::SliceWriter;
224
225    #[test]
226    fn parse_bytes() {
227        let example_bytes = &[
228            0x14, 0x0b, 0x54, 0x65, 0x73, 0x74, 0x20, 0x55, 0x73, 0x65, 0x72, 0x20, 0x31,
229        ];
230
231        let teletex_string = TeletexStringRef::from_der(example_bytes).unwrap();
232        assert_eq!(teletex_string.as_str(), "Test User 1");
233        let mut out = [0_u8; 30];
234        let mut writer = SliceWriter::new(&mut out);
235        writer.encode(&teletex_string).unwrap();
236        let encoded = writer.finish().unwrap();
237        assert_eq!(encoded, example_bytes);
238    }
239}