1use core::{cmp::Ordering, convert::Infallible, fmt::Debug};
2
3use super::{
4 env::internal::{Env as _, EnvBase as _, StringObject},
5 ConversionError, Env, TryFromVal, TryIntoVal, Val,
6};
7
8use crate::unwrap::{UnwrapInfallible, UnwrapOptimized};
9#[cfg(doc)]
10use crate::{storage::Storage, Map, Vec};
11
12#[cfg(not(target_family = "wasm"))]
13use super::xdr::{ScString, ScVal};
14
15#[derive(Clone)]
37pub struct String {
38 env: Env,
39 obj: StringObject,
40}
41
42impl Debug for String {
43 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
44 #[cfg(target_family = "wasm")]
45 write!(f, "String(..)")?;
46 #[cfg(not(target_family = "wasm"))]
47 write!(f, "String({})", self.to_string())?;
48 Ok(())
49 }
50}
51
52impl Eq for String {}
53
54impl PartialEq for String {
55 fn eq(&self, other: &Self) -> bool {
56 self.partial_cmp(other) == Some(Ordering::Equal)
57 }
58}
59
60impl PartialOrd for String {
61 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
62 Some(Ord::cmp(self, other))
63 }
64}
65
66impl Ord for String {
67 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
68 #[cfg(not(target_family = "wasm"))]
69 if !self.env.is_same_env(&other.env) {
70 return ScVal::from(self).cmp(&ScVal::from(other));
71 }
72 let v = self
73 .env
74 .obj_cmp(self.obj.to_val(), other.obj.to_val())
75 .unwrap_infallible();
76 v.cmp(&0)
77 }
78}
79
80impl TryFromVal<Env, String> for String {
81 type Error = ConversionError;
82
83 fn try_from_val(_env: &Env, v: &String) -> Result<Self, Self::Error> {
84 Ok(v.clone())
85 }
86}
87
88impl TryFromVal<Env, StringObject> for String {
89 type Error = Infallible;
90
91 fn try_from_val(env: &Env, val: &StringObject) -> Result<Self, Self::Error> {
92 Ok(unsafe { String::unchecked_new(env.clone(), *val) })
93 }
94}
95
96impl TryFromVal<Env, Val> for String {
97 type Error = ConversionError;
98
99 fn try_from_val(env: &Env, val: &Val) -> Result<Self, Self::Error> {
100 Ok(StringObject::try_from_val(env, val)?
101 .try_into_val(env)
102 .unwrap_infallible())
103 }
104}
105
106impl TryFromVal<Env, String> for Val {
107 type Error = ConversionError;
108
109 fn try_from_val(_env: &Env, v: &String) -> Result<Self, Self::Error> {
110 Ok(v.to_val())
111 }
112}
113
114impl From<String> for Val {
115 #[inline(always)]
116 fn from(v: String) -> Self {
117 v.obj.into()
118 }
119}
120
121impl From<String> for StringObject {
122 #[inline(always)]
123 fn from(v: String) -> Self {
124 v.obj
125 }
126}
127
128impl From<&String> for StringObject {
129 #[inline(always)]
130 fn from(v: &String) -> Self {
131 v.obj
132 }
133}
134
135impl From<&String> for String {
136 #[inline(always)]
137 fn from(v: &String) -> Self {
138 v.clone()
139 }
140}
141
142#[cfg(not(target_family = "wasm"))]
143impl From<&String> for ScVal {
144 fn from(v: &String) -> Self {
145 ScVal::try_from_val(&v.env, &v.obj.to_val()).unwrap()
151 }
152}
153
154#[cfg(not(target_family = "wasm"))]
155impl From<String> for ScVal {
156 fn from(v: String) -> Self {
157 (&v).into()
158 }
159}
160
161#[cfg(not(target_family = "wasm"))]
162impl TryFromVal<Env, ScVal> for String {
163 type Error = ConversionError;
164 fn try_from_val(env: &Env, val: &ScVal) -> Result<Self, Self::Error> {
165 Ok(
166 StringObject::try_from_val(env, &Val::try_from_val(env, val)?)?
167 .try_into_val(env)
168 .unwrap_infallible(),
169 )
170 }
171}
172
173impl TryFromVal<Env, &str> for String {
174 type Error = ConversionError;
175
176 fn try_from_val(env: &Env, v: &&str) -> Result<Self, Self::Error> {
177 Ok(String::from_str(env, v))
178 }
179}
180
181#[cfg(not(target_family = "wasm"))]
182impl ToString for String {
183 fn to_string(&self) -> std::string::String {
184 let sc_val: ScVal = self.try_into().unwrap();
185 if let ScVal::String(ScString(s)) = sc_val {
186 s.to_utf8_string().unwrap()
187 } else {
188 panic!("value is not a string");
189 }
190 }
191}
192
193impl String {
194 #[inline(always)]
195 pub(crate) unsafe fn unchecked_new(env: Env, obj: StringObject) -> Self {
196 Self { env, obj }
197 }
198
199 #[inline(always)]
200 pub fn env(&self) -> &Env {
201 &self.env
202 }
203
204 pub fn as_val(&self) -> &Val {
205 self.obj.as_val()
206 }
207
208 pub fn to_val(&self) -> Val {
209 self.obj.to_val()
210 }
211
212 pub fn as_object(&self) -> &StringObject {
213 &self.obj
214 }
215
216 pub fn to_object(&self) -> StringObject {
217 self.obj
218 }
219
220 #[inline(always)]
221 #[doc(hidden)]
222 #[deprecated(note = "use from_str")]
223 pub fn from_slice(env: &Env, slice: &str) -> String {
224 Self::from_str(env, slice)
225 }
226
227 #[inline(always)]
228 pub fn from_bytes(env: &Env, b: &[u8]) -> String {
229 String {
230 env: env.clone(),
231 obj: env.string_new_from_slice(b).unwrap_optimized(),
232 }
233 }
234
235 #[inline(always)]
236 pub fn from_str(env: &Env, s: &str) -> String {
237 String {
238 env: env.clone(),
239 obj: env.string_new_from_slice(s.as_bytes()).unwrap_optimized(),
240 }
241 }
242
243 #[inline(always)]
244 pub fn len(&self) -> u32 {
245 self.env().string_len(self.obj).unwrap_infallible().into()
246 }
247
248 #[inline(always)]
249 pub fn is_empty(&self) -> bool {
250 self.len() == 0
251 }
252
253 #[inline(always)]
259 pub fn copy_into_slice(&self, slice: &mut [u8]) {
260 let env = self.env();
261 if self.len() as usize != slice.len() {
262 sdk_panic!("String::copy_into_slice with mismatched slice length")
263 }
264 env.string_copy_to_slice(self.to_object(), Val::U32_ZERO, slice)
265 .unwrap_optimized();
266 }
267}
268
269#[cfg(test)]
270mod test {
271 use super::*;
272
273 #[test]
274 fn string_from_and_to_slices() {
275 let env = Env::default();
276
277 let msg = "a message";
278 let s = String::from_str(&env, msg);
279 let mut out = [0u8; 9];
280 s.copy_into_slice(&mut out);
281 assert_eq!(msg.as_bytes(), out)
282 }
283
284 #[test]
285 fn string_from_and_to_bytes() {
286 let env = Env::default();
287
288 let msg = b"a message";
289 let s = String::from_bytes(&env, msg);
290 let mut out = [0u8; 9];
291 s.copy_into_slice(&mut out);
292 assert_eq!(msg, &out)
293 }
294
295 #[test]
296 #[should_panic]
297 fn string_to_short_slice() {
298 let env = Env::default();
299 let msg = "a message";
300 let s = String::from_str(&env, msg);
301 let mut out = [0u8; 8];
302 s.copy_into_slice(&mut out);
303 }
304
305 #[test]
306 #[should_panic]
307 fn string_to_long_slice() {
308 let env = Env::default();
309 let msg = "a message";
310 let s = String::from_str(&env, msg);
311 let mut out = [0u8; 10];
312 s.copy_into_slice(&mut out);
313 }
314}