havok_types/
string_ptr.rs

1use crate::{NULL_STR, lib::*};
2
3/// # StringPtr
4///
5/// # C++ Info
6/// - name: `hkStringPtr`
7/// - type_size: ` 4`(x86)/` 8`(x86_64)
8/// - align: ` 4`(x86)/` 8`(x86_64)
9///
10/// # Null representation
11/// - hkx: It will not be written to the binary data.
12/// - XML: `\u{2400}`.
13/// - Rust: If it is null then [`Option::None`].(To eliminate the risk of always being null ptr by type)
14///
15/// # Deserialization patterns
16/// - hkx(`Vec<u8>`)   -> Struct([`str`] in `Cow<'_, str>`) => non copy
17/// - xml([`String`])  -> Struct([`str`] in `Cow<'_, str>`) => non copy
18/// - json: [`String`] -> Struct([`str`] in `Cow<'_, str>`) => non copy
19///
20/// # Serialization is alloc
21/// Struct([`str`]) -> (alloc [`String`])
22///
23/// [`str`]: https://doc.rust-lang.org/std/primitive.str.html
24#[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    /// Create a new `StringPtr`
36    #[inline]
37    pub const fn new(inner: Option<Cow<'a, str>>) -> Self {
38        Self { inner }
39    }
40
41    /// Get inner value.
42    #[inline]
43    pub fn into_inner(self) -> Option<Cow<'a, str>> {
44        self.inner
45    }
46
47    /// Get inner ref.
48    #[inline]
49    pub const fn get_ref(&self) -> &Option<Cow<'a, str>> {
50        &self.inner
51    }
52
53    /// Cast [`str`] with non copying.
54    #[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    /// Inner to [`Self`]
63    #[inline]
64    pub const fn from_option(s: Option<Cow<'a, str>>) -> Self {
65        Self { inner: s }
66    }
67
68    /// Null pointer or not?
69    ///
70    /// This indicates that no binary data was present.
71    #[inline]
72    pub const fn is_null(&self) -> bool {
73        self.get_ref().is_none()
74    }
75
76    /// Should the data pointed to by the pointer be written to the binary data or not?
77    ///
78    /// This is an invalid value or not.
79    #[inline]
80    pub const fn should_write_binary(&self) -> bool {
81        self.get_ref().is_some()
82    }
83
84    /// Gets a reference as [`&str`].
85    #[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}