@@ -57,37 +57,67 @@ use crate::worker::Worker;
5757/// - Emit events with [`emit_event`](Self::emit_event)
5858/// - Cancel tasks with [`cancel_task`](Self::cancel_task)
5959///
60+ /// # Type Parameter
61+ ///
62+ /// * `State` - Application state type passed to task handlers. Use `()` if you
63+ /// don't need any state. The state must implement `Clone + Send + Sync + 'static`.
64+ ///
6065/// # Example
6166///
6267/// ```ignore
68+ /// // Without state (default)
6369/// let client = Durable::builder()
6470/// .database_url("postgres://localhost/myapp")
6571/// .queue_name("tasks")
6672/// .build()
6773/// .await?;
6874///
75+ /// // With application state
76+ /// #[derive(Clone)]
77+ /// struct AppState {
78+ /// http_client: reqwest::Client,
79+ /// }
80+ ///
81+ /// let app_state = AppState { http_client: reqwest::Client::new() };
82+ /// let client = Durable::builder()
83+ /// .database_url("postgres://localhost/myapp")
84+ /// .queue_name("tasks")
85+ /// .build_with_state(app_state)
86+ /// .await?;
87+ ///
6988/// client.register::<MyTask>().await;
7089/// client.spawn::<MyTask>(params).await?;
7190/// ```
72- pub struct Durable {
91+ pub struct Durable < State = ( ) >
92+ where
93+ State : Clone + Send + Sync + ' static ,
94+ {
7395 pool : PgPool ,
7496 owns_pool : bool ,
7597 queue_name : String ,
7698 default_max_attempts : u32 ,
77- registry : Arc < RwLock < TaskRegistry > > ,
99+ registry : Arc < RwLock < TaskRegistry < State > > > ,
100+ state : State ,
78101}
79102
80103/// Builder for configuring a [`Durable`] client.
81104///
82105/// # Example
83106///
84107/// ```ignore
108+ /// // Without state
85109/// let client = Durable::builder()
86110/// .database_url("postgres://localhost/myapp")
87111/// .queue_name("orders")
88112/// .default_max_attempts(3)
89113/// .build()
90114/// .await?;
115+ ///
116+ /// // With state
117+ /// let client = Durable::builder()
118+ /// .database_url("postgres://localhost/myapp")
119+ /// .build_with_state(my_app_state)
120+ /// .await?;
91121/// ```
92122pub struct DurableBuilder {
93123 database_url : Option < String > ,
@@ -130,8 +160,43 @@ impl DurableBuilder {
130160 self
131161 }
132162
133- /// Build the Durable client
134- pub async fn build ( self ) -> anyhow:: Result < Durable > {
163+ /// Build the Durable client without application state.
164+ ///
165+ /// Use this when your tasks don't need access to shared resources
166+ /// like HTTP clients or database pools.
167+ pub async fn build ( self ) -> anyhow:: Result < Durable < ( ) > > {
168+ self . build_with_state ( ( ) ) . await
169+ }
170+
171+ /// Build the Durable client with application state.
172+ ///
173+ /// The state will be cloned and passed to each task execution.
174+ /// Use this to provide shared resources like HTTP clients, database pools,
175+ /// or other application state to your tasks.
176+ ///
177+ /// # Example
178+ ///
179+ /// ```ignore
180+ /// #[derive(Clone)]
181+ /// struct AppState {
182+ /// http_client: reqwest::Client,
183+ /// db_pool: PgPool,
184+ /// }
185+ ///
186+ /// let state = AppState {
187+ /// http_client: reqwest::Client::new(),
188+ /// db_pool: pool.clone(),
189+ /// };
190+ ///
191+ /// let client = Durable::builder()
192+ /// .database_url("postgres://localhost/myapp")
193+ /// .build_with_state(state)
194+ /// .await?;
195+ /// ```
196+ pub async fn build_with_state < State > ( self , state : State ) -> anyhow:: Result < Durable < State > >
197+ where
198+ State : Clone + Send + Sync + ' static ,
199+ {
135200 let ( pool, owns_pool) = if let Some ( pool) = self . pool {
136201 ( pool, false )
137202 } else {
@@ -148,6 +213,7 @@ impl DurableBuilder {
148213 queue_name : self . queue_name ,
149214 default_max_attempts : self . default_max_attempts ,
150215 registry : Arc :: new ( RwLock :: new ( HashMap :: new ( ) ) ) ,
216+ state,
151217 } )
152218 }
153219}
@@ -158,8 +224,8 @@ impl Default for DurableBuilder {
158224 }
159225}
160226
161- impl Durable {
162- /// Create a new client with default settings
227+ impl Durable < ( ) > {
228+ /// Create a new client with default settings (no application state).
163229 pub async fn new ( database_url : & str ) -> anyhow:: Result < Self > {
164230 DurableBuilder :: new ( )
165231 . database_url ( database_url)
@@ -171,7 +237,12 @@ impl Durable {
171237 pub fn builder ( ) -> DurableBuilder {
172238 DurableBuilder :: new ( )
173239 }
240+ }
174241
242+ impl < State > Durable < State >
243+ where
244+ State : Clone + Send + Sync + ' static ,
245+ {
175246 /// Get a reference to the underlying connection pool
176247 pub fn pool ( & self ) -> & PgPool {
177248 & self . pool
@@ -182,21 +253,29 @@ impl Durable {
182253 & self . queue_name
183254 }
184255
256+ /// Get a reference to the application state
257+ pub fn state ( & self ) -> & State {
258+ & self . state
259+ }
260+
185261 /// Register a task type. Required before spawning or processing.
186- pub async fn register < T : Task > ( & self ) -> & Self {
262+ pub async fn register < T : Task < State > > ( & self ) -> & Self {
187263 let mut registry = self . registry . write ( ) . await ;
188- registry. insert ( T :: NAME . to_string ( ) , Arc :: new ( TaskWrapper :: < T > :: new ( ) ) ) ;
264+ registry. insert (
265+ T :: NAME . to_string ( ) ,
266+ Arc :: new ( TaskWrapper :: < T , State > :: new ( ) ) ,
267+ ) ;
189268 self
190269 }
191270
192271 /// Spawn a task (type-safe version)
193- pub async fn spawn < T : Task > ( & self , params : T :: Params ) -> anyhow:: Result < SpawnResult > {
272+ pub async fn spawn < T : Task < State > > ( & self , params : T :: Params ) -> anyhow:: Result < SpawnResult > {
194273 self . spawn_with_options :: < T > ( params, SpawnOptions :: default ( ) )
195274 . await
196275 }
197276
198277 /// Spawn a task with options (type-safe version)
199- pub async fn spawn_with_options < T : Task > (
278+ pub async fn spawn_with_options < T : Task < State > > (
200279 & self ,
201280 params : T :: Params ,
202281 options : SpawnOptions ,
@@ -240,7 +319,7 @@ impl Durable {
240319 params : T :: Params ,
241320 ) -> anyhow:: Result < SpawnResult >
242321 where
243- T : Task ,
322+ T : Task < State > ,
244323 E : Executor < ' e , Database = Postgres > ,
245324 {
246325 self . spawn_with_options_with :: < T , E > ( executor, params, SpawnOptions :: default ( ) )
@@ -255,7 +334,7 @@ impl Durable {
255334 options : SpawnOptions ,
256335 ) -> anyhow:: Result < SpawnResult >
257336 where
258- T : Task ,
337+ T : Task < State > ,
259338 E : Executor < ' e , Database = Postgres > ,
260339 {
261340 self . spawn_by_name_with ( executor, T :: NAME , serde_json:: to_value ( & params) ?, options)
@@ -377,6 +456,7 @@ impl Durable {
377456 self . queue_name . clone ( ) ,
378457 self . registry . clone ( ) ,
379458 options,
459+ self . state . clone ( ) ,
380460 )
381461 . await
382462 }
0 commit comments