Skip to main content

gpui_core/
shared_string.rs

1use schemars::JsonSchema;
2use serde::{Deserialize, Serialize};
3use smol_str::SmolStr;
4use std::{
5    borrow::{Borrow, Cow},
6    iter,
7    ops::Deref,
8    sync::Arc,
9};
10
11/// A shared string is an immutable string that can be cheaply cloned in GPUI
12/// tasks. Uses SmolStr for efficient storage with inline optimization for small strings.
13#[derive(Eq, PartialEq, PartialOrd, Ord, Hash, Clone, Default)]
14pub struct SharedString(SmolStr);
15
16impl SharedString {
17    /// Creates a static [`SharedString`] from a `&'static str`.
18    pub const fn new_static(str: &'static str) -> Self {
19        Self(SmolStr::new_static(str))
20    }
21
22    /// Creates a [`SharedString`] from anything that can become a SmolStr
23    pub fn new(str: impl Into<SmolStr>) -> Self {
24        SharedString(str.into())
25    }
26
27    /// Get a &str from the underlying string.
28    pub fn as_str(&self) -> &str {
29        self.0.as_str()
30    }
31}
32
33impl Deref for SharedString {
34    type Target = str;
35
36    fn deref(&self) -> &Self::Target {
37        self.0.as_str()
38    }
39}
40
41impl JsonSchema for SharedString {
42    fn inline_schema() -> bool {
43        String::inline_schema()
44    }
45
46    fn schema_name() -> Cow<'static, str> {
47        String::schema_name()
48    }
49
50    fn json_schema(generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
51        String::json_schema(generator)
52    }
53}
54
55impl AsRef<str> for SharedString {
56    fn as_ref(&self) -> &str {
57        &self.0
58    }
59}
60
61impl Borrow<str> for SharedString {
62    fn borrow(&self) -> &str {
63        self.as_ref()
64    }
65}
66
67impl std::fmt::Debug for SharedString {
68    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
69        self.0.fmt(f)
70    }
71}
72
73impl std::fmt::Display for SharedString {
74    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
75        write!(f, "{}", self.0.as_str())
76    }
77}
78
79impl PartialEq<String> for SharedString {
80    fn eq(&self, other: &String) -> bool {
81        self.as_ref() == other
82    }
83}
84
85impl PartialEq<SharedString> for String {
86    fn eq(&self, other: &SharedString) -> bool {
87        self == other.as_ref()
88    }
89}
90
91impl PartialEq<str> for SharedString {
92    fn eq(&self, other: &str) -> bool {
93        self.as_ref() == other
94    }
95}
96
97impl<'a> PartialEq<&'a str> for SharedString {
98    fn eq(&self, other: &&'a str) -> bool {
99        self.as_ref() == *other
100    }
101}
102
103impl From<&SharedString> for SharedString {
104    fn from(value: &SharedString) -> Self {
105        value.clone()
106    }
107}
108
109impl From<SharedString> for Arc<str> {
110    fn from(val: SharedString) -> Self {
111        // Reuses the underlying Arc when SmolStr is in its Heap variant (zero-copy).
112        Arc::<str>::from(val.0)
113    }
114}
115
116impl From<&str> for SharedString {
117    fn from(value: &str) -> Self {
118        Self(SmolStr::from(value))
119    }
120}
121
122impl From<char> for SharedString {
123    #[inline]
124    fn from(c: char) -> SharedString {
125        SharedString(SmolStr::from_iter(iter::once(c)))
126    }
127}
128
129impl From<String> for SharedString {
130    fn from(value: String) -> Self {
131        Self(SmolStr::from(value))
132    }
133}
134
135impl From<Arc<str>> for SharedString {
136    fn from(value: Arc<str>) -> Self {
137        // SmolStr's `From<Arc<str>>` keeps the original Arc on the heap path,
138        // avoiding an allocation+copy for long strings.
139        Self(SmolStr::from(value))
140    }
141}
142
143impl From<&String> for SharedString {
144    fn from(value: &String) -> Self {
145        Self(SmolStr::from(value))
146    }
147}
148
149impl From<SharedString> for String {
150    fn from(val: SharedString) -> Self {
151        val.0.to_string()
152    }
153}
154
155impl Serialize for SharedString {
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_ref())
161    }
162}
163
164impl<'de> Deserialize<'de> for SharedString {
165    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
166    where
167        D: serde::Deserializer<'de>,
168    {
169        let s = String::deserialize(deserializer)?;
170        Ok(SharedString::from(s))
171    }
172}
173
174#[cfg(test)]
175mod tests {
176    use super::SharedString;
177
178    #[test]
179    fn from_char_builds_a_one_character_string() {
180        assert_eq!(SharedString::from('a').as_str(), "a");
181        assert_eq!(SharedString::from('字').as_str(), "字");
182        let emoji: SharedString = '😀'.into();
183        assert_eq!(emoji.as_str(), "😀");
184    }
185}