mzrs-sdk 0.1.20

High-level Rust SDK for Mezon platform
Documentation
//! Attachment source abstraction retained for API compatibility.
//!
//! Mezon does not currently support direct attachment upload for bot tokens.
//! [`AttachmentSource`] remains available so existing callers continue to
//! compile, but [`MzrsClient::upload_attachment`](crate::MzrsClient::upload_attachment)
//! now returns an unsupported error instead of performing an upload.

use std::path::{Path, PathBuf};

use base64::{engine::general_purpose::STANDARD, Engine};
use bytes::Bytes;

use crate::error::SdkError;

/// The source of data to upload as a file attachment.
///
/// Construct via the [`From`] impls — you do not need to name a variant
/// directly in normal usage.
pub enum AttachmentSource {
    /// Pre-resolved raw bytes — used as-is.
    Bytes(Bytes),
    /// Base64-encoded string — decoded to bytes before upload.
    Base64(String),
    /// Path to a file on disk — read asynchronously before upload.
    Path(PathBuf),
}

impl AttachmentSource {
    /// Resolve the source into raw [`Bytes`].
    ///
    /// # Errors
    ///
    /// - [`SdkError::Base64`] if the string is not valid standard base64.
    /// - [`SdkError::Io`] if the file cannot be read from disk.
    pub async fn into_bytes(self) -> Result<Bytes, SdkError> {
        match self {
            AttachmentSource::Bytes(b) => Ok(b),
            AttachmentSource::Base64(s) => {
                let decoded = STANDARD
                    .decode(s.trim())
                    .map_err(|e| SdkError::Base64(e.to_string()))?;
                Ok(Bytes::from(decoded))
            }
            AttachmentSource::Path(path) => {
                let data = tokio::fs::read(&path).await?;
                Ok(Bytes::from(data))
            }
        }
    }
}

// ── From impls ──────────────────────────────────────────────────────

impl From<Bytes> for AttachmentSource {
    fn from(b: Bytes) -> Self {
        AttachmentSource::Bytes(b)
    }
}

impl From<Vec<u8>> for AttachmentSource {
    fn from(v: Vec<u8>) -> Self {
        AttachmentSource::Bytes(Bytes::from(v))
    }
}

impl From<&[u8]> for AttachmentSource {
    fn from(s: &[u8]) -> Self {
        AttachmentSource::Bytes(Bytes::copy_from_slice(s))
    }
}

/// Interprets the [`String`] as a **base64-encoded** payload.
///
/// If you have a file path as a string, wrap it in [`Path::new`] first.
impl From<String> for AttachmentSource {
    fn from(s: String) -> Self {
        AttachmentSource::Base64(s)
    }
}

/// Interprets the `&str` as a **base64-encoded** payload.
///
/// If you have a file path as a string literal, use `Path::new("...")`.
impl From<&str> for AttachmentSource {
    fn from(s: &str) -> Self {
        AttachmentSource::Base64(s.to_owned())
    }
}

impl From<PathBuf> for AttachmentSource {
    fn from(p: PathBuf) -> Self {
        AttachmentSource::Path(p)
    }
}

impl From<&Path> for AttachmentSource {
    fn from(p: &Path) -> Self {
        AttachmentSource::Path(p.to_owned())
    }
}