macro_rules! into_i16_index {
( $( $t:ty ),+ $(,)? ) => {
$(
impl From<$t> for i16 {
fn from(t: $t) -> Self {
t.0
}
}
impl From<$t> for i64 {
fn from(t: $t) -> Self {
t.0 as i64
}
}
)+
};
}
macro_rules! wrap_uuid {
(
$(#[$outer:meta])*
$vis:vis struct $t:ident
) => {
#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize, Deserialize, PathPart, Hash)]
$(#[$outer])*
#[cfg_attr(feature = "backend", derive(sqlx::Type))]
#[cfg_attr(feature = "backend", sqlx(transparent))]
$vis struct $t(pub uuid::Uuid);
impl From<$t> for uuid::Uuid {
fn from(t: $t) -> Self {
t.0
}
}
impl std::fmt::Display for $t {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
impl std::str::FromStr for $t {
type Err = uuid::Error;
#[inline]
fn from_str(value: &str) -> Result<Self, Self::Err> {
Ok(Self(uuid::Uuid::from_str(value)?))
}
}
impl $t {
#[inline]
#[must_use]
$vis const fn from_u128(v: u128) -> Self {
Self(uuid::Uuid::from_u128(v))
}
}
}
}
pub mod additional_resource;
pub mod admin;
pub mod animation;
pub mod asset;
pub mod audio;
pub mod billing;
pub mod category;
pub mod circle;
pub mod course;
pub mod image;
pub mod jig;
pub mod locale;
pub mod media;
pub mod meta;
pub mod module;
pub mod pdf;
pub mod playlist;
pub mod resource;
pub mod search;
pub mod ser;
pub mod session;
pub mod user;
#[deprecated]
pub mod auth {
#[deprecated]
pub use super::session::AUTH_COOKIE_NAME;
#[deprecated]
pub use super::session::CSRF_HEADER_NAME;
#[deprecated]
pub use super::user::CreateProfileRequest as RegisterRequest;
}
use chrono::Utc;
use ser::{csv_encode_i16_indices, csv_encode_uuids, deserialize_optional_field, from_csv, to_csv};
use serde::{Deserialize, Serialize};
use std::fmt::{Display, Formatter};
use uuid::Uuid;
#[derive(Debug)]
pub struct Base64<T>(pub T);
impl<T: Display> serde::Serialize for Base64<T> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(&base64::encode(&self.0.to_string()))
}
}
impl<'de, E: std::fmt::Debug, T: std::str::FromStr<Err = E>> serde::Deserialize<'de> for Base64<T> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
Ok(Self(deserializer.deserialize_str(ser::FromStrVisitor(
std::marker::PhantomData,
))?))
}
}
#[derive(Debug, serde::Serialize, serde::Deserialize, Clone)]
pub struct CreateResponse<T: Into<Uuid>> {
pub id: T,
}
#[derive(Copy, Clone, Eq, PartialEq, serde::Serialize, serde::Deserialize, Debug)]
pub enum Publish {
At(chrono::DateTime<Utc>),
In(std::time::Duration),
}
impl Publish {
#[must_use]
#[allow(clippy::missing_const_for_fn)]
pub fn now() -> Self {
Self::In(std::time::Duration::new(0, 0))
}
}
impl From<Publish> for chrono::DateTime<Utc> {
fn from(publish: Publish) -> Self {
match publish {
Publish::At(t) => t,
Publish::In(d) => {
Utc::now() + chrono::Duration::from_std(d).expect("Really really big duration?")
}
}
}
}
#[derive(Clone, Debug, Serialize, Default)]
#[serde(untagged)]
pub enum UpdateNullable<T> {
#[default]
Keep,
Unset,
Change(T),
}
impl<T> From<Option<T>> for UpdateNullable<T> {
fn from(value: Option<T>) -> Self {
match value {
Some(value) => Self::Change(value),
None => Self::Unset,
}
}
}
impl<'de, T> Deserialize<'de> for UpdateNullable<T>
where
T: Deserialize<'de>,
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
#[derive(Debug, Deserialize)]
#[serde(untagged)]
enum UpdateMap<T> {
Unset,
Change(T),
}
let mapping = UpdateMap::deserialize(deserializer)?;
let update = match mapping {
UpdateMap::Unset => Self::Unset,
UpdateMap::Change(val) => Self::Change(val),
};
Ok(update)
}
}
impl<T> UpdateNullable<T> {
pub const fn is_keep(&self) -> bool {
matches!(self, Self::Keep)
}
pub const fn is_unset(&self) -> bool {
matches!(self, Self::Unset)
}
pub const fn is_change(&self) -> bool {
matches!(self, Self::Change(_))
}
pub fn into_option(self) -> Option<T> {
match self {
Self::Keep | Self::Unset => None,
Self::Change(v) => Some(v),
}
}
}
#[derive(Clone, Debug, Serialize, Default)]
#[serde(untagged)]
pub enum UpdateNonNullable<T> {
#[default]
Keep,
Change(T),
}
impl<'de, T> Deserialize<'de> for UpdateNonNullable<T>
where
T: Deserialize<'de>,
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
Ok(Self::Change(T::deserialize(deserializer)?))
}
}
impl<T> UpdateNonNullable<T> {
pub const fn is_keep(&self) -> bool {
matches!(self, Self::Keep)
}
pub const fn is_change(&self) -> bool {
matches!(self, Self::Change(_))
}
pub fn into_option(self) -> Option<T> {
match self {
Self::Keep => None,
Self::Change(v) => Some(v),
}
}
}
impl<T> From<Option<T>> for UpdateNonNullable<T> {
fn from(value: Option<T>) -> Self {
value.map(UpdateNonNullable::Change).unwrap_or_default()
}
}
#[derive(Copy, Debug, Default, Clone, Ord, PartialOrd, Eq, PartialEq, Serialize, Deserialize)]
pub struct Page(usize);
impl From<usize> for Page {
fn from(value: usize) -> Self {
Self(value)
}
}
impl From<Page> for usize {
fn from(value: Page) -> Self {
value.0
}
}
#[cfg(feature = "backend")]
impl From<Page> for i64 {
fn from(value: Page) -> Self {
value.0 as i64
}
}
impl Display for Page {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
impl Page {
pub fn next_page(self) -> Self {
Self(self.0.saturating_add(1))
}
pub fn prev_page(self) -> Self {
Self(self.0.saturating_sub(1))
}
}
const DEFAULT_PAGE_LIMIT: usize = 20;
#[derive(Serialize, Deserialize, Copy, Debug, Clone, Ord, PartialOrd, Eq, PartialEq)]
pub struct PageLimit(usize);
impl Default for PageLimit {
fn default() -> Self {
Self(DEFAULT_PAGE_LIMIT)
}
}
impl From<usize> for PageLimit {
fn from(value: usize) -> Self {
Self(value)
}
}
impl From<PageLimit> for usize {
fn from(value: PageLimit) -> Self {
value.0
}
}
#[cfg(feature = "backend")]
impl From<PageLimit> for i64 {
fn from(value: PageLimit) -> Self {
value.0 as i64
}
}
impl PageLimit {
#[cfg(feature = "backend")]
pub fn offset(&self, page: Page) -> i64 {
(self.0 * page.0) as i64
}
}
#[derive(Serialize, Deserialize, Copy, Debug, Clone, Ord, PartialOrd, Eq, PartialEq)]
pub struct ItemCount(usize);
impl From<usize> for ItemCount {
fn from(value: usize) -> Self {
Self(value)
}
}
impl From<ItemCount> for usize {
fn from(value: ItemCount) -> Self {
value.0
}
}
#[cfg(feature = "backend")]
impl From<ItemCount> for i64 {
fn from(value: ItemCount) -> Self {
value.0 as i64
}
}
impl ItemCount {
pub fn paged(&self, limit: PageLimit) -> Self {
let page_count = self.0 / limit.0 + (self.0 % limit.0 != 0) as usize;
page_count.into()
}
}
#[derive(Debug, Default, Copy, Clone, PartialEq, PartialOrd, Serialize, Deserialize)]
pub struct Percent(f64);
impl Display for Percent {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}%", self.0 * 100.0)
}
}
impl From<f64> for Percent {
fn from(value: f64) -> Self {
Self(value)
}
}
impl From<Percent> for f64 {
fn from(value: Percent) -> Self {
value.0
}
}
#[cfg(feature = "backend")]
impl From<sqlx::types::BigDecimal> for Percent {
fn from(value: sqlx::types::BigDecimal) -> Self {
use bigdecimal::ToPrimitive;
Self(value.to_f64().unwrap_or_default())
}
}
#[cfg(feature = "backend")]
impl From<Percent> for sqlx::types::BigDecimal {
fn from(value: Percent) -> Self {
Self::try_from(value.0).ok().unwrap_or_default()
}
}