//! Trait implementations for `ByteStr`.

use crate::bstr::ByteStr;
use crate::cmp::Ordering;
use crate::slice::SliceIndex;
use crate::{hash, ops, range};

#[unstable(feature = "bstr", issue = "134915")]
impl Ord for ByteStr {
    #[inline]
    fn cmp(&self, other: &ByteStr) -> Ordering {
        Ord::cmp(&self.0, &other.0)
    }
}

#[unstable(feature = "bstr", issue = "134915")]
impl PartialOrd for ByteStr {
    #[inline]
    fn partial_cmp(&self, other: &ByteStr) -> Option<Ordering> {
        PartialOrd::partial_cmp(&self.0, &other.0)
    }
}

#[unstable(feature = "bstr", issue = "134915")]
impl PartialEq<ByteStr> for ByteStr {
    #[inline]
    fn eq(&self, other: &ByteStr) -> bool {
        &self.0 == &other.0
    }
}

#[unstable(feature = "bstr", issue = "134915")]
impl Eq for ByteStr {}

#[unstable(feature = "bstr", issue = "134915")]
impl hash::Hash for ByteStr {
    #[inline]
    fn hash<H: hash::Hasher>(&self, state: &mut H) {
        self.0.hash(state);
    }
}

#[doc(hidden)]
#[macro_export]
#[unstable(feature = "bstr_internals", issue = "none")]
macro_rules! impl_partial_eq {
    ($lhs:ty, $rhs:ty) => {
        impl PartialEq<$rhs> for $lhs {
            #[inline]
            fn eq(&self, other: &$rhs) -> bool {
                let other: &[u8] = other.as_ref();
                PartialEq::eq(self.as_bytes(), other)
            }
        }

        impl PartialEq<$lhs> for $rhs {
            #[inline]
            fn eq(&self, other: &$lhs) -> bool {
                let this: &[u8] = self.as_ref();
                PartialEq::eq(this, other.as_bytes())
            }
        }
    };
}

#[doc(hidden)]
#[unstable(feature = "bstr_internals", issue = "none")]
pub use impl_partial_eq;

#[doc(hidden)]
#[macro_export]
#[unstable(feature = "bstr_internals", issue = "none")]
macro_rules! impl_partial_eq_ord {
    ($lhs:ty, $rhs:ty) => {
        $crate::bstr::impl_partial_eq!($lhs, $rhs);

        #[unstable(feature = "bstr", issue = "134915")]
        impl PartialOrd<$rhs> for $lhs {
            #[inline]
            fn partial_cmp(&self, other: &$rhs) -> Option<Ordering> {
                let other: &[u8] = other.as_ref();
                PartialOrd::partial_cmp(self.as_bytes(), other)
            }
        }

        #[unstable(feature = "bstr", issue = "134915")]
        impl PartialOrd<$lhs> for $rhs {
            #[inline]
            fn partial_cmp(&self, other: &$lhs) -> Option<Ordering> {
                let this: &[u8] = self.as_ref();
                PartialOrd::partial_cmp(this, other.as_bytes())
            }
        }
    };
}

#[doc(hidden)]
#[unstable(feature = "bstr_internals", issue = "none")]
pub use impl_partial_eq_ord;

#[doc(hidden)]
#[macro_export]
#[unstable(feature = "bstr_internals", issue = "none")]
macro_rules! impl_partial_eq_n {
    ($lhs:ty, $rhs:ty) => {
        #[unstable(feature = "bstr", issue = "134915")]
        impl<const N: usize> PartialEq<$rhs> for $lhs {
            #[inline]
            fn eq(&self, other: &$rhs) -> bool {
                let other: &[u8] = other.as_ref();
                PartialEq::eq(self.as_bytes(), other)
            }
        }

        #[unstable(feature = "bstr", issue = "134915")]
        impl<const N: usize> PartialEq<$lhs> for $rhs {
            #[inline]
            fn eq(&self, other: &$lhs) -> bool {
                let this: &[u8] = self.as_ref();
                PartialEq::eq(this, other.as_bytes())
            }
        }
    };
}

#[doc(hidden)]
#[unstable(feature = "bstr_internals", issue = "none")]
pub use impl_partial_eq_n;

// PartialOrd with `[u8]` omitted to avoid inference failures
impl_partial_eq!(ByteStr, [u8]);
// PartialOrd with `&[u8]` omitted to avoid inference failures
impl_partial_eq!(ByteStr, &[u8]);
// PartialOrd with `str` omitted to avoid inference failures
impl_partial_eq!(ByteStr, str);
// PartialOrd with `&str` omitted to avoid inference failures
impl_partial_eq!(ByteStr, &str);
// PartialOrd with `[u8; N]` omitted to avoid inference failures
impl_partial_eq_n!(ByteStr, [u8; N]);
// PartialOrd with `[u8; N]` omitted to avoid inference failures
impl_partial_eq_n!(ByteStr, &[u8; N]);

#[unstable(feature = "bstr", issue = "134915")]
impl<I> ops::Index<I> for ByteStr
where
    I: SliceIndex<ByteStr>,
{
    type Output = I::Output;

    #[inline]
    fn index(&self, index: I) -> &I::Output {
        index.index(self)
    }
}

#[unstable(feature = "bstr", issue = "134915")]
impl<I> ops::IndexMut<I> for ByteStr
where
    I: SliceIndex<ByteStr>,
{
    #[inline]
    fn index_mut(&mut self, index: I) -> &mut I::Output {
        index.index_mut(self)
    }
}

#[unstable(feature = "bstr", issue = "134915")]
unsafe impl SliceIndex<ByteStr> for ops::RangeFull {
    type Output = ByteStr;
    #[inline]
    fn get(self, slice: &ByteStr) -> Option<&Self::Output> {
        Some(slice)
    }
    #[inline]
    fn get_mut(self, slice: &mut ByteStr) -> Option<&mut Self::Output> {
        Some(slice)
    }
    #[inline]
    unsafe fn get_unchecked(self, slice: *const ByteStr) -> *const Self::Output {
        slice
    }
    #[inline]
    unsafe fn get_unchecked_mut(self, slice: *mut ByteStr) -> *mut Self::Output {
        slice
    }
    #[inline]
    fn index(self, slice: &ByteStr) -> &Self::Output {
        slice
    }
    #[inline]
    fn index_mut(self, slice: &mut ByteStr) -> &mut Self::Output {
        slice
    }
}

#[unstable(feature = "bstr", issue = "134915")]
unsafe impl SliceIndex<ByteStr> for usize {
    type Output = u8;
    #[inline]
    fn get(self, slice: &ByteStr) -> Option<&Self::Output> {
        self.get(slice.as_bytes())
    }
    #[inline]
    fn get_mut(self, slice: &mut ByteStr) -> Option<&mut Self::Output> {
        self.get_mut(slice.as_bytes_mut())
    }
    #[inline]
    unsafe fn get_unchecked(self, slice: *const ByteStr) -> *const Self::Output {
        // SAFETY: the caller has to uphold the safety contract for `get_unchecked`.
        unsafe { self.get_unchecked(slice as *const [u8]) }
    }
    #[inline]
    unsafe fn get_unchecked_mut(self, slice: *mut ByteStr) -> *mut Self::Output {
        // SAFETY: the caller has to uphold the safety contract for `get_unchecked_mut`.
        unsafe { self.get_unchecked_mut(slice as *mut [u8]) }
    }
    #[inline]
    fn index(self, slice: &ByteStr) -> &Self::Output {
        self.index(slice.as_bytes())
    }
    #[inline]
    fn index_mut(self, slice: &mut ByteStr) -> &mut Self::Output {
        self.index_mut(slice.as_bytes_mut())
    }
}

macro_rules! impl_slice_index {
    ($index:ty) => {
        #[unstable(feature = "bstr", issue = "134915")]
        unsafe impl SliceIndex<ByteStr> for $index {
            type Output = ByteStr;
            #[inline]
            fn get(self, slice: &ByteStr) -> Option<&Self::Output> {
                self.get(slice.as_bytes()).map(ByteStr::from_bytes)
            }
            #[inline]
            fn get_mut(self, slice: &mut ByteStr) -> Option<&mut Self::Output> {
                self.get_mut(slice.as_bytes_mut()).map(ByteStr::from_bytes_mut)
            }
            #[inline]
            unsafe fn get_unchecked(self, slice: *const ByteStr) -> *const Self::Output {
                // SAFETY: the caller has to uphold the safety contract for `get_unchecked`.
                unsafe { self.get_unchecked(slice as *const [u8]) as *const ByteStr }
            }
            #[inline]
            unsafe fn get_unchecked_mut(self, slice: *mut ByteStr) -> *mut Self::Output {
                // SAFETY: the caller has to uphold the safety contract for `get_unchecked_mut`.
                unsafe { self.get_unchecked_mut(slice as *mut [u8]) as *mut ByteStr }
            }
            #[inline]
            fn index(self, slice: &ByteStr) -> &Self::Output {
                ByteStr::from_bytes(self.index(slice.as_bytes()))
            }
            #[inline]
            fn index_mut(self, slice: &mut ByteStr) -> &mut Self::Output {
                ByteStr::from_bytes_mut(self.index_mut(slice.as_bytes_mut()))
            }
        }
    };
}

impl_slice_index!(ops::IndexRange);
impl_slice_index!(ops::Range<usize>);
impl_slice_index!(range::Range<usize>);
impl_slice_index!(ops::RangeTo<usize>);
impl_slice_index!(ops::RangeFrom<usize>);
impl_slice_index!(range::RangeFrom<usize>);
impl_slice_index!(ops::RangeInclusive<usize>);
impl_slice_index!(range::RangeInclusive<usize>);
impl_slice_index!(ops::RangeToInclusive<usize>);
impl_slice_index!(range::RangeToInclusive<usize>);
impl_slice_index!((ops::Bound<usize>, ops::Bound<usize>));
