use std::collections::HashSet;
use std::hash::Hash;
#[derive(Debug, thiserror::Error)]
#[error("unable to parse enum, found {value:?}")]
pub struct ParseEnumError {
pub value: String,
}
impl ParseEnumError {
pub fn new(value: impl Into<String>) -> Self {
Self {
value: value.into(),
}
}
}
pub trait AsStr {
fn as_str(&self) -> &str;
}
#[cfg_attr(feature = "facet", derive(facet::Facet))]
#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
pub struct Page<const N: u32 = 50> {
#[serde(
default = "Page::<N>::default_limit",
skip_serializing_if = "Page::<N>::is_default_limit"
)]
pub limit: u32,
#[serde(
default = "Page::<N>::default_offset",
skip_serializing_if = "Page::<N>::is_default_offset"
)]
pub offset: u32,
}
impl<const N: u32> Default for Page<N> {
fn default() -> Self {
Self {
limit: Self::default_limit(),
offset: Self::default_offset(),
}
}
}
impl<const N: u32> Page<N> {
pub const fn default_limit() -> u32 {
N
}
pub const fn is_default_limit(value: &u32) -> bool {
*value == N
}
pub const fn default_offset() -> u32 {
0
}
pub const fn is_default_offset(value: &u32) -> bool {
*value == 0
}
pub const fn is_default(&self) -> bool {
self.limit == Self::default_limit() && self.offset == Self::default_offset()
}
}
#[cfg(feature = "server")]
impl From<Page> for entertainarr_domain::prelude::Page {
fn from(value: Page) -> Self {
Self {
limit: value.limit,
offset: value.offset,
}
}
}
#[cfg_attr(feature = "facet", derive(facet::Facet))]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Sort<T> {
pub field: T,
pub order: SortOrder,
}
impl<T: Default + PartialEq> Sort<T> {
pub fn is_default(&self) -> bool {
self.field == T::default() && self.order == SortOrder::Asc
}
}
impl<T: Default> Default for Sort<T> {
fn default() -> Self {
Self {
field: T::default(),
order: SortOrder::Asc,
}
}
}
impl<T> serde::Serialize for Sort<T>
where
T: AsStr,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
match self.order {
SortOrder::Desc => {
let value = format!("-{}", self.field.as_str());
serializer.serialize_str(value.as_str())
}
SortOrder::Asc => serializer.serialize_str(self.field.as_str()),
}
}
}
impl<'de, T> serde::Deserialize<'de> for Sort<T>
where
T: std::str::FromStr,
T::Err: std::fmt::Display,
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let value = String::deserialize(deserializer)?;
if let Some(value) = value.strip_prefix("-") {
Ok(Self {
field: T::from_str(value).map_err(serde::de::Error::custom)?,
order: SortOrder::Desc,
})
} else {
let value = value.trim_start_matches("+");
Ok(Self {
field: T::from_str(value).map_err(serde::de::Error::custom)?,
order: SortOrder::Asc,
})
}
}
}
#[cfg_attr(feature = "facet", derive(facet::Facet))]
#[cfg_attr(feature = "facet", repr(C))]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum SortOrder {
Asc,
Desc,
}
#[cfg(feature = "server")]
impl From<entertainarr_domain::prelude::SortOrder> for SortOrder {
fn from(value: entertainarr_domain::prelude::SortOrder) -> Self {
match value {
entertainarr_domain::prelude::SortOrder::Asc => Self::Asc,
entertainarr_domain::prelude::SortOrder::Desc => Self::Desc,
}
}
}
#[cfg(feature = "server")]
impl From<SortOrder> for entertainarr_domain::prelude::SortOrder {
fn from(value: SortOrder) -> Self {
match value {
SortOrder::Asc => Self::Asc,
SortOrder::Desc => Self::Desc,
}
}
}
#[cfg_attr(feature = "facet", derive(facet::Facet))]
#[derive(Clone, Debug, Default, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct FindQueryParams<Include>
where
Include: Eq + Hash,
{
#[serde(default, skip_serializing_if = "HashSet::is_empty")]
pub include: HashSet<Include>,
}