zvariant/
str.rs

1use serde::{Deserialize, Deserializer, Serialize, Serializer};
2use std::{
3    borrow::Cow,
4    cmp::Ordering,
5    hash::{Hash, Hasher},
6    sync::Arc,
7};
8
9use crate::{Basic, Type};
10
11/// A string wrapper.
12///
13///
14/// This is very similar to the [`std::borrow::Cow`] type, but it:
15///
16/// * is specialized for strings.
17/// * treats `&'static str` as a separate type. This allows you to avoid allocations and copying
18///   when turning an `Str` instance created from a `&'static str` into an owned version in generic
19///   code that doesn't/can't assume the inner lifetime of the source `Str` instance.
20/// * `Clone` doesn't copy+allocate when the inner type is `&str`.
21///
22/// This type is used for keeping strings in a [`Value`], among other things.
23///
24/// API is provided to convert from, and to a [`&str`] and [`String`].
25///
26/// [`Value`]: enum.Value.html#variant.Str
27/// [`&str`]: https://doc.rust-lang.org/std/str/index.html
28/// [`String`]: https://doc.rust-lang.org/std/string/struct.String.html
29#[derive(Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Serialize, Deserialize)]
30pub struct Str<'a>(#[serde(borrow)] Inner<'a>);
31
32#[derive(Eq, Clone)]
33enum Inner<'a> {
34    Static(&'static str),
35    Borrowed(&'a str),
36    Owned(Arc<str>),
37}
38
39impl Default for Inner<'_> {
40    fn default() -> Self {
41        Self::Static("")
42    }
43}
44
45impl<'a> PartialEq for Inner<'a> {
46    fn eq(&self, other: &Inner<'a>) -> bool {
47        self.as_str() == other.as_str()
48    }
49}
50
51impl<'a> Ord for Inner<'a> {
52    fn cmp(&self, other: &Inner<'a>) -> Ordering {
53        self.as_str().cmp(other.as_str())
54    }
55}
56
57impl<'a> PartialOrd for Inner<'a> {
58    fn partial_cmp(&self, other: &Inner<'a>) -> Option<Ordering> {
59        Some(self.cmp(other))
60    }
61}
62
63impl Hash for Inner<'_> {
64    fn hash<H: Hasher>(&self, h: &mut H) {
65        self.as_str().hash(h)
66    }
67}
68
69impl Inner<'_> {
70    /// The underlying string.
71    pub fn as_str(&self) -> &str {
72        match self {
73            Inner::Static(s) => s,
74            Inner::Borrowed(s) => s,
75            Inner::Owned(s) => s,
76        }
77    }
78}
79
80impl Serialize for Inner<'_> {
81    fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
82        s.serialize_str(self.as_str())
83    }
84}
85
86impl<'de: 'a, 'a> Deserialize<'de> for Inner<'a> {
87    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
88    where
89        D: Deserializer<'de>,
90    {
91        <&'a str>::deserialize(deserializer).map(Inner::Borrowed)
92    }
93}
94
95impl Str<'_> {
96    /// An owned string without allocations
97    pub const fn from_static(s: &'static str) -> Self {
98        Str(Inner::Static(s))
99    }
100
101    /// This is faster than `Clone::clone` when `self` contains owned data.
102    pub fn as_ref(&self) -> Str<'_> {
103        match &self.0 {
104            Inner::Static(s) => Str(Inner::Static(s)),
105            Inner::Borrowed(s) => Str(Inner::Borrowed(s)),
106            Inner::Owned(s) => Str(Inner::Borrowed(s)),
107        }
108    }
109
110    /// The underlying string.
111    pub fn as_str(&self) -> &str {
112        self.0.as_str()
113    }
114
115    /// Creates an owned clone of `self`.
116    pub fn to_owned(&self) -> Str<'static> {
117        self.clone().into_owned()
118    }
119
120    /// Creates an owned clone of `self`.
121    pub fn into_owned(self) -> Str<'static> {
122        match self.0 {
123            Inner::Static(s) => Str(Inner::Static(s)),
124            Inner::Borrowed(s) => Str(Inner::Owned(s.to_owned().into())),
125            Inner::Owned(s) => Str(Inner::Owned(s)),
126        }
127    }
128}
129
130impl Basic for Str<'_> {
131    const SIGNATURE_CHAR: char = <&str>::SIGNATURE_CHAR;
132    const SIGNATURE_STR: &'static str = <&str>::SIGNATURE_STR;
133}
134
135impl Type for Str<'_> {
136    const SIGNATURE: &'static crate::Signature = &crate::Signature::Str;
137}
138
139impl<'a> From<&'a str> for Str<'a> {
140    fn from(value: &'a str) -> Self {
141        Self(Inner::Borrowed(value))
142    }
143}
144
145impl<'a> From<&'a String> for Str<'a> {
146    fn from(value: &'a String) -> Self {
147        Self(Inner::Borrowed(value))
148    }
149}
150
151impl From<String> for Str<'_> {
152    fn from(value: String) -> Self {
153        Self(Inner::Owned(value.into()))
154    }
155}
156
157impl From<Arc<str>> for Str<'_> {
158    fn from(value: Arc<str>) -> Self {
159        Self(Inner::Owned(value))
160    }
161}
162
163impl<'a> From<Cow<'a, str>> for Str<'a> {
164    fn from(value: Cow<'a, str>) -> Self {
165        match value {
166            Cow::Owned(value) => value.into(),
167            Cow::Borrowed(value) => value.into(),
168        }
169    }
170}
171
172impl<'a> From<Str<'a>> for String {
173    fn from(value: Str<'a>) -> String {
174        match value.0 {
175            Inner::Static(s) => s.into(),
176            Inner::Borrowed(s) => s.into(),
177            Inner::Owned(s) => s.to_string(),
178        }
179    }
180}
181
182impl<'a> From<&'a Str<'_>> for &'a str {
183    fn from(value: &'a Str<'_>) -> &'a str {
184        value.as_str()
185    }
186}
187
188impl std::ops::Deref for Str<'_> {
189    type Target = str;
190
191    fn deref(&self) -> &Self::Target {
192        self.as_str()
193    }
194}
195
196impl PartialEq<str> for Str<'_> {
197    fn eq(&self, other: &str) -> bool {
198        self.as_str() == other
199    }
200}
201
202impl PartialEq<&str> for Str<'_> {
203    fn eq(&self, other: &&str) -> bool {
204        self.as_str() == *other
205    }
206}
207
208impl std::fmt::Debug for Str<'_> {
209    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
210        std::fmt::Debug::fmt(self.as_str(), f)
211    }
212}
213
214impl std::fmt::Display for Str<'_> {
215    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
216        std::fmt::Display::fmt(self.as_str(), f)
217    }
218}
219
220#[cfg(test)]
221mod tests {
222    use super::Str;
223
224    #[test]
225    fn from_string() {
226        let string = String::from("value");
227        let v = Str::from(&string);
228        assert_eq!(v.as_str(), "value");
229    }
230
231    #[test]
232    fn test_ordering() {
233        let first = Str::from("a".to_string());
234        let second = Str::from_static("b");
235        assert!(first < second);
236    }
237}