use core::{
cmp::Ordering,
fmt,
hash::{Hash, Hasher},
marker::PhantomData,
mem::transmute,
ops::Deref,
};
#[cfg(feature = "unsafe-assert")]
use core::hint::assert_unchecked;
use crate::{
context::NoContext,
errors::{Error, Recoverable, RecoverableRef},
predicate::Predicate,
types::TypeStr,
};
#[repr(transparent)]
pub struct Refinement<T: ?Sized, P: Predicate<T> + ?Sized, C: TypeStr + ?Sized = NoContext> {
predicate: PhantomData<P>,
context: PhantomData<C>,
value: T,
}
impl<T: fmt::Debug + ?Sized, P: Predicate<T> + ?Sized, C: TypeStr + ?Sized> fmt::Debug
for Refinement<T, P, C>
{
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
self.get_ref().fmt(formatter)
}
}
impl<T: fmt::Display + ?Sized, P: Predicate<T> + ?Sized, C: TypeStr + ?Sized> fmt::Display
for Refinement<T, P, C>
{
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
self.get_ref().fmt(formatter)
}
}
impl<T: PartialEq + ?Sized, P: Predicate<T> + ?Sized, C: TypeStr + ?Sized> PartialEq
for Refinement<T, P, C>
{
fn eq(&self, other: &Self) -> bool {
self.get_ref().eq(other.get_ref())
}
}
impl<T: Eq + ?Sized, P: Predicate<T> + ?Sized, C: TypeStr + ?Sized> Eq for Refinement<T, P, C> {}
impl<T: PartialOrd + ?Sized, P: Predicate<T> + ?Sized, C: TypeStr + ?Sized> PartialOrd
for Refinement<T, P, C>
{
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.get_ref().partial_cmp(other.get_ref())
}
}
impl<T: Ord + ?Sized, P: Predicate<T> + ?Sized, C: TypeStr + ?Sized> Ord for Refinement<T, P, C> {
fn cmp(&self, other: &Self) -> Ordering {
self.get_ref().cmp(other.get_ref())
}
}
impl<T: Hash + ?Sized, P: Predicate<T> + ?Sized, C: TypeStr + ?Sized> Hash for Refinement<T, P, C> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.get_ref().hash(state);
}
}
impl<T: ?Sized, P: Predicate<T> + ?Sized, C: TypeStr + ?Sized> AsRef<T> for Refinement<T, P, C> {
fn as_ref(&self) -> &T {
self.get_ref()
}
}
impl<T: ?Sized, P: Predicate<T> + ?Sized, C: TypeStr + ?Sized> Deref for Refinement<T, P, C> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.get_ref()
}
}
mod sealed {
pub trait Sealed {}
}
pub type RecoverableRefinement<R> =
Recoverable<R, <R as Refining>::Predicate, <R as Refining>::Context, <R as Refining>::Value>;
pub type RecoverableRefinementRef<'a, R> = RecoverableRef<
'a,
R,
<R as Refining>::Predicate,
<R as Refining>::Context,
<R as Refining>::Value,
>;
pub trait Refining: Deref<Target = Self::Value> + sealed::Sealed {
type Value: ?Sized;
type Predicate: Predicate<Self::Value> + ?Sized;
type Context: TypeStr + ?Sized;
unsafe fn unchecked_ref(value: &Self::Value) -> &Self;
unsafe fn unchecked(value: Self::Value) -> Self
where
Self::Value: Sized;
fn is_fine(value: &Self::Value) -> bool {
Self::Predicate::check(value)
}
fn get_ref(&self) -> &Self::Value;
fn get(self) -> Self::Value
where
Self::Value: Sized;
fn checked_ref(value: &Self::Value) -> RecoverableRefinementRef<'_, Self>;
fn checked(value: Self::Value) -> RecoverableRefinement<Self>
where
Self::Value: Sized,
Self: Sized;
}
impl<T: ?Sized, P: Predicate<T> + ?Sized, C: TypeStr + ?Sized> Refinement<T, P, C> {
pub const unsafe fn unchecked_ref(value: &T) -> &Self {
unsafe { transmute(value) }
}
#[allow(clippy::missing_const_for_fn)] pub fn get_ref(&self) -> &T {
let reference = self.get_ref_no_assert();
#[cfg(feature = "unsafe-assert")]
unsafe {
assert_unchecked(Self::is_fine(reference));
}
reference
}
pub const fn get_ref_no_assert(&self) -> &T {
&self.value
}
pub fn is_fine(value: &T) -> bool {
P::check(value)
}
pub fn checked_ref(value: &T) -> RecoverableRef<'_, Self, P, C, T> {
if Self::is_fine(value) {
Ok(unsafe { Self::unchecked_ref(value) })
} else {
Err(Error::new_ref(value))
}
}
}
impl<T, P: Predicate<T> + ?Sized, C: TypeStr + ?Sized> Refinement<T, P, C> {
pub const unsafe fn unchecked(value: T) -> Self {
Self {
predicate: PhantomData,
context: PhantomData,
value,
}
}
pub fn get(self) -> T {
let value = self.get_no_assert();
#[cfg(feature = "unsafe-assert")]
unsafe {
assert_unchecked(Self::is_fine(&value));
}
value
}
pub fn get_no_assert(self) -> T {
self.value
}
pub fn checked(value: T) -> Recoverable<Self, P, C, T> {
if Self::is_fine(&value) {
Ok(unsafe { Self::unchecked(value) })
} else {
Err(Error::new(value))
}
}
}
impl<T: ?Sized, P: Predicate<T> + ?Sized, C: TypeStr + ?Sized> sealed::Sealed
for Refinement<T, P, C>
{
}
impl<T: ?Sized, P: Predicate<T> + ?Sized, C: TypeStr + ?Sized> Refining for Refinement<T, P, C> {
type Value = T;
type Predicate = P;
type Context = C;
unsafe fn unchecked_ref(value: &Self::Value) -> &Self {
unsafe { Self::unchecked_ref(value) }
}
unsafe fn unchecked(value: Self::Value) -> Self
where
Self::Value: Sized,
{
unsafe { Self::unchecked(value) }
}
fn is_fine(value: &Self::Value) -> bool {
Self::is_fine(value)
}
fn get_ref(&self) -> &Self::Value {
self.get_ref()
}
fn get(self) -> Self::Value
where
Self::Value: Sized,
{
self.get()
}
fn checked_ref(value: &Self::Value) -> RecoverableRefinementRef<'_, Self> {
Self::checked_ref(value)
}
fn checked(value: Self::Value) -> RecoverableRefinement<Self>
where
Self::Value: Sized,
{
Self::checked(value)
}
}
pub trait Refine {
fn refine_ref<R: Refining<Value = Self> + ?Sized>(&self) -> RecoverableRefinementRef<'_, R> {
R::checked_ref(self)
}
fn refine<R: Refining<Value = Self>>(self) -> RecoverableRefinement<R>
where
Self: Sized,
{
R::checked(self)
}
}
impl<T: ?Sized> Refine for T {}