mirror of
https://github.com/pimalaya/himalaya.git
synced 2026-06-17 21:37:55 +08:00
refactor envelope with clap derive api
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
use clap::Parser;
|
||||
use email::account::config::DEFAULT_INBOX_FOLDER;
|
||||
|
||||
/// The folder name argument parser
|
||||
#[derive(Debug, Parser)]
|
||||
@@ -7,3 +8,11 @@ pub struct FolderNameArg {
|
||||
#[arg(name = "folder-name", value_name = "FOLDER")]
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
/// The optional folder name argument parser
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct FolderNameOptionalArg {
|
||||
/// The name of the folder
|
||||
#[arg(name = "folder-name", value_name = "FOLDER", default_value = DEFAULT_INBOX_FOLDER)]
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
@@ -1,241 +0,0 @@
|
||||
//! Folder CLI module.
|
||||
//!
|
||||
//! This module provides subcommands, arguments and a command matcher
|
||||
//! related to the folder domain.
|
||||
|
||||
use std::collections::HashSet;
|
||||
|
||||
use anyhow::Result;
|
||||
use clap::{self, Arg, ArgAction, ArgMatches, Command};
|
||||
use log::{debug, info};
|
||||
|
||||
use crate::ui::table;
|
||||
|
||||
const ARG_ALL: &str = "all";
|
||||
const ARG_EXCLUDE: &str = "exclude";
|
||||
const ARG_INCLUDE: &str = "include";
|
||||
const ARG_GLOBAL_SOURCE: &str = "global-source";
|
||||
const ARG_SOURCE: &str = "source";
|
||||
const ARG_TARGET: &str = "target";
|
||||
const CMD_CREATE: &str = "create";
|
||||
const CMD_DELETE: &str = "delete";
|
||||
const CMD_EXPUNGE: &str = "expunge";
|
||||
const CMD_FOLDER: &str = "folder";
|
||||
const CMD_LIST: &str = "list";
|
||||
|
||||
/// Represents the folder commands.
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum Cmd {
|
||||
Create,
|
||||
List(table::args::MaxTableWidth),
|
||||
Expunge,
|
||||
Delete,
|
||||
}
|
||||
|
||||
/// Represents the folder command matcher.
|
||||
pub fn matches(m: &ArgMatches) -> Result<Option<Cmd>> {
|
||||
let cmd = if let Some(m) = m.subcommand_matches(CMD_FOLDER) {
|
||||
if let Some(_) = m.subcommand_matches(CMD_EXPUNGE) {
|
||||
info!("expunge folder subcommand matched");
|
||||
Some(Cmd::Expunge)
|
||||
} else if let Some(_) = m.subcommand_matches(CMD_CREATE) {
|
||||
debug!("create folder command matched");
|
||||
Some(Cmd::Create)
|
||||
} else if let Some(m) = m.subcommand_matches(CMD_LIST) {
|
||||
debug!("list folders command matched");
|
||||
let max_table_width = table::args::parse_max_width(m);
|
||||
Some(Cmd::List(max_table_width))
|
||||
} else if let Some(_) = m.subcommand_matches(CMD_DELETE) {
|
||||
debug!("delete folder command matched");
|
||||
Some(Cmd::Delete)
|
||||
} else {
|
||||
info!("no folder subcommand matched, falling back to subcommand list");
|
||||
Some(Cmd::List(None))
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Ok(cmd)
|
||||
}
|
||||
|
||||
/// Represents the folder subcommand.
|
||||
pub fn subcmd() -> Command {
|
||||
Command::new(CMD_FOLDER)
|
||||
.about("Subcommand to manage folders")
|
||||
.long_about("Subcommand to manage folders like list, expunge or delete")
|
||||
.subcommands([
|
||||
Command::new(CMD_EXPUNGE).about("Delete emails marked for deletion"),
|
||||
Command::new(CMD_CREATE)
|
||||
.aliases(["add", "new"])
|
||||
.about("Create a new folder"),
|
||||
Command::new(CMD_LIST)
|
||||
.about("List folders")
|
||||
.arg(table::args::max_width()),
|
||||
Command::new(CMD_DELETE)
|
||||
.aliases(["remove", "rm"])
|
||||
.about("Delete a folder with all its emails"),
|
||||
])
|
||||
}
|
||||
|
||||
/// Represents the source folder argument.
|
||||
pub fn global_args() -> impl IntoIterator<Item = Arg> {
|
||||
[Arg::new(ARG_GLOBAL_SOURCE)
|
||||
.help("Override the default INBOX folder")
|
||||
.long_help(
|
||||
"Override the default INBOX folder.
|
||||
|
||||
The given folder will be used by default for all other commands (when
|
||||
applicable).",
|
||||
)
|
||||
.long("folder")
|
||||
.short('f')
|
||||
.global(true)
|
||||
.value_name("name")]
|
||||
}
|
||||
|
||||
pub fn parse_global_source_arg(matches: &ArgMatches) -> Option<&str> {
|
||||
matches
|
||||
.get_one::<String>(ARG_GLOBAL_SOURCE)
|
||||
.map(String::as_str)
|
||||
}
|
||||
|
||||
pub fn source_arg(help: &'static str) -> Arg {
|
||||
Arg::new(ARG_SOURCE).help(help).value_name("name")
|
||||
}
|
||||
|
||||
pub fn parse_source_arg(matches: &ArgMatches) -> Option<&str> {
|
||||
matches.get_one::<String>(ARG_SOURCE).map(String::as_str)
|
||||
}
|
||||
|
||||
/// Represents the all folders argument.
|
||||
pub fn all_arg(help: &'static str) -> Arg {
|
||||
Arg::new(ARG_ALL)
|
||||
.help(help)
|
||||
.long("all-folders")
|
||||
.alias("all")
|
||||
.short('A')
|
||||
.action(ArgAction::SetTrue)
|
||||
.conflicts_with(ARG_SOURCE)
|
||||
.conflicts_with(ARG_INCLUDE)
|
||||
.conflicts_with(ARG_EXCLUDE)
|
||||
}
|
||||
|
||||
/// Represents the folders to include argument.
|
||||
pub fn include_arg(help: &'static str) -> Arg {
|
||||
Arg::new(ARG_INCLUDE)
|
||||
.help(help)
|
||||
.long("include-folder")
|
||||
.alias("only")
|
||||
.short('F')
|
||||
.value_name("FOLDER")
|
||||
.num_args(1..)
|
||||
.action(ArgAction::Append)
|
||||
.conflicts_with(ARG_SOURCE)
|
||||
.conflicts_with(ARG_ALL)
|
||||
.conflicts_with(ARG_EXCLUDE)
|
||||
}
|
||||
|
||||
/// Represents the folders to exclude argument.
|
||||
pub fn exclude_arg(help: &'static str) -> Arg {
|
||||
Arg::new(ARG_EXCLUDE)
|
||||
.help(help)
|
||||
.long("exclude-folder")
|
||||
.alias("except")
|
||||
.short('x')
|
||||
.value_name("FOLDER")
|
||||
.num_args(1..)
|
||||
.action(ArgAction::Append)
|
||||
.conflicts_with(ARG_SOURCE)
|
||||
.conflicts_with(ARG_ALL)
|
||||
.conflicts_with(ARG_INCLUDE)
|
||||
}
|
||||
|
||||
/// Represents the folders to exclude argument parser.
|
||||
pub fn parse_exclude_arg(m: &ArgMatches) -> HashSet<String> {
|
||||
m.get_many::<String>(ARG_EXCLUDE)
|
||||
.unwrap_or_default()
|
||||
.map(ToOwned::to_owned)
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Represents the target folder argument.
|
||||
pub fn target_arg() -> Arg {
|
||||
Arg::new(ARG_TARGET)
|
||||
.help("Specifies the target folder")
|
||||
.value_name("TARGET")
|
||||
.required(true)
|
||||
}
|
||||
|
||||
/// Represents the target folder argument parser.
|
||||
pub fn parse_target_arg(matches: &ArgMatches) -> &str {
|
||||
matches.get_one::<String>(ARG_TARGET).unwrap().as_str()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use clap::{error::ErrorKind, Command};
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn it_should_match_cmds() {
|
||||
let arg = Command::new("himalaya")
|
||||
.subcommand(subcmd())
|
||||
.get_matches_from(&["himalaya", "folders"]);
|
||||
assert_eq!(Some(Cmd::List(None)), matches(&arg).unwrap());
|
||||
|
||||
let arg = Command::new("himalaya")
|
||||
.subcommand(subcmd())
|
||||
.get_matches_from(&["himalaya", "folders", "list", "--max-width", "20"]);
|
||||
assert_eq!(Some(Cmd::List(Some(20))), matches(&arg).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_should_match_source_arg() {
|
||||
macro_rules! get_matches_from {
|
||||
($($arg:expr),*) => {
|
||||
Command::new("himalaya")
|
||||
.arg(source_arg())
|
||||
.get_matches_from(&["himalaya", $($arg,)*])
|
||||
};
|
||||
}
|
||||
|
||||
let app = get_matches_from![];
|
||||
assert_eq!(None, app.get_one::<String>(ARG_SOURCE).map(String::as_str));
|
||||
|
||||
let app = get_matches_from!["-f", "SOURCE"];
|
||||
assert_eq!(
|
||||
Some("SOURCE"),
|
||||
app.get_one::<String>(ARG_SOURCE).map(String::as_str)
|
||||
);
|
||||
|
||||
let app = get_matches_from!["--folder", "SOURCE"];
|
||||
assert_eq!(
|
||||
Some("SOURCE"),
|
||||
app.get_one::<String>(ARG_SOURCE).map(String::as_str)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_should_match_target_arg() {
|
||||
macro_rules! get_matches_from {
|
||||
($($arg:expr),*) => {
|
||||
Command::new("himalaya")
|
||||
.arg(target_arg())
|
||||
.try_get_matches_from_mut(&["himalaya", $($arg,)*])
|
||||
};
|
||||
}
|
||||
|
||||
let app = get_matches_from![];
|
||||
assert_eq!(ErrorKind::MissingRequiredArgument, app.unwrap_err().kind());
|
||||
|
||||
let app = get_matches_from!["TARGET"];
|
||||
assert_eq!(
|
||||
Some("TARGET"),
|
||||
app.unwrap()
|
||||
.get_one::<String>(ARG_TARGET)
|
||||
.map(String::as_str)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -49,3 +49,194 @@ impl FolderSubcommand {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use async_trait::async_trait;
|
||||
use email::{
|
||||
account::config::AccountConfig,
|
||||
backend::Backend,
|
||||
envelope::{Envelope, Envelopes},
|
||||
flag::Flags,
|
||||
folder::{Folder, Folders},
|
||||
message::Messages,
|
||||
};
|
||||
use std::{any::Any, fmt::Debug, io};
|
||||
use termcolor::ColorSpec;
|
||||
|
||||
use crate::printer::{Print, PrintTable, WriteColor};
|
||||
|
||||
use super::*;
|
||||
|
||||
#[tokio::test]
|
||||
async fn it_should_list_mboxes() {
|
||||
#[derive(Debug, Default, Clone)]
|
||||
struct StringWriter {
|
||||
content: String,
|
||||
}
|
||||
|
||||
impl io::Write for StringWriter {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.content
|
||||
.push_str(&String::from_utf8(buf.to_vec()).unwrap());
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
self.content = String::default();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl termcolor::WriteColor for StringWriter {
|
||||
fn supports_color(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn set_color(&mut self, _spec: &ColorSpec) -> io::Result<()> {
|
||||
io::Result::Ok(())
|
||||
}
|
||||
|
||||
fn reset(&mut self) -> io::Result<()> {
|
||||
io::Result::Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl WriteColor for StringWriter {}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct PrinterServiceTest {
|
||||
pub writer: StringWriter,
|
||||
}
|
||||
|
||||
impl Printer for PrinterServiceTest {
|
||||
fn print_table<T: Debug + PrintTable + erased_serde::Serialize + ?Sized>(
|
||||
&mut self,
|
||||
data: Box<T>,
|
||||
opts: PrintTableOpts,
|
||||
) -> anyhow::Result<()> {
|
||||
data.print_table(&mut self.writer, opts)?;
|
||||
Ok(())
|
||||
}
|
||||
fn print_log<T: Debug + Print>(&mut self, _data: T) -> anyhow::Result<()> {
|
||||
unimplemented!()
|
||||
}
|
||||
fn print<T: Debug + Print + serde::Serialize>(
|
||||
&mut self,
|
||||
_data: T,
|
||||
) -> anyhow::Result<()> {
|
||||
unimplemented!()
|
||||
}
|
||||
fn is_json(&self) -> bool {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
struct TestBackend;
|
||||
|
||||
#[async_trait]
|
||||
impl Backend for TestBackend {
|
||||
fn name(&self) -> String {
|
||||
unimplemented!();
|
||||
}
|
||||
async fn add_folder(&mut self, _: &str) -> email::Result<()> {
|
||||
unimplemented!();
|
||||
}
|
||||
async fn list_folders(&mut self) -> email::Result<Folders> {
|
||||
Ok(Folders::from_iter([
|
||||
Folder {
|
||||
name: "INBOX".into(),
|
||||
desc: "desc".into(),
|
||||
},
|
||||
Folder {
|
||||
name: "Sent".into(),
|
||||
desc: "desc".into(),
|
||||
},
|
||||
]))
|
||||
}
|
||||
async fn expunge_folder(&mut self, _: &str) -> email::Result<()> {
|
||||
unimplemented!();
|
||||
}
|
||||
async fn purge_folder(&mut self, _: &str) -> email::Result<()> {
|
||||
unimplemented!();
|
||||
}
|
||||
async fn delete_folder(&mut self, _: &str) -> email::Result<()> {
|
||||
unimplemented!();
|
||||
}
|
||||
async fn get_envelope(&mut self, _: &str, _: &str) -> email::Result<Envelope> {
|
||||
unimplemented!();
|
||||
}
|
||||
async fn list_envelopes(
|
||||
&mut self,
|
||||
_: &str,
|
||||
_: usize,
|
||||
_: usize,
|
||||
) -> email::Result<Envelopes> {
|
||||
unimplemented!()
|
||||
}
|
||||
async fn search_envelopes(
|
||||
&mut self,
|
||||
_: &str,
|
||||
_: &str,
|
||||
_: &str,
|
||||
_: usize,
|
||||
_: usize,
|
||||
) -> email::Result<Envelopes> {
|
||||
unimplemented!()
|
||||
}
|
||||
async fn add_email(&mut self, _: &str, _: &[u8], _: &Flags) -> email::Result<String> {
|
||||
unimplemented!()
|
||||
}
|
||||
async fn get_emails(&mut self, _: &str, _: Vec<&str>) -> email::Result<Messages> {
|
||||
unimplemented!()
|
||||
}
|
||||
async fn preview_emails(&mut self, _: &str, _: Vec<&str>) -> email::Result<Messages> {
|
||||
unimplemented!()
|
||||
}
|
||||
async fn copy_emails(&mut self, _: &str, _: &str, _: Vec<&str>) -> email::Result<()> {
|
||||
unimplemented!()
|
||||
}
|
||||
async fn move_emails(&mut self, _: &str, _: &str, _: Vec<&str>) -> email::Result<()> {
|
||||
unimplemented!()
|
||||
}
|
||||
async fn delete_emails(&mut self, _: &str, _: Vec<&str>) -> email::Result<()> {
|
||||
unimplemented!()
|
||||
}
|
||||
async fn add_flags(&mut self, _: &str, _: Vec<&str>, _: &Flags) -> email::Result<()> {
|
||||
unimplemented!()
|
||||
}
|
||||
async fn set_flags(&mut self, _: &str, _: Vec<&str>, _: &Flags) -> email::Result<()> {
|
||||
unimplemented!()
|
||||
}
|
||||
async fn remove_flags(
|
||||
&mut self,
|
||||
_: &str,
|
||||
_: Vec<&str>,
|
||||
_: &Flags,
|
||||
) -> email::Result<()> {
|
||||
unimplemented!()
|
||||
}
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
let account_config = AccountConfig::default();
|
||||
let mut printer = PrinterServiceTest::default();
|
||||
let mut backend = TestBackend {};
|
||||
|
||||
assert!(list(&account_config, &mut printer, &mut backend, None)
|
||||
.await
|
||||
.is_ok());
|
||||
assert_eq!(
|
||||
concat![
|
||||
"\n",
|
||||
"NAME │DESC \n",
|
||||
"INBOX │desc \n",
|
||||
"Sent │desc \n",
|
||||
"\n"
|
||||
],
|
||||
printer.writer.content
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,247 +0,0 @@
|
||||
//! Folder handling module.
|
||||
//!
|
||||
//! This module gathers all folder actions triggered by the CLI.
|
||||
|
||||
use anyhow::Result;
|
||||
use dialoguer::Confirm;
|
||||
use email::account::config::AccountConfig;
|
||||
use std::process;
|
||||
|
||||
use crate::{
|
||||
backend::Backend,
|
||||
printer::{PrintTableOpts, Printer},
|
||||
};
|
||||
|
||||
use super::Folders;
|
||||
|
||||
pub async fn expunge<P: Printer>(printer: &mut P, backend: &Backend, folder: &str) -> Result<()> {
|
||||
backend.expunge_folder(folder).await?;
|
||||
printer.print(format!("Folder {folder} successfully expunged!"))
|
||||
}
|
||||
|
||||
pub async fn list<P: Printer>(
|
||||
config: &AccountConfig,
|
||||
printer: &mut P,
|
||||
backend: &Backend,
|
||||
max_width: Option<usize>,
|
||||
) -> Result<()> {
|
||||
let folders: Folders = backend.list_folders().await?.into();
|
||||
printer.print_table(
|
||||
// TODO: remove Box
|
||||
Box::new(folders),
|
||||
PrintTableOpts {
|
||||
format: &config.email_reading_format,
|
||||
max_width,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn create<P: Printer>(printer: &mut P, backend: &Backend, folder: &str) -> Result<()> {
|
||||
backend.add_folder(folder).await?;
|
||||
printer.print("Folder successfully created!")
|
||||
}
|
||||
|
||||
pub async fn delete<P: Printer>(printer: &mut P, backend: &Backend, folder: &str) -> Result<()> {
|
||||
if let Some(false) | None = Confirm::new()
|
||||
.with_prompt(format!("Confirm deletion of folder {folder}?"))
|
||||
.default(false)
|
||||
.report(false)
|
||||
.interact_opt()?
|
||||
{
|
||||
process::exit(0);
|
||||
};
|
||||
|
||||
backend.delete_folder(folder).await?;
|
||||
printer.print("Folder successfully deleted!")
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use async_trait::async_trait;
|
||||
use email::{
|
||||
account::config::AccountConfig,
|
||||
backend::Backend,
|
||||
envelope::{Envelope, Envelopes},
|
||||
flag::Flags,
|
||||
folder::{Folder, Folders},
|
||||
message::Messages,
|
||||
};
|
||||
use std::{any::Any, fmt::Debug, io};
|
||||
use termcolor::ColorSpec;
|
||||
|
||||
use crate::printer::{Print, PrintTable, WriteColor};
|
||||
|
||||
use super::*;
|
||||
|
||||
#[tokio::test]
|
||||
async fn it_should_list_mboxes() {
|
||||
#[derive(Debug, Default, Clone)]
|
||||
struct StringWriter {
|
||||
content: String,
|
||||
}
|
||||
|
||||
impl io::Write for StringWriter {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.content
|
||||
.push_str(&String::from_utf8(buf.to_vec()).unwrap());
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
self.content = String::default();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl termcolor::WriteColor for StringWriter {
|
||||
fn supports_color(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn set_color(&mut self, _spec: &ColorSpec) -> io::Result<()> {
|
||||
io::Result::Ok(())
|
||||
}
|
||||
|
||||
fn reset(&mut self) -> io::Result<()> {
|
||||
io::Result::Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl WriteColor for StringWriter {}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct PrinterServiceTest {
|
||||
pub writer: StringWriter,
|
||||
}
|
||||
|
||||
impl Printer for PrinterServiceTest {
|
||||
fn print_table<T: Debug + PrintTable + erased_serde::Serialize + ?Sized>(
|
||||
&mut self,
|
||||
data: Box<T>,
|
||||
opts: PrintTableOpts,
|
||||
) -> anyhow::Result<()> {
|
||||
data.print_table(&mut self.writer, opts)?;
|
||||
Ok(())
|
||||
}
|
||||
fn print_log<T: Debug + Print>(&mut self, _data: T) -> anyhow::Result<()> {
|
||||
unimplemented!()
|
||||
}
|
||||
fn print<T: Debug + Print + serde::Serialize>(
|
||||
&mut self,
|
||||
_data: T,
|
||||
) -> anyhow::Result<()> {
|
||||
unimplemented!()
|
||||
}
|
||||
fn is_json(&self) -> bool {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
struct TestBackend;
|
||||
|
||||
#[async_trait]
|
||||
impl Backend for TestBackend {
|
||||
fn name(&self) -> String {
|
||||
unimplemented!();
|
||||
}
|
||||
async fn add_folder(&mut self, _: &str) -> email::Result<()> {
|
||||
unimplemented!();
|
||||
}
|
||||
async fn list_folders(&mut self) -> email::Result<Folders> {
|
||||
Ok(Folders::from_iter([
|
||||
Folder {
|
||||
name: "INBOX".into(),
|
||||
desc: "desc".into(),
|
||||
},
|
||||
Folder {
|
||||
name: "Sent".into(),
|
||||
desc: "desc".into(),
|
||||
},
|
||||
]))
|
||||
}
|
||||
async fn expunge_folder(&mut self, _: &str) -> email::Result<()> {
|
||||
unimplemented!();
|
||||
}
|
||||
async fn purge_folder(&mut self, _: &str) -> email::Result<()> {
|
||||
unimplemented!();
|
||||
}
|
||||
async fn delete_folder(&mut self, _: &str) -> email::Result<()> {
|
||||
unimplemented!();
|
||||
}
|
||||
async fn get_envelope(&mut self, _: &str, _: &str) -> email::Result<Envelope> {
|
||||
unimplemented!();
|
||||
}
|
||||
async fn list_envelopes(
|
||||
&mut self,
|
||||
_: &str,
|
||||
_: usize,
|
||||
_: usize,
|
||||
) -> email::Result<Envelopes> {
|
||||
unimplemented!()
|
||||
}
|
||||
async fn search_envelopes(
|
||||
&mut self,
|
||||
_: &str,
|
||||
_: &str,
|
||||
_: &str,
|
||||
_: usize,
|
||||
_: usize,
|
||||
) -> email::Result<Envelopes> {
|
||||
unimplemented!()
|
||||
}
|
||||
async fn add_email(&mut self, _: &str, _: &[u8], _: &Flags) -> email::Result<String> {
|
||||
unimplemented!()
|
||||
}
|
||||
async fn get_emails(&mut self, _: &str, _: Vec<&str>) -> email::Result<Messages> {
|
||||
unimplemented!()
|
||||
}
|
||||
async fn preview_emails(&mut self, _: &str, _: Vec<&str>) -> email::Result<Messages> {
|
||||
unimplemented!()
|
||||
}
|
||||
async fn copy_emails(&mut self, _: &str, _: &str, _: Vec<&str>) -> email::Result<()> {
|
||||
unimplemented!()
|
||||
}
|
||||
async fn move_emails(&mut self, _: &str, _: &str, _: Vec<&str>) -> email::Result<()> {
|
||||
unimplemented!()
|
||||
}
|
||||
async fn delete_emails(&mut self, _: &str, _: Vec<&str>) -> email::Result<()> {
|
||||
unimplemented!()
|
||||
}
|
||||
async fn add_flags(&mut self, _: &str, _: Vec<&str>, _: &Flags) -> email::Result<()> {
|
||||
unimplemented!()
|
||||
}
|
||||
async fn set_flags(&mut self, _: &str, _: Vec<&str>, _: &Flags) -> email::Result<()> {
|
||||
unimplemented!()
|
||||
}
|
||||
async fn remove_flags(
|
||||
&mut self,
|
||||
_: &str,
|
||||
_: Vec<&str>,
|
||||
_: &Flags,
|
||||
) -> email::Result<()> {
|
||||
unimplemented!()
|
||||
}
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
let account_config = AccountConfig::default();
|
||||
let mut printer = PrinterServiceTest::default();
|
||||
let mut backend = TestBackend {};
|
||||
|
||||
assert!(list(&account_config, &mut printer, &mut backend, None)
|
||||
.await
|
||||
.is_ok());
|
||||
assert_eq!(
|
||||
concat![
|
||||
"\n",
|
||||
"NAME │DESC \n",
|
||||
"INBOX │desc \n",
|
||||
"Sent │desc \n",
|
||||
"\n"
|
||||
],
|
||||
printer.writer.content
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,6 @@
|
||||
pub mod arg;
|
||||
pub mod args;
|
||||
pub mod command;
|
||||
pub mod config;
|
||||
pub mod handlers;
|
||||
|
||||
use anyhow::Result;
|
||||
use serde::Serialize;
|
||||
|
||||
Reference in New Issue
Block a user