use core::ffi::CStr;
use core::num::{
NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize, NonZeroU128,
NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize,
};
use maybe_valid::{
AsValidated, CStrInvalidReason, IntoValidated, MaybeValidOwned, MaybeValidRef, ZeroReason,
};
#[test]
fn utf8_as_validated_valid_and_invalid() {
let valid_bytes: &[u8] = b"hello";
let valid: MaybeValidRef<'_, str, [u8]> = valid_bytes.as_validated();
assert!(valid.is_valid());
assert!(!valid.is_invalid());
assert_eq!(valid.valid(), Some("hello"));
let invalid_bytes: &[u8] = &[0xff, 0x61];
let invalid: MaybeValidRef<'_, str, [u8]> = invalid_bytes.as_validated();
assert!(invalid.is_invalid());
assert!(!invalid.is_valid());
let (precursor, reason) = invalid.invalid_parts().expect("expected invalid");
assert_eq!(precursor, invalid_bytes);
assert_eq!(reason.valid_up_to(), 0);
let partial_invalid_bytes: &[u8] = b"a\xff";
let partial_invalid: MaybeValidRef<'_, str, [u8]> = partial_invalid_bytes.as_validated();
let partial_reason = partial_invalid
.invalid_reason()
.expect("expected invalid utf-8");
assert_eq!(partial_reason.valid_up_to(), 1);
}
#[test]
fn cstr_as_validated_reasons() {
let valid_bytes: &[u8] = b"ok\0";
let valid: MaybeValidRef<'_, CStr, [u8]> = valid_bytes.as_validated();
match valid {
MaybeValidRef::Valid(cstr) => assert_eq!(cstr.to_bytes_with_nul(), b"ok\0"),
MaybeValidRef::Invalid(_, _) => panic!("expected valid CStr"),
}
let missing_nul: &[u8] = b"ok";
let missing: MaybeValidRef<'_, CStr, [u8]> = missing_nul.as_validated();
match missing {
MaybeValidRef::Valid(_) => panic!("expected missing nul"),
MaybeValidRef::Invalid(bytes, reason) => {
assert_eq!(bytes, missing_nul);
assert_eq!(reason, CStrInvalidReason::MissingNul);
}
}
let interior_nul: &[u8] = b"a\0b\0";
let interior: MaybeValidRef<'_, CStr, [u8]> = interior_nul.as_validated();
match interior {
MaybeValidRef::Valid(_) => panic!("expected interior nul"),
MaybeValidRef::Invalid(bytes, reason) => {
assert_eq!(bytes, interior_nul);
assert_eq!(reason, CStrInvalidReason::InteriorNul { position: 1 });
}
}
let interior_nul_later: &[u8] = b"ab\0cd\0";
let interior_later: MaybeValidRef<'_, CStr, [u8]> = interior_nul_later.as_validated();
match interior_later {
MaybeValidRef::Valid(_) => panic!("expected interior nul"),
MaybeValidRef::Invalid(bytes, reason) => {
assert_eq!(bytes, interior_nul_later);
assert_eq!(reason, CStrInvalidReason::InteriorNul { position: 2 });
}
}
}
#[test]
fn char_into_validated() {
let valid: MaybeValidOwned<char, u32> = 0x41_u32.into_validated();
assert!(valid.is_valid());
assert_eq!(valid.valid(), Some('A'));
let invalid: MaybeValidOwned<char, u32> = 0x11_0000_u32.into_validated();
assert!(invalid.is_invalid());
assert_eq!(invalid.invalid_precursor(), Some(0x11_0000_u32));
let max_valid: MaybeValidOwned<char, u32> = 0x10_FFFF_u32.into_validated();
assert_eq!(max_valid.valid(), Some('\u{10FFFF}'));
let surrogate_invalid: MaybeValidOwned<char, u32> = 0xD800_u32.into_validated();
assert!(surrogate_invalid.is_invalid());
assert_eq!(surrogate_invalid.invalid_precursor(), Some(0xD800_u32));
}
macro_rules! test_nonzero {
($raw:ty, $nz:ty, $one:expr, $zero:expr) => {{
let valid_ref: MaybeValidRef<'_, $nz, $raw> = ($one as $raw).as_validated();
assert!(valid_ref.is_valid());
let invalid_ref: MaybeValidRef<'_, $nz, $raw> = ($zero as $raw).as_validated();
match invalid_ref {
MaybeValidRef::Valid(_) => panic!("expected invalid non-zero ref"),
MaybeValidRef::Invalid(v, reason) => {
assert_eq!(*v, $zero as $raw);
assert_eq!(reason, ZeroReason);
}
}
let valid_owned: MaybeValidOwned<$nz, $raw> = ($one as $raw).into_validated();
assert!(valid_owned.is_valid());
let invalid_owned: MaybeValidOwned<$nz, $raw> = ($zero as $raw).into_validated();
match invalid_owned {
MaybeValidOwned::Valid(_) => panic!("expected invalid non-zero owned"),
MaybeValidOwned::Invalid(v, reason) => {
assert_eq!(v, $zero as $raw);
assert_eq!(reason, ZeroReason);
}
}
}};
}
#[test]
fn nonzero_conversions_cover_all_integer_variants() {
test_nonzero!(u8, NonZeroU8, 1, 0);
test_nonzero!(u16, NonZeroU16, 1, 0);
test_nonzero!(u32, NonZeroU32, 1, 0);
test_nonzero!(u64, NonZeroU64, 1, 0);
test_nonzero!(u128, NonZeroU128, 1, 0);
test_nonzero!(usize, NonZeroUsize, 1, 0);
test_nonzero!(i8, NonZeroI8, 1, 0);
test_nonzero!(i16, NonZeroI16, 1, 0);
test_nonzero!(i32, NonZeroI32, 1, 0);
test_nonzero!(i64, NonZeroI64, 1, 0);
test_nonzero!(i128, NonZeroI128, 1, 0);
test_nonzero!(isize, NonZeroIsize, 1, 0);
}
#[cfg(feature = "alloc")]
#[test]
fn maybe_valid_ref_methods_and_into_owned() {
let valid_bytes: &[u8] = b"ok";
let valid: MaybeValidRef<'_, str, [u8]> = valid_bytes.as_validated();
let valid_borrowed = valid.as_ref();
assert!(matches!(valid_borrowed, MaybeValidRef::Valid("ok")));
let valid_result = valid.into_result();
assert_eq!(valid_result, Ok("ok"));
let valid_again: MaybeValidRef<'_, str, [u8]> = valid_bytes.as_validated();
assert_eq!(valid_again.invalid_precursor(), None);
let valid_again: MaybeValidRef<'_, str, [u8]> = valid_bytes.as_validated();
assert_eq!(valid_again.invalid_reason(), None);
let valid_again: MaybeValidRef<'_, str, [u8]> = valid_bytes.as_validated();
assert_eq!(valid_again.invalid_parts(), None);
let invalid_bytes: &[u8] = &[0xff];
let invalid: MaybeValidRef<'_, str, [u8]> = invalid_bytes.as_validated();
let invalid_reason = invalid
.as_ref()
.into_result_reason_only()
.expect_err("expected invalid");
assert_eq!(invalid_reason.valid_up_to(), 0);
let invalid_again: MaybeValidRef<'_, str, [u8]> = invalid_bytes.as_validated();
assert_eq!(invalid_again.invalid_precursor(), Some(invalid_bytes));
let invalid_again: MaybeValidRef<'_, str, [u8]> = invalid_bytes.as_validated();
assert_eq!(
invalid_again
.invalid_reason()
.expect("expected invalid reason")
.valid_up_to(),
0
);
let invalid_again: MaybeValidRef<'_, str, [u8]> = invalid_bytes.as_validated();
let (parts_bytes, parts_reason) = invalid_again.invalid_parts().expect("expected parts");
assert_eq!(parts_bytes, invalid_bytes);
assert_eq!(parts_reason.valid_up_to(), 0);
let invalid_owned = invalid.into_owned();
match invalid_owned {
MaybeValidOwned::Valid(_) => panic!("expected invalid owned"),
MaybeValidOwned::Invalid(bytes, reason) => {
assert_eq!(bytes, vec![0xff]);
assert_eq!(reason.valid_up_to(), 0);
}
}
}
#[cfg(feature = "alloc")]
#[test]
fn maybe_valid_owned_methods() {
let valid: MaybeValidOwned<String, Vec<u8>> = b"ok".to_vec().into_validated();
let valid_ref = valid.as_ref();
match valid_ref {
MaybeValidRef::Valid(s) => assert_eq!(s.as_str(), "ok"),
MaybeValidRef::Invalid(_, _) => panic!("expected valid"),
}
let valid_again: MaybeValidOwned<String, Vec<u8>> = b"ok".to_vec().into_validated();
assert_eq!(
valid_again.into_result_reason_only(),
Ok(String::from("ok"))
);
let invalid: MaybeValidOwned<String, Vec<u8>> = vec![0xff].into_validated();
assert!(invalid.is_invalid());
assert!(!invalid.is_valid());
let invalid_ref = invalid.as_ref();
assert!(invalid_ref.is_invalid());
let invalid_for_reason: MaybeValidOwned<String, Vec<u8>> = vec![0xff].into_validated();
assert_eq!(
invalid_for_reason
.invalid_reason()
.expect("expected invalid reason")
.valid_up_to(),
0
);
let invalid_for_parts: MaybeValidOwned<String, Vec<u8>> = vec![0xff].into_validated();
let (parts_bytes, parts_reason) = invalid_for_parts
.invalid_parts()
.expect("expected invalid parts");
assert_eq!(parts_bytes, vec![0xff]);
assert_eq!(parts_reason.valid_up_to(), 0);
let invalid_for_reason_only: MaybeValidOwned<String, Vec<u8>> = vec![0xff].into_validated();
let reason_only = invalid_for_reason_only
.into_result_reason_only()
.expect_err("expected invalid");
assert_eq!(reason_only.valid_up_to(), 0);
let invalid_for_result: MaybeValidOwned<String, Vec<u8>> = vec![0xff].into_validated();
let (bytes, reason) = invalid_for_result
.into_result()
.expect_err("expected invalid");
assert_eq!(bytes, vec![0xff]);
assert_eq!(reason.valid_up_to(), 0);
}
#[cfg(feature = "alloc")]
#[test]
fn string_and_cstring_into_validated() {
let s_valid: MaybeValidOwned<String, Vec<u8>> = b"hello".to_vec().into_validated();
assert_eq!(s_valid.valid().as_deref(), Some("hello"));
let s_invalid: MaybeValidOwned<String, Vec<u8>> = vec![0xff, 0xfe].into_validated();
match s_invalid {
MaybeValidOwned::Valid(_) => panic!("expected invalid UTF-8"),
MaybeValidOwned::Invalid(bytes, reason) => {
assert_eq!(bytes, vec![0xff, 0xfe]);
assert_eq!(reason.valid_up_to(), 0);
}
}
let c_valid: MaybeValidOwned<std::ffi::CString, Vec<u8>> = b"ok\0".to_vec().into_validated();
match c_valid {
MaybeValidOwned::Valid(cstr) => assert_eq!(cstr.to_bytes_with_nul(), b"ok\0"),
MaybeValidOwned::Invalid(_, _) => panic!("expected valid CString"),
}
let c_invalid_missing: MaybeValidOwned<std::ffi::CString, Vec<u8>> =
b"ok".to_vec().into_validated();
match c_invalid_missing {
MaybeValidOwned::Valid(_) => panic!("expected invalid CString"),
MaybeValidOwned::Invalid(bytes, reason) => {
assert_eq!(bytes, b"ok".to_vec());
assert_eq!(reason, CStrInvalidReason::Unspecified);
}
}
let c_invalid_interior: MaybeValidOwned<std::ffi::CString, Vec<u8>> =
b"a\0b\0".to_vec().into_validated();
match c_invalid_interior {
MaybeValidOwned::Valid(_) => panic!("expected invalid CString"),
MaybeValidOwned::Invalid(bytes, reason) => {
assert_eq!(bytes, b"a\0b\0".to_vec());
assert_eq!(reason, CStrInvalidReason::Unspecified);
}
}
}