async_nats/
subject.rs

1use bytes::Bytes;
2use serde::{Deserialize, Serialize};
3use std::fmt;
4use std::ops::Deref;
5use std::str::{from_utf8, Utf8Error};
6
7/// A `Subject` is an immutable string type that guarantees valid UTF-8 contents.
8#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
9pub struct Subject {
10    bytes: Bytes,
11}
12
13impl Subject {
14    /// Creates a new `Subject` from Bytes.
15    ///
16    /// # Safety
17    /// Function is unsafe because it does not check if the bytes are valid UTF-8.
18    ///
19    /// # Examples
20    ///
21    /// ```
22    /// use async_nats::Subject;
23    /// use bytes::Bytes;
24    ///
25    /// let bytes = Bytes::from_static(b"Static string");
26    ///
27    /// let subject = unsafe { Subject::from_bytes_unchecked(bytes) };
28    /// assert_eq!(subject.as_str(), "Static string");
29    /// ```
30    pub const unsafe fn from_bytes_unchecked(bytes: Bytes) -> Self {
31        Subject { bytes }
32    }
33
34    /// Creates a new `Subject` from a static string.
35    ///
36    /// # Examples
37    ///
38    /// ```
39    /// use async_nats::Subject;
40    ///
41    /// let subject = Subject::from_static("Static string");
42    /// assert_eq!(subject.as_str(), "Static string");
43    /// ```
44    pub const fn from_static(input: &'static str) -> Self {
45        Subject {
46            bytes: Bytes::from_static(input.as_bytes()),
47        }
48    }
49
50    /// Creates a new `Subject` from a UTF-8 encoded byte vector.
51    ///
52    /// Returns an error if the input is not valid UTF-8.
53    ///
54    /// # Examples
55    ///
56    /// ```
57    /// use async_nats::Subject;
58    ///
59    /// let utf8_input = vec![72, 101, 108, 108, 111]; // "Hello" in UTF-8
60    /// let subject = Subject::from_utf8(utf8_input).unwrap();
61    /// assert_eq!(subject.as_ref(), "Hello");
62    /// ```
63    pub fn from_utf8<T>(input: T) -> Result<Self, Utf8Error>
64    where
65        T: Into<Bytes>,
66    {
67        let bytes = input.into();
68        from_utf8(bytes.as_ref())?;
69
70        Ok(Subject { bytes })
71    }
72
73    /// Extracts a string slice containing the entire `Subject`.
74    ///
75    /// # Examples
76    ///
77    /// Basic usage:
78    ///
79    /// ```
80    /// use async_nats::Subject;
81    ///
82    /// let s = Subject::from("foo");
83    /// assert_eq!("foo", s.as_str());
84    /// ```
85    #[inline]
86    pub fn as_str(&self) -> &str {
87        self
88    }
89
90    /// Turns the `Subject` into a `String`, consuming it.
91    ///
92    /// Note that this function is not implemented as `From<Subject> for String` as the conversion
93    /// from the underlying type could involve an allocation. If the `Subject` is owned data, it
94    /// will not allocate, but if it was constructed from borrowed data, it will.
95    ///
96    /// # Examples
97    ///
98    /// ```
99    /// use async_nats::Subject;
100    ///
101    /// let s = Subject::from("foo");
102    /// let sub = s.into_string();
103    /// ```
104    pub fn into_string(self) -> String {
105        // SAFETY: We have guaranteed that the bytes in the `Subject` struct are valid UTF-8.
106        unsafe { String::from_utf8_unchecked(self.bytes.into()) }
107    }
108}
109
110impl<'a> From<&'a str> for Subject {
111    fn from(s: &'a str) -> Self {
112        // Since &str is guaranteed to be valid UTF-8, we can create the Subject instance by copying the contents of the &str
113        Subject {
114            bytes: Bytes::copy_from_slice(s.as_bytes()),
115        }
116    }
117}
118
119impl From<String> for Subject {
120    fn from(s: String) -> Self {
121        // Since the input `String` is guaranteed to be valid UTF-8, we can
122        // safely transmute the internal Vec<u8> to a Bytes value.
123        let bytes = Bytes::from(s.into_bytes());
124        Subject { bytes }
125    }
126}
127
128impl TryFrom<Bytes> for Subject {
129    type Error = Utf8Error;
130
131    fn try_from(bytes: Bytes) -> Result<Self, Self::Error> {
132        from_utf8(bytes.as_ref())?;
133        Ok(Subject { bytes })
134    }
135}
136
137impl AsRef<str> for Subject {
138    fn as_ref(&self) -> &str {
139        self
140    }
141}
142
143impl Deref for Subject {
144    type Target = str;
145
146    fn deref(&self) -> &Self::Target {
147        // SAFETY: It is safe to perform an unchecked conversion from bytes to a string slice
148        // here because we guarantee that the bytes in the `Subject` struct are valid UTF-8.
149        // This is enforced during the construction of `Subject` through the `from_static`,
150        // and `from_utf8` methods. In both cases, the input is either checked for UTF-8 validity or
151        // known to be valid UTF-8 as a static string.
152        unsafe { std::str::from_utf8_unchecked(&self.bytes) }
153    }
154}
155
156impl fmt::Display for Subject {
157    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
158        write!(f, "{}", self.as_str())
159    }
160}
161
162pub trait ToSubject {
163    fn to_subject(&self) -> Subject;
164}
165
166impl ToSubject for Subject {
167    fn to_subject(&self) -> Subject {
168        self.to_owned()
169    }
170}
171
172impl ToSubject for &'static str {
173    fn to_subject(&self) -> Subject {
174        Subject::from_static(self)
175    }
176}
177
178impl ToSubject for String {
179    fn to_subject(&self) -> Subject {
180        Subject::from(self.as_str())
181    }
182}
183
184impl Serialize for Subject {
185    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
186    where
187        S: serde::Serializer,
188    {
189        serializer.serialize_str(self.as_str())
190    }
191}
192
193impl<'de> Deserialize<'de> for Subject {
194    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
195    where
196        D: serde::Deserializer<'de>,
197    {
198        Ok(String::deserialize(deserializer)?.into())
199    }
200}