1use crate::MessageConformance;
2use std::sync::Arc;
3
4pub enum SharedString<'a> {
9 Owned(Arc<String>),
10 Borrowed(&'a str),
11 Sliced {
12 other: Arc<String>,
13 range: std::ops::Range<usize>,
14 },
15}
16
17impl std::cmp::PartialEq<Self> for SharedString<'_> {
18 fn eq(&self, other: &Self) -> bool {
19 self.as_str().eq(other.as_str())
20 }
21}
22
23impl std::cmp::PartialEq<&str> for SharedString<'_> {
24 fn eq(&self, other: &&str) -> bool {
25 self.as_str().eq(*other)
26 }
27}
28
29impl std::fmt::Display for SharedString<'_> {
30 fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
31 let str = self.as_str();
32 fmt.write_str(str)
33 }
34}
35
36impl std::fmt::Debug for SharedString<'_> {
37 fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
38 let str = self.as_str();
39 write!(fmt, "{str:?}")
40 }
41}
42
43impl std::ops::Deref for SharedString<'_> {
44 type Target = str;
45 fn deref(&self) -> &str {
46 self.as_str()
47 }
48}
49
50impl std::ops::Index<usize> for SharedString<'_> {
51 type Output = u8;
52 fn index(&self, index: usize) -> &u8 {
53 &self.as_str().as_bytes()[index]
54 }
55}
56
57impl Clone for SharedString<'_> {
58 fn clone(&self) -> Self {
59 match self {
60 Self::Owned(s) => Self::Sliced {
61 other: Arc::clone(s),
62 range: 0..s.len(),
63 },
64 Self::Borrowed(s) => Self::Borrowed(s),
65 Self::Sliced { other, range } => Self::Sliced {
66 other: Arc::clone(other),
67 range: range.clone(),
68 },
69 }
70 }
71}
72
73impl SharedString<'_> {
74 pub fn slice(&self, slice_range: std::ops::Range<usize>) -> Self {
75 self.assert_slice(slice_range.clone());
76 match self {
77 Self::Owned(s) => Self::Sliced {
78 other: Arc::clone(s),
79 range: slice_range,
80 },
81 Self::Borrowed(s) => Self::Borrowed(s.get(slice_range).unwrap()),
82 Self::Sliced { other, range } => {
83 let len = slice_range.end - slice_range.start;
84 Self::Sliced {
85 other: Arc::clone(other),
86 range: range.start + slice_range.start..range.start + slice_range.start + len,
87 }
88 }
89 }
90 }
91
92 fn assert_slice(&self, slice_range: std::ops::Range<usize>) {
93 if self.as_str().get(slice_range.clone()).is_none() {
94 panic!("slice range {slice_range:?} is invalid for {self:?}");
95 }
96 }
97
98 pub fn as_str(&self) -> &str {
99 match self {
100 Self::Owned(s) => s.as_str(),
101 Self::Borrowed(s) => s,
102 Self::Sliced { other, range } => other.as_str().get(range.clone()).unwrap(),
103 }
104 }
105
106 pub fn len(&self) -> usize {
107 match self {
108 Self::Owned(s) => s.len(),
109 Self::Borrowed(s) => s.len(),
110 Self::Sliced { range, .. } => range.len(),
111 }
112 }
113}
114
115impl From<String> for SharedString<'_> {
116 fn from(s: String) -> Self {
117 Self::Owned(Arc::new(s))
118 }
119}
120
121impl<'a> From<&'a str> for SharedString<'a> {
122 fn from(s: &'a str) -> Self {
123 Self::Borrowed(s)
124 }
125}
126
127impl<'a> TryFrom<&'a [u8]> for SharedString<'a> {
128 type Error = std::str::Utf8Error;
129 fn try_from(s: &'a [u8]) -> Result<Self, Self::Error> {
130 let s = std::str::from_utf8(s)?;
131 Ok(Self::Borrowed(s))
132 }
133}
134
135pub trait IntoSharedString<'a> {
136 fn into_shared_string(self) -> (SharedString<'a>, MessageConformance);
137}
138
139impl<'a> IntoSharedString<'a> for SharedString<'a> {
140 fn into_shared_string(self) -> (SharedString<'a>, MessageConformance) {
141 (self, MessageConformance::default())
142 }
143}
144
145impl<'a> IntoSharedString<'a> for String {
146 fn into_shared_string(self) -> (SharedString<'a>, MessageConformance) {
147 (
148 SharedString::Owned(Arc::new(self)),
149 MessageConformance::default(),
150 )
151 }
152}
153
154impl<'a> IntoSharedString<'a> for &'a str {
155 fn into_shared_string(self) -> (SharedString<'a>, MessageConformance) {
156 (SharedString::Borrowed(self), MessageConformance::default())
157 }
158}
159
160impl<'a> IntoSharedString<'a> for &'a [u8] {
161 fn into_shared_string(self) -> (SharedString<'a>, MessageConformance) {
162 match std::str::from_utf8(self) {
163 Ok(s) => (SharedString::Borrowed(s), MessageConformance::default()),
164 Err(_) => (
165 SharedString::Owned(Arc::new(String::from_utf8_lossy(self).to_string())),
166 MessageConformance::NEEDS_TRANSFER_ENCODING,
167 ),
168 }
169 }
170}