havok_types/
string_ptr.rs1use crate::{NULL_STR, lib::*};
2
3#[cfg_attr(feature = "json_schema", derive(schemars::JsonSchema))]
25#[cfg_attr(feature = "json_schema", schemars(transparent))]
26#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
27#[cfg_attr(feature = "serde", serde(transparent))]
28#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
29pub struct StringPtr<'a> {
30 #[cfg_attr(feature = "serde", serde(borrow))]
31 inner: Option<Cow<'a, str>>,
32}
33
34impl<'a> StringPtr<'a> {
35 #[inline]
37 pub const fn new(inner: Option<Cow<'a, str>>) -> Self {
38 Self { inner }
39 }
40
41 #[inline]
43 pub fn into_inner(self) -> Option<Cow<'a, str>> {
44 self.inner
45 }
46
47 #[inline]
49 pub const fn get_ref(&self) -> &Option<Cow<'a, str>> {
50 &self.inner
51 }
52
53 #[allow(clippy::should_implement_trait)]
55 #[inline]
56 pub const fn from_str(s: &'a str) -> Self {
57 Self {
58 inner: Some(Cow::Borrowed(s)),
59 }
60 }
61
62 #[inline]
64 pub const fn from_option(s: Option<Cow<'a, str>>) -> Self {
65 Self { inner: s }
66 }
67
68 #[inline]
72 pub const fn is_null(&self) -> bool {
73 self.get_ref().is_none()
74 }
75
76 #[inline]
80 pub const fn should_write_binary(&self) -> bool {
81 self.get_ref().is_some()
82 }
83
84 #[inline]
86 pub fn as_str(&self) -> &str {
87 self.inner.as_ref().map_or(NULL_STR, |s| s.as_ref())
88 }
89}
90
91impl fmt::Display for StringPtr<'_> {
92 #[inline]
93 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
94 write!(f, "{}", self.as_str())
95 }
96}
97
98impl PartialEq<str> for StringPtr<'_> {
99 #[inline]
100 fn eq(&self, other: &str) -> bool {
101 self.as_str() == other
102 }
103}
104
105impl PartialEq<&str> for StringPtr<'_> {
106 #[inline]
107 fn eq(&self, other: &&str) -> bool {
108 self.as_str() == *other
109 }
110}
111
112impl PartialEq<String> for StringPtr<'_> {
113 #[inline]
114 fn eq(&self, other: &String) -> bool {
115 self.as_str() == other.as_str()
116 }
117}
118
119impl<'a> From<&'a str> for StringPtr<'a> {
120 #[inline]
121 fn from(s: &'a str) -> Self {
122 Self::from_str(s)
123 }
124}
125
126impl<'a> From<Cow<'a, str>> for StringPtr<'a> {
127 #[inline]
128 fn from(value: Cow<'a, str>) -> Self {
129 Self { inner: Some(value) }
130 }
131}
132
133impl<'a> From<Option<Cow<'a, str>>> for StringPtr<'a> {
134 #[inline]
135 fn from(inner: Option<Cow<'a, str>>) -> Self {
136 Self { inner }
137 }
138}
139
140#[cfg(test)]
141mod tests {
142 use super::*;
143
144 const TEST_STR: &str = "test";
145
146 #[test]
147 fn stringptr_is_null() {
148 let string_ptr = StringPtr::new(None);
149 assert!(string_ptr.is_null());
150 let string_ptr_with_value = StringPtr::from_str(TEST_STR);
151 assert!(!string_ptr_with_value.is_null());
152 }
153
154 #[test]
155 fn stringptr_should_write_binary() {
156 let string_ptr = StringPtr::from_str(TEST_STR);
157 assert!(string_ptr.should_write_binary());
158
159 let none_string_ptr = StringPtr::new(None);
160 assert!(!none_string_ptr.should_write_binary());
161 }
162
163 #[test]
164 fn stringptr_display() {
165 let string_ptr = StringPtr::from_str(TEST_STR);
166 assert_eq!(string_ptr, TEST_STR);
167
168 let none_string_ptr = StringPtr::new(None);
169 assert_eq!(none_string_ptr, NULL_STR);
170 }
171
172 #[cfg(feature = "serde")]
173 #[test]
174 fn stringptr_serialize() {
175 let string_ptr = StringPtr::from_str(TEST_STR);
176 let json = serde_json::to_string(&string_ptr).unwrap();
177 assert_eq!(json, format!("\"{TEST_STR}\""));
178
179 let none_string_ptr = StringPtr::new(None);
180 let json = serde_json::to_string(&none_string_ptr).unwrap();
181 assert_eq!(json, "null");
182 }
183
184 #[cfg(feature = "serde")]
185 #[test]
186 fn stringptr_deserialize() {
187 let json = format!("\"{TEST_STR}\"");
188 let string_ptr: StringPtr = serde_json::from_str(&json).unwrap();
189 assert_eq!(string_ptr.get_ref().as_deref(), Some(TEST_STR));
190
191 let none_json = "null";
192 let none_string_ptr: StringPtr = serde_json::from_str(none_json).unwrap();
193 assert!(none_string_ptr.is_null());
194 }
195}