1use derive_more::{Deref, DerefMut, Display, From, Index, IndexMut, Into};
2use serde::{Deserialize, Serialize};
3use smartstring::alias::String as SmartString;
4use std::{
5 borrow::{Borrow, BorrowMut, Cow},
6 convert::Infallible,
7 ffi::OsStr,
8 fmt::{self, Write as FmtWrite},
9 iter::FromIterator,
10 path::Path,
11 str,
12 str::{FromStr, Utf8Error},
13 string::FromUtf8Error,
14};
15
16use crate::MAX_INLINE;
17
18#[cfg(feature = "postgres_types")]
19use bytes::BytesMut;
20#[cfg(feature = "postgres_types")]
21use postgres_types::{FromSql, IsNull, ToSql, Type};
22
23#[cfg(feature = "rweb-openapi")]
24use rweb::openapi::{
25 ComponentDescriptor, ComponentOrInlineSchema, Entity, ResponseEntity, Responses,
26};
27
28#[cfg(feature = "rweb-openapi")]
29use hyper::Body;
30
31#[cfg(feature = "async_graphql")]
32use async_graphql::{InputValueError, InputValueResult, Scalar, ScalarType, Value};
33
34#[derive(
35 Display,
36 Serialize,
37 Deserialize,
38 Deref,
39 DerefMut,
40 Index,
41 IndexMut,
42 Debug,
43 Clone,
44 Into,
45 From,
46 PartialEq,
47 Eq,
48 Hash,
49 Default,
50 PartialOrd,
51 Ord,
52)]
53pub struct StackString(SmartString);
54
55impl StackString {
56 #[must_use]
57 pub fn new() -> Self {
58 Self(SmartString::new())
59 }
60
61 #[must_use]
62 pub fn as_str(&self) -> &str {
63 self.0.as_str()
64 }
65
66 #[must_use]
67 pub fn split_off(&mut self, index: usize) -> Self {
68 Self(self.0.split_off(index))
69 }
70
71 pub fn from_utf8(v: &[u8]) -> Result<Self, Utf8Error> {
76 str::from_utf8(v).map(Into::into)
77 }
78
79 pub fn from_utf8_vec(vec: Vec<u8>) -> Result<Self, FromUtf8Error> {
84 String::from_utf8(vec).map(Into::into)
85 }
86
87 #[must_use]
88 pub fn from_utf8_lossy(v: &[u8]) -> Self {
89 if v.len() > MAX_INLINE {
90 match String::from_utf8_lossy(v) {
91 Cow::Borrowed(s) => s.into(),
92 Cow::Owned(s) => s.into(),
93 }
94 } else {
95 let (v, up_to, error_len) = match str::from_utf8(v) {
96 Ok(s) => return s.into(),
97 Err(error) => (v, error.valid_up_to(), error.error_len()),
98 };
99 let mut buf = StackString::new();
100 let (valid, after_valid) = v.split_at(up_to);
101 buf.push_str(unsafe { str::from_utf8_unchecked(valid) });
102 buf.push('\u{FFFD}');
103 let mut input = after_valid;
104 if let Some(invalid_sequence_length) = error_len {
105 input = &after_valid[invalid_sequence_length..];
106 }
107 loop {
108 match str::from_utf8(input) {
109 Ok(s) => {
110 buf.push_str(s);
111 break;
112 }
113 Err(error) => {
114 let (valid, after_valid) = input.split_at(error.valid_up_to());
115 buf.push_str(unsafe { str::from_utf8_unchecked(valid) });
116 buf.push('\u{FFFD}');
117 if let Some(invalid_sequence_length) = error.error_len() {
118 input = &after_valid[invalid_sequence_length..];
119 } else {
120 break;
121 }
122 }
123 }
124 }
125 buf
126 }
127 }
128
129 pub fn from_display(buf: impl fmt::Display) -> Self {
134 let mut s = Self::new();
135 write!(s, "{buf}").unwrap();
136 s
137 }
138}
139
140impl From<StackString> for String {
141 fn from(item: StackString) -> Self {
142 item.0.into()
143 }
144}
145
146impl From<&StackString> for String {
147 fn from(item: &StackString) -> Self {
148 item.as_str().into()
149 }
150}
151
152impl From<&StackString> for StackString {
153 fn from(item: &StackString) -> Self {
154 item.clone()
155 }
156}
157
158impl From<String> for StackString {
159 fn from(item: String) -> Self {
160 Self(item.into())
161 }
162}
163
164impl From<&String> for StackString {
165 fn from(item: &String) -> Self {
166 Self(item.into())
167 }
168}
169
170impl From<&str> for StackString {
171 fn from(item: &str) -> Self {
172 Self(item.into())
173 }
174}
175
176impl<'a> From<&'a StackString> for &'a str {
177 fn from(item: &StackString) -> &str {
178 item.as_str()
179 }
180}
181
182impl<'a> From<Cow<'a, str>> for StackString {
183 fn from(item: Cow<'a, str>) -> Self {
184 match item {
185 Cow::Borrowed(s) => s.into(),
186 Cow::Owned(s) => s.into(),
187 }
188 }
189}
190
191impl From<StackString> for Cow<'_, str> {
192 fn from(item: StackString) -> Self {
193 Cow::Owned(item.into())
194 }
195}
196
197impl Borrow<str> for StackString {
198 fn borrow(&self) -> &str {
199 self.0.borrow()
200 }
201}
202
203impl BorrowMut<str> for StackString {
204 fn borrow_mut(&mut self) -> &mut str {
205 self.0.borrow_mut()
206 }
207}
208
209impl AsRef<str> for StackString {
210 fn as_ref(&self) -> &str {
211 self.0.as_str()
212 }
213}
214
215impl AsRef<[u8]> for StackString {
216 fn as_ref(&self) -> &[u8] {
217 self.0.as_ref()
218 }
219}
220
221impl AsRef<OsStr> for StackString {
222 fn as_ref(&self) -> &OsStr {
223 self.as_str().as_ref()
224 }
225}
226
227impl AsRef<Path> for StackString {
228 fn as_ref(&self) -> &Path {
229 Path::new(self)
230 }
231}
232
233impl FromStr for StackString {
234 type Err = Infallible;
235 fn from_str(s: &str) -> Result<Self, Self::Err> {
236 Ok(s.into())
237 }
238}
239
240impl<'a> PartialEq<Cow<'a, str>> for StackString {
241 #[inline]
242 fn eq(&self, other: &Cow<'a, str>) -> bool {
243 PartialEq::eq(&self[..], &other[..])
244 }
245}
246
247impl PartialEq<String> for StackString {
248 #[inline]
249 fn eq(&self, other: &String) -> bool {
250 PartialEq::eq(&self[..], &other[..])
251 }
252}
253
254impl PartialEq<str> for StackString {
255 #[inline]
256 fn eq(&self, other: &str) -> bool {
257 PartialEq::eq(&self.0, other)
258 }
259}
260
261impl<'a> PartialEq<&'a str> for StackString {
262 #[inline]
263 fn eq(&self, other: &&'a str) -> bool {
264 PartialEq::eq(&self[..], &other[..])
265 }
266}
267
268impl FromIterator<char> for StackString {
269 fn from_iter<I: IntoIterator<Item = char>>(iter: I) -> Self {
270 let s = SmartString::from_iter(iter);
271 Self(s)
272 }
273}
274
275#[cfg(feature = "postgres_types")]
276impl<'a> FromSql<'a> for StackString {
277 fn from_sql(
278 ty: &Type,
279 raw: &'a [u8],
280 ) -> Result<Self, Box<dyn std::error::Error + 'static + Send + Sync>> {
281 let s = <&'a str as FromSql>::from_sql(ty, raw)?;
282 Ok(s.into())
283 }
284
285 fn accepts(ty: &Type) -> bool {
286 <&'a str as FromSql>::accepts(ty)
287 }
288}
289
290#[cfg(feature = "postgres_types")]
291impl ToSql for StackString {
292 fn to_sql(
293 &self,
294 ty: &Type,
295 out: &mut BytesMut,
296 ) -> Result<IsNull, Box<dyn std::error::Error + Sync + Send>>
297 where
298 Self: Sized,
299 {
300 ToSql::to_sql(&self.as_str(), ty, out)
301 }
302
303 fn accepts(ty: &Type) -> bool
304 where
305 Self: Sized,
306 {
307 <String as ToSql>::accepts(ty)
308 }
309
310 fn to_sql_checked(
311 &self,
312 ty: &Type,
313 out: &mut BytesMut,
314 ) -> Result<IsNull, Box<dyn std::error::Error + Sync + Send>> {
315 self.as_str().to_sql_checked(ty, out)
316 }
317}
318
319#[cfg(feature = "rweb-openapi")]
320impl Entity for StackString {
321 fn type_name() -> Cow<'static, str> {
322 <str as Entity>::type_name()
323 }
324
325 #[inline]
326 fn describe(comp_d: &mut ComponentDescriptor) -> ComponentOrInlineSchema {
327 str::describe(comp_d)
328 }
329}
330
331#[cfg(feature = "rweb-openapi")]
332impl ResponseEntity for StackString {
333 #[inline]
334 fn describe_responses(comp_d: &mut ComponentDescriptor) -> Responses {
335 String::describe_responses(comp_d)
336 }
337}
338
339#[cfg(feature = "rweb-openapi")]
340impl From<StackString> for Body {
341 #[inline]
342 fn from(s: StackString) -> Body {
343 let s: String = s.into();
344 Body::from(s)
345 }
346}
347
348#[macro_export]
349macro_rules! format_sstr {
350 ($($arg:tt)*) => {{
351 use std::fmt::Write;
352 let mut buf = $crate::StackString::new();
353 std::write!(buf, "{}", std::format_args!($($arg)*)).unwrap();
354 buf
355 }}
356}
357
358#[cfg(feature = "async_graphql")]
360#[Scalar]
361impl ScalarType for StackString {
362 fn parse(value: Value) -> InputValueResult<Self> {
363 if let Value::String(s) = value {
364 let s: StackString = s.into();
365 Ok(s)
366 } else {
367 Err(InputValueError::expected_type(value))
368 }
369 }
370
371 fn is_valid(value: &Value) -> bool {
372 matches!(value, Value::String(_))
373 }
374
375 fn to_value(&self) -> Value {
376 Value::String(self.to_string())
377 }
378}
379
380#[cfg(test)]
381mod tests {
382 use rand::{thread_rng, Rng};
383
384 use crate::StackString;
385
386 #[test]
387 fn test_default() {
388 assert_eq!(StackString::new(), StackString::default());
389 }
390
391 #[test]
392 fn test_split_off() {
393 let mut s0 = "hello there".to_string();
394 let s1 = s0.split_off(3);
395 let mut s2: StackString = "hello there".into();
396 let s3 = s2.split_off(3);
397 assert_eq!(s0.as_str(), s2.as_str());
398 assert_eq!(s1.as_str(), s3.as_str());
399 }
400
401 #[test]
402 fn test_from_utf8() {
403 let mut rng = thread_rng();
404 let v: Vec<_> = (0..20).map(|_| rng.gen::<u8>() & 0x7f).collect();
405 let s0 = String::from_utf8(v.clone()).unwrap();
406 let s1 = StackString::from_utf8(&v).unwrap();
407 assert_eq!(s0.as_str(), s1.as_str());
408
409 let v: Vec<_> = (0..20).map(|_| rng.gen::<u8>()).collect();
410 let s0 = String::from_utf8(v.clone());
411 let s1 = StackString::from_utf8(&v);
412
413 match s0 {
414 Ok(s) => assert_eq!(s.as_str(), s1.unwrap().as_str()),
415 Err(e) => assert_eq!(e.utf8_error(), s1.unwrap_err()),
416 }
417 }
418
419 #[test]
420 fn test_from_utf8_vec() {
421 let mut rng = thread_rng();
422 let v: Vec<_> = (0..20).map(|_| rng.gen::<u8>() & 0x7f).collect();
423 let s0 = String::from_utf8(v.clone()).unwrap();
424 let s1 = StackString::from_utf8_vec(v).unwrap();
425 assert_eq!(s0.as_str(), s1.as_str());
426
427 let v: Vec<_> = (0..20).map(|_| rng.gen::<u8>()).collect();
428 let s0 = String::from_utf8(v.clone());
429 let s1 = StackString::from_utf8_vec(v);
430
431 match s0 {
432 Ok(s) => assert_eq!(s.as_str(), s1.unwrap().as_str()),
433 Err(e) => assert_eq!(e, s1.unwrap_err()),
434 }
435 }
436
437 #[test]
438 fn test_string_from_stackstring() {
439 let s0 = StackString::from("Hello there");
440 let s1: String = s0.clone().into();
441 assert_eq!(s0.as_str(), s1.as_str());
442 }
443
444 #[test]
445 fn test_stackstring_from_string() {
446 let s0 = String::from("Hello there");
447 let s1: StackString = s0.clone().into();
448 assert_eq!(s0.as_str(), s1.as_str());
449 let s1: StackString = (&s0).into();
450 assert_eq!(s0.as_str(), s1.as_str());
451 }
452
453 #[test]
454 fn test_borrow() {
455 use std::borrow::Borrow;
456 let s = StackString::from("Hello");
457 let st: &str = s.borrow();
458 assert_eq!(st, "Hello");
459 }
460
461 #[test]
462 fn test_as_ref() {
463 use std::path::Path;
464
465 let s = StackString::from("Hello");
466 let st: &str = s.as_ref();
467 assert_eq!(st, s.as_str());
468 let bt: &[u8] = s.as_ref();
469 assert_eq!(bt, s.as_bytes());
470 let pt: &Path = s.as_ref();
471 assert_eq!(pt, Path::new("Hello"));
472 }
473
474 #[test]
475 fn test_from_str() {
476 let s = StackString::from("Hello");
477 let st: StackString = "Hello".parse().unwrap();
478 assert_eq!(s, st);
479 }
480
481 #[test]
482 fn test_partialeq_cow() {
483 use std::path::Path;
484 let p = Path::new("Hello");
485 let ps = p.to_string_lossy();
486 let s = StackString::from("Hello");
487 assert_eq!(s, ps);
488 }
489
490 #[test]
491 fn test_partial_eq_string() {
492 assert_eq!(StackString::from("Hello"), String::from("Hello"));
493 assert_eq!(StackString::from("Hello"), "Hello");
494 assert_eq!(&StackString::from("Hello"), "Hello");
495 }
496
497 #[test]
498 fn test_from_iterator_char() {
499 let mut rng = thread_rng();
500 let v: Vec<char> = (0..20).map(|_| rng.gen::<char>()).collect();
501 let s0: StackString = v.iter().map(|x| *x).collect();
502 let s1: String = v.iter().map(|x| *x).collect();
503 assert_eq!(s0, s1);
504 }
505
506 #[test]
507 fn test_contains_stackstring() {
508 let a: StackString = "hey there".into();
509 let b: StackString = "hey".into();
510 assert!(a.contains(b.as_str()));
511 }
512
513 #[test]
514 fn test_contains_char() {
515 let a: StackString = "hey there".into();
516 assert!(a.contains(' '));
517 }
518
519 #[test]
520 fn test_equality() {
521 let s: StackString = "hey".into();
522 assert_eq!(Some(&s).map(Into::into), Some("hey"));
523 }
524
525 #[cfg(feature = "postgres_types")]
526 use bytes::BytesMut;
527 #[cfg(feature = "postgres_types")]
528 use postgres_types::{FromSql, IsNull, ToSql, Type};
529
530 #[cfg(feature = "postgres_types")]
531 #[test]
532 fn test_from_sql() {
533 let raw = b"Hello There";
534 let t = Type::TEXT;
535 let s = StackString::from_sql(&t, raw).unwrap();
536 assert_eq!(s, StackString::from("Hello There"));
537
538 assert!(<StackString as FromSql>::accepts(&t));
539 }
540
541 #[cfg(feature = "postgres_types")]
542 #[test]
543 fn test_to_sql() {
544 let s = StackString::from("Hello There");
545 let t = Type::TEXT;
546 assert!(<StackString as ToSql>::accepts(&t));
547 let mut buf = BytesMut::new();
548 match s.to_sql(&t, &mut buf).unwrap() {
549 IsNull::Yes => assert!(false),
550 IsNull::No => {}
551 }
552 assert_eq!(buf.as_ref(), b"Hello There");
553 }
554
555 #[test]
556 fn test_from_display() {
557 use std::fmt::Display;
558
559 struct Test {}
560
561 impl Display for Test {
562 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
563 f.write_str("THIS IS A TEST")
564 }
565 }
566
567 let t = Test {};
568 let s = StackString::from_display(t);
569 assert_eq!(s, StackString::from("THIS IS A TEST"));
570 }
571
572 #[test]
573 fn test_format_sstr() {
574 use crate::format_sstr;
575
576 let s = format_sstr!("This is a test {}", 22);
577 assert_eq!(s, StackString::from("This is a test 22"));
578 }
579
580 #[test]
581 fn test_from_utf8_lossy() {
582 let mut v = Vec::new();
583 v.extend_from_slice("this is a test".as_bytes());
584 v.push(0xff);
585 v.extend_from_slice("yes".as_bytes());
586 let s = StackString::from_utf8_lossy(&v);
587 assert_eq!(s.len(), 20);
588 assert_eq!(s.is_inline(), true);
589 }
590
591 #[test]
592 fn test_serde() {
593 use serde::Deserialize;
594
595 let s = StackString::from("HELLO");
596 let t = "HELLO";
597 let s = serde_json::to_vec(&s).unwrap();
598 let t = serde_json::to_vec(t).unwrap();
599 assert_eq!(s, t);
600
601 let s = r#"{"a": "b"}"#;
602
603 #[derive(Deserialize)]
604 struct A {
605 a: StackString,
606 }
607
608 #[derive(Deserialize)]
609 struct B {
610 a: String,
611 }
612
613 let a: A = serde_json::from_str(s).unwrap();
614 let b: B = serde_json::from_str(s).unwrap();
615 assert_eq!(a.a.as_str(), b.a.as_str());
616 }
617
618 #[cfg(feature = "async_graphql")]
619 #[test]
620 fn test_stackstring_async_graphql() {
621 use async_graphql::{
622 dataloader::{DataLoader, Loader},
623 Context, EmptyMutation, EmptySubscription, Object, Schema,
624 };
625 use async_trait::async_trait;
626 use std::{collections::HashMap, convert::Infallible};
627
628 struct StackStringLoader;
629
630 impl StackStringLoader {
631 fn new() -> Self {
632 Self
633 }
634 }
635
636 #[async_trait]
637 impl Loader<StackString> for StackStringLoader {
638 type Value = StackString;
639 type Error = Infallible;
640
641 async fn load(
642 &self,
643 _: &[StackString],
644 ) -> Result<HashMap<StackString, Self::Value>, Self::Error> {
645 let mut m = HashMap::new();
646 m.insert("HELLO".into(), "WORLD".into());
647 Ok(m)
648 }
649 }
650
651 struct QueryRoot;
652
653 #[Object]
654 impl<'a> QueryRoot {
655 async fn hello(&self, ctx: &Context<'a>) -> Result<Option<StackString>, Infallible> {
656 let hello = ctx
657 .data::<DataLoader<StackStringLoader>>()
658 .unwrap()
659 .load_one("hello".into())
660 .await
661 .unwrap();
662 Ok(hello)
663 }
664 }
665
666 let expected_sdl = include_str!("../tests/data/sdl_file_stackstring.txt");
667
668 let schema = Schema::build(QueryRoot, EmptyMutation, EmptySubscription)
669 .data(DataLoader::new(
670 StackStringLoader::new(),
671 tokio::task::spawn,
672 ))
673 .finish();
674 let sdl = schema.sdl();
675
676 assert_eq!(&sdl, expected_sdl);
677 }
678}