refactor(id_type): use macros for defining ID types and implementing common traits (#5471)

This commit is contained in:
Sanchith Hegde
2024-07-30 20:21:29 +05:30
committed by GitHub
parent be9347b8d5
commit 1d4fb1d247
34 changed files with 321 additions and 384 deletions

View File

@ -1,9 +1,10 @@
#![allow(missing_docs)]
//! Utility macros
#[allow(missing_docs)]
#[macro_export]
macro_rules! newtype_impl {
($is_pub:vis, $name:ident, $ty_path:path) => {
impl std::ops::Deref for $name {
impl core::ops::Deref for $name {
type Target = $ty_path;
fn deref(&self) -> &Self::Target {
@ -11,7 +12,7 @@ macro_rules! newtype_impl {
}
}
impl std::ops::DerefMut for $name {
impl core::ops::DerefMut for $name {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
@ -31,6 +32,7 @@ macro_rules! newtype_impl {
};
}
#[allow(missing_docs)]
#[macro_export]
macro_rules! newtype {
($is_pub:vis $name:ident = $ty_path:path) => {
@ -59,6 +61,7 @@ macro_rules! openapi_route {
}};
}
#[allow(missing_docs)]
#[macro_export]
macro_rules! fallback_reverse_lookup_not_found {
($a:expr,$b:expr) => {
@ -81,6 +84,8 @@ macro_rules! fallback_reverse_lookup_not_found {
};
}
/// Collects names of all optional fields that are `None`.
/// This is typically useful for constructing error messages including a list of all missing fields.
#[macro_export]
macro_rules! collect_missing_value_keys {
[$(($key:literal, $option:expr)),+] => {
@ -96,6 +101,8 @@ macro_rules! collect_missing_value_keys {
};
}
/// Implements the `ToSql` and `FromSql` traits on a type to allow it to be serialized/deserialized
/// to/from JSON data in the database.
#[macro_export]
macro_rules! impl_to_sql_from_sql_json {
($type:ty, $diesel_type:ty) => {
@ -135,3 +142,162 @@ macro_rules! impl_to_sql_from_sql_json {
$crate::impl_to_sql_from_sql_json!($type, diesel::sql_types::Jsonb);
};
}
mod id_type {
/// Defines an ID type.
#[macro_export]
macro_rules! id_type {
($type:ident, $doc:literal, $diesel_type:ty, $max_length:expr, $min_length:expr) => {
#[doc = $doc]
#[derive(
Clone,
Hash,
PartialEq,
Eq,
serde::Serialize,
serde::Deserialize,
diesel::expression::AsExpression,
)]
#[diesel(sql_type = $diesel_type)]
pub struct $type($crate::id_type::LengthId<$max_length, $min_length>);
};
($type:ident, $doc:literal) => {
$crate::id_type!(
$type,
$doc,
diesel::sql_types::Text,
{ $crate::consts::MAX_ALLOWED_MERCHANT_REFERENCE_ID_LENGTH },
{ $crate::consts::MIN_REQUIRED_MERCHANT_REFERENCE_ID_LENGTH }
);
};
}
/// Implements common methods on the specified ID type.
#[macro_export]
macro_rules! impl_id_type_methods {
($type:ty, $field_name:literal) => {
impl $type {
/// Get the string representation of the ID type.
pub fn get_string_repr(&self) -> &str {
&self.0 .0 .0
}
}
};
}
/// Implements the `Debug` trait on the specified ID type.
#[macro_export]
macro_rules! impl_debug_id_type {
($type:ty) => {
impl core::fmt::Debug for $type {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_tuple(stringify!($type))
.field(&self.0 .0 .0)
.finish()
}
}
};
}
/// Implements the `TryFrom<Cow<'static, str>>` trait on the specified ID type.
#[macro_export]
macro_rules! impl_try_from_cow_str_id_type {
($type:ty, $field_name:literal) => {
impl TryFrom<std::borrow::Cow<'static, str>> for $type {
type Error = error_stack::Report<$crate::errors::ValidationError>;
fn try_from(value: std::borrow::Cow<'static, str>) -> Result<Self, Self::Error> {
use error_stack::ResultExt;
let merchant_ref_id = $crate::id_type::LengthId::from(value).change_context(
$crate::errors::ValidationError::IncorrectValueProvided {
field_name: $field_name,
},
)?;
Ok(Self(merchant_ref_id))
}
}
};
}
/// Implements the `Default` trait on the specified ID type.
#[macro_export]
macro_rules! impl_default_id_type {
($type:ty, $prefix:literal) => {
impl Default for $type {
fn default() -> Self {
Self($crate::generate_ref_id_with_default_length($prefix))
}
}
};
}
/// Implements the `SerializableSecret` trait on the specified ID type.
#[macro_export]
macro_rules! impl_serializable_secret_id_type {
($type:ty) => {
impl masking::SerializableSecret for $type {}
};
}
/// Implements the `ToSql` and `FromSql` traits on the specified ID type.
#[macro_export]
macro_rules! impl_to_sql_from_sql_id_type {
($type:ty, $diesel_type:ty, $max_length:expr, $min_length:expr) => {
impl<DB> diesel::serialize::ToSql<$diesel_type, DB> for $type
where
DB: diesel::backend::Backend,
$crate::id_type::LengthId<$max_length, $min_length>:
diesel::serialize::ToSql<$diesel_type, DB>,
{
fn to_sql<'b>(
&'b self,
out: &mut diesel::serialize::Output<'b, '_, DB>,
) -> diesel::serialize::Result {
self.0.to_sql(out)
}
}
impl<DB> diesel::deserialize::FromSql<$diesel_type, DB> for $type
where
DB: diesel::backend::Backend,
$crate::id_type::LengthId<$max_length, $min_length>:
diesel::deserialize::FromSql<$diesel_type, DB>,
{
fn from_sql(value: DB::RawValue<'_>) -> diesel::deserialize::Result<Self> {
$crate::id_type::LengthId::<$max_length, $min_length>::from_sql(value).map(Self)
}
}
};
($type:ty) => {
$crate::impl_to_sql_from_sql_id_type!(
$type,
diesel::sql_types::Text,
{ $crate::consts::MAX_ALLOWED_MERCHANT_REFERENCE_ID_LENGTH },
{ $crate::consts::MIN_REQUIRED_MERCHANT_REFERENCE_ID_LENGTH }
);
};
}
/// Implements the `Queryable` trait on the specified ID type.
#[macro_export]
macro_rules! impl_queryable_id_type {
($type:ty, $diesel_type:ty) => {
impl<DB> diesel::Queryable<$diesel_type, DB> for $type
where
DB: diesel::backend::Backend,
Self: diesel::deserialize::FromSql<$diesel_type, DB>,
{
type Row = Self;
fn build(row: Self::Row) -> diesel::deserialize::Result<Self> {
Ok(row)
}
}
};
($type:ty) => {
$crate::impl_queryable_id_type!($type, diesel::sql_types::Text);
};
}
}