diff --git a/.vscode/settings.json b/.vscode/settings.json index 9ee6745..07d5094 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,6 @@ { "rust-analyzer.linkedProjects": [ "./pg-worm/Cargo.toml", - "./pg-worm/Cargo.toml" + "./pg-worm-derive/Cargo.toml" ] } \ No newline at end of file diff --git a/pg-worm-derive/src/parse.rs b/pg-worm-derive/src/parse.rs index 2b6a643..0c48a31 100644 --- a/pg-worm-derive/src/parse.rs +++ b/pg-worm-derive/src/parse.rs @@ -86,6 +86,7 @@ impl ModelInput { let columns = self.impl_columns(); let insert = self.impl_insert(); let model = self.impl_model(); + let column_info = self.impl_colun_info(); quote!( impl #ident { @@ -96,6 +97,17 @@ impl ModelInput { #try_from_row #model + #column_info + ) + } + + fn impl_colun_info(&self) -> TokenStream { + let impls = self.all_fields().map(|f| f.impl_column_info(&self)); + + quote!( + #( + #impls + )* ) } @@ -516,4 +528,18 @@ impl ModelField { #(#props)*; ) } + + fn impl_column_info(&self, table: &ModelInput) -> TokenStream { + let model_ident = table.ident(); + let field_ident = self.ident(); + let table_name = table.table_name(); + let col_name = self.column_name(); + + quote!( + impl pg_worm::query::ColumnInfo for #model_ident::#field_ident { + const TABLE_NAME: &'static str = #table_name; + const COLUMN_NAME: &'static str = #col_name; + } + ) + } } diff --git a/pg-worm/src/lib.rs b/pg-worm/src/lib.rs index 6e457df..8573333 100644 --- a/pg-worm/src/lib.rs +++ b/pg-worm/src/lib.rs @@ -28,7 +28,7 @@ and you are ready to go! Here's a quick example: -```rust +```ignore // Import the prelude to get started quickly use pg_worm::prelude::*; @@ -236,7 +236,7 @@ _*`T` must be another supported type. Nesting and mixing `Option`/`Vec` is curre ### JSON, timestamps and more are supported, too. To use them activate the respective feature, like so: -```ignore +```toml # Cargo.toml [dependencies] pg-worm = { version = "latest-version", features = ["foo"] } @@ -270,7 +270,7 @@ This is done by using one of the two attributes `pg-worm` exposes. The `#[table]` attribute can be used to pass configurations to a `Model` which affect the respective table itself. -```rust +```ignore use pg_worm::prelude::*; #[derive(Model)] @@ -288,7 +288,7 @@ Option | Meaning | Usage | Default The `#[column]` attribute can be used to pass configurations to a `Model`'s field which affect the respective column. -```rust +```ignore use pg_worm::prelude::*; #[derive(Model)] @@ -301,17 +301,16 @@ struct Book { Option | Meaning | Usage | Default -------|---------|-------|-------- `column_name` | Set this column's name. | `#[column(column_name = "new_column_name")]` | The fields's name converted to snake case using [this crate](https://crates.io/crates/convert_case). -`primary_key` | Make this column the `PRIMARY KEY`. Only use this once per `Model`. If you want this column to be auto generated use `auto` as well. | `#[column(primary_key)]` | `false` +`primary_key` | Make this column the primary key. Only use this once per `Model`. If you want this column to be auto generated use `auto` as well. | `#[column(primary_key)]` | `false` `auto` | Make this column auto generated. Works only for `i16`, `i32` and `i64`, as well as `Uuid` *if* the `"uuid"` feature has been enabled *and* you use PostgreSQL version 13 or later. | `#[column(auto)]` | `false` -`unique` | Make this column `UNIQUE`. | `#[column(unique)]` | `false` + ## MSRV The minimum supported rust version is `1.70` as this crate uses the recently introduced `OnceLock` from the standard library. ## License This project is dual-licensed under the MIT and Apache 2.0 licenses. - -*/ + */ #![deny(missing_docs)] @@ -336,8 +335,8 @@ pub use futures_util; #[doc(hidden)] pub use tokio_postgres as pg; -pub use pool::{fetch_client, set_pool}; pub use pg_worm_derive::Model; +pub use pool::{fetch_client, set_pool}; /// This module contains all necessary imports to get you started /// easily. @@ -432,7 +431,6 @@ pub trait Model: FromRow { fn query(_: impl Into, _: Vec<&(dyn ToSql + Sync)>) -> Query<'_, Vec>; } - /// Create a table for your model. /// /// Use the [`try_create_table!`] macro for a more convenient api. diff --git a/pg-worm/src/pool.rs b/pg-worm/src/pool.rs index b64c05a..4e86665 100644 --- a/pg-worm/src/pool.rs +++ b/pg-worm/src/pool.rs @@ -2,7 +2,7 @@ use std::{ ops::{Deref, DerefMut}, str::FromStr, - sync::{OnceLock, Arc, Mutex} + sync::{Arc, Mutex, OnceLock}, }; use deadpool::managed::{self, Object}; @@ -20,9 +20,8 @@ static POOL: OnceLock = OnceLock::new(); /// This is a single client which is used for prepared statements. static PREPARED_CLIENT: OnceLock = OnceLock::new(); /// This hashmap keeps track of all prepared statements. -static PREPARED_STATEMENTS: Lazy>>> = Lazy::new(|| - Arc::new(Mutex::new(HashMap::new())) -); +static PREPARED_STATEMENTS: Lazy>>> = + Lazy::new(|| Arc::new(Mutex::new(HashMap::new()))); /// The pool which houses all connections to the PostgreSQL sever. type Pool = managed::Pool; @@ -69,18 +68,17 @@ pub async fn fetch_client() -> Result { .map_err(|_| Error::NoConnectionInPool) } - #[doc(hidden)] #[inline] pub async fn fetch_prepared_client() -> Result<&'static Client, Error> { - PREPARED_CLIENT.get() - .ok_or(Error::NotConnected) + PREPARED_CLIENT.get().ok_or(Error::NotConnected) } #[doc(hidden)] #[inline] pub async fn ensure_prepared(statement: &str) -> Result<(), Error> { - let is_prepared = PREPARED_STATEMENTS.lock() + let is_prepared = PREPARED_STATEMENTS + .lock() .map_err(|_| Error::NotConnected)? .contains_key(statement); @@ -88,16 +86,15 @@ pub async fn ensure_prepared(statement: &str) -> Result<(), Error> { return Ok(()); } - let prepared_stmt = fetch_prepared_client().await? - .prepare(statement) - .await?; + let prepared_stmt = fetch_prepared_client().await?.prepare(statement).await?; let owned_stmt = statement.to_string(); - PREPARED_STATEMENTS.lock() + PREPARED_STATEMENTS + .lock() .map_err(|_| Error::NotConnected)? .insert(owned_stmt, prepared_stmt); - Ok(()) + Ok(()) } /// Hidden function so set the pool from the `config` module. @@ -123,7 +120,7 @@ impl ConnectionBuilder { PgConfig::from_str(&self.conn_string).map_err(|_| Error::InvalidPoolConfig)?; let manager = Manager::new(pg_config); - + let pool = Pool::builder(manager) .build() .map_err(|_| Error::InvalidPoolConfig)?; diff --git a/pg-worm/src/query/mod.rs b/pg-worm/src/query/mod.rs index d358cbd..fe5d524 100644 --- a/pg-worm/src/query/mod.rs +++ b/pg-worm/src/query/mod.rs @@ -7,7 +7,7 @@ mod table; mod transaction; mod update; -pub use table::{Column, TypedColumn}; +pub use table::{Column, ColumnInfo, TypedColumn}; use std::{ future::{Future, IntoFuture}, diff --git a/pg-worm/src/query/table.rs b/pg-worm/src/query/table.rs index 47d9a81..b9b7024 100644 --- a/pg-worm/src/query/table.rs +++ b/pg-worm/src/query/table.rs @@ -53,6 +53,15 @@ pub struct Column { generated: bool, } +/// +#[doc(hidden)] +pub trait ColumnInfo { + /// + const TABLE_NAME: &'static str; + /// + const COLUMN_NAME: &'static str; +} + macro_rules! impl_prop_typed_col { ($($prop:ident),+) => { $(