1use derive_more::Display;
2use serde::{self, Deserialize, Deserializer, Serialize, Serializer};
3use std::{
4 borrow::{Borrow, Cow},
5 convert::Infallible,
6 ffi::OsStr,
7 fmt::{self, Write as FmtWrite},
8 iter::FromIterator,
9 ops::Deref,
10 path::Path,
11 str::FromStr,
12 string::FromUtf8Error,
13};
14
15use crate::MAX_INLINE;
16
17use crate::stack_string::StackString;
18
19#[cfg(feature = "diesel_types")]
20use diesel::{
21 backend::Backend,
22 deserialize::{FromSql as DeFromSql, Result as DeResult},
23 serialize::{Output, Result as SerResult, ToSql as DeToSql},
24 sql_types::Text,
25};
26
27#[cfg(feature = "diesel_types")]
28use std::io::Write;
29
30#[cfg(feature = "postgres_types")]
31use bytes::BytesMut;
32#[cfg(feature = "postgres_types")]
33use postgres_types::{FromSql, IsNull, ToSql, Type};
34
35#[cfg(feature = "rweb-openapi")]
36use rweb::openapi::{
37 ComponentDescriptor, ComponentOrInlineSchema, Entity, ResponseEntity, Responses,
38};
39
40#[cfg(feature = "rweb-openapi")]
41use hyper::Body;
42
43#[derive(Display, Debug, Clone, PartialEq, Eq, Hash)]
44#[cfg_attr(feature = "diesel_types", derive(FromSqlRow, AsExpression))]
45#[cfg_attr(feature = "diesel_types", sql_type = "Text")]
46pub enum StackCow<'a> {
47 Borrowed(&'a str),
48 Owned(StackString),
49}
50
51impl Default for StackCow<'_> {
52 fn default() -> Self {
53 Self::new()
54 }
55}
56
57impl<'a> StackCow<'a> {
58 #[must_use]
59 pub fn new() -> Self {
60 Self::Owned(StackString::new())
61 }
62
63 #[must_use]
64 pub fn to_owned(&self) -> StackCow<'static> {
65 self.clone().into_owned()
66 }
67
68 #[must_use]
69 pub fn into_owned(self) -> StackCow<'static> {
70 match self {
71 Self::Borrowed(b) => StackCow::Owned(b.into()),
72 Self::Owned(o) => StackCow::Owned(o),
73 }
74 }
75
76 #[must_use]
77 pub fn is_borrowed(&self) -> bool {
78 match self {
79 Self::Borrowed(_) => true,
80 Self::Owned(_) => false,
81 }
82 }
83
84 #[must_use]
85 pub fn is_owned(&self) -> bool {
86 !self.is_borrowed()
87 }
88
89 #[must_use]
90 pub fn as_str(&self) -> &str {
91 match self {
92 Self::Borrowed(s) => s,
93 Self::Owned(o) => o.as_str(),
94 }
95 }
96
97 pub fn from_utf8(vec: Vec<u8>) -> Result<Self, FromUtf8Error> {
102 String::from_utf8(vec).map(Into::into)
103 }
104
105 #[must_use]
106 pub fn from_utf8_lossy(v: &'a [u8]) -> Self {
107 if v.len() > MAX_INLINE {
108 String::from_utf8_lossy(v).into()
109 } else {
110 StackString::from_utf8_lossy(v).into()
111 }
112 }
113
114 pub fn from_display(buf: impl fmt::Display) -> Self {
119 let mut s = StackString::new();
120 write!(s, "{buf}").unwrap();
121 s.into()
122 }
123}
124
125impl Deref for StackCow<'_> {
126 type Target = str;
127
128 fn deref(&self) -> &Self::Target {
129 match self {
130 Self::Borrowed(b) => b,
131 Self::Owned(o) => o,
132 }
133 }
134}
135
136impl<'a> PartialOrd<Self> for StackCow<'a> {
137 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
138 self.as_str().partial_cmp(other.as_str())
139 }
140}
141
142impl<'a> Ord for StackCow<'a> {
143 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
144 self.as_str().cmp(other.as_str())
145 }
146}
147
148impl<'a> Serialize for StackCow<'a> {
149 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
150 where
151 S: Serializer,
152 {
153 serializer.serialize_str(self.as_str())
154 }
155}
156
157impl<'de> Deserialize<'de> for StackCow<'_> {
158 fn deserialize<D>(deserializer: D) -> Result<StackCow<'static>, D::Error>
159 where
160 D: Deserializer<'de>,
161 {
162 StackString::deserialize(deserializer).map(Into::into)
163 }
164}
165
166impl<'a> From<StackString> for StackCow<'a> {
167 fn from(item: StackString) -> Self {
168 Self::Owned(item)
169 }
170}
171
172impl<'a> From<StackCow<'a>> for StackString {
173 fn from(item: StackCow<'a>) -> Self {
174 match item {
175 StackCow::Borrowed(s) => s.into(),
176 StackCow::Owned(s) => s,
177 }
178 }
179}
180
181impl<'a> From<Cow<'a, str>> for StackCow<'a> {
182 fn from(item: Cow<'a, str>) -> Self {
183 match item {
184 Cow::Borrowed(s) => Self::Borrowed(s),
185 Cow::Owned(s) => Self::Owned(s.into()),
186 }
187 }
188}
189
190impl<'a> From<StackCow<'a>> for String {
191 fn from(item: StackCow) -> Self {
192 match item {
193 StackCow::Borrowed(s) => s.into(),
194 StackCow::Owned(s) => s.into(),
195 }
196 }
197}
198
199impl<'a> From<&StackCow<'a>> for String {
200 fn from(item: &StackCow) -> Self {
201 item.as_str().into()
202 }
203}
204
205impl<'a> From<String> for StackCow<'a> {
206 fn from(item: String) -> Self {
207 Self::Owned(item.into())
208 }
209}
210
211impl<'a> From<&'a String> for StackCow<'a> {
212 fn from(item: &'a String) -> Self {
213 Self::Borrowed(item.as_str())
214 }
215}
216
217impl<'a> From<&'a str> for StackCow<'a> {
218 fn from(item: &'a str) -> Self {
219 StackCow::Borrowed(item)
220 }
221}
222
223impl<'a> From<&'a StackCow<'a>> for &'a str {
224 fn from(item: &'a StackCow) -> &'a str {
225 item.as_str()
226 }
227}
228
229impl<'a> Borrow<str> for StackCow<'a> {
230 fn borrow(&self) -> &str {
231 self.as_str()
232 }
233}
234
235impl<'a> AsRef<str> for StackCow<'a> {
236 fn as_ref(&self) -> &str {
237 self.as_str()
238 }
239}
240
241impl<'a> AsRef<[u8]> for StackCow<'a> {
242 fn as_ref(&self) -> &[u8] {
243 self.as_str().as_bytes()
244 }
245}
246
247impl<'a> AsRef<OsStr> for StackCow<'a> {
248 fn as_ref(&self) -> &OsStr {
249 self.as_str().as_ref()
250 }
251}
252
253impl<'a> AsRef<Path> for StackCow<'a> {
254 fn as_ref(&self) -> &Path {
255 Path::new(self)
256 }
257}
258
259impl<'a> FromStr for StackCow<'a> {
260 type Err = Infallible;
261 fn from_str(s: &str) -> Result<Self, Self::Err> {
262 Ok(Self::Owned(s.into()))
263 }
264}
265
266impl<'a> PartialEq<Cow<'a, str>> for StackCow<'a> {
267 #[inline]
268 fn eq(&self, other: &Cow<'a, str>) -> bool {
269 PartialEq::eq(&self[..], &other[..])
270 }
271}
272
273impl<'a> PartialEq<String> for StackCow<'a> {
274 #[inline]
275 fn eq(&self, other: &String) -> bool {
276 PartialEq::eq(&self[..], &other[..])
277 }
278}
279
280impl<'a> PartialEq<str> for StackCow<'a> {
281 #[inline]
282 fn eq(&self, other: &str) -> bool {
283 let s: &str = self.as_ref();
284 PartialEq::eq(s, other)
285 }
286}
287
288impl<'a> PartialEq<&'a str> for StackCow<'a> {
289 #[inline]
290 fn eq(&self, other: &&'a str) -> bool {
291 PartialEq::eq(&self[..], &other[..])
292 }
293}
294
295impl<'a> FromIterator<char> for StackCow<'a> {
296 fn from_iter<I: IntoIterator<Item = char>>(iter: I) -> Self {
297 Self::Owned(StackString::from_iter(iter))
298 }
299}
300
301#[cfg(feature = "postgres_types")]
302impl<'a> FromSql<'a> for StackCow<'a> {
303 fn from_sql(
304 ty: &Type,
305 raw: &'a [u8],
306 ) -> Result<Self, Box<dyn std::error::Error + 'static + Send + Sync>> {
307 let s = <&'a str as FromSql>::from_sql(ty, raw)?;
308 Ok(s.into())
309 }
310
311 fn accepts(ty: &Type) -> bool {
312 <&'a str as FromSql>::accepts(ty)
313 }
314}
315
316#[cfg(feature = "postgres_types")]
317impl<'a> ToSql for StackCow<'a> {
318 fn to_sql(
319 &self,
320 ty: &Type,
321 out: &mut BytesMut,
322 ) -> Result<IsNull, Box<dyn std::error::Error + Sync + Send>>
323 where
324 Self: Sized,
325 {
326 ToSql::to_sql(&self.as_str(), ty, out)
327 }
328
329 fn accepts(ty: &Type) -> bool
330 where
331 Self: Sized,
332 {
333 <String as ToSql>::accepts(ty)
334 }
335
336 fn to_sql_checked(
337 &self,
338 ty: &Type,
339 out: &mut BytesMut,
340 ) -> Result<IsNull, Box<dyn std::error::Error + Sync + Send>> {
341 self.as_str().to_sql_checked(ty, out)
342 }
343}
344
345#[cfg(feature = "rweb-openapi")]
346impl<'a> Entity for StackCow<'a> {
347 fn type_name() -> Cow<'static, str> {
348 str::type_name()
349 }
350
351 #[inline]
352 fn describe(comp_d: &mut ComponentDescriptor) -> ComponentOrInlineSchema {
353 str::describe(comp_d)
354 }
355}
356
357#[cfg(feature = "rweb-openapi")]
358impl<'a> ResponseEntity for StackCow<'a> {
359 #[inline]
360 fn describe_responses(comp_d: &mut ComponentDescriptor) -> Responses {
361 String::describe_responses(comp_d)
362 }
363}
364
365#[cfg(feature = "rweb-openapi")]
366impl<'a> From<StackCow<'a>> for Body {
367 #[inline]
368 fn from(s: StackCow) -> Body {
369 let s: String = s.into();
370 Body::from(s)
371 }
372}
373
374#[cfg(test)]
375mod tests {
376 use rand::{thread_rng, Rng};
377 use serde::Deserialize;
378
379 use crate::{StackCow, StackString};
380
381 #[test]
382 fn test_default() {
383 assert_eq!(StackCow::new(), StackCow::default());
384 }
385
386 #[test]
387 fn test_from_utf8() {
388 let mut rng = thread_rng();
389 let v: Vec<_> = (0..20).map(|_| rng.gen::<u8>() & 0x7f).collect();
390 let s0 = String::from_utf8(v.clone()).unwrap();
391 let s1 = StackCow::from_utf8(v).unwrap();
392 assert_eq!(s0.as_str(), s1.as_str());
393
394 let v: Vec<_> = (0..20).map(|_| rng.gen::<u8>()).collect();
395 let s0 = String::from_utf8(v.clone());
396 let s1 = StackCow::from_utf8(v);
397
398 match s0 {
399 Ok(s) => assert_eq!(s.as_str(), s1.unwrap().as_str()),
400 Err(e) => assert_eq!(e, s1.unwrap_err()),
401 }
402 }
403
404 #[test]
405 fn test_string_from_stack_cow() {
406 let s0 = StackCow::from("Hello there");
407 let s1: String = s0.clone().into();
408 assert_eq!(s0.as_str(), s1.as_str());
409 }
410
411 #[test]
412 fn test_stack_cow_from_string() {
413 let s0 = String::from("Hello there");
414 let s1: StackCow = s0.clone().into();
415 assert_eq!(s0.as_str(), s1.as_str());
416 let s1: StackCow = (&s0).into();
417 assert_eq!(s0.as_str(), s1.as_str());
418 }
419
420 #[test]
421 fn test_borrow() {
422 use std::borrow::Borrow;
423 let s = StackCow::from("Hello");
424 let st: &str = s.borrow();
425 assert_eq!(st, "Hello");
426 }
427
428 #[test]
429 fn test_as_ref() {
430 use std::path::Path;
431
432 let s = StackCow::from("Hello");
433 let st: &str = s.as_ref();
434 assert_eq!(st, s.as_str());
435 let bt: &[u8] = s.as_ref();
436 assert_eq!(bt, s.as_bytes());
437 let pt: &Path = s.as_ref();
438 assert_eq!(pt, Path::new("Hello"));
439 }
440
441 #[test]
442 fn test_from_str() {
443 let s = StackCow::from("Hello");
444 assert_eq!(s, StackCow::Borrowed("Hello"));
445 }
446
447 #[test]
448 fn test_partialeq_cow() {
449 use std::path::Path;
450 let p = Path::new("Hello");
451 let ps = p.to_string_lossy();
452 let s = StackCow::from("Hello");
453 assert_eq!(s, ps);
454 }
455
456 #[test]
457 fn test_partial_eq_string() {
458 assert_eq!(StackCow::from("Hello"), String::from("Hello"));
459 assert_eq!(StackCow::from("Hello"), "Hello");
460 assert_eq!(&StackCow::from("Hello"), "Hello");
461 }
462
463 #[test]
464 fn test_from_iterator_char() {
465 let mut rng = thread_rng();
466 let v: Vec<char> = (0..20).map(|_| rng.gen::<char>()).collect();
467 let s0: StackCow = v.iter().map(|x| *x).collect();
468 let s1: String = v.iter().map(|x| *x).collect();
469 assert_eq!(s0, s1);
470 }
471
472 #[test]
473 fn test_contains_stack_cow() {
474 let a: StackCow = "hey there".into();
475 let b: StackCow = "hey".into();
476 assert!(a.contains(b.as_str()));
477 }
478
479 #[test]
480 fn test_contains_char() {
481 let a: StackCow = "hey there".into();
482 assert!(a.contains(' '));
483 }
484
485 #[test]
486 fn test_equality() {
487 let s: StackCow = "hey".into();
488 assert_eq!(Some(&s).map(Into::into), Some("hey"));
489 }
490
491 #[cfg(feature = "postgres_types")]
492 use bytes::BytesMut;
493 #[cfg(feature = "postgres_types")]
494 use postgres_types::{FromSql, IsNull, ToSql, Type};
495
496 #[cfg(feature = "postgres_types")]
497 #[test]
498 fn test_from_sql() {
499 let raw = b"Hello There";
500 let t = Type::TEXT;
501 let s = StackCow::from_sql(&t, raw).unwrap();
502 assert_eq!(s, StackCow::from("Hello There"));
503
504 assert!(<StackCow as FromSql>::accepts(&t));
505 }
506
507 #[cfg(feature = "postgres_types")]
508 #[test]
509 fn test_to_sql() {
510 let s = StackCow::from("Hello There");
511 let t = Type::TEXT;
512 assert!(<StackCow as ToSql>::accepts(&t));
513 let mut buf = BytesMut::new();
514 match s.to_sql(&t, &mut buf).unwrap() {
515 IsNull::Yes => assert!(false),
516 IsNull::No => {}
517 }
518 assert_eq!(buf.as_ref(), b"Hello There");
519 }
520
521 #[test]
522 fn test_from_display() {
523 use std::fmt::Display;
524
525 struct Test {}
526
527 impl Display for Test {
528 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
529 f.write_str("THIS IS A TEST")
530 }
531 }
532
533 let t = Test {};
534 let s = StackCow::from_display(t);
535 assert_eq!(s, StackCow::from(StackString::from("THIS IS A TEST")));
536 }
537
538 #[test]
539 fn test_from_utf8_lossy() {
540 let mut v = Vec::new();
541 v.extend_from_slice("this is a test".as_bytes());
542 v.push(0xff);
543 v.extend_from_slice("yes".as_bytes());
544 let s = StackCow::from_utf8_lossy(&v);
545 assert_eq!(s.len(), 20);
546 assert_eq!(s.is_owned(), true);
547 let s: StackString = s.into();
548 assert_eq!(s.len(), 20);
549 assert_eq!(s.is_inline(), true);
550 }
551
552 #[test]
553 fn test_serde() {
554 let s = StackCow::from("HELLO");
555 let t = "HELLO";
556 let s = serde_json::to_vec(&s).unwrap();
557 let t = serde_json::to_vec(t).unwrap();
558 assert_eq!(s, t);
559
560 let s = r#"{"a": "b"}"#;
561
562 #[derive(Deserialize)]
563 struct A<'a> {
564 a: StackCow<'a>,
565 }
566
567 #[derive(Deserialize)]
568 struct B {
569 a: String,
570 }
571
572 let a: A = serde_json::from_str(s).unwrap();
573 let b: B = serde_json::from_str(s).unwrap();
574 assert_eq!(a.a.as_str(), b.a.as_str());
575 }
576}