1use core::{cmp::Ordering, convert::Infallible, fmt::Debug};
2
3use super::{
4 env::internal::{Env as _, EnvBase as _, StringObject},
5 Bytes, ConversionError, Env, IntoVal, 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})")?;
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 TryFromVal<Env, &String> for Val {
115 type Error = ConversionError;
116
117 fn try_from_val(_env: &Env, v: &&String) -> Result<Self, Self::Error> {
118 Ok(v.to_val())
119 }
120}
121
122impl From<String> for Val {
123 #[inline(always)]
124 fn from(v: String) -> Self {
125 v.obj.into()
126 }
127}
128
129impl From<String> for StringObject {
130 #[inline(always)]
131 fn from(v: String) -> Self {
132 v.obj
133 }
134}
135
136impl From<&String> for StringObject {
137 #[inline(always)]
138 fn from(v: &String) -> Self {
139 v.obj
140 }
141}
142
143impl From<&String> for String {
144 #[inline(always)]
145 fn from(v: &String) -> Self {
146 v.clone()
147 }
148}
149
150impl From<&String> for Bytes {
151 fn from(v: &String) -> Self {
152 Env::string_to_bytes(&v.env, v.obj.clone())
153 .unwrap_infallible()
154 .into_val(&v.env)
155 }
156}
157
158impl From<String> for Bytes {
159 fn from(v: String) -> Self {
160 (&v).into()
161 }
162}
163
164#[cfg(not(target_family = "wasm"))]
165impl From<&String> for ScVal {
166 fn from(v: &String) -> Self {
167 ScVal::try_from_val(&v.env, &v.obj.to_val()).unwrap()
173 }
174}
175
176#[cfg(not(target_family = "wasm"))]
177impl From<String> for ScVal {
178 fn from(v: String) -> Self {
179 (&v).into()
180 }
181}
182
183#[cfg(not(target_family = "wasm"))]
184impl TryFromVal<Env, ScVal> for String {
185 type Error = ConversionError;
186 fn try_from_val(env: &Env, val: &ScVal) -> Result<Self, Self::Error> {
187 Ok(
188 StringObject::try_from_val(env, &Val::try_from_val(env, val)?)?
189 .try_into_val(env)
190 .unwrap_infallible(),
191 )
192 }
193}
194
195impl TryFromVal<Env, &str> for String {
196 type Error = ConversionError;
197
198 fn try_from_val(env: &Env, v: &&str) -> Result<Self, Self::Error> {
199 Ok(String::from_str(env, v))
200 }
201}
202
203#[cfg(not(target_family = "wasm"))]
204impl core::fmt::Display for String {
205 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
206 let sc_val: ScVal = self.try_into().unwrap();
207 if let ScVal::String(ScString(s)) = sc_val {
208 let utf8_s = s.to_utf8_string().unwrap();
209 write!(f, "{utf8_s}")?;
210 } else {
211 panic!("value is not a string");
212 }
213 Ok(())
214 }
215}
216
217impl String {
218 #[inline(always)]
219 pub(crate) unsafe fn unchecked_new(env: Env, obj: StringObject) -> Self {
220 Self { env, obj }
221 }
222
223 #[inline(always)]
224 pub fn env(&self) -> &Env {
225 &self.env
226 }
227
228 pub fn as_val(&self) -> &Val {
229 self.obj.as_val()
230 }
231
232 pub fn to_val(&self) -> Val {
233 self.obj.to_val()
234 }
235
236 pub fn as_object(&self) -> &StringObject {
237 &self.obj
238 }
239
240 pub fn to_object(&self) -> StringObject {
241 self.obj
242 }
243
244 #[inline(always)]
245 #[doc(hidden)]
246 #[deprecated(note = "use from_str")]
247 pub fn from_slice(env: &Env, slice: &str) -> String {
248 Self::from_str(env, slice)
249 }
250
251 #[inline(always)]
252 pub fn from_bytes(env: &Env, b: &[u8]) -> String {
253 String {
254 env: env.clone(),
255 obj: env.string_new_from_slice(b).unwrap_optimized(),
256 }
257 }
258
259 #[inline(always)]
260 pub fn from_str(env: &Env, s: &str) -> String {
261 String {
262 env: env.clone(),
263 obj: env.string_new_from_slice(s.as_bytes()).unwrap_optimized(),
264 }
265 }
266
267 #[inline(always)]
268 pub fn len(&self) -> u32 {
269 self.env().string_len(self.obj).unwrap_infallible().into()
270 }
271
272 #[inline(always)]
273 pub fn is_empty(&self) -> bool {
274 self.len() == 0
275 }
276
277 #[inline(always)]
283 pub fn copy_into_slice(&self, slice: &mut [u8]) {
284 let env = self.env();
285 if self.len() as usize != slice.len() {
286 sdk_panic!("String::copy_into_slice with mismatched slice length")
287 }
288 env.string_copy_to_slice(self.to_object(), Val::U32_ZERO, slice)
289 .unwrap_optimized();
290 }
291
292 pub fn to_bytes(&self) -> Bytes {
294 self.into()
295 }
296}
297
298#[cfg(test)]
299mod test {
300 use super::*;
301 use crate::IntoVal;
302
303 #[test]
304 fn string_from_and_to_slices() {
305 let env = Env::default();
306
307 let msg = "a message";
308 let s = String::from_str(&env, msg);
309 let mut out = [0u8; 9];
310 s.copy_into_slice(&mut out);
311 assert_eq!(msg.as_bytes(), out)
312 }
313
314 #[test]
315 fn string_from_and_to_bytes() {
316 let env = Env::default();
317
318 let msg = b"a message";
319 let s = String::from_bytes(&env, msg);
320 let mut out = [0u8; 9];
321 s.copy_into_slice(&mut out);
322 assert_eq!(msg, &out)
323 }
324
325 #[test]
326 #[should_panic]
327 fn string_to_short_slice() {
328 let env = Env::default();
329 let msg = "a message";
330 let s = String::from_str(&env, msg);
331 let mut out = [0u8; 8];
332 s.copy_into_slice(&mut out);
333 }
334
335 #[test]
336 #[should_panic]
337 fn string_to_long_slice() {
338 let env = Env::default();
339 let msg = "a message";
340 let s = String::from_str(&env, msg);
341 let mut out = [0u8; 10];
342 s.copy_into_slice(&mut out);
343 }
344
345 #[test]
346 fn string_to_val() {
347 let env = Env::default();
348
349 let s = String::from_str(&env, "abcdef");
350 let val: Val = s.clone().into_val(&env);
351 let rt: String = val.into_val(&env);
352
353 assert_eq!(s, rt);
354 }
355
356 #[test]
357 fn ref_string_to_val() {
358 let env = Env::default();
359
360 let s = String::from_str(&env, "abcdef");
361 let val: Val = (&s).into_val(&env);
362 let rt: String = val.into_val(&env);
363
364 assert_eq!(s, rt);
365 }
366
367 #[test]
368 fn double_ref_string_to_val() {
369 let env = Env::default();
370
371 let s = String::from_str(&env, "abcdef");
372 let val: Val = (&&s).into_val(&env);
373 let rt: String = val.into_val(&env);
374
375 assert_eq!(s, rt);
376 }
377
378 #[test]
379 fn test_string_to_bytes() {
380 let env = Env::default();
381 let s = String::from_str(&env, "abcdef");
382 let b: Bytes = s.clone().into();
383 assert_eq!(b.len(), 6);
384 let mut slice = [0u8; 6];
385 b.copy_into_slice(&mut slice);
386 assert_eq!(&slice, b"abcdef");
387 let b2 = s.to_bytes();
388 assert_eq!(b, b2);
389 }
390
391 #[test]
392 fn test_string_accepts_any_bytes_even_invalid_utf8() {
393 let env = Env::default();
394 let input = b"a\xc3\x28d"; let s = String::from_bytes(&env, &input[..]);
396 let b = s.to_bytes().to_buffer::<4>();
397 assert_eq!(b.as_slice(), input);
398 }
399
400 #[test]
401 fn test_string_display_to_string() {
402 let env = Env::default();
403 let input = "abcdef";
404 let s = String::from_str(&env, input);
405 let rt = s.to_string();
406 assert_eq!(input, &rt);
407 }
408
409 #[test]
410 #[should_panic = "Utf8Error"]
411 fn test_string_display_to_string_invalid_utf8() {
412 let env = Env::default();
413 let input = b"a\xc3\x28d"; let s = String::from_bytes(&env, &input[..]);
415 let _ = s.to_string();
416 }
417}