1use thiserror::Error;
2use tokio::sync::mpsc;
3
4use crate::Task;
5
6#[derive(Debug)]
9pub enum ProducerMsg<D: Clone> {
10 Enqueue(Task<D>),
11}
12
13#[derive(Clone, Debug)]
17pub struct ProducerHandle<D: Clone> {
18 tx: mpsc::Sender<ProducerMsg<D>>,
19}
20
21impl<D> ProducerHandle<D>
22where
23 D: Clone + Send + 'static,
24{
25 pub fn new(tx: mpsc::Sender<ProducerMsg<D>>) -> Self {
26 Self { tx }
27 }
28
29 pub fn channel(
31 buffer: usize,
32 ) -> (ProducerHandle<D>, mpsc::Receiver<ProducerMsg<D>>) {
33 let (tx, rx) = mpsc::channel(buffer);
34 (ProducerHandle { tx }, rx)
35 }
36
37 pub async fn enqueue(&self, task: Task<D>) -> Result<(), ProducerError> {
38 self.tx
39 .send(ProducerMsg::Enqueue(task))
40 .await
41 .map_err(|_| ProducerError::ChannelClosed)
42 }
43
44 pub fn sender(&self) -> mpsc::Sender<ProducerMsg<D>> {
45 self.tx.clone()
46 }
47}
48
49#[derive(Debug)]
51pub enum WorkerResult<D: Clone> {
52 Ack { task: Task<D> },
53 Nack { task: Task<D>, error: String },
54}
55
56#[derive(Debug, Error)]
57pub enum ProducerError {
58 #[error("producer channel closed")]
59 ChannelClosed,
60}
61
62#[cfg(test)]
63mod tests {
64 use super::*;
65 use crate::Task;
66
67 #[tokio::test]
68 async fn channel_enqueue_delivers_task() {
69 let (producer, mut rx) = ProducerHandle::channel(4);
70 let task = Task::new(42u32);
71
72 producer.enqueue(task.clone()).await.unwrap();
73
74 match rx.recv().await {
75 Some(ProducerMsg::Enqueue(received)) => {
76 assert_eq!(received.payload, task.payload);
77 }
78 other => panic!("unexpected message: {other:?}"),
79 }
80 }
81
82 #[tokio::test]
83 async fn sender_clone_enqueues() {
84 let (tx, mut rx) = mpsc::channel(4);
85 let producer = ProducerHandle::new(tx);
86 let sender = producer.sender();
87 let task = Task::new("hi");
88
89 sender.send(ProducerMsg::Enqueue(task)).await.unwrap();
90
91 let msg = rx.recv().await;
92 assert!(matches!(msg, Some(ProducerMsg::Enqueue(_))));
93 }
94
95 #[tokio::test]
96 async fn enqueue_errors_when_channel_closed() {
97 let (producer, rx) = ProducerHandle::channel(1);
98 drop(rx);
99
100 let result = producer.enqueue(Task::new(7u32)).await;
101 assert!(matches!(result, Err(ProducerError::ChannelClosed)));
102 }
103}