email-cli 0.6.4

Agent-friendly email CLI for Resend
use anyhow::{Result, anyhow, bail};
use rusqlite::params;
use serde_json::json;
use std::fs;
use std::path::Path;

use crate::app::App;
use crate::cli::{AttachmentGetArgs, AttachmentListArgs};
use crate::helpers::write_file_safely;
use crate::output::print_success_or;

impl App {
    pub fn attachments_list(&self, args: AttachmentListArgs) -> Result<()> {
        let message = self.get_message(args.message_id)?;
        if message.direction == "received" {
            let account = self.get_account(&message.account_email)?;
            let client = self.client_for_profile(&account.profile_name)?;
            let attachments = client.list_received_attachments(&message.remote_id)?;
            self.store_received_attachments(message.id, &attachments)?;
        }
        let rows = self.list_attachments(args.message_id)?;

        print_success_or(self.format, &rows, |rows| {
            for row in rows {
                let remote = row
                    .remote_attachment_id
                    .clone()
                    .unwrap_or_else(|| "unknown".to_string());
                let name = row
                    .filename
                    .clone()
                    .unwrap_or_else(|| "attachment".to_string());
                println!("{} {}", remote, name);
            }
        });

        Ok(())
    }

    pub fn attachments_get(&self, args: AttachmentGetArgs) -> Result<()> {
        let message = self.get_message(args.message_id)?;
        if message.direction != "received" {
            bail!("attachment download is only supported for received messages");
        }
        let account = self.get_account(&message.account_email)?;
        let client = self.client_for_profile(&account.profile_name)?;
        let attachments = client.list_received_attachments(&message.remote_id)?;
        self.store_received_attachments(message.id, &attachments)?;
        let attachment = self
            .find_attachment(args.message_id, &args.attachment_id)?
            .ok_or_else(|| anyhow!("attachment {} not found", args.attachment_id))?;
        let download_url = attachment
            .download_url
            .clone()
            .ok_or_else(|| anyhow!("attachment {} has no download url", args.attachment_id))?;
        let output_dir = args.output.unwrap_or_else(|| {
            self.db_path
                .parent()
                .unwrap_or(Path::new("."))
                .join("downloads")
        });
        fs::create_dir_all(&output_dir)?;
        let preferred_filename = attachment
            .filename
            .clone()
            .unwrap_or_else(|| format!("attachment-{}", args.attachment_id));
        let bytes = client.download_attachment(&download_url)?;
        let output_path = write_file_safely(&output_dir, &preferred_filename, &bytes)?;
        self.conn.execute(
            "UPDATE attachments SET local_path = ?1 WHERE id = ?2",
            params![output_path.display().to_string(), attachment.id],
        )?;

        let data = json!({
            "message_id": args.message_id,
            "attachment_id": args.attachment_id,
            "path": output_path.display().to_string(),
        });
        print_success_or(self.format, &data, |_d| {
            println!("{}", output_path.display());
        });

        Ok(())
    }
}