Skip to main content

der/asn1/
utf8_string.rs

1//! ASN.1 `UTF8String` support.
2
3use crate::{
4    EncodeValue, Error, FixedTag, Length, Result, StringRef, Tag, Writer, asn1::AnyRef,
5    ord::OrdIsValueOrd,
6};
7use core::{fmt, ops::Deref, str};
8
9#[cfg(feature = "alloc")]
10use {
11    crate::{DecodeValue, Header, Reader},
12    alloc::{borrow::ToOwned, string::String},
13};
14
15/// ASN.1 `UTF8String` type.
16///
17/// Supports the full UTF-8 encoding.
18///
19/// Note that the [`Decode`][`crate::Decode`] and [`Encode`][`crate::Encode`]
20/// traits are impl'd for Rust's [`str`][`prim@str`] primitive, which
21/// decodes/encodes as a [`Utf8StringRef`].
22///
23/// You are free to use [`str`][`prim@str`] instead of this type, however it's
24/// still provided for explicitness in cases where it might be ambiguous with
25/// other ASN.1 string encodings such as
26/// [`PrintableStringRef`][`crate::asn1::PrintableStringRef`].
27///
28/// This is a zero-copy reference type which borrows from the input data.
29#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord)]
30pub struct Utf8StringRef<'a> {
31    /// Inner value
32    inner: &'a StringRef,
33}
34
35impl<'a> Utf8StringRef<'a> {
36    /// Create a new ASN.1 `UTF8String`.
37    ///
38    /// # Errors
39    /// If `input` contains invalid characters.
40    pub fn new<T>(input: &'a T) -> Result<Self>
41    where
42        T: AsRef<[u8]> + ?Sized,
43    {
44        StringRef::from_bytes(input.as_ref()).map(|inner| Self { inner })
45    }
46
47    /// Borrow the inner `str`.
48    #[must_use]
49    pub fn as_str(&self) -> &'a str {
50        self.inner.as_str()
51    }
52}
53
54impl_string_type!(Utf8StringRef<'a>, 'a);
55
56impl<'a> Deref for Utf8StringRef<'a> {
57    type Target = StringRef;
58
59    fn deref(&self) -> &Self::Target {
60        self.inner
61    }
62}
63
64impl FixedTag for Utf8StringRef<'_> {
65    const TAG: Tag = Tag::Utf8String;
66}
67
68impl<'a> From<&Utf8StringRef<'a>> for Utf8StringRef<'a> {
69    fn from(value: &Utf8StringRef<'a>) -> Utf8StringRef<'a> {
70        *value
71    }
72}
73
74impl<'a> From<Utf8StringRef<'a>> for AnyRef<'a> {
75    fn from(utf_string: Utf8StringRef<'a>) -> AnyRef<'a> {
76        AnyRef::from_tag_and_value(Tag::Utf8String, utf_string.inner.as_ref())
77    }
78}
79
80impl<'a> TryFrom<&'a str> for Utf8StringRef<'a> {
81    type Error = Error;
82
83    fn try_from(s: &'a str) -> Result<Self> {
84        Self::new(s)
85    }
86}
87
88impl fmt::Debug for Utf8StringRef<'_> {
89    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
90        write!(f, "Utf8String({:?})", self.as_str())
91    }
92}
93
94impl<'a> TryFrom<AnyRef<'a>> for &'a str {
95    type Error = Error;
96
97    fn try_from(any: AnyRef<'a>) -> Result<&'a str> {
98        Utf8StringRef::try_from(any).map(|s| s.as_str())
99    }
100}
101
102impl EncodeValue for str {
103    fn value_len(&self) -> Result<Length> {
104        Utf8StringRef::new(self)?.value_len()
105    }
106
107    fn encode_value(&self, writer: &mut impl Writer) -> Result<()> {
108        Utf8StringRef::new(self)?.encode_value(writer)
109    }
110}
111
112impl FixedTag for str {
113    const TAG: Tag = Tag::Utf8String;
114}
115
116impl OrdIsValueOrd for str {}
117
118#[cfg(feature = "alloc")]
119impl<'a> From<Utf8StringRef<'a>> for String {
120    fn from(s: Utf8StringRef<'a>) -> String {
121        s.as_str().to_owned()
122    }
123}
124
125#[cfg(feature = "alloc")]
126impl<'a> TryFrom<AnyRef<'a>> for String {
127    type Error = Error;
128
129    fn try_from(any: AnyRef<'a>) -> Result<String> {
130        Utf8StringRef::try_from(any).map(|s| s.as_str().to_owned())
131    }
132}
133
134#[cfg(feature = "alloc")]
135impl<'a> TryFrom<&'a String> for Utf8StringRef<'a> {
136    type Error = Error;
137
138    fn try_from(s: &'a String) -> Result<Self> {
139        Self::new(s.as_str())
140    }
141}
142
143#[cfg(feature = "alloc")]
144impl<'a> DecodeValue<'a> for String {
145    type Error = Error;
146
147    fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> {
148        Ok(String::from_utf8(reader.read_vec(header.length())?)?)
149    }
150}
151
152#[cfg(feature = "alloc")]
153impl EncodeValue for String {
154    fn value_len(&self) -> Result<Length> {
155        Utf8StringRef::new(self)?.value_len()
156    }
157
158    fn encode_value(&self, writer: &mut impl Writer) -> Result<()> {
159        Utf8StringRef::new(self)?.encode_value(writer)
160    }
161}
162
163#[cfg(feature = "alloc")]
164impl FixedTag for String {
165    const TAG: Tag = Tag::Utf8String;
166}
167
168#[cfg(feature = "alloc")]
169impl OrdIsValueOrd for String {}
170
171#[cfg(test)]
172#[allow(clippy::unwrap_used)]
173mod tests {
174    use super::Utf8StringRef;
175    use crate::Decode;
176
177    #[test]
178    fn parse_ascii_bytes() {
179        let example_bytes = &[
180            0x0c, 0x0b, 0x54, 0x65, 0x73, 0x74, 0x20, 0x55, 0x73, 0x65, 0x72, 0x20, 0x31,
181        ];
182
183        let utf8_string = Utf8StringRef::from_der(example_bytes).unwrap();
184        assert_eq!(utf8_string.as_str(), "Test User 1");
185    }
186
187    #[test]
188    fn parse_utf8_bytes() {
189        let example_bytes = &[0x0c, 0x06, 0x48, 0x65, 0x6c, 0x6c, 0xc3, 0xb3];
190        let utf8_string = Utf8StringRef::from_der(example_bytes).unwrap();
191        assert_eq!(utf8_string.as_str(), "Helló");
192    }
193}