@@ -57,7 +57,7 @@ impl Client {
5757
5858 // Fail fast on bad configuration.
5959 let _ = pool. get ( ) . await ?;
60- Ok ( Self :: from_pool ( pool, queue_name) )
60+ Self :: from_pool ( pool, queue_name)
6161 }
6262
6363 pub async fn from_env ( ) -> Result < Self > {
@@ -68,13 +68,15 @@ impl Client {
6868 Self :: connect_queue ( resolve_database_url ( None ) , queue_name) . await
6969 }
7070
71- pub fn from_pool ( pool : Pool , queue_name : impl Into < String > ) -> Self {
72- Self {
71+ pub fn from_pool ( pool : Pool , queue_name : impl Into < String > ) -> Result < Self > {
72+ let queue_name = queue_name. into ( ) ;
73+ validate_queue_name ( & queue_name) ?;
74+ Ok ( Self {
7375 pool,
74- queue_name : queue_name . into ( ) ,
76+ queue_name,
7577 default_max_attempts : 5 ,
7678 registry : Arc :: new ( RwLock :: new ( HashMap :: new ( ) ) ) ,
77- }
79+ } )
7880 }
7981
8082 pub fn queue_name ( & self ) -> & str {
@@ -86,7 +88,7 @@ impl Client {
8688 self
8789 }
8890
89- pub fn register < P , R , F , Fut > ( & self , task : Task < P , R > , handler : F ) -> Result < ( ) >
91+ pub fn register < P , R , F , Fut > ( & self , task : & Task < P , R > , handler : F ) -> Result < ( ) >
9092 where
9193 P : DeserializeOwned + Send + ' static ,
9294 R : Serialize + Send + ' static ,
@@ -139,10 +141,14 @@ impl Client {
139141 handler : erased,
140142 } ;
141143
142- self . registry
144+ let mut registry = self
145+ . registry
143146 . write ( )
144- . map_err ( |_| Error :: Config ( "task registry lock poisoned" . to_string ( ) ) ) ?
145- . insert ( options. name , registered) ;
147+ . map_err ( |_| Error :: Config ( "task registry lock poisoned" . to_string ( ) ) ) ?;
148+ if registry. contains_key ( & options. name ) {
149+ return Err ( Error :: TaskAlreadyRegistered ( options. name ) ) ;
150+ }
151+ registry. insert ( options. name , registered) ;
146152 Ok ( ( ) )
147153 }
148154
@@ -155,7 +161,8 @@ impl Client {
155161 where
156162 P : Serialize ,
157163 {
158- self . spawn_named ( task. name ( ) , params, options) . await
164+ self . spawn_with_task_options ( task. options ( ) , params, options)
165+ . await
159166 }
160167
161168 pub async fn spawn_named < P > (
@@ -167,12 +174,39 @@ impl Client {
167174 where
168175 P : Serialize ,
169176 {
170- let task_name = task_name. as_ref ( ) ;
177+ self . spawn_resolved ( task_name. as_ref ( ) , None , params, options)
178+ . await
179+ }
180+
181+ async fn spawn_with_task_options < P > (
182+ & self ,
183+ task_options : & TaskOptions ,
184+ params : P ,
185+ options : SpawnOptions ,
186+ ) -> Result < SpawnResult >
187+ where
188+ P : Serialize ,
189+ {
190+ self . spawn_resolved ( & task_options. name , Some ( task_options) , params, options)
191+ . await
192+ }
193+
194+ async fn spawn_resolved < P > (
195+ & self ,
196+ task_name : & str ,
197+ task_options : Option < & TaskOptions > ,
198+ params : P ,
199+ options : SpawnOptions ,
200+ ) -> Result < SpawnResult >
201+ where
202+ P : Serialize ,
203+ {
171204 if task_name. trim ( ) . is_empty ( ) {
172205 return Err ( Error :: Config ( "task name must be provided" . to_string ( ) ) ) ;
173206 }
174207
175- let ( queue_name, max_attempts, cancellation) = self . resolve_spawn ( task_name, & options) ?;
208+ let ( queue_name, max_attempts, cancellation) =
209+ self . resolve_spawn ( task_name, task_options, & options) ?;
176210 let params = serde_json:: to_value ( params) ?;
177211 let options_json = options. to_sql_json ( max_attempts, cancellation) ;
178212
@@ -194,8 +228,13 @@ impl Client {
194228 } )
195229 }
196230
197- pub async fn create_queue ( & self , queue_name : Option < & str > ) -> Result < ( ) > {
198- let queue_name = queue_name. unwrap_or ( & self . queue_name ) ;
231+ pub async fn create_queue ( & self ) -> Result < ( ) > {
232+ let queue_name = self . queue_name . clone ( ) ;
233+ self . create_queue_in ( queue_name) . await
234+ }
235+
236+ pub async fn create_queue_in ( & self , queue_name : impl AsRef < str > ) -> Result < ( ) > {
237+ let queue_name = queue_name. as_ref ( ) ;
199238 validate_queue_name ( queue_name) ?;
200239 let client = self . pool . get ( ) . await ?;
201240 client
@@ -205,8 +244,13 @@ impl Client {
205244 Ok ( ( ) )
206245 }
207246
208- pub async fn drop_queue ( & self , queue_name : Option < & str > ) -> Result < ( ) > {
209- let queue_name = queue_name. unwrap_or ( & self . queue_name ) ;
247+ pub async fn drop_queue ( & self ) -> Result < ( ) > {
248+ let queue_name = self . queue_name . clone ( ) ;
249+ self . drop_queue_in ( queue_name) . await
250+ }
251+
252+ pub async fn drop_queue_in ( & self , queue_name : impl AsRef < str > ) -> Result < ( ) > {
253+ let queue_name = queue_name. as_ref ( ) ;
210254 validate_queue_name ( queue_name) ?;
211255 let client = self . pool . get ( ) . await ?;
212256 client
@@ -304,7 +348,7 @@ impl Client {
304348
305349 pub async fn work_batch ( & self , options : WorkBatchOptions ) -> Result < usize > {
306350 let worker_id = options. worker_id_value ( ) ;
307- let claim_timeout_seconds = options. claim_timeout_seconds ( ) ;
351+ let claim_timeout_seconds = options. claim_timeout_seconds ( ) ? ;
308352 let batch_size = options. batch_size . max ( 1 ) . min ( i32:: MAX as usize ) as i32 ;
309353 let tasks = self
310354 . claim_tasks ( & worker_id, claim_timeout_seconds, batch_size)
@@ -327,7 +371,7 @@ impl Client {
327371 Ok ( count)
328372 }
329373
330- pub fn start_worker ( & self , options : WorkerOptions ) -> Worker {
374+ pub fn start_worker ( & self , options : WorkerOptions ) -> Result < Worker > {
331375 Worker :: start (
332376 self . pool . clone ( ) ,
333377 self . queue_name . clone ( ) ,
@@ -364,6 +408,7 @@ impl Client {
364408 fn resolve_spawn (
365409 & self ,
366410 task_name : & str ,
411+ task_options : Option < & TaskOptions > ,
367412 options : & SpawnOptions ,
368413 ) -> Result < ( String , i32 , Option < CancellationPolicy > ) > {
369414 let registration = self . get_registration ( task_name) ?;
@@ -377,19 +422,30 @@ impl Client {
377422 ) ) ) ;
378423 }
379424 }
425+ if let Some ( task_queue) = task_options. and_then ( |task| task. queue . as_deref ( ) ) {
426+ if task_queue != registration. queue_name {
427+ return Err ( Error :: Config ( format ! (
428+ "typed task {task_name:?} targets queue {task_queue:?}, but registered queue is {:?}" ,
429+ registration. queue_name
430+ ) ) ) ;
431+ }
432+ }
380433 registration. queue_name . clone ( )
434+ } else if let Some ( queue) = options. queue . clone ( ) {
435+ queue
436+ } else if let Some ( queue) = task_options. and_then ( |task| task. queue . clone ( ) ) {
437+ queue
381438 } else {
382- options. queue . clone ( ) . ok_or_else ( || {
383- Error :: Config ( format ! (
384- "task {task_name:?} is not registered; provide SpawnOptions::queue for unregistered tasks"
385- ) )
386- } ) ?
439+ return Err ( Error :: Config ( format ! (
440+ "task {task_name:?} is not registered; provide SpawnOptions::queue or Task::queue for unregistered tasks"
441+ ) ) ) ;
387442 } ;
388443 validate_queue_name ( & queue_name) ?;
389444
390445 let max_attempts = options
391446 . max_attempts
392447 . or_else ( || registration. as_ref ( ) . and_then ( |r| r. default_max_attempts ) )
448+ . or_else ( || task_options. and_then ( |task| task. default_max_attempts ) )
393449 . unwrap_or ( self . default_max_attempts ) ;
394450 if max_attempts < 1 {
395451 return Err ( Error :: Config ( "max attempts must be at least 1" . to_string ( ) ) ) ;
@@ -398,7 +454,8 @@ impl Client {
398454 let cancellation = options
399455 . cancellation
400456 . clone ( )
401- . or_else ( || registration. and_then ( |r| r. default_cancellation ) ) ;
457+ . or_else ( || registration. and_then ( |r| r. default_cancellation ) )
458+ . or_else ( || task_options. and_then ( |task| task. default_cancellation . clone ( ) ) ) ;
402459
403460 Ok ( ( queue_name, max_attempts, cancellation) )
404461 }
@@ -448,6 +505,11 @@ fn resolve_database_url(explicit: Option<&str>) -> String {
448505 return url;
449506 }
450507 }
508+ if let Ok ( url) = std:: env:: var ( "DATABASE_URL" ) {
509+ if !url. trim ( ) . is_empty ( ) {
510+ return url;
511+ }
512+ }
451513 if let Ok ( pgdatabase) = std:: env:: var ( "PGDATABASE" ) {
452514 if !pgdatabase. trim ( ) . is_empty ( ) {
453515 if pgdatabase. contains ( "://" ) || pgdatabase. contains ( '=' ) {
0 commit comments