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}