refluxer 0.2.0

Rust API wrapper for Fluxer
Documentation
use std::future::{Future, IntoFuture};
use std::pin::Pin;

use super::cache::Cache;
use crate::error::HttpError;
use crate::http::client::HttpClient;
use crate::http::endpoints::messages::{Attachment, CreateMessage, EmbedBuilder};
use crate::model::id::{ChannelId, MessageId};
use crate::model::message::Message;

#[derive(Debug, Clone)]
pub struct Context {
    pub http: HttpClient,
    pub cache: Cache,
}

impl Context {
    pub(crate) fn new(http: HttpClient, cache: Cache) -> Self {
        Self { http, cache }
    }

    /// Quick send — just text content.
    pub async fn send_message(
        &self,
        channel_id: ChannelId,
        content: impl Into<String>,
    ) -> Result<Message, HttpError> {
        self.http
            .create_message(channel_id, &CreateMessage::text(content))
            .await
    }

    /// Fluent message builder. Call `.await` to send.
    ///
    /// ```ignore
    /// ctx.send(channel_id)
    ///     .content("Pong!")
    ///     .reply(msg.id)
    ///     .embed(|e| e.title("Info").color(0x00FF00))
    ///     .await?;
    /// ```
    pub fn send(&self, channel_id: ChannelId) -> MessageBuilder {
        MessageBuilder {
            http: self.http.clone(),
            channel_id,
            inner: CreateMessage::new(),
            files: Vec::new(),
        }
    }
}

/// Fluent builder for sending messages. Implements [`IntoFuture`] so you can
/// `.await` it directly.
pub struct MessageBuilder {
    http: HttpClient,
    channel_id: ChannelId,
    inner: CreateMessage,
    files: Vec<(String, Vec<u8>)>,
}

impl MessageBuilder {
    pub fn content(mut self, content: impl Into<String>) -> Self {
        self.inner = self.inner.content(content);
        self
    }

    pub fn tts(mut self, tts: bool) -> Self {
        self.inner = self.inner.tts(tts);
        self
    }

    pub fn embed(mut self, build: impl FnOnce(EmbedBuilder) -> EmbedBuilder) -> Self {
        self.inner = self.inner.embed(build);
        self
    }

    pub fn reply(mut self, message_id: MessageId) -> Self {
        self.inner = self.inner.reply(message_id);
        self
    }

    pub fn nonce(mut self, nonce: impl Into<String>) -> Self {
        self.inner = self.inner.nonce(nonce);
        self
    }

    /// Attach a file. The file is sent as multipart/form-data and registered
    /// as an `Attachment` on the message so embeds can reference it via
    /// `attachment://<name>`.
    pub fn file(mut self, name: impl Into<String>, bytes: Vec<u8>) -> Self {
        let name = name.into();
        let id = self.files.len() as u32;
        let attachments = self.inner.attachments.get_or_insert_with(Vec::new);
        attachments.push(Attachment {
            id,
            filename: name.clone(),
            description: None,
        });
        self.files.push((name, bytes));
        self
    }
}

impl IntoFuture for MessageBuilder {
    type Output = Result<Message, HttpError>;
    type IntoFuture = Pin<Box<dyn Future<Output = Self::Output> + Send>>;

    fn into_future(self) -> Self::IntoFuture {
        let http = self.http;
        let channel_id = self.channel_id;
        let params = self.inner;
        let files = self.files;
        Box::pin(async move {
            if files.is_empty() {
                http.create_message(channel_id, &params).await
            } else {
                http.create_message_with_files(channel_id, &params, files)
                    .await
            }
        })
    }
}