use core::{fmt, marker::PhantomData};
#[cfg(feature = "diagnostics")]
use miette::Diagnostic;
use thiserror::Error;
use crate::{
core::Predicate,
logic::{And, Not},
};
pub trait HasLength {
fn length(&self) -> usize;
}
#[derive(Debug, Error)]
#[error("received value with length >= {other}")]
#[cfg_attr(
feature = "diagnostics",
derive(Diagnostic),
diagnostic(code(length::lt), help("make sure the length is less than {other}"))
)]
pub struct LessError {
pub other: usize,
}
impl LessError {
pub const fn new(other: usize) -> Self {
Self { other }
}
}
pub struct Less<const N: usize> {
private: PhantomData<()>,
}
impl<const N: usize, T: HasLength + ?Sized> Predicate<T> for Less<N> {
type Error = LessError;
fn check(value: &T) -> Result<(), Self::Error> {
if value.length() < N {
Ok(())
} else {
Err(Self::Error::new(N))
}
}
fn expect(formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(formatter, "value with length < {N}")
}
fn expect_code(formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(formatter, "length::lt<{N}>")
}
}
#[derive(Debug, Error)]
#[error("received value with length > {other}")]
#[cfg_attr(
feature = "diagnostics",
derive(Diagnostic),
diagnostic(
code(length::le),
help("make sure the length is less than or equal to {other}")
)
)]
pub struct LessOrEqualError {
pub other: usize,
}
impl LessOrEqualError {
pub const fn new(other: usize) -> Self {
Self { other }
}
}
pub struct LessOrEqual<const N: usize> {
private: PhantomData<()>,
}
impl<const N: usize, T: HasLength + ?Sized> Predicate<T> for LessOrEqual<N> {
type Error = LessOrEqualError;
fn check(value: &T) -> Result<(), Self::Error> {
if value.length() <= N {
Ok(())
} else {
Err(Self::Error::new(N))
}
}
fn expect(formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(formatter, "value with length <= {N}")
}
fn expect_code(formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(formatter, "length::le<{N}>")
}
}
#[derive(Debug, Error)]
#[error("received value with length <= {other}")]
#[cfg_attr(
feature = "diagnostics",
derive(Diagnostic),
diagnostic(code(length::gt), help("make sure the length is greater than {other}"))
)]
pub struct GreaterError {
pub other: usize,
}
impl GreaterError {
pub const fn new(other: usize) -> Self {
Self { other }
}
}
pub struct Greater<const N: usize> {
private: PhantomData<()>,
}
impl<const N: usize, T: HasLength + ?Sized> Predicate<T> for Greater<N> {
type Error = GreaterError;
fn check(value: &T) -> Result<(), Self::Error> {
if value.length() > N {
Ok(())
} else {
Err(Self::Error::new(N))
}
}
fn expect(formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(formatter, "value with length > {N}")
}
fn expect_code(formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(formatter, "length::gt<{N}>")
}
}
#[derive(Debug, Error)]
#[error("received value with length < {other}")]
#[cfg_attr(
feature = "diagnostics",
derive(Diagnostic),
diagnostic(
code(length::ge),
help("make sure the length is greater than or equal to {other}")
)
)]
pub struct GreaterOrEqualError {
pub other: usize,
}
impl GreaterOrEqualError {
pub const fn new(other: usize) -> Self {
Self { other }
}
}
pub struct GreaterOrEqual<const N: usize> {
private: PhantomData<()>,
}
impl<const N: usize, T: HasLength + ?Sized> Predicate<T> for GreaterOrEqual<N> {
type Error = GreaterOrEqualError;
fn check(value: &T) -> Result<(), Self::Error> {
if value.length() >= N {
Ok(())
} else {
Err(Self::Error::new(N))
}
}
fn expect(formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(formatter, "value with length >= {N}")
}
fn expect_code(formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(formatter, "length::ge<{N}>")
}
}
#[derive(Debug, Error)]
#[error("received value with length != {other}")]
#[cfg_attr(
feature = "diagnostics",
derive(Diagnostic),
diagnostic(code(length::eq), help("make sure the length is equal to {other}"))
)]
pub struct EqualError {
pub other: usize,
}
impl EqualError {
pub const fn new(other: usize) -> Self {
Self { other }
}
}
pub struct Equal<const N: usize> {
private: PhantomData<()>,
}
impl<const N: usize, T: HasLength + ?Sized> Predicate<T> for Equal<N> {
type Error = EqualError;
fn check(value: &T) -> Result<(), Self::Error> {
if value.length() == N {
Ok(())
} else {
Err(Self::Error::new(N))
}
}
fn expect(formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(formatter, "value with length == {N}")
}
fn expect_code(formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(formatter, "length::eq<{N}>")
}
}
#[derive(Debug, Error)]
#[error("received value with length == {other}")]
#[cfg_attr(
feature = "diagnostics",
derive(Diagnostic),
diagnostic(code(length::ne), help("make sure the length is not equal to {other}"))
)]
pub struct NotEqualError {
pub other: usize,
}
impl NotEqualError {
pub const fn new(other: usize) -> Self {
Self { other }
}
}
pub struct NotEqual<const N: usize> {
private: PhantomData<()>,
}
impl<const N: usize, T: HasLength + ?Sized> Predicate<T> for NotEqual<N> {
type Error = NotEqualError;
#[allow(clippy::if_not_else)]
fn check(value: &T) -> Result<(), Self::Error> {
if value.length() != N {
Ok(())
} else {
Err(Self::Error::new(N))
}
}
fn expect(formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(formatter, "value with length != {N}")
}
fn expect_code(formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(formatter, "length::ne<{N}>")
}
}
pub type Open<const M: usize, const N: usize> = And<Greater<M>, Less<N>>;
pub type ClosedOpen<const M: usize, const N: usize> = And<GreaterOrEqual<M>, Less<N>>;
pub type OpenClosed<const M: usize, const N: usize> = And<Greater<M>, LessOrEqual<N>>;
pub type Closed<const M: usize, const N: usize> = And<GreaterOrEqual<M>, LessOrEqual<N>>;
pub type Zero = Equal<0>;
pub type NonZero = NotEqual<0>;
#[derive(Debug, Error)]
#[error("received value % {divisor} != {modulo}")]
pub struct ModuloError {
pub divisor: usize,
pub modulo: usize,
}
impl ModuloError {
pub const fn new(divisor: usize, modulo: usize) -> Self {
Self { divisor, modulo }
}
}
pub struct Modulo<const D: usize, const M: usize> {
private: PhantomData<()>,
}
impl<const D: usize, const M: usize, T: HasLength + ?Sized> Predicate<T> for Modulo<D, M> {
type Error = ModuloError;
fn check(value: &T) -> Result<(), Self::Error> {
if value.length() % D == M {
Ok(())
} else {
Err(Self::Error::new(D, M))
}
}
fn expect(formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(formatter, "length % {D} == {M}")
}
fn expect_code(formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(formatter, "length::mod<{D}, {M}>")
}
}
pub type Divisible<const D: usize> = Modulo<D, 0>;
pub type Even = Divisible<2>;
pub type Odd = Not<Even>;
impl HasLength for str {
fn length(&self) -> usize {
self.len()
}
}
impl<T> HasLength for [T] {
fn length(&self) -> usize {
self.len()
}
}
impl<T: HasLength + ?Sized> HasLength for &T {
fn length(&self) -> usize {
T::length(self)
}
}
#[cfg(feature = "alloc")]
use alloc::{boxed::Box, string::String, vec::Vec};
#[cfg(any(feature = "alloc", feature = "std"))]
impl<T: HasLength + ?Sized> HasLength for Box<T> {
fn length(&self) -> usize {
T::length(self)
}
}
#[cfg(any(feature = "alloc", feature = "std"))]
impl HasLength for String {
fn length(&self) -> usize {
self.len()
}
}
#[cfg(any(feature = "alloc", feature = "std"))]
impl<T> HasLength for Vec<T> {
fn length(&self) -> usize {
self.len()
}
}
#[cfg(feature = "alloc")]
use alloc::borrow::{Cow, ToOwned};
#[cfg(all(not(feature = "alloc"), feature = "std"))]
use std::borrow::{Cow, ToOwned};
#[cfg(any(feature = "alloc", feature = "std"))]
impl<T: ToOwned + HasLength + ?Sized> HasLength for Cow<'_, T> {
fn length(&self) -> usize {
T::length(self)
}
}
#[cfg(feature = "alloc")]
use alloc::rc::Rc;
#[cfg(all(not(feature = "alloc"), feature = "std"))]
use std::rc::Rc;
#[cfg(any(feature = "alloc", feature = "std"))]
impl<T: HasLength + ?Sized> HasLength for Rc<T> {
fn length(&self) -> usize {
T::length(self)
}
}
#[cfg(feature = "alloc")]
use alloc::sync::Arc;
#[cfg(all(not(feature = "alloc"), feature = "std"))]
use std::sync::Arc;
#[cfg(any(feature = "alloc", feature = "std"))]
impl<T: HasLength + ?Sized> HasLength for Arc<T> {
fn length(&self) -> usize {
T::length(self)
}
}
#[cfg(feature = "alloc")]
use alloc::collections::{BTreeMap, BTreeSet, BinaryHeap, LinkedList, VecDeque};
#[cfg(all(not(feature = "alloc"), feature = "std"))]
use std::collections::{BTreeMap, BTreeSet, BinaryHeap, LinkedList, VecDeque};
#[cfg(any(feature = "alloc", feature = "std"))]
impl<K, V> HasLength for BTreeMap<K, V> {
fn length(&self) -> usize {
self.len()
}
}
#[cfg(any(feature = "alloc", feature = "std"))]
impl<T> HasLength for BTreeSet<T> {
fn length(&self) -> usize {
self.len()
}
}
#[cfg(any(feature = "alloc", feature = "std"))]
impl<T> HasLength for BinaryHeap<T> {
fn length(&self) -> usize {
self.len()
}
}
#[cfg(any(feature = "alloc", feature = "std"))]
impl<T> HasLength for LinkedList<T> {
fn length(&self) -> usize {
self.len()
}
}
#[cfg(any(feature = "alloc", feature = "std"))]
impl<T> HasLength for VecDeque<T> {
fn length(&self) -> usize {
self.len()
}
}
#[cfg(feature = "std")]
use std::collections::{HashMap, HashSet};
#[cfg(feature = "std")]
impl<K, V, S> HasLength for HashMap<K, V, S> {
fn length(&self) -> usize {
self.len()
}
}
#[cfg(feature = "std")]
impl<T, S> HasLength for HashSet<T, S> {
fn length(&self) -> usize {
self.len()
}
}
#[cfg(feature = "std")]
use std::ffi::{OsStr, OsString};
#[cfg(feature = "std")]
impl HasLength for OsStr {
fn length(&self) -> usize {
self.len()
}
}
#[cfg(feature = "std")]
impl HasLength for OsString {
fn length(&self) -> usize {
self.len()
}
}
#[cfg(feature = "std")]
use std::path::{Path, PathBuf};
#[cfg(feature = "std")]
impl HasLength for Path {
fn length(&self) -> usize {
self.as_os_str().length()
}
}
#[cfg(feature = "std")]
impl HasLength for PathBuf {
fn length(&self) -> usize {
self.as_os_str().length()
}
}