mirror of
https://github.com/pimalaya/himalaya.git
synced 2026-06-18 05:47:54 +08:00
wip: use shared stuff from pimalaya-tui
This commit is contained in:
+19
-11
@@ -1,11 +1,16 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use clap::Parser;
|
||||
use color_eyre::Result;
|
||||
use email::{backend::feature::BackendFeatureSource, folder::add::AddFolder};
|
||||
use pimalaya_tui::{
|
||||
himalaya::backend::BackendBuilder,
|
||||
terminal::{cli::printer::Printer, config::TomlConfig as _},
|
||||
};
|
||||
use tracing::info;
|
||||
|
||||
use crate::{
|
||||
account::arg::name::AccountNameFlag, backend::Backend, config::Config,
|
||||
folder::arg::name::FolderNameArg, printer::Printer,
|
||||
account::arg::name::AccountNameFlag, config::TomlConfig, folder::arg::name::FolderNameArg,
|
||||
};
|
||||
|
||||
/// Create a new folder.
|
||||
@@ -22,26 +27,29 @@ pub struct AddFolderCommand {
|
||||
}
|
||||
|
||||
impl AddFolderCommand {
|
||||
pub async fn execute(self, printer: &mut impl Printer, config: &Config) -> Result<()> {
|
||||
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> {
|
||||
info!("executing create folder command");
|
||||
|
||||
let folder = &self.folder.name;
|
||||
|
||||
let (toml_account_config, account_config) = config
|
||||
.clone()
|
||||
.into_account_configs(self.account.name.as_deref())?;
|
||||
|
||||
let add_folder_kind = toml_account_config.add_folder_kind();
|
||||
|
||||
let backend = Backend::new(
|
||||
toml_account_config.clone(),
|
||||
account_config,
|
||||
add_folder_kind,
|
||||
|builder| builder.set_add_folder(BackendFeatureSource::Context),
|
||||
let backend = BackendBuilder::new(
|
||||
Arc::new(toml_account_config),
|
||||
Arc::new(account_config),
|
||||
|builder| {
|
||||
builder
|
||||
.without_features()
|
||||
.with_add_folder(BackendFeatureSource::Context)
|
||||
},
|
||||
)
|
||||
.build()
|
||||
.await?;
|
||||
|
||||
backend.add_folder(folder).await?;
|
||||
|
||||
printer.log(format!("Folder {folder} successfully created!"))
|
||||
printer.out(format!("Folder {folder} successfully created!\n"))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
use std::process;
|
||||
use std::{process, sync::Arc};
|
||||
|
||||
use clap::Parser;
|
||||
use color_eyre::Result;
|
||||
use email::{backend::feature::BackendFeatureSource, folder::delete::DeleteFolder};
|
||||
use pimalaya_tui::prompt;
|
||||
use pimalaya_tui::{
|
||||
himalaya::backend::BackendBuilder,
|
||||
terminal::{cli::printer::Printer, config::TomlConfig as _, prompt},
|
||||
};
|
||||
use tracing::info;
|
||||
|
||||
use crate::{
|
||||
account::arg::name::AccountNameFlag, backend::Backend, config::Config,
|
||||
folder::arg::name::FolderNameArg, printer::Printer,
|
||||
account::arg::name::AccountNameFlag, config::TomlConfig, folder::arg::name::FolderNameArg,
|
||||
};
|
||||
|
||||
/// Delete a folder.
|
||||
@@ -25,12 +27,13 @@ pub struct FolderDeleteCommand {
|
||||
}
|
||||
|
||||
impl FolderDeleteCommand {
|
||||
pub async fn execute(self, printer: &mut impl Printer, config: &Config) -> Result<()> {
|
||||
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> {
|
||||
info!("executing delete folder command");
|
||||
|
||||
let folder = &self.folder.name;
|
||||
|
||||
let confirm = format!("Do you really want to delete the folder {folder}? All emails will be definitely deleted.");
|
||||
let confirm = format!("Do you really want to delete the folder {folder}");
|
||||
let confirm = format!("{confirm}? All emails will be definitely deleted.");
|
||||
|
||||
if !prompt::bool(confirm, false)? {
|
||||
process::exit(0);
|
||||
@@ -40,18 +43,20 @@ impl FolderDeleteCommand {
|
||||
.clone()
|
||||
.into_account_configs(self.account.name.as_deref())?;
|
||||
|
||||
let delete_folder_kind = toml_account_config.delete_folder_kind();
|
||||
|
||||
let backend = Backend::new(
|
||||
toml_account_config.clone(),
|
||||
account_config,
|
||||
delete_folder_kind,
|
||||
|builder| builder.set_delete_folder(BackendFeatureSource::Context),
|
||||
let backend = BackendBuilder::new(
|
||||
Arc::new(toml_account_config),
|
||||
Arc::new(account_config),
|
||||
|builder| {
|
||||
builder
|
||||
.without_features()
|
||||
.with_delete_folder(BackendFeatureSource::Context)
|
||||
},
|
||||
)
|
||||
.build()
|
||||
.await?;
|
||||
|
||||
backend.delete_folder(folder).await?;
|
||||
|
||||
printer.log(format!("Folder {folder} successfully deleted!"))
|
||||
printer.out(format!("Folder {folder} successfully deleted!\n"))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use clap::Parser;
|
||||
use color_eyre::Result;
|
||||
use email::{backend::feature::BackendFeatureSource, folder::expunge::ExpungeFolder};
|
||||
use pimalaya_tui::{
|
||||
himalaya::backend::BackendBuilder,
|
||||
terminal::{cli::printer::Printer, config::TomlConfig as _},
|
||||
};
|
||||
use tracing::info;
|
||||
|
||||
use crate::{
|
||||
account::arg::name::AccountNameFlag, backend::Backend, config::Config,
|
||||
folder::arg::name::FolderNameArg, printer::Printer,
|
||||
account::arg::name::AccountNameFlag, config::TomlConfig, folder::arg::name::FolderNameArg,
|
||||
};
|
||||
|
||||
/// Expunge a folder.
|
||||
@@ -23,7 +28,7 @@ pub struct FolderExpungeCommand {
|
||||
}
|
||||
|
||||
impl FolderExpungeCommand {
|
||||
pub async fn execute(self, printer: &mut impl Printer, config: &Config) -> Result<()> {
|
||||
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> {
|
||||
info!("executing expunge folder command");
|
||||
|
||||
let folder = &self.folder.name;
|
||||
@@ -31,18 +36,20 @@ impl FolderExpungeCommand {
|
||||
.clone()
|
||||
.into_account_configs(self.account.name.as_deref())?;
|
||||
|
||||
let expunge_folder_kind = toml_account_config.expunge_folder_kind();
|
||||
|
||||
let backend = Backend::new(
|
||||
toml_account_config.clone(),
|
||||
account_config,
|
||||
expunge_folder_kind,
|
||||
|builder| builder.set_expunge_folder(BackendFeatureSource::Context),
|
||||
let backend = BackendBuilder::new(
|
||||
Arc::new(toml_account_config),
|
||||
Arc::new(account_config),
|
||||
|builder| {
|
||||
builder
|
||||
.without_features()
|
||||
.with_expunge_folder(BackendFeatureSource::Context)
|
||||
},
|
||||
)
|
||||
.build()
|
||||
.await?;
|
||||
|
||||
backend.expunge_folder(folder).await?;
|
||||
|
||||
printer.log(format!("Folder {folder} successfully expunged!"))
|
||||
printer.out(format!("Folder {folder} successfully expunged!\n"))
|
||||
}
|
||||
}
|
||||
|
||||
+20
-13
@@ -1,15 +1,18 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use clap::Parser;
|
||||
use color_eyre::Result;
|
||||
use email::{backend::feature::BackendFeatureSource, folder::list::ListFolders};
|
||||
use pimalaya_tui::{
|
||||
himalaya::{
|
||||
backend::BackendBuilder,
|
||||
config::{Folders, FoldersTable},
|
||||
},
|
||||
terminal::{cli::printer::Printer, config::TomlConfig as _},
|
||||
};
|
||||
use tracing::info;
|
||||
|
||||
use crate::{
|
||||
account::arg::name::AccountNameFlag,
|
||||
backend::Backend,
|
||||
config::Config,
|
||||
folder::{Folders, FoldersTable},
|
||||
printer::Printer,
|
||||
};
|
||||
use crate::{account::arg::name::AccountNameFlag, config::TomlConfig};
|
||||
|
||||
/// List all folders.
|
||||
///
|
||||
@@ -29,21 +32,25 @@ pub struct FolderListCommand {
|
||||
}
|
||||
|
||||
impl FolderListCommand {
|
||||
pub async fn execute(self, printer: &mut impl Printer, config: &Config) -> Result<()> {
|
||||
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> {
|
||||
info!("executing list folders command");
|
||||
|
||||
let (toml_account_config, account_config) = config
|
||||
.clone()
|
||||
.into_account_configs(self.account.name.as_deref())?;
|
||||
|
||||
let list_folders_kind = toml_account_config.list_folders_kind();
|
||||
let toml_account_config = Arc::new(toml_account_config);
|
||||
|
||||
let backend = Backend::new(
|
||||
let backend = BackendBuilder::new(
|
||||
toml_account_config.clone(),
|
||||
account_config.clone(),
|
||||
list_folders_kind,
|
||||
|builder| builder.set_list_folders(BackendFeatureSource::Context),
|
||||
Arc::new(account_config),
|
||||
|builder| {
|
||||
builder
|
||||
.without_features()
|
||||
.with_list_folders(BackendFeatureSource::Context)
|
||||
},
|
||||
)
|
||||
.build()
|
||||
.await?;
|
||||
|
||||
let folders = Folders::from(backend.list_folders().await?);
|
||||
|
||||
@@ -6,8 +6,9 @@ mod purge;
|
||||
|
||||
use clap::Subcommand;
|
||||
use color_eyre::Result;
|
||||
use pimalaya_tui::terminal::cli::printer::Printer;
|
||||
|
||||
use crate::{config::Config, printer::Printer};
|
||||
use crate::config::TomlConfig;
|
||||
|
||||
use self::{
|
||||
add::AddFolderCommand, delete::FolderDeleteCommand, expunge::FolderExpungeCommand,
|
||||
@@ -38,7 +39,7 @@ pub enum FolderSubcommand {
|
||||
|
||||
impl FolderSubcommand {
|
||||
#[allow(unused)]
|
||||
pub async fn execute(self, printer: &mut impl Printer, config: &Config) -> Result<()> {
|
||||
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> {
|
||||
match self {
|
||||
Self::Add(cmd) => cmd.execute(printer, config).await,
|
||||
Self::List(cmd) => cmd.execute(printer, config).await,
|
||||
|
||||
+19
-14
@@ -1,14 +1,16 @@
|
||||
use std::process;
|
||||
use std::{process, sync::Arc};
|
||||
|
||||
use clap::Parser;
|
||||
use color_eyre::Result;
|
||||
use email::{backend::feature::BackendFeatureSource, folder::purge::PurgeFolder};
|
||||
use pimalaya_tui::prompt;
|
||||
use pimalaya_tui::{
|
||||
himalaya::backend::BackendBuilder,
|
||||
terminal::{cli::printer::Printer, config::TomlConfig as _, prompt},
|
||||
};
|
||||
use tracing::info;
|
||||
|
||||
use crate::{
|
||||
account::arg::name::AccountNameFlag, backend::Backend, config::Config,
|
||||
folder::arg::name::FolderNameArg, printer::Printer,
|
||||
account::arg::name::AccountNameFlag, config::TomlConfig, folder::arg::name::FolderNameArg,
|
||||
};
|
||||
|
||||
/// Purge a folder.
|
||||
@@ -25,12 +27,13 @@ pub struct FolderPurgeCommand {
|
||||
}
|
||||
|
||||
impl FolderPurgeCommand {
|
||||
pub async fn execute(self, printer: &mut impl Printer, config: &Config) -> Result<()> {
|
||||
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> {
|
||||
info!("executing purge folder command");
|
||||
|
||||
let folder = &self.folder.name;
|
||||
|
||||
let confirm = format!("Do you really want to purge the folder {folder}? All emails will be definitely deleted.");
|
||||
let confirm = format!("Do you really want to purge the folder {folder}");
|
||||
let confirm = format!("{confirm}? All emails will be definitely deleted.");
|
||||
|
||||
if !prompt::bool(confirm, false)? {
|
||||
process::exit(0);
|
||||
@@ -40,18 +43,20 @@ impl FolderPurgeCommand {
|
||||
.clone()
|
||||
.into_account_configs(self.account.name.as_deref())?;
|
||||
|
||||
let purge_folder_kind = toml_account_config.purge_folder_kind();
|
||||
|
||||
let backend = Backend::new(
|
||||
toml_account_config.clone(),
|
||||
account_config,
|
||||
purge_folder_kind,
|
||||
|builder| builder.set_purge_folder(BackendFeatureSource::Context),
|
||||
let backend = BackendBuilder::new(
|
||||
Arc::new(toml_account_config),
|
||||
Arc::new(account_config),
|
||||
|builder| {
|
||||
builder
|
||||
.without_features()
|
||||
.with_purge_folder(BackendFeatureSource::Context)
|
||||
},
|
||||
)
|
||||
.build()
|
||||
.await?;
|
||||
|
||||
backend.purge_folder(folder).await?;
|
||||
|
||||
printer.log(format!("Folder {folder} successfully purged!"))
|
||||
printer.out(format!("Folder {folder} successfully purged!\n"))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,157 +0,0 @@
|
||||
use comfy_table::presets;
|
||||
use crossterm::style::Color;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
use crate::{backend::BackendKind, ui::map_color};
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
|
||||
pub struct FolderConfig {
|
||||
#[serde(alias = "aliases")]
|
||||
pub alias: Option<HashMap<String, String>>,
|
||||
pub add: Option<FolderAddConfig>,
|
||||
pub list: Option<FolderListConfig>,
|
||||
pub expunge: Option<FolderExpungeConfig>,
|
||||
pub purge: Option<FolderPurgeConfig>,
|
||||
pub delete: Option<FolderDeleteConfig>,
|
||||
}
|
||||
|
||||
impl FolderConfig {
|
||||
pub fn get_used_backends(&self) -> HashSet<&BackendKind> {
|
||||
let mut kinds = HashSet::default();
|
||||
|
||||
if let Some(add) = &self.add {
|
||||
kinds.extend(add.get_used_backends());
|
||||
}
|
||||
|
||||
if let Some(list) = &self.list {
|
||||
kinds.extend(list.get_used_backends());
|
||||
}
|
||||
|
||||
if let Some(expunge) = &self.expunge {
|
||||
kinds.extend(expunge.get_used_backends());
|
||||
}
|
||||
|
||||
if let Some(purge) = &self.purge {
|
||||
kinds.extend(purge.get_used_backends());
|
||||
}
|
||||
|
||||
if let Some(delete) = &self.delete {
|
||||
kinds.extend(delete.get_used_backends());
|
||||
}
|
||||
|
||||
kinds
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
|
||||
pub struct FolderAddConfig {
|
||||
pub backend: Option<BackendKind>,
|
||||
}
|
||||
|
||||
impl FolderAddConfig {
|
||||
pub fn get_used_backends(&self) -> HashSet<&BackendKind> {
|
||||
let mut kinds = HashSet::default();
|
||||
|
||||
if let Some(kind) = &self.backend {
|
||||
kinds.insert(kind);
|
||||
}
|
||||
|
||||
kinds
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub struct FolderListConfig {
|
||||
pub backend: Option<BackendKind>,
|
||||
pub table: Option<ListFoldersTableConfig>,
|
||||
|
||||
#[serde(flatten)]
|
||||
pub remote: email::folder::list::config::FolderListConfig,
|
||||
}
|
||||
|
||||
impl FolderListConfig {
|
||||
pub fn get_used_backends(&self) -> HashSet<&BackendKind> {
|
||||
let mut kinds = HashSet::default();
|
||||
|
||||
if let Some(kind) = &self.backend {
|
||||
kinds.insert(kind);
|
||||
}
|
||||
|
||||
kinds
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub struct ListFoldersTableConfig {
|
||||
pub preset: Option<String>,
|
||||
pub name_color: Option<Color>,
|
||||
pub desc_color: Option<Color>,
|
||||
}
|
||||
|
||||
impl ListFoldersTableConfig {
|
||||
pub fn preset(&self) -> &str {
|
||||
self.preset.as_deref().unwrap_or(presets::ASCII_MARKDOWN)
|
||||
}
|
||||
|
||||
pub fn name_color(&self) -> comfy_table::Color {
|
||||
map_color(self.name_color.unwrap_or(Color::Blue))
|
||||
}
|
||||
|
||||
pub fn desc_color(&self) -> comfy_table::Color {
|
||||
map_color(self.desc_color.unwrap_or(Color::Green))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
|
||||
pub struct FolderExpungeConfig {
|
||||
pub backend: Option<BackendKind>,
|
||||
}
|
||||
|
||||
impl FolderExpungeConfig {
|
||||
pub fn get_used_backends(&self) -> HashSet<&BackendKind> {
|
||||
let mut kinds = HashSet::default();
|
||||
|
||||
if let Some(kind) = &self.backend {
|
||||
kinds.insert(kind);
|
||||
}
|
||||
|
||||
kinds
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
|
||||
pub struct FolderPurgeConfig {
|
||||
pub backend: Option<BackendKind>,
|
||||
}
|
||||
|
||||
impl FolderPurgeConfig {
|
||||
pub fn get_used_backends(&self) -> HashSet<&BackendKind> {
|
||||
let mut kinds = HashSet::default();
|
||||
|
||||
if let Some(kind) = &self.backend {
|
||||
kinds.insert(kind);
|
||||
}
|
||||
|
||||
kinds
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
|
||||
pub struct FolderDeleteConfig {
|
||||
pub backend: Option<BackendKind>,
|
||||
}
|
||||
|
||||
impl FolderDeleteConfig {
|
||||
pub fn get_used_backends(&self) -> HashSet<&BackendKind> {
|
||||
let mut kinds = HashSet::default();
|
||||
|
||||
if let Some(kind) = &self.backend {
|
||||
kinds.insert(kind);
|
||||
}
|
||||
|
||||
kinds
|
||||
}
|
||||
}
|
||||
@@ -1,126 +1,2 @@
|
||||
pub mod arg;
|
||||
pub mod command;
|
||||
pub mod config;
|
||||
|
||||
use comfy_table::{Cell, ContentArrangement, Row, Table};
|
||||
use crossterm::style::Color;
|
||||
use serde::{Serialize, Serializer};
|
||||
use std::{fmt, ops::Deref};
|
||||
|
||||
use self::config::ListFoldersTableConfig;
|
||||
|
||||
#[derive(Clone, Debug, Default, Serialize)]
|
||||
pub struct Folder {
|
||||
pub name: String,
|
||||
pub desc: String,
|
||||
}
|
||||
|
||||
impl Folder {
|
||||
pub fn to_row(&self, config: &ListFoldersTableConfig) -> Row {
|
||||
let mut row = Row::new();
|
||||
row.max_height(1);
|
||||
|
||||
row.add_cell(Cell::new(&self.name).fg(config.name_color()));
|
||||
row.add_cell(Cell::new(&self.desc).fg(config.desc_color()));
|
||||
|
||||
row
|
||||
}
|
||||
}
|
||||
|
||||
impl From<email::folder::Folder> for Folder {
|
||||
fn from(folder: email::folder::Folder) -> Self {
|
||||
Folder {
|
||||
name: folder.name,
|
||||
desc: folder.desc,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Serialize)]
|
||||
pub struct Folders(Vec<Folder>);
|
||||
|
||||
impl Deref for Folders {
|
||||
type Target = Vec<Folder>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<email::folder::Folders> for Folders {
|
||||
fn from(folders: email::folder::Folders) -> Self {
|
||||
Folders(folders.into_iter().map(Folder::from).collect())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct FoldersTable {
|
||||
folders: Folders,
|
||||
width: Option<u16>,
|
||||
config: ListFoldersTableConfig,
|
||||
}
|
||||
|
||||
impl FoldersTable {
|
||||
pub fn with_some_width(mut self, width: Option<u16>) -> Self {
|
||||
self.width = width;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_some_preset(mut self, preset: Option<String>) -> Self {
|
||||
self.config.preset = preset;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_some_name_color(mut self, color: Option<Color>) -> Self {
|
||||
self.config.name_color = color;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_some_desc_color(mut self, color: Option<Color>) -> Self {
|
||||
self.config.desc_color = color;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Folders> for FoldersTable {
|
||||
fn from(folders: Folders) -> Self {
|
||||
Self {
|
||||
folders,
|
||||
width: None,
|
||||
config: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for FoldersTable {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let mut table = Table::new();
|
||||
|
||||
table
|
||||
.load_preset(self.config.preset())
|
||||
.set_content_arrangement(ContentArrangement::DynamicFullWidth)
|
||||
.set_header(Row::from([Cell::new("NAME"), Cell::new("DESC")]))
|
||||
.add_rows(
|
||||
self.folders
|
||||
.iter()
|
||||
.map(|folder| folder.to_row(&self.config)),
|
||||
);
|
||||
|
||||
if let Some(width) = self.width {
|
||||
table.set_width(width);
|
||||
}
|
||||
|
||||
writeln!(f)?;
|
||||
write!(f, "{table}")?;
|
||||
writeln!(f)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for FoldersTable {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
self.folders.serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user