gremlin_orm/lib.rs
1//! # `gremlin-orm`
2//!
3//! This crate provides a lightweight, type-safe ORM for PostgreSQL in Rust, focusing on minimal boilerplate and compile-time query safety.
4//!
5//! ## Example
6//!
7//! ```rust
8//! use gremlin_orm::Entity;
9//!
10//! #[derive(Entity, sqlx::FromRow)]
11//! #[orm(table = "public.artist")]
12//! struct Artist {
13//! #[orm(pk, generated)]
14//! id: i32,
15//! name: String,
16//! #[orm(generated)]
17//! slug: String,
18//! }
19//!
20//! // Example with soft delete support
21//! #[derive(Entity, sqlx::FromRow)]
22//! #[orm(table = "public.soft_delete", soft_delete = "deleted_at")]
23//! struct SoftDelete {
24//! #[orm(pk, generated)]
25//! id: i32,
26//! value: i32,
27//! #[orm(default)]
28//! deleted_at: Option<chrono::NaiveDateTime>,
29//! }
30//! ```
31//!
32//! ## Annotations
33//!
34//! The `#[orm(...)]` attribute supports both struct-level and field-level annotations to
35//! control code generation and database mapping.
36//!
37//! ### Struct-level Annotations
38//!
39//! - `#[orm(table = "schema.table")]`: Specifies the database table for the entity.
40//! - `#[orm(soft_delete = "column_name")]`: Enables soft delete support for the entity. The given column (typically an `Option<chrono::NaiveDateTime>`) will be set to the current timestamp instead of deleting the row. Entities with a non-NULL value in this column are considered deleted and will be excluded from fetch, stream, and update operations.
41//!
42//! ### Field-level Annotations
43//!
44//! - `#[orm(pk)]`: Marks the field as a primary key. Multiple fields can be marked as primary keys for composite keys.
45//! - `#[orm(generated)]`: Indicates the field is auto-generated by the database (e.g., auto-increment or computed columns). Such fields are excluded from inserts and updates.
46//! - `#[orm(deref)]`: Used for optional/reference types (e.g., `Option<T>`, `&str`, etc.), allowing the macro to handle dereferencing when generating queries.
47//! - `#[orm(as_ref)]`: Used for optional primitive types (e.g., `Option<i32>`, `Option<bool>`), calling `.as_ref()` instead of `.as_deref()` when generating update queries. This is useful for optional primitive values that don't need dereferencing.
48//! - `#[orm(default)]`: Allows the field to use a default value when inserting, by wrapping it in `Defaultable<T>`.
49//! - `#[orm(cast = "Type")]`: Casts the field to the specified SQL type in generated queries. This is useful when you want to explicitly cast a column in SQL (e.g., for custom types or to resolve type mismatches).
50//!
51//! ## Traits Overview
52//!
53//! ### [`InsertableEntity`]
54//! For types that can be inserted into the database. An "Insertable" struct is generated for
55//! each entity, containing only the fields that should be provided on insert.
56//!
57//! Fields annotated with the default annotation are wrapped in [`Defaultable`], indicating if the
58//! default value should be used, or the provided one.
59//!
60//! ### [`FetchableEntity`]
61//!
62//! For types that can be fetched by primary key(s) from the database. A "Pk" struct is generated
63//! for each entity, containing only the primary key fields.
64//!
65//! > If the entity uses soft delete, fetch operations will return `None` for rows where the soft delete column is set (i.e., not NULL).
66//!
67//! ### [`StreamableEntity`]
68//!
69//! For types that can be streamed (selected) from the database. This trait is implemented for
70//! the entity struct, allowing you to stream all rows from the table.
71//!
72//! > If the entity uses soft delete, only rows where the soft delete column is NULL will be streamed.
73//!
74//! ### [`UpdatableEntity`]
75//!
76//! For types that can be updated in the database. An "Updatable" struct is generated for each
77//! entity, containing the primary key(s) and updatable fields.
78//!
79//! > Updates will only affect rows that are not soft deleted (i.e., where the soft delete column is NULL).
80//!
81//! ### [`DeletableEntity`]
82//!
83//! For types that can be deleted from the database. This trait is implemented for the entity
84//! struct, allowing you to delete a row by its primary key(s).
85//!
86//! > If the entity uses soft delete, calling `delete` will set the soft delete column to the current timestamp instead of removing the row from the database.
87//!
88
89pub use futures::Stream;
90pub use gremlin_orm_macro::Entity;
91use sqlx::PgExecutor;
92
93/// Used for inserting values, use either the default or the provided value
94pub enum Defaultable<T> {
95 /// Use the default value
96 Default,
97 /// Use the provided value
98 Value(T),
99}
100
101/// Trait for types that can be inserted into the database.
102/// An "Insertable" struct is generated for each entity, containing only the fields that should be provided on insert.
103pub trait InsertableEntity {
104 /// The entity type returned after insertion (typically the main entity struct).
105 type SourceEntity;
106
107 /// Insert the entity into the database.
108 ///
109 /// # Arguments
110 ///
111 /// * `pool` - A reference to a PostgreSQL connection pool.
112 ///
113 /// # Returns
114 ///
115 /// A future resolving to either the inserted entity or a SQLx error.
116 fn insert<'a>(
117 &self,
118 executor: impl PgExecutor<'a>,
119 ) -> impl Future<Output = Result<Self::SourceEntity, sqlx::Error>>;
120}
121
122/// Trait for types that can be fetched by primary key(s) from the database.
123/// A "Pk" struct is generated for each entity, containing only the primary key fields.
124pub trait FetchableEntity {
125 /// The entity type returned after fetching (typically the main entity struct).
126 type SourceEntity;
127
128 /// Fetch the entity from the database by its primary key(s).
129 ///
130 /// # Arguments
131 ///
132 /// * `pool` - A reference to a PostgreSQL connection pool.
133 ///
134 /// # Returns
135 ///
136 /// A future resolving to either `Some(entity)` if found, `None` if not found, or a SQLx error.
137 fn fetch<'a>(
138 &self,
139 executor: impl PgExecutor<'a>,
140 ) -> impl Future<Output = Result<Option<Self::SourceEntity>, sqlx::Error>>;
141}
142
143/// Trait for types that can be streamed (selected) from the database.
144/// This trait is implemented for the entity struct, allowing you to stream all rows from the table.
145pub trait StreamableEntity: Sized {
146 /// Stream all entities from the database table.
147 ///
148 /// # Arguments
149 ///
150 /// * `pool` - A reference to a PostgreSQL connection pool.
151 ///
152 /// # Returns
153 ///
154 /// An async stream of results, each being either the entity or a SQLx error.
155 fn stream<'a>(
156 executor: impl PgExecutor<'a> + 'a,
157 ) -> impl Stream<Item = Result<Self, sqlx::Error>>;
158}
159
160/// Trait for types that can be updated in the database.
161/// An "Updatable" struct is generated for each entity, containing the primary key(s) and updatable fields.
162pub trait UpdatableEntity {
163 /// The entity type returned after updating (typically the main entity struct).
164 type SourceEntity;
165
166 /// Update the entity in the database.
167 ///
168 /// # Arguments
169 ///
170 /// * `pool` - A reference to a PostgreSQL connection pool.
171 ///
172 /// # Returns
173 ///
174 /// A future resolving to either the updated entity or a SQLx error.
175 fn update<'a>(
176 &self,
177 executor: impl PgExecutor<'a>,
178 ) -> impl Future<Output = Result<Self::SourceEntity, sqlx::Error>>;
179}
180
181/// Trait for types that can be deleted from the database.
182/// This trait is implemented for the entity struct, allowing you to delete a row by its primary key(s).
183pub trait DeletableEntity {
184 /// Delete the entity from the database by its primary key(s).
185 ///
186 /// # Arguments
187 ///
188 /// * `pool` - A reference to a PostgreSQL connection pool.
189 ///
190 /// # Returns
191 ///
192 /// A future resolving to `()` if successful, or a SQLx error.
193 fn delete<'a>(
194 &self,
195 executor: impl PgExecutor<'a>,
196 ) -> impl Future<Output = Result<(), sqlx::Error>>;
197}