sqlx-record/src/lib.rs

191 lines
5.9 KiB
Rust

use chrono::Utc;
use rand::random;
use uuid::Uuid;
mod conn_provider;
mod filter;
mod helpers;
pub mod models;
mod pagination;
pub mod repositories;
mod transaction;
mod value;
pub use pagination::{Page, PageRequest};
// transaction! macro is exported via #[macro_export] in transaction.rs
// Re-export the sqlx_record_derive module on feature flag
#[cfg(feature = "derive")]
pub use sqlx_record_derive::{Entity, Update};
/// Creates a time-ordered UUID with timestamp prefix for better database indexing.
/// First 8 bytes: millisecond timestamp (big-endian)
/// Last 8 bytes: random data
#[inline]
pub fn new_uuid() -> Uuid {
let timestamp = Utc::now().timestamp_millis() as u64;
let random = random::<u64>();
let mut bytes = [0u8; 16];
bytes[..8].copy_from_slice(&timestamp.to_be_bytes());
bytes[8..].copy_from_slice(&random.to_be_bytes());
Uuid::from_bytes(bytes)
}
/// Creates a lookup table entity struct with an associated code enum.
///
/// Generates:
/// - A struct with `code`, `name`, `description`, and `is_active` fields
/// - An enum `{Name}Code` with variants for each code
/// - Constants on the struct for each code string
/// - `Display` impl for the enum
/// - `TryFrom<&str>` impl for the enum
///
/// # Example
/// ```ignore
/// lookup_table!(UserStatus, "active", "inactive", "suspended");
/// // Creates:
/// // - struct UserStatus { code, name, description, is_active }
/// // - enum UserStatusCode { Active, Inactive, Suspended }
/// // - UserStatus::ACTIVE, UserStatus::INACTIVE, UserStatus::SUSPENDED constants
/// ```
#[macro_export]
macro_rules! lookup_table {
($name:ident, $($code:literal),+ $(,)?) => {
#[allow(dead_code)]
#[derive(Debug, Clone, sqlx_record::Entity, sqlx::FromRow)]
pub struct $name {
#[primary_key]
pub code: String,
pub name: String,
pub description: String,
pub is_active: bool,
}
paste::paste! {
#[allow(dead_code)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum [<$name Code>] {
$(
#[allow(non_camel_case_types)]
[<$code:camel>],
)+
}
impl std::fmt::Display for [<$name Code>] {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
$(
[<$name Code>]::[<$code:camel>] => write!(f, $code),
)+
}
}
}
#[allow(dead_code)]
impl $name {
$(
pub const [<$code:upper>]: &'static str = $code;
)+
}
impl TryFrom<&str> for [<$name Code>] {
type Error = String;
fn try_from(s: &str) -> Result<Self, Self::Error> {
match s {
$(
$code => Ok([<$name Code>]::[<$code:camel>]),
)+
_ => Err(format!("Unknown {} code: {}", stringify!($name).to_lowercase(), s)),
}
}
}
}
};
}
/// Creates a code enum without an associated entity struct.
///
/// Use this when you need type-safe code constants but the lookup data
/// is stored elsewhere or doesn't need CRUD operations.
///
/// Generates:
/// - An enum `{Name}Code` with variants for each code
/// - Constants on a unit struct for each code string
/// - `Display` impl for the enum
/// - `TryFrom<&str>` impl for the enum
///
/// # Example
/// ```ignore
/// lookup_options!(RefundPolicy, "pro-rata", "full-24-hours", "no-refunds");
/// // Creates:
/// // - enum RefundPolicyCode { ProRata, Full24Hours, NoRefunds }
/// // - RefundPolicy::PRO_RATA, RefundPolicy::FULL_24_HOURS, etc.
/// ```
#[macro_export]
macro_rules! lookup_options {
($name:ident, $($code:literal),+ $(,)?) => {
paste::paste! {
#[allow(dead_code)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum [<$name Code>] {
$(
#[allow(non_camel_case_types)]
[<$code:camel>],
)+
}
impl std::fmt::Display for [<$name Code>] {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
$(
[<$name Code>]::[<$code:camel>] => write!(f, $code),
)+
}
}
}
/// Constants for code string values
#[allow(dead_code)]
pub struct $name;
#[allow(dead_code)]
impl $name {
$(
pub const [<$code:upper>]: &'static str = $code;
)+
}
impl TryFrom<&str> for [<$name Code>] {
type Error = String;
fn try_from(s: &str) -> Result<Self, Self::Error> {
match s {
$(
$code => Ok([<$name Code>]::[<$code:camel>]),
)+
_ => Err(format!("Unknown {} code: {}", stringify!($name).to_lowercase(), s)),
}
}
}
}
};
}
pub mod prelude {
pub use crate::filter::*;
pub use crate::pagination::{Page, PageRequest};
pub use crate::value::*;
pub use crate::values;
pub use crate::{filter_and as and, filter_or as or};
pub use crate::{filter_and, filter_or, filters, update_entity_func};
pub use crate::{lookup_options, lookup_table, new_uuid, transaction};
#[cfg(any(feature = "mysql", feature = "postgres", feature = "sqlite"))]
pub use crate::conn_provider::ConnProvider;
#[cfg(feature = "derive")]
pub use sqlx_record_derive::{Entity, Update};
}