strid_examples/
validated.rs

1//! An example of constructing a strongly-typed wrapper around
2//! a validated string value.
3//!
4//! The types in this module perform validation prior to allowing the
5//! type to be instantiated. If a value is invalid, then an error will
6//! be returned rather than allowing the invalid value to be constructed.
7//!
8//! Refer to the [`Validator`][strid::Validator] implementation
9//! for a given type for additional information on what is considered
10//! a valid value for the type.
11
12use std::{convert::Infallible, error, fmt};
13
14use strid::braid;
15
16/// An error indicating that the provided string is not a valid scope token
17#[derive(Debug)]
18pub enum InvalidScopeToken {
19    /// A scope token cannot be the empty string
20    EmptyString,
21    /// The string contained a byte that is not legal for a scope token
22    InvalidCharacter { position: usize, value: u8 },
23}
24
25impl fmt::Display for InvalidScopeToken {
26    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
27        match self {
28            Self::EmptyString => f.write_str("scope cannot be empty"),
29            Self::InvalidCharacter { position, value } => f.write_fmt(format_args!(
30                "invalid scope character at position {}: {:02x}",
31                position, value
32            )),
33        }
34    }
35}
36
37impl From<Infallible> for InvalidScopeToken {
38    #[inline(always)]
39    fn from(x: Infallible) -> Self {
40        match x {}
41    }
42}
43
44impl error::Error for InvalidScopeToken {}
45
46/// A scope token as defined in RFC6749, Section 3.3
47///
48/// This type maintains an invariant that ensures that a
49/// value of this type cannot be constructed that contains
50/// invalid data.
51///
52/// The borrowed form of this type is generated by appending
53/// _Ref_ to the end of the owned form: [`ScopeTokenRef`].
54#[braid(serde, validator, ref_doc = "A borrowed reference to a [`ScopeToken`]")]
55pub struct ScopeToken(String);
56
57impl strid::Validator for ScopeToken {
58    type Error = InvalidScopeToken;
59
60    fn validate(s: &str) -> Result<(), Self::Error> {
61        if s.is_empty() {
62            Err(InvalidScopeToken::EmptyString)
63        } else if let Some((position, &value)) = s
64            .as_bytes()
65            .iter()
66            .enumerate()
67            .find(|&(_, &b)| b <= 0x20 || b == 0x22 || b == 0x5C || 0x7F <= b)
68        {
69            Err(InvalidScopeToken::InvalidCharacter { position, value })
70        } else {
71            Ok(())
72        }
73    }
74}