Skip to main content

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