cleanup.
This commit is contained in:
parent
d3a5a8d74c
commit
a16ad316a3
8 changed files with 136 additions and 18 deletions
90
Cargo.lock
generated
90
Cargo.lock
generated
|
@ -2,6 +2,16 @@
|
|||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "Inflector"
|
||||
version = "0.11.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ahash"
|
||||
version = "0.8.11"
|
||||
|
@ -14,12 +24,32 @@ dependencies = [
|
|||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "allocator-api2"
|
||||
version = "0.2.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
|
||||
|
||||
[[package]]
|
||||
name = "async-trait"
|
||||
version = "0.1.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atoi"
|
||||
version = "2.0.0"
|
||||
|
@ -282,7 +312,9 @@ dependencies = [
|
|||
name = "ezpg"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"ezpg_macros",
|
||||
"futures",
|
||||
"sqlx",
|
||||
"uuid",
|
||||
]
|
||||
|
@ -291,6 +323,7 @@ dependencies = [
|
|||
name = "ezpg_macros"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"Inflector",
|
||||
"darling",
|
||||
"itertools",
|
||||
"proc-macro2",
|
||||
|
@ -330,6 +363,21 @@ dependencies = [
|
|||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-executor",
|
||||
"futures-io",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
"futures-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-channel"
|
||||
version = "0.3.31"
|
||||
|
@ -374,6 +422,17 @@ version = "0.3.31"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
|
||||
|
||||
[[package]]
|
||||
name = "futures-macro"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-sink"
|
||||
version = "0.3.31"
|
||||
|
@ -392,8 +451,10 @@ version = "0.3.31"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
"futures-macro",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
"memchr",
|
||||
|
@ -957,6 +1018,35 @@ dependencies = [
|
|||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-automata",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
||||
|
||||
[[package]]
|
||||
name = "rsa"
|
||||
version = "0.9.7"
|
||||
|
|
|
@ -7,6 +7,7 @@ edition = "2021"
|
|||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
Inflector = "0.11.4"
|
||||
darling = "0.20.10"
|
||||
itertools = "0.13.0"
|
||||
proc-macro2 = "1.0.92"
|
||||
|
|
|
@ -23,8 +23,7 @@ impl Column {
|
|||
/// returns the column's name, either the `rename` attribute, or the
|
||||
/// column name itself.
|
||||
pub fn name(&self) -> String {
|
||||
self
|
||||
.rename
|
||||
self.rename
|
||||
.as_ref()
|
||||
.cloned()
|
||||
.unwrap_or(self.ident().to_string())
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
use crate::Column;
|
||||
use darling::{ast::Data, FromDeriveInput};
|
||||
use inflector::Inflector;
|
||||
use itertools::Itertools;
|
||||
use quote::quote;
|
||||
use syn::Ident;
|
||||
use crate::Column;
|
||||
|
||||
#[derive(FromDeriveInput)]
|
||||
#[darling(attributes(sqlx), supports(struct_named))]
|
||||
|
@ -17,7 +18,7 @@ impl RecordDerive {
|
|||
self.rename
|
||||
.as_ref()
|
||||
.cloned()
|
||||
.unwrap_or(self.ident.to_string().to_lowercase())
|
||||
.unwrap_or(self.ident.to_string().to_plural().to_snake_case())
|
||||
}
|
||||
|
||||
// returns all columns that are not marked `skip`.
|
||||
|
@ -52,16 +53,17 @@ impl RecordDerive {
|
|||
|
||||
// e.g., "select id, name, weight from items limit $1 offset $2"
|
||||
let index_query =
|
||||
format!("select id, {column_names_join} from {table_name} limit $1 offset $2");
|
||||
format!("select {column_names_join} from {table_name} limit $1 offset $2");
|
||||
|
||||
// e.g., "select id, name, weight from items where id = $1"
|
||||
let read_query = format!("select id, {column_names_join} from {table_name} where id = $1");
|
||||
let read_query = format!("select {column_names_join} from {table_name} where id = $1");
|
||||
|
||||
let update_query = {
|
||||
let value_pairs = self
|
||||
.columns()
|
||||
.enumerate()
|
||||
.map(|(i, col)| format!("{} = ${}", col.name(), i + 2)) // enum starts at 0, id is 1, other columns start at 2.
|
||||
.filter(|(_, c)| c.name() != "id")
|
||||
.map(|(i, col)| format!("{} = ${}", col.name(), i + 1)) // enum starts at 0, id is 1, other columns start at 2.
|
||||
.join(",");
|
||||
|
||||
format!("update {table_name} set {value_pairs} where id = $1")
|
||||
|
@ -70,6 +72,12 @@ impl RecordDerive {
|
|||
let delete_query = format!("delete from {table_name} where id = $1");
|
||||
|
||||
quote! {
|
||||
impl ::ezpg::Record for #ident {
|
||||
fn id(&self) -> uuid::Uuid {
|
||||
self.id
|
||||
}
|
||||
}
|
||||
|
||||
impl ::ezpg::WithQueries for #ident {
|
||||
const COLUMNS: &'static [&'static str] = &[#(#column_names,)*];
|
||||
const CREATE_QUERY: &'static str = #create_query;
|
||||
|
|
|
@ -3,8 +3,8 @@ pub(crate) use column::Column;
|
|||
mod derive;
|
||||
use darling::FromDeriveInput;
|
||||
use proc_macro::TokenStream;
|
||||
use syn::DeriveInput;
|
||||
use quote::quote;
|
||||
use syn::DeriveInput;
|
||||
|
||||
#[proc_macro_derive(Record, attributes(sqlx))]
|
||||
pub fn derive_record(input: TokenStream) -> TokenStream {
|
||||
|
@ -17,6 +17,7 @@ pub fn derive_record(input: TokenStream) -> TokenStream {
|
|||
|
||||
quote! {
|
||||
#queries_impl
|
||||
|
||||
#column_impl
|
||||
}
|
||||
.into()
|
||||
|
|
|
@ -4,6 +4,8 @@ version = "0.0.0"
|
|||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
async-trait = "0.1.83"
|
||||
ezpg_macros = { version = "0.0.0", path = "../ezpg-macros" }
|
||||
futures = "0.3.31"
|
||||
sqlx = { version = "0.8.2", features = ["macros", "postgres", "uuid"] }
|
||||
uuid = { version = "1.11.0", features = ["v7"] }
|
||||
|
|
|
@ -1,15 +1,17 @@
|
|||
//! traits used for c/r/u/d behaviours.
|
||||
|
||||
use crate::Record;
|
||||
use async_trait::async_trait;
|
||||
use sqlx::{PgPool, Postgres};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::Record;
|
||||
|
||||
/// [c]reate
|
||||
#[async_trait]
|
||||
pub trait Create<'a, R> {
|
||||
async fn create(&self, pool: &PgPool) -> sqlx::Result<R>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<'a, R> Create<'a, R> for R
|
||||
where
|
||||
R: Record,
|
||||
|
@ -35,11 +37,13 @@ where
|
|||
}
|
||||
|
||||
/// [r]ead
|
||||
#[async_trait]
|
||||
pub trait Read<'a, R>: Record {
|
||||
async fn index(offset: i64, limit: i64, pool: &PgPool) -> sqlx::Result<Vec<R>>;
|
||||
async fn read(id: Uuid, pool: &PgPool) -> sqlx::Result<R>;
|
||||
async fn find_by_id(id: Uuid, pool: &PgPool) -> sqlx::Result<R>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<'a, R> Read<'a, R> for R
|
||||
where
|
||||
R: Record,
|
||||
|
@ -53,7 +57,7 @@ where
|
|||
.await
|
||||
}
|
||||
|
||||
async fn read(id: Uuid, pool: &PgPool) -> sqlx::Result<R> {
|
||||
async fn find_by_id(id: Uuid, pool: &PgPool) -> sqlx::Result<R> {
|
||||
sqlx::query_as::<Postgres, R>(R::READ_QUERY)
|
||||
.bind(id)
|
||||
.fetch_one(pool)
|
||||
|
@ -62,10 +66,12 @@ where
|
|||
}
|
||||
|
||||
/// [c]reate
|
||||
#[async_trait]
|
||||
pub trait Update<'a, R> {
|
||||
async fn update(&self, pool: &PgPool) -> sqlx::Result<R>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<'a, R> Update<'a, R> for R
|
||||
where
|
||||
R: Record,
|
||||
|
@ -74,19 +80,24 @@ where
|
|||
async fn update(&self, pool: &PgPool) -> sqlx::Result<R> {
|
||||
R::COLUMNS
|
||||
.iter()
|
||||
.fold(sqlx::query_as(R::UPDATE_QUERY), |q, c| {
|
||||
self.bind_column(q, c)
|
||||
})
|
||||
.fold(sqlx::query(R::UPDATE_QUERY), |q, c| self.bind_column(q, c))
|
||||
.execute(pool)
|
||||
.await?;
|
||||
|
||||
sqlx::query_as::<Postgres, R>(R::READ_QUERY)
|
||||
.bind(self.id())
|
||||
.fetch_one(pool)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
/// [d]elete
|
||||
#[async_trait]
|
||||
pub trait Delete<'a, R> {
|
||||
async fn delete(&self, pool: &PgPool) -> sqlx::Result<()>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<'a, R> Delete<'a, R> for R
|
||||
where
|
||||
R: Record,
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
mod crud_traits;
|
||||
pub use crud_traits::{Create, Delete, Read, Update};
|
||||
|
||||
use sqlx::{postgres::{PgArguments, PgRow}, query::{Query, QueryAs}, Encode, FromRow, Postgres, Type};
|
||||
use sqlx::{
|
||||
postgres::{PgArguments, PgRow},
|
||||
query::{Query, QueryAs},
|
||||
Encode, FromRow, Postgres, Type,
|
||||
};
|
||||
use uuid::Uuid;
|
||||
|
||||
// re-export the record macro.
|
||||
pub use ezpg_macros::Record;
|
||||
|
||||
// the following were stolen from [miniorm by meuter](https://github.com/meuter/miniorm-rs):
|
||||
pub trait QueryBind<'q> {
|
||||
fn bind<T>(self, value: T) -> Self
|
||||
|
@ -49,6 +53,8 @@ pub trait BindColumn {
|
|||
}
|
||||
|
||||
/// a record stored within the postgres db.
|
||||
pub trait Record: WithQueries + BindColumn + for<'r> FromRow<'r, PgRow> + Send + Unpin {
|
||||
pub trait Record:
|
||||
WithQueries + BindColumn + for<'r> FromRow<'r, PgRow> + Send + Sync + Unpin
|
||||
{
|
||||
fn id(&self) -> Uuid;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue