refactor: remove composer and reader

Composers and readers did not work as expected. It is just not possible for
himalaya to spawn a command that spawns $EDITOR, piping and redirection cannot
satisfy all the needs. Either the $EDITOR does not spawn (hangs over), either
himalaya does not collect any output from edition. The simplest way is to use an
intermediate temp file, or use process substitution. For eg., using mml:

  mml compose >(himalaya message send)

You can also write into a file then feed himalaya with it.
This commit is contained in:
Clément DOUIN
2026-06-01 15:19:45 +02:00
parent 3a1a981b8c
commit 662bd26eb1
151 changed files with 864 additions and 1291 deletions
+12 -6
View File
@@ -19,6 +19,7 @@ use anyhow::Result;
use clap::Subcommand;
use pimalaya_cli::printer::Printer;
use crate::account::context::Account;
use crate::imap::{
client::ImapClient, envelope::cli::ImapEnvelopeCommand, flag::cli::ImapFlagCommand,
id::ImapIdCommand, mailbox::cli::ImapMailboxCommand, message::cli::ImapMessageCommand,
@@ -46,14 +47,19 @@ pub enum ImapCommand {
}
impl ImapCommand {
pub fn execute(self, printer: &mut impl Printer, client: ImapClient) -> Result<()> {
pub fn execute(
self,
printer: &mut impl Printer,
account: &mut Account,
client: &mut ImapClient,
) -> Result<()> {
match self {
Self::Id(cmd) => cmd.execute(printer, client),
Self::Id(cmd) => cmd.execute(printer, account, client),
Self::Envelopes(cmd) => cmd.execute(printer, client),
Self::Flags(cmd) => cmd.execute(printer, client),
Self::Mailboxes(cmd) => cmd.execute(printer, client),
Self::Messages(cmd) => cmd.execute(printer, client),
Self::Envelopes(cmd) => cmd.execute(printer, account, client),
Self::Flags(cmd) => cmd.execute(printer, account, client),
Self::Mailboxes(cmd) => cmd.execute(printer, account, client),
Self::Messages(cmd) => cmd.execute(printer, account, client),
}
}
}
+14 -13
View File
@@ -15,12 +15,12 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//! Himalaya wrapper around [`io_imap::client::ImapClientStd`] that
//! bundles the merged [`Account`] alongside the live IMAP client.
//! Himalaya wrapper around [`io_imap::client::ImapClientStd`].
//!
//! This is what every IMAP-specific subcommand receives: the dispatch
//! layer (`crate::cli`) opens the session up front via
//! [`build_imap_client`] and hands the ready-to-use wrapper down.
//! [`build_imap_client`] and hands the ready-to-use wrapper down,
//! together with the merged [`Account`] as a sibling argument.
use std::{
ops::{Deref, DerefMut},
@@ -40,23 +40,21 @@ use crate::{
pub struct ImapClient {
inner: Inner,
pub account: Account,
}
impl ImapClient {
/// Opens the IMAP connection (TCP/TLS/STARTTLS, greeting, SASL)
/// then wraps the resulting client alongside `account`. The
/// capability list reported by the connect handshake is discarded;
/// IMAP-specific subcommands that need it should call
/// Opens the IMAP connection (TCP/TLS/STARTTLS, greeting, SASL).
/// The capability list reported by the connect handshake is
/// discarded; IMAP-specific subcommands that need it should call
/// [`Inner::capability`] explicitly.
pub fn new(config: ImapConfig, account: Account) -> Result<Self> {
pub fn new(config: ImapConfig) -> Result<Self> {
let mut tls: Tls = config.tls.into();
tls.rustls.alpn = vec!["imap".into()];
let sasl: Option<Sasl> = config.sasl.map(Sasl::try_from).transpose()?;
let auto_id = resolve_auto_id_params(&config.id)?;
let server = parse_imap_server(&config.server)?;
let (inner, _capability) = Inner::connect(&server, &tls, config.starttls, sasl, auto_id)?;
Ok(Self { inner, account })
Ok(Self { inner })
}
}
@@ -92,11 +90,13 @@ impl DerefMut for ImapClient {
/// Loads the configuration, picks the active account, builds the
/// merged [`Account`] then opens the IMAP session. Bails when the
/// account has no `[imap]` block.
/// account has no `[imap]` block. Returns the live client paired
/// with the merged account so subcommands receive both as sibling
/// arguments.
pub fn build_imap_client(
config_paths: &[PathBuf],
account_name: Option<&str>,
) -> Result<ImapClient> {
) -> Result<(Account, ImapClient)> {
let mut config = load_or_wizard(config_paths)?;
let (name, mut ac) = config
.take_account(account_name)?
@@ -106,5 +106,6 @@ pub fn build_imap_client(
.take()
.ok_or_else(|| anyhow!("IMAP config is missing for account `{name}`"))?;
let account = Account::from(config).merge(Account::from(ac));
ImapClient::new(imap_config, account)
let client = ImapClient::new(imap_config)?;
Ok((account, client))
}
+11 -5
View File
@@ -19,6 +19,7 @@ use anyhow::Result;
use clap::Subcommand;
use pimalaya_cli::printer::Printer;
use crate::account::context::Account;
use crate::imap::{
client::ImapClient,
envelope::{
@@ -43,12 +44,17 @@ pub enum ImapEnvelopeCommand {
}
impl ImapEnvelopeCommand {
pub fn execute(self, printer: &mut impl Printer, client: ImapClient) -> Result<()> {
pub fn execute(
self,
printer: &mut impl Printer,
account: &mut Account,
client: &mut ImapClient,
) -> Result<()> {
match self {
Self::Get(cmd) => cmd.execute(printer, client),
Self::List(cmd) => cmd.execute(printer, client),
Self::Search(cmd) => cmd.execute(printer, client),
Self::Sort(cmd) => cmd.execute(printer, client),
Self::Get(cmd) => cmd.execute(printer, account, client),
Self::List(cmd) => cmd.execute(printer, account, client),
Self::Search(cmd) => cmd.execute(printer, account, client),
Self::Sort(cmd) => cmd.execute(printer, account, client),
Self::Thread(cmd) => cmd.execute(printer, client),
}
}
+8 -2
View File
@@ -27,6 +27,7 @@ use io_imap::types::{
use pimalaya_cli::printer::Printer;
use serde::Serialize;
use crate::account::context::Account;
use crate::imap::{
client::ImapClient,
envelope::list::{decode_mime, format_address},
@@ -54,7 +55,12 @@ pub struct ImapEnvelopeGetCommand {
}
impl ImapEnvelopeGetCommand {
pub fn execute(self, printer: &mut impl Printer, mut client: ImapClient) -> Result<()> {
pub fn execute(
self,
printer: &mut impl Printer,
account: &mut Account,
client: &mut ImapClient,
) -> Result<()> {
let mailbox = self.mailbox_name.inner.try_into()?;
if !self.mailbox_no_select.inner {
@@ -76,7 +82,7 @@ impl ImapEnvelopeGetCommand {
};
let table = EnvelopeTable {
preset: client.account.table_preset().to_string(),
preset: account.table_preset().to_string(),
envelope: items.into(),
};
+13 -7
View File
@@ -32,6 +32,7 @@ use pimalaya_cli::printer::Printer;
use rfc2047_decoder::{Decoder, RecoverStrategy};
use serde::Serialize;
use crate::account::context::Account;
use crate::imap::{
client::ImapClient,
mailbox::arg::{MailboxNameOptionalFlag, MailboxNoSelectFlag},
@@ -67,7 +68,12 @@ pub struct ImapEnvelopeListCommand {
}
impl ImapEnvelopeListCommand {
pub fn execute(self, printer: &mut impl Printer, mut client: ImapClient) -> Result<()> {
pub fn execute(
self,
printer: &mut impl Printer,
account: &mut Account,
client: &mut ImapClient,
) -> Result<()> {
let mailbox = self.mailbox_name.inner.try_into()?;
let exists = if self.mailbox_no_select.inner {
@@ -100,13 +106,13 @@ impl ImapEnvelopeListCommand {
let data = client.fetch(sequence_set, item_names, !self.sequence && has_sequence)?;
let table = EnvelopesTable {
preset: client.account.table_preset().to_string(),
arrangement: client.account.table_arrangement(),
preset: account.table_preset().to_string(),
arrangement: account.table_arrangement(),
colors: EnvelopeColors {
id: client.account.envelopes_list_table_id_color(),
subject: client.account.envelopes_list_table_subject_color(),
from: client.account.envelopes_list_table_from_color(),
date: client.account.envelopes_list_table_date_color(),
id: account.envelopes_list_table_id_color(),
subject: account.envelopes_list_table_subject_color(),
from: account.envelopes_list_table_from_color(),
date: account.envelopes_list_table_date_color(),
},
envelopes: map_envelopes_table_entries(data),
};
+10 -4
View File
@@ -28,6 +28,7 @@ use io_imap::types::{
use pimalaya_cli::printer::Printer;
use serde::Serialize;
use crate::account::context::Account;
use crate::imap::{
client::ImapClient,
mailbox::arg::{MailboxNameOptionalFlag, MailboxNoSelectFlag},
@@ -75,7 +76,12 @@ pub struct ImapEnvelopeSearchCommand {
}
impl ImapEnvelopeSearchCommand {
pub fn execute(self, printer: &mut impl Printer, mut client: ImapClient) -> Result<()> {
pub fn execute(
self,
printer: &mut impl Printer,
account: &mut Account,
client: &mut ImapClient,
) -> Result<()> {
let mailbox = self.mailbox_name.inner.try_into()?;
if !self.mailbox_no_select.inner {
@@ -86,9 +92,9 @@ impl ImapEnvelopeSearchCommand {
let ids = client.search(criteria, !self.seq)?;
let table = SearchTable {
preset: client.account.table_preset().to_string(),
arrangement: client.account.table_arrangement(),
id_color: client.account.envelopes_list_table_id_color(),
preset: account.table_preset().to_string(),
arrangement: account.table_arrangement(),
id_color: account.envelopes_list_table_id_color(),
ids: ids
.into_iter()
.map(|id| SearchResult { id: id.get() })
+8 -2
View File
@@ -27,6 +27,7 @@ use io_imap::types::{
use pimalaya_cli::printer::Printer;
use serde::Serialize;
use crate::account::context::Account;
use crate::imap::{
client::ImapClient, envelope::search::parse_query, mailbox::arg::MailboxNameOptionalArg,
};
@@ -68,7 +69,12 @@ pub struct ImapEnvelopeSortCommand {
}
impl ImapEnvelopeSortCommand {
pub fn execute(self, printer: &mut impl Printer, mut client: ImapClient) -> Result<()> {
pub fn execute(
self,
printer: &mut impl Printer,
account: &mut Account,
client: &mut ImapClient,
) -> Result<()> {
let mailbox = self.mailbox_name.inner.try_into()?;
client.select(mailbox)?;
@@ -82,7 +88,7 @@ impl ImapEnvelopeSortCommand {
let ids = client.sort(sort_criteria, search_criteria, !self.seq)?;
let id_color = client.account.envelopes_list_table_id_color();
let id_color = account.envelopes_list_table_id_color();
let table = SortResultsTable::new(ids, !self.seq, id_color);
printer.out(table)?;
+2 -2
View File
@@ -62,7 +62,7 @@ pub struct ImapEnvelopeThreadCommand {
}
impl ImapEnvelopeThreadCommand {
pub fn execute(self, printer: &mut impl Printer, mut client: ImapClient) -> Result<()> {
pub fn execute(self, printer: &mut impl Printer, client: &mut ImapClient) -> Result<()> {
let mailbox = self.mailbox_name.inner.try_into()?;
if !self.mailbox_no_select.inner {
@@ -76,7 +76,7 @@ impl ImapEnvelopeThreadCommand {
let all_ids = collect_thread_ids(&threads);
let subjects = if !all_ids.is_empty() {
fetch_subjects(&mut client, &all_ids, !self.seq)?
fetch_subjects(client, &all_ids, !self.seq)?
} else {
HashMap::new()
};
+1 -1
View File
@@ -52,7 +52,7 @@ pub struct ImapFlagAddCommand {
}
impl ImapFlagAddCommand {
pub fn execute(self, printer: &mut impl Printer, mut client: ImapClient) -> Result<()> {
pub fn execute(self, printer: &mut impl Printer, client: &mut ImapClient) -> Result<()> {
let mailbox = self.mailbox_name.inner.try_into()?;
if !self.mailbox_no_select.inner {
+8 -2
View File
@@ -19,6 +19,7 @@ use anyhow::Result;
use clap::Subcommand;
use pimalaya_cli::printer::Printer;
use crate::account::context::Account;
use crate::imap::{
client::ImapClient,
flag::{
@@ -40,9 +41,14 @@ pub enum ImapFlagCommand {
}
impl ImapFlagCommand {
pub fn execute(self, printer: &mut impl Printer, client: ImapClient) -> Result<()> {
pub fn execute(
self,
printer: &mut impl Printer,
account: &mut Account,
client: &mut ImapClient,
) -> Result<()> {
match self {
Self::List(cmd) => cmd.execute(printer, client),
Self::List(cmd) => cmd.execute(printer, account, client),
Self::Add(cmd) => cmd.execute(printer, client),
Self::Set(cmd) => cmd.execute(printer, client),
Self::Remove(cmd) => cmd.execute(printer, client),
+9 -3
View File
@@ -24,6 +24,7 @@ use io_imap::types::flag::{Flag, FlagPerm};
use pimalaya_cli::printer::Printer;
use serde::{Serialize, Serializer};
use crate::account::context::Account;
use crate::imap::{client::ImapClient, mailbox::arg::MailboxNameArg};
/// List available IMAP flags for the given mailbox.
@@ -38,7 +39,12 @@ pub struct ImapFlagListCommand {
}
impl ImapFlagListCommand {
pub fn execute(self, printer: &mut impl Printer, mut client: ImapClient) -> Result<()> {
pub fn execute(
self,
printer: &mut impl Printer,
account: &mut Account,
client: &mut ImapClient,
) -> Result<()> {
let mailbox = self.mailbox_name.inner.try_into()?;
let data = client.select(mailbox)?;
@@ -46,8 +52,8 @@ impl ImapFlagListCommand {
let permanent_flags = data.permanent_flags.unwrap_or_default();
let table = FlagsTable {
preset: client.account.table_preset().to_string(),
arrangement: client.account.table_arrangement(),
preset: account.table_preset().to_string(),
arrangement: account.table_arrangement(),
flags,
permanent_flags,
};
+1 -1
View File
@@ -52,7 +52,7 @@ pub struct ImapFlagRemoveCommand {
}
impl ImapFlagRemoveCommand {
pub fn execute(self, printer: &mut impl Printer, mut client: ImapClient) -> Result<()> {
pub fn execute(self, printer: &mut impl Printer, client: &mut ImapClient) -> Result<()> {
let mailbox = self.mailbox_name.inner.try_into()?;
if !self.mailbox_no_select.inner {
+1 -1
View File
@@ -52,7 +52,7 @@ pub struct ImapFlagSetCommand {
}
impl ImapFlagSetCommand {
pub fn execute(self, printer: &mut impl Printer, mut client: ImapClient) -> Result<()> {
pub fn execute(self, printer: &mut impl Printer, client: &mut ImapClient) -> Result<()> {
let mailbox = self.mailbox_name.inner.try_into()?;
if !self.mailbox_no_select.inner {
+8 -2
View File
@@ -27,6 +27,7 @@ use io_imap::types::{
use pimalaya_cli::printer::Printer;
use serde::Serialize;
use crate::account::context::Account;
use crate::{config::ImapIdConfig, imap::client::ImapClient};
/// Get information about the IMAP server.
@@ -44,7 +45,12 @@ pub struct ImapIdCommand {
}
impl ImapIdCommand {
pub fn execute(self, printer: &mut impl Printer, mut client: ImapClient) -> Result<()> {
pub fn execute(
self,
printer: &mut impl Printer,
account: &mut Account,
client: &mut ImapClient,
) -> Result<()> {
let mut params: HashMap<IString<'static>, NString<'static>> = HashMap::new();
for key in ["name", "version", "vendor", "support-url"] {
let (k, v) = build_canned_pair(key)?;
@@ -58,7 +64,7 @@ impl ImapIdCommand {
let params = client.id(Some(params.into_iter().collect()))?;
let table = ServerIdTable {
preset: client.account.table_preset().to_string(),
preset: account.table_preset().to_string(),
server_id: params
.unwrap_or_default()
.into_iter()
+9 -3
View File
@@ -19,6 +19,7 @@ use anyhow::Result;
use clap::Subcommand;
use pimalaya_cli::printer::Printer;
use crate::account::context::Account;
use crate::imap::{
client::ImapClient,
mailbox::{
@@ -55,17 +56,22 @@ pub enum ImapMailboxCommand {
}
impl ImapMailboxCommand {
pub fn execute(self, printer: &mut impl Printer, client: ImapClient) -> Result<()> {
pub fn execute(
self,
printer: &mut impl Printer,
account: &mut Account,
client: &mut ImapClient,
) -> Result<()> {
match self {
Self::Close(cmd) => cmd.execute(printer, client),
Self::Create(cmd) => cmd.execute(printer, client),
Self::Delete(cmd) => cmd.execute(printer, client),
Self::Expunge(cmd) => cmd.execute(printer, client),
Self::List(cmd) => cmd.execute(printer, client),
Self::List(cmd) => cmd.execute(printer, account, client),
Self::Purge(cmd) => cmd.execute(printer, client),
Self::Rename(cmd) => cmd.execute(printer, client),
Self::Select(cmd) => cmd.execute(printer, client),
Self::Status(cmd) => cmd.execute(printer, client),
Self::Status(cmd) => cmd.execute(printer, account, client),
Self::Subscribe(cmd) => cmd.execute(printer, client),
Self::Unselect(cmd) => cmd.execute(printer, client),
Self::Unsubscribe(cmd) => cmd.execute(printer, client),
+1 -1
View File
@@ -34,7 +34,7 @@ use crate::imap::client::ImapClient;
pub struct ImapMailboxCloseCommand;
impl ImapMailboxCloseCommand {
pub fn execute(self, printer: &mut impl Printer, mut client: ImapClient) -> Result<()> {
pub fn execute(self, printer: &mut impl Printer, client: &mut ImapClient) -> Result<()> {
client.close()?;
printer.out(Message::new("Mailbox successfully closed"))
}
+1 -1
View File
@@ -32,7 +32,7 @@ pub struct ImapMailboxCreateCommand {
}
impl ImapMailboxCreateCommand {
pub fn execute(self, printer: &mut impl Printer, mut client: ImapClient) -> Result<()> {
pub fn execute(self, printer: &mut impl Printer, client: &mut ImapClient) -> Result<()> {
let mailbox = self.mailbox_name.inner.try_into()?;
client.create(mailbox)?;
printer.out(Message::new("Mailbox successfully created"))
+1 -1
View File
@@ -32,7 +32,7 @@ pub struct ImapMailboxDeleteCommand {
}
impl ImapMailboxDeleteCommand {
pub fn execute(self, printer: &mut impl Printer, mut client: ImapClient) -> Result<()> {
pub fn execute(self, printer: &mut impl Printer, client: &mut ImapClient) -> Result<()> {
let mailbox = self.mailbox_name.inner.try_into()?;
client.delete(mailbox)?;
printer.out(Message::new("Mailbox successfully deleted"))
+1 -1
View File
@@ -37,7 +37,7 @@ pub struct ImapMailboxExpungeCommand {
}
impl ImapMailboxExpungeCommand {
pub fn execute(self, printer: &mut impl Printer, mut client: ImapClient) -> Result<()> {
pub fn execute(self, printer: &mut impl Printer, client: &mut ImapClient) -> Result<()> {
let mailbox = self.mailbox_name.inner.try_into()?;
if !self.mailbox_no_select.inner {
+9 -3
View File
@@ -25,6 +25,7 @@ use io_imap::types::{core::QuotedChar, flag::FlagNameAttribute, mailbox::Mailbox
use pimalaya_cli::printer::Printer;
use serde::Serialize;
use crate::account::context::Account;
use crate::imap::client::ImapClient;
/// List, search and filter mailboxes.
@@ -48,7 +49,12 @@ pub struct ImapMailboxListCommand {
}
impl ImapMailboxListCommand {
pub fn execute(self, printer: &mut impl Printer, mut client: ImapClient) -> Result<()> {
pub fn execute(
self,
printer: &mut impl Printer,
account: &mut Account,
client: &mut ImapClient,
) -> Result<()> {
let reference = self.reference.try_into()?;
let pattern = self.pattern.try_into()?;
@@ -59,8 +65,8 @@ impl ImapMailboxListCommand {
};
let table = MailboxesTable {
preset: client.account.table_preset().to_string(),
name_color: client.account.mailboxes_list_table_name_color(),
preset: account.table_preset().to_string(),
name_color: account.mailboxes_list_table_name_color(),
mailboxes: mailboxes.into_iter().map(From::from).collect(),
};
+1 -1
View File
@@ -39,7 +39,7 @@ pub struct ImapMailboxPurgeCommand {
}
impl ImapMailboxPurgeCommand {
pub fn execute(self, printer: &mut impl Printer, mut client: ImapClient) -> Result<()> {
pub fn execute(self, printer: &mut impl Printer, client: &mut ImapClient) -> Result<()> {
let mailbox = self.mailbox_name.inner.try_into()?;
if !self.mailbox_no_select.inner {
+1 -1
View File
@@ -36,7 +36,7 @@ pub struct ImapMailboxRenameCommand {
}
impl ImapMailboxRenameCommand {
pub fn execute(self, printer: &mut impl Printer, mut client: ImapClient) -> Result<()> {
pub fn execute(self, printer: &mut impl Printer, client: &mut ImapClient) -> Result<()> {
let from = self.mailbox_source_name.inner.try_into()?;
let to = self.mailbox_dest_name.inner.try_into()?;
client.rename(from, to)?;
+1 -1
View File
@@ -36,7 +36,7 @@ pub struct ImapMailboxSelectCommand {
}
impl ImapMailboxSelectCommand {
pub fn execute(self, printer: &mut impl Printer, mut client: ImapClient) -> Result<()> {
pub fn execute(self, printer: &mut impl Printer, client: &mut ImapClient) -> Result<()> {
let mailbox = self.mailbox_name.inner.try_into()?;
client.select(mailbox)?;
printer.out(Message::new("Mailbox successfully selected"))
+8 -2
View File
@@ -24,6 +24,7 @@ use io_imap::types::status::{StatusDataItem, StatusDataItemName};
use pimalaya_cli::printer::Printer;
use serde::{Serialize, Serializer};
use crate::account::context::Account;
use crate::imap::{client::ImapClient, mailbox::arg::MailboxNameArg};
/// Get the status of the given mailbox.
@@ -37,7 +38,12 @@ pub struct ImapMailboxStatusCommand {
}
impl ImapMailboxStatusCommand {
pub fn execute(self, printer: &mut impl Printer, mut client: ImapClient) -> Result<()> {
pub fn execute(
self,
printer: &mut impl Printer,
account: &mut Account,
client: &mut ImapClient,
) -> Result<()> {
let mailbox = self.mailbox_name.inner.try_into()?;
let item_names = vec![
StatusDataItemName::Messages,
@@ -50,7 +56,7 @@ impl ImapMailboxStatusCommand {
let items = client.status(mailbox, item_names)?;
let table = MailboxStatusTable {
preset: client.account.table_preset().to_string(),
preset: account.table_preset().to_string(),
status: items.into(),
};
+1 -1
View File
@@ -32,7 +32,7 @@ pub struct ImapMailboxSubscribeCommand {
}
impl ImapMailboxSubscribeCommand {
pub fn execute(self, printer: &mut impl Printer, mut client: ImapClient) -> Result<()> {
pub fn execute(self, printer: &mut impl Printer, client: &mut ImapClient) -> Result<()> {
let mailbox = self.mailbox_name.inner.try_into()?;
client.subscribe(mailbox)?;
printer.out(Message::new("Mailbox successfully subscribed"))
+1 -1
View File
@@ -33,7 +33,7 @@ use crate::imap::client::ImapClient;
pub struct ImapMailboxUnselectCommand;
impl ImapMailboxUnselectCommand {
pub fn execute(self, printer: &mut impl Printer, mut client: ImapClient) -> Result<()> {
pub fn execute(self, printer: &mut impl Printer, client: &mut ImapClient) -> Result<()> {
client.unselect()?;
printer.out(Message::new("Mailbox successfully unselected"))
}
+1 -1
View File
@@ -32,7 +32,7 @@ pub struct ImapMailboxUnsubscribeCommand {
}
impl ImapMailboxUnsubscribeCommand {
pub fn execute(self, printer: &mut impl Printer, mut client: ImapClient) -> Result<()> {
pub fn execute(self, printer: &mut impl Printer, client: &mut ImapClient) -> Result<()> {
let mailbox = self.mailbox_name.inner.try_into()?;
client.unsubscribe(mailbox)?;
printer.out(Message::new("Mailbox successfully unsubscribed"))
+8 -2
View File
@@ -19,6 +19,7 @@ use anyhow::Result;
use clap::Subcommand;
use pimalaya_cli::printer::Printer;
use crate::account::context::Account;
use crate::imap::{
client::ImapClient,
message::{
@@ -43,12 +44,17 @@ pub enum ImapMessageCommand {
}
impl ImapMessageCommand {
pub fn execute(self, printer: &mut impl Printer, client: ImapClient) -> Result<()> {
pub fn execute(
self,
printer: &mut impl Printer,
account: &mut Account,
client: &mut ImapClient,
) -> Result<()> {
match self {
Self::Save(cmd) => cmd.execute(printer, client),
Self::Get(cmd) => cmd.execute(printer, client),
Self::Read(cmd) => cmd.execute(printer, client),
Self::Export(cmd) => cmd.execute(printer, client),
Self::Export(cmd) => cmd.execute(printer, account, client),
Self::Copy(cmd) => cmd.execute(printer, client),
Self::Move(cmd) => cmd.execute(printer, client),
}
+1 -1
View File
@@ -48,7 +48,7 @@ pub struct ImapMessageCopyCommand {
}
impl ImapMessageCopyCommand {
pub fn execute(self, printer: &mut impl Printer, mut client: ImapClient) -> Result<()> {
pub fn execute(self, printer: &mut impl Printer, client: &mut ImapClient) -> Result<()> {
let mailbox = self.mailbox_name.inner.try_into()?;
if !self.mailbox_no_select.inner {
+8 -4
View File
@@ -27,6 +27,7 @@ use io_imap::types::fetch::{MacroOrMessageDataItemNames, MessageDataItem, Messag
use mail_parser::{MessageParser, MimeHeaders};
use pimalaya_cli::printer::{Message, Printer};
use crate::account::context::Account;
use crate::imap::{client::ImapClient, mailbox::arg::MailboxNameOptionalFlag};
/// Export type for message export.
@@ -73,7 +74,12 @@ pub struct ImapMessageExportCommand {
}
impl ImapMessageExportCommand {
pub fn execute(self, printer: &mut impl Printer, mut client: ImapClient) -> Result<()> {
pub fn execute(
self,
printer: &mut impl Printer,
account: &mut Account,
client: &mut ImapClient,
) -> Result<()> {
let mailbox = self.mailbox_name.inner.try_into()?;
client.select(mailbox)?;
@@ -133,9 +139,7 @@ impl ImapMessageExportCommand {
// Generate filename from subject or message-id
let filename = generate_eml_filename(&message, self.id);
let dir = self
.directory
.unwrap_or_else(|| client.account.downloads_dir());
let dir = self.directory.unwrap_or_else(|| account.downloads_dir());
if !dir.exists() {
fs::create_dir_all(&dir)?;
+1 -1
View File
@@ -49,7 +49,7 @@ pub struct ImapMessageGetCommand {
}
impl ImapMessageGetCommand {
pub fn execute(self, printer: &mut impl Printer, mut client: ImapClient) -> Result<()> {
pub fn execute(self, printer: &mut impl Printer, client: &mut ImapClient) -> Result<()> {
let mailbox = self.mailbox_name.inner.try_into()?;
if self.id == 0 {
bail!("ID must be non-zero");
+1 -1
View File
@@ -49,7 +49,7 @@ pub struct ImapMessageMoveCommand {
}
impl ImapMessageMoveCommand {
pub fn execute(self, printer: &mut impl Printer, mut client: ImapClient) -> Result<()> {
pub fn execute(self, printer: &mut impl Printer, client: &mut ImapClient) -> Result<()> {
let mailbox = self.mailbox_name.inner.try_into()?;
if !self.mailbox_no_select.inner {
+1 -1
View File
@@ -54,7 +54,7 @@ pub struct ImapMessageReadCommand {
}
impl ImapMessageReadCommand {
pub fn execute(self, printer: &mut impl Printer, mut client: ImapClient) -> Result<()> {
pub fn execute(self, printer: &mut impl Printer, client: &mut ImapClient) -> Result<()> {
let mailbox = self.mailbox_name.inner.try_into()?;
if !self.mailbox_no_select.inner {
+1 -1
View File
@@ -46,7 +46,7 @@ pub struct ImapMessageSaveCommand {
}
impl ImapMessageSaveCommand {
pub fn execute(self, printer: &mut impl Printer, mut client: ImapClient) -> Result<()> {
pub fn execute(self, printer: &mut impl Printer, client: &mut ImapClient) -> Result<()> {
let mailbox: Mailbox<'static> = self.mailbox.inner.try_into()?;
let message = self.message.parse()?;
let message = Literal::try_from(message)?;