Skip to content

Commit

Permalink
replace ratatui by reedline, init dynamic backend system
Browse files Browse the repository at this point in the history
  • Loading branch information
soywod committed Oct 2, 2024
1 parent 3820797 commit aacb25d
Show file tree
Hide file tree
Showing 10 changed files with 1,422 additions and 520 deletions.
478 changes: 219 additions & 259 deletions Cargo.lock

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,13 @@ pgp-native = ["email-lib/pgp-native", "mml-lib/pgp-native", "pgp"]
[dependencies]
ariadne = "0.2"
async-trait = "0.1"
clap = { version = "4.4", features = ["derive", "env", "wrap_help"] }
color-eyre = "0.6.3"
comfy-table = { version = "7.1", default-features = false }
comfy-table = { version = "7.1" }
crossterm = { version = "0.27", features = ["serde"] }
dirs = "4"
email-lib = { version = "=0.25.0", default-features = false, features = ["derive", "thread", "tracing"] }
email_address = { version = "0.2", optional = true }
fuzzy-matcher = "0.3"
mail-builder = "0.3"
md5 = "0.7"
mml-lib = { version = "=1.0.14", default-features = false, features = ["derive"] }
Expand All @@ -65,7 +65,7 @@ once_cell = "1.16"
petgraph = "0.6"
pimalaya-tui = { version = "=0.1.0", default-features = false, features = ["email", "path", "cli", "config", "tracing"] }
process-lib = { version = "=0.4.2", features = ["derive"] }
ratatui = "=0.28.1"
reedline = "0.35.0"
secret-lib = { version = "=0.4.6", default-features = false, features = ["command", "derive"], optional = true }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
Expand Down
100 changes: 100 additions & 0 deletions src/account/config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
//! Deserialized account config module.
//!
//! This module contains the raw deserialized representation of an
//! account in the accounts section of the user configuration file.
use std::path::PathBuf;

use crossterm::style::Color;
#[cfg(feature = "pgp")]
use email::account::config::pgp::PgpConfig;
#[cfg(feature = "imap")]
use email::imap::config::ImapConfig;
#[cfg(feature = "maildir")]
use email::maildir::config::MaildirConfig;
#[cfg(feature = "notmuch")]
use email::notmuch::config::NotmuchConfig;
#[cfg(feature = "sendmail")]
use email::sendmail::config::SendmailConfig;
#[cfg(feature = "smtp")]
use email::smtp::config::SmtpConfig;
use process::Command;
use serde::{Deserialize, Serialize};

use crate::backend::BackendKind;

// use crate::{
// backend::BackendKind, envelope::config::EnvelopeConfig, flag::config::FlagConfig,
// folder::config::FolderConfig, message::config::MessageConfig,
// };

/// Represents all existing kind of account config.
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case", deny_unknown_fields)]
pub struct TomlAccountConfig {
pub default: Option<bool>,
pub email: String,
pub display_name: Option<String>,
pub signature: Option<String>,
pub signature_delim: Option<String>,
pub downloads_dir: Option<PathBuf>,
pub backend: Option<BackendKind>,

#[cfg(feature = "pgp")]
pub pgp: Option<PgpConfig>,

// pub folder: Option<FolderConfig>,
// pub envelope: Option<EnvelopeConfig>,
// pub flag: Option<FlagConfig>,
pub message: Option<MessageConfig>,
// pub template: Option<TemplateConfig>,
#[cfg(feature = "imap")]
pub imap: Option<ImapConfig>,
#[cfg(feature = "maildir")]
pub maildir: Option<MaildirConfig>,
#[cfg(feature = "notmuch")]
pub notmuch: Option<NotmuchConfig>,
#[cfg(feature = "smtp")]
pub smtp: Option<SmtpConfig>,
#[cfg(feature = "sendmail")]
pub sendmail: Option<SendmailConfig>,
}

#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
pub struct MessageConfig {
pub send: Option<MessageSendConfig>,
}

#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
pub struct MessageSendConfig {
pub backend: Option<BackendKind>,
pub save_copy: Option<bool>,
pub pre_hook: Option<Command>,
}

#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
pub struct ListAccountsTableConfig {
pub preset: Option<String>,
pub name_color: Option<Color>,
pub backends_color: Option<Color>,
pub default_color: Option<Color>,
}

// impl ListAccountsTableConfig {
// 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::Green))
// }

// pub fn backends_color(&self) -> comfy_table::Color {
// map_color(self.backends_color.unwrap_or(Color::Blue))
// }

// pub fn default_color(&self) -> comfy_table::Color {
// map_color(self.default_color.unwrap_or(Color::Reset))
// }
// }
1 change: 1 addition & 0 deletions src/account/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod config;
178 changes: 178 additions & 0 deletions src/backend.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
use async_trait::async_trait;
#[cfg(feature = "imap")]
use email::imap::{config::ImapConfig, ImapContext, ImapContextBuilder};
#[cfg(feature = "maildir")]
use email::maildir::{config::MaildirConfig, MaildirContextBuilder, MaildirContextSync};
#[cfg(feature = "notmuch")]
use email::notmuch::{config::NotmuchConfig, NotmuchContextBuilder, NotmuchContextSync};
#[cfg(feature = "sendmail")]
use email::sendmail::{config::SendmailConfig, SendmailContextBuilder, SendmailContextSync};
#[cfg(feature = "smtp")]
use email::smtp::{config::SmtpConfig, SmtpContextBuilder, SmtpContextSync};
use email::{
backend::{
context::BackendContextBuilder, feature::BackendFeature, macros::BackendContext,
mapper::SomeBackendContextBuilderMapper,
},
folder::list::ListFolders,
AnyResult,
};
use serde::{Deserialize, Serialize};

#[derive(Clone, Debug, Eq, PartialEq)]
pub enum BackendConfig {
#[cfg(feature = "imap")]
Imap(ImapConfig),
#[cfg(feature = "maildir")]
Maildir(MaildirConfig),
#[cfg(feature = "notmuch")]
Notmuch(NotmuchConfig),
#[cfg(feature = "smtp")]
Smtp(SmtpConfig),
#[cfg(feature = "sendmail")]
Sendmail(SendmailConfig),
}

#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum BackendKind {
None,
#[cfg(feature = "imap")]
Imap,
#[cfg(feature = "maildir")]
Maildir,
#[cfg(feature = "notmuch")]
Notmuch,
#[cfg(feature = "smtp")]
Smtp,
#[cfg(feature = "sendmail")]
Sendmail,
}

#[derive(BackendContext)]
pub struct Context {
#[cfg(feature = "imap")]
imap: Option<ImapContext>,
#[cfg(feature = "maildir")]
maildir: Option<MaildirContextSync>,
#[cfg(feature = "notmuch")]
notmuch: Option<NotmuchContextSync>,
#[cfg(feature = "smtp")]
smtp: Option<SmtpContextSync>,
#[cfg(feature = "sendmail")]
sendmail: Option<SendmailContextSync>,
}

#[cfg(feature = "imap")]
impl AsRef<Option<ImapContext>> for Context {
fn as_ref(&self) -> &Option<ImapContext> {
&self.imap
}
}

#[cfg(feature = "maildir")]
impl AsRef<Option<MaildirContextSync>> for Context {
fn as_ref(&self) -> &Option<MaildirContextSync> {
&self.maildir
}
}

#[cfg(feature = "notmuch")]
impl AsRef<Option<NotmuchContextSync>> for Context {
fn as_ref(&self) -> &Option<NotmuchContextSync> {
&self.notmuch
}
}

#[cfg(feature = "smtp")]
impl AsRef<Option<SmtpContextSync>> for Context {
fn as_ref(&self) -> &Option<SmtpContextSync> {
&self.smtp
}
}

#[cfg(feature = "sendmail")]
impl AsRef<Option<SendmailContextSync>> for Context {
fn as_ref(&self) -> &Option<SendmailContextSync> {
&self.sendmail
}
}

#[derive(Clone)]
pub struct ContextBuilder {
pub backend: BackendKind,
pub sending_backend: BackendKind,

#[cfg(feature = "imap")]
pub imap: Option<ImapContextBuilder>,
#[cfg(feature = "maildir")]
pub maildir: Option<MaildirContextBuilder>,
#[cfg(feature = "notmuch")]
pub notmuch: Option<NotmuchContextBuilder>,
#[cfg(feature = "sendmail")]
pub sendmail: Option<SendmailContextBuilder>,
#[cfg(feature = "smtp")]
pub smtp: Option<SmtpContextBuilder>,
}

#[async_trait]
impl BackendContextBuilder for ContextBuilder {
type Context = Context;

fn list_folders(&self) -> Option<BackendFeature<Self::Context, dyn ListFolders>> {
match self.backend {
#[cfg(feature = "imap")]
BackendKind::Imap => self.list_folders_with_some(&self.imap),
#[cfg(feature = "maildir")]
BackendKind::Maildir => self.list_folders_with_some(&self.maildir),
#[cfg(feature = "notmuch")]
BackendKind::Notmuch => self.list_folders_with_some(&self.notmuch),
_ => None,
}
}

async fn build(self) -> AnyResult<Self::Context> {
#[cfg(feature = "imap")]
let imap = match self.imap {
Some(imap) => Some(imap.build().await?),
None => None,
};

#[cfg(feature = "maildir")]
let maildir = match self.maildir {
Some(maildir) => Some(maildir.build().await?),
None => None,
};

#[cfg(feature = "notmuch")]
let notmuch = match self.notmuch {
Some(notmuch) => Some(notmuch.build().await?),
None => None,
};

#[cfg(feature = "smtp")]
let smtp = match self.smtp {
Some(smtp) => Some(smtp.build().await?),
None => None,
};

#[cfg(feature = "sendmail")]
let sendmail = match self.sendmail {
Some(sendmail) => Some(sendmail.build().await?),
None => None,
};

Ok(Context {
#[cfg(feature = "imap")]
imap,
#[cfg(feature = "maildir")]
maildir,
#[cfg(feature = "notmuch")]
notmuch,
#[cfg(feature = "smtp")]
smtp,
#[cfg(feature = "sendmail")]
sendmail,
})
}
}
35 changes: 35 additions & 0 deletions src/cli.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
use std::path::PathBuf;

use clap::Parser;
use pimalaya_tui::cli::arg::path_parser;

#[derive(Parser, Debug)]
#[command(name = "himalaya", author, version, about)]
#[command(propagate_version = true, infer_subcommands = true)]
pub struct Cli {
/// Override the default configuration file path.
///
/// The given paths are shell-expanded then canonicalized (if
/// applicable). If the first path does not point to a valid file,
/// the wizard will propose to assist you in the creation of the
/// configuration file. Other paths are merged with the first one,
/// which allows you to separate your public config from your
/// private(s) one(s).
#[arg(short, long = "config", global = true, env = "HIMALAYA_CONFIG")]
#[arg(value_name = "PATH", value_parser = path_parser)]
pub config_paths: Vec<PathBuf>,

/// Enable logs with spantrace.
///
/// This is the same as running the command with `RUST_LOG=debug`
/// environment variable.
#[arg(long, global = true, conflicts_with = "trace")]
pub debug: bool,

/// Enable verbose logs with backtrace.
///
/// This is the same as running the command with `RUST_LOG=trace`
/// and `RUST_BACKTRACE=1` environment variables.
#[arg(long, global = true, conflicts_with = "debug")]
pub trace: bool,
}
Loading

0 comments on commit aacb25d

Please sign in to comment.