aliri_braid_examples 0.3.1

Examples demonstrating usage of the `aliri_braid` crate
Documentation
//! An example of constructing a strongly-typed wrapper around
//! a validated string value.
//!
//! The types in this module perform validation prior to allowing the
//! type to be instantiated. If a value is invalid, then an error will
//! be returned rather than allowing the invalid value to be constructed.
//!
//! Refer to the [`Validator`][aliri_braid::Validator] implementation
//! for a given type for additional information on what is considered
//! a valid value for the type.

use std::{convert::Infallible, error, fmt};

use aliri_braid::braid;

/// An error indicating that the provided string is not a valid scope token
#[derive(Debug)]
pub enum InvalidScopeToken {
    /// A scope token cannot be the empty string
    EmptyString,
    /// The string contained a byte that is not legal for a scope token
    InvalidCharacter { position: usize, value: u8 },
}

impl fmt::Display for InvalidScopeToken {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Self::EmptyString => f.write_str("scope cannot be empty"),
            Self::InvalidCharacter { position, value } => f.write_fmt(format_args!(
                "invalid scope character at position {}: {:02x}",
                position, value
            )),
        }
    }
}

impl From<Infallible> for InvalidScopeToken {
    #[inline(always)]
    fn from(x: Infallible) -> Self {
        match x {}
    }
}

impl error::Error for InvalidScopeToken {}

/// A scope token as defined in RFC6749, Section 3.3
///
/// This type maintains an invariant that ensures that a
/// value of this type cannot be constructed that contains
/// invalid data.
///
/// The borrowed form of this type is generated by appending
/// _Ref_ to the end of the owned form: [`ScopeTokenRef`].
#[braid(serde, validator, ref_doc = "A borrowed reference to a [`ScopeToken`]")]
pub struct ScopeToken;

impl aliri_braid::Validator for ScopeToken {
    type Error = InvalidScopeToken;

    fn validate(s: &str) -> Result<(), Self::Error> {
        if s.is_empty() {
            Err(InvalidScopeToken::EmptyString)
        } else if let Some((position, &value)) = s
            .as_bytes()
            .iter()
            .enumerate()
            .find(|(_, &b)| b <= 0x20 || b == 0x22 || b == 0x5C || 0x7F <= b)
        {
            Err(InvalidScopeToken::InvalidCharacter { position, value })
        } else {
            Ok(())
        }
    }
}