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