strid_examples/
sso_wrapper.rs

1//! An example of constructing a strongly-typed wrapper around
2//! a string with small-string optimization.
3//!
4//! The types in this module do not perform any validation or normalization
5//! of their values, so every valid UTF-8 string is potentially valid for
6//! these types.
7//!
8//! Note: This example requires facet support for `smartstring::SmartString` and
9//! `compact_str::CompactString`. See: https://github.com/facet-rs/facet/issues/1283 and https://github.com/facet-rs/facet/issues/1282
10
11#![allow(dead_code)]
12
13#[cfg(all(feature = "smartstring-facet", feature = "compact_str-facet"))]
14use std::{borrow::Cow, error, fmt};
15
16#[cfg(all(feature = "smartstring-facet", feature = "compact_str-facet"))]
17use smartstring::alias::String;
18#[cfg(all(feature = "smartstring-facet", feature = "compact_str-facet"))]
19use strid::braid;
20
21/// An example of a wrapper around a [`smartstring::SmartString`] with
22/// small-string optimization
23///
24/// This type ends in _Buf_, so the borrowed form of this type
25/// will be named [`SmartUsername`].
26///
27/// Because the no type is explicitly named here, the inner field will
28/// implicitly use the `String` type in the namespace where it is defined.
29#[cfg(all(feature = "smartstring-facet", feature = "compact_str-facet"))]
30#[braid(serde, ref_doc = "A borrowed reference to a string slice wrapper")]
31pub struct SmartUsernameBuf;
32
33/// An example of a wrapper with small-string optimization
34///
35/// This type wraps the around a [`compact_str::CompactString`], but that
36/// implementation detail won't be exposed through the type API due to
37/// the use of the `no_expose` braid parameter.
38#[cfg(all(feature = "smartstring-facet", feature = "compact_str-facet"))]
39#[braid(serde, no_expose)]
40pub struct CompactData(compact_str::CompactString);
41
42/// A non-empty [`String`] normalized to lowercase with small-string optimization
43///
44/// This type maintains an invariant that ensures that a
45/// value of this type cannot be constructed that contains
46/// invalid data. Data that _can_ be normalized to a valid
47/// instance of this type will be.
48///
49/// Because this type does normalization, the type explicitly
50/// does _not_ implement [`Borrow<str>`][::std::borrow::Borrow],
51/// as doing so would could violate the contract of that trait,
52/// potentially resulting in lost data. If a user of
53/// the crate would like to override this, then they can
54/// explicitly implement the trait.
55#[cfg(all(feature = "smartstring-facet", feature = "compact_str-facet"))]
56#[braid(
57    serde,
58    no_expose,
59    normalizer,
60    ref_doc = "A borrowed reference to a non-empty, lowercase string"
61)]
62pub struct LowerCompactString(compact_str::CompactString);
63
64#[cfg(all(feature = "smartstring-facet", feature = "compact_str-facet"))]
65impl strid::Validator for LowerCompactString {
66    type Error = InvalidString;
67
68    fn validate(raw: &str) -> Result<(), Self::Error> {
69        if raw.is_empty() {
70            Err(InvalidString::EmptyString)
71        } else if raw.chars().any(char::is_uppercase) {
72            Err(InvalidString::InvalidCharacter)
73        } else {
74            Ok(())
75        }
76    }
77}
78
79#[cfg(all(feature = "smartstring-facet", feature = "compact_str-facet"))]
80impl strid::Normalizer for LowerCompactString {
81    fn normalize(s: &str) -> Result<Cow<str>, Self::Error> {
82        if s.is_empty() {
83            Err(InvalidString::EmptyString)
84        } else if s.contains(char::is_uppercase) {
85            Ok(Cow::Owned(s.to_lowercase()))
86        } else {
87            Ok(Cow::Borrowed(s))
88        }
89    }
90}
91
92/// An error indicating that the provided value was invalid
93#[cfg(all(feature = "smartstring-facet", feature = "compact_str-facet"))]
94#[derive(Debug)]
95pub enum InvalidString {
96    EmptyString,
97    InvalidCharacter,
98}
99
100#[cfg(all(feature = "smartstring-facet", feature = "compact_str-facet"))]
101impl fmt::Display for InvalidString {
102    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
103        match self {
104            Self::EmptyString => f.write_str("string cannot be empty"),
105            Self::InvalidCharacter => f.write_str("string contains invalid uppercase character"),
106        }
107    }
108}
109
110#[cfg(all(feature = "smartstring-facet", feature = "compact_str-facet"))]
111impl error::Error for InvalidString {}
112
113#[cfg(all(feature = "smartstring-facet", feature = "compact_str-facet"))]
114strid::from_infallible!(InvalidString);