Skip to main content

tuitbot_core/automation/adapters/
queue.rs

1//! Posting queue adapter implementations.
2
3use tokio::sync::mpsc;
4
5use super::super::loop_helpers::{LoopError, PostSender};
6use super::super::posting_queue::{ApprovalQueue, PostAction};
7use crate::storage::{self, DbPool};
8
9/// Adapts `mpsc::Sender<PostAction>` to the `PostSender` port trait.
10pub struct PostSenderAdapter {
11    tx: mpsc::Sender<PostAction>,
12}
13
14impl PostSenderAdapter {
15    pub fn new(tx: mpsc::Sender<PostAction>) -> Self {
16        Self { tx }
17    }
18}
19
20#[async_trait::async_trait]
21impl PostSender for PostSenderAdapter {
22    async fn send_reply(&self, tweet_id: &str, content: &str) -> Result<(), LoopError> {
23        let (result_tx, result_rx) = tokio::sync::oneshot::channel();
24        self.tx
25            .send(PostAction::Reply {
26                tweet_id: tweet_id.to_string(),
27                content: content.to_string(),
28                media_ids: vec![],
29                result_tx: Some(result_tx),
30            })
31            .await
32            .map_err(|e| LoopError::Other(format!("posting queue send failed: {e}")))?;
33
34        result_rx
35            .await
36            .map_err(|e| LoopError::Other(format!("posting queue result recv failed: {e}")))?
37            .map_err(|e| LoopError::Other(format!("post action failed: {e}")))?;
38
39        Ok(())
40    }
41}
42
43/// Adapts `DbPool` to the `ApprovalQueue` port trait.
44pub struct ApprovalQueueAdapter {
45    pool: DbPool,
46}
47
48impl ApprovalQueueAdapter {
49    pub fn new(pool: DbPool) -> Self {
50        Self { pool }
51    }
52}
53
54#[async_trait::async_trait]
55impl ApprovalQueue for ApprovalQueueAdapter {
56    async fn queue_reply(
57        &self,
58        tweet_id: &str,
59        content: &str,
60        media_paths: &[String],
61    ) -> Result<i64, String> {
62        let media_json = serde_json::to_string(media_paths).unwrap_or_else(|_| "[]".to_string());
63        storage::approval_queue::enqueue(
64            &self.pool,
65            "reply",
66            tweet_id,
67            "", // target_author not available here
68            content,
69            "",  // topic
70            "",  // archetype
71            0.0, // score
72            &media_json,
73        )
74        .await
75        .map_err(|e| e.to_string())
76    }
77
78    async fn queue_tweet(&self, content: &str, media_paths: &[String]) -> Result<i64, String> {
79        let media_json = serde_json::to_string(media_paths).unwrap_or_else(|_| "[]".to_string());
80        storage::approval_queue::enqueue(
81            &self.pool,
82            "tweet",
83            "", // no target tweet
84            "", // no target author
85            content,
86            "",  // topic
87            "",  // archetype
88            0.0, // score
89            &media_json,
90        )
91        .await
92        .map_err(|e| e.to_string())
93    }
94}