safe_uri 0.1.0-beta.4

Simple and safe URI types.
Documentation
mod generated_struct;
mod generated_validate;

use shared_bytes::SharedStr;

pub use self::generated_struct::UserInfo;

use crate::validation::{self, is_sub_delimiter, is_unreserved, InvalidComponent};
use std::{error::Error, fmt};

impl fmt::Debug for UserInfo {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        struct SecretSafeDebug<'s>(&'s str);

        impl fmt::Debug for SecretSafeDebug<'_> {
            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
                match self.0.split_once(':') {
                    Some((user, _secret)) => write!(f, "\"{}:***\"", user),
                    None => write!(f, "\"{}\"", self.0),
                }
            }
        }

        f.debug_tuple("UserInfo")
            .field(&SecretSafeDebug(self.as_str()))
            .finish()
    }
}

#[derive(Debug, Clone)]
pub struct InvalidUserInfo(InvalidComponent);

impl fmt::Display for InvalidUserInfo {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "invalid user info: {}", self.0)
    }
}

impl Error for InvalidUserInfo {}

fn validate_with_normalized_percent_encoding(
    string: &str,
) -> Result<Option<SharedStr>, InvalidUserInfo> {
    validation::with_normalized_percent_encoding(string, is_valid_byte).map_err(InvalidUserInfo)
}

const fn validate_static(bytes: &'static [u8]) -> Result<(), InvalidUserInfo> {
    match generated_validate::validate(bytes) {
        Ok(x) => Ok(x),
        Err(e) => Err(InvalidUserInfo(e)),
    }
}

const fn is_valid_byte(b: u8) -> bool {
    is_unreserved(b) || is_sub_delimiter(b) || b == b':'
}

#[cfg(test)]
mod tests {
    use super::*;
    use assert_matches::assert_matches;

    #[test]
    fn qc_generated_validate_matches_validate_with_normalized_percent_encoding() {
        quickcheck::quickcheck(
            test_generated_validate_matches_validate_with_normalized_percent_encoding as fn(String),
        )
    }

    fn test_generated_validate_matches_validate_with_normalized_percent_encoding(string: String) {
        let result = generated_validate::validate(string.as_bytes());
        let result_with_normalization = validate_with_normalized_percent_encoding(&string);
        match result_with_normalization {
            Ok(None) => assert_matches!(result, Ok(())),
            Ok(Some(_normalized)) => {
                assert_matches!(result, Err(InvalidComponent::PercentEncodedByte(_)))
            }
            Err(_error) => assert_matches!(result, Err(_)),
        }
    }
}