typeid_prefix 1.2.0-alpha

A Rust library that implements a type-safe version of the TypePrefix section of the `TypeID` Specification
Documentation
#[cfg(kani)]
mod verification {
    use std::convert::TryFrom;
    use typeid_prefix::{TypeIdPrefix, Sanitize};
    use kani::Arbitrary;

    #[derive(Debug)]
    struct TypeIdPrefixInput {
        input: String,
    }

    impl Arbitrary for TypeIdPrefixInput {
        fn any() -> Self {
            // Generate a random length for the string (between 0 and 100 to test beyond the 63 character limit)
            let len: usize = kani::any();
            kani::assume(len <= 100);

            // Generate arbitrary ASCII characters (including non-lowercase and non-underscore)
            let input: String = (0..len)
                .map(|_| {
                    let c: u8 = kani::any();
                    kani::assume(c.is_ascii());
                    c as char
                })
                .collect();
            TypeIdPrefixInput { input }
        }
    }

    #[kani::proof]
    fn verify_typeidprefix_try_from_and_sanitize() {
        let input: TypeIdPrefixInput = kani::any();
        let try_from_result = TypeIdPrefix::try_from(input.input.clone());
        let sanitized = input.input.create_prefix_sanitized();

        // Verify try_from behavior
        if input.input.len() > 63 {
            kani::assert(try_from_result.is_err(), "Input length exceeds 63 characters, should be error.");
        } else if input.input.is_empty() {
            kani::assert(try_from_result.is_ok(), "Empty input should be ok.");
        } else {
            let is_ascii = input.input.is_ascii();
            let starts_with_valid_char = input.input.chars().next().map_or(false, |c| c.is_ascii_lowercase());
            let ends_with_valid_char = input.input.chars().last().map_or(false, |c| c.is_ascii_lowercase());
            let contains_only_valid_chars = input.input.chars().all(|c| c.is_ascii_lowercase() || c == '_');

            if !is_ascii || !starts_with_valid_char || !ends_with_valid_char || !contains_only_valid_chars {
                kani::assert(try_from_result.is_err(), "Invalid input should be error.");
            } else {
                kani::assert(try_from_result.is_ok(), "Valid input should be ok.");
            }
        }

        // Verify sanitized output
        kani::assert(sanitized.len() <= 63, "Sanitized output should not exceed 63 characters.");
        kani::assert(sanitized.chars().all(|c| c.is_ascii_lowercase() || c == '_'), "Sanitized output should only contain lowercase ASCII or underscore.");
        kani::assert(!sanitized.starts_with('_'), "Sanitized output should not start with underscore.");
        kani::assert(!sanitized.ends_with('_'), "Sanitized output should not end with underscore.");

        // Ensure sanitized version is always valid
        kani::assert(TypeIdPrefix::try_from(sanitized.as_str()).is_ok(), "Sanitized output should always be valid.");

        // If the original input was valid, ensure it matches the sanitized version
        if try_from_result.is_ok() {
            kani::assert(input.input == sanitized, "Valid input should match its sanitized version.");
        }
    }

    #[kani::proof]
    fn verify_typeidprefix_try_from_str_and_sanitize() {
        let input: TypeIdPrefixInput = kani::any();
        let try_from_result = TypeIdPrefix::try_from(input.input.as_str());
        let sanitized = input.input.create_prefix_sanitized();

        // Verify try_from behavior (same as in the previous proof)
        if input.input.len() > 63 {
            kani::assert(try_from_result.is_err(), "Input length exceeds 63 characters, should be error.");
        } else if input.input.is_empty() {
            kani::assert(try_from_result.is_ok(), "Empty input should be ok.");
        } else {
            let is_ascii = input.input.is_ascii();
            let starts_with_valid_char = input.input.chars().next().map_or(false, |c| c.is_ascii_lowercase());
            let ends_with_valid_char = input.input.chars().last().map_or(false, |c| c.is_ascii_lowercase());
            let contains_only_valid_chars = input.input.chars().all(|c| c.is_ascii_lowercase() || c == '_');

            if !is_ascii || !starts_with_valid_char || !ends_with_valid_char || !contains_only_valid_chars {
                kani::assert(try_from_result.is_err(), "Invalid input should be error.");
            } else {
                kani::assert(try_from_result.is_ok(), "Valid input should be ok.");
            }
        }

        // Verify sanitized output (same as in the previous proof)
        kani::assert(sanitized.len() <= 63, "Sanitized output should not exceed 63 characters.");
        kani::assert(sanitized.chars().all(|c| c.is_ascii_lowercase() || c == '_'), "Sanitized output should only contain lowercase ASCII or underscore.");
        kani::assert(!sanitized.starts_with('_'), "Sanitized output should not start with underscore.");
        kani::assert(!sanitized.ends_with('_'), "Sanitized output should not end with underscore.");

        // Ensure sanitized version is always valid
        kani::assert(TypeIdPrefix::try_from(sanitized.as_str()).is_ok(), "Sanitized output should always be valid.");

        // If the original input was valid, ensure it matches the sanitized version
        if try_from_result.is_ok() {
            kani::assert(input.input == sanitized, "Valid input should match its sanitized version.");
        }
    }
}