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}