mirror of
https://github.com/pimalaya/himalaya.git
synced 2026-06-16 04:17:56 +08:00
repair mailbox table, add unix stream support
This commit is contained in:
@@ -1,8 +1,13 @@
|
||||
use std::{fmt, ops::Deref};
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
use clap::Parser;
|
||||
use comfy_table::{presets, Cell, ContentArrangement, Row, Table};
|
||||
use crossterm::style::Color;
|
||||
use io_imap::coroutines::list::*;
|
||||
use io_stream::runtimes::std::handle;
|
||||
use pimalaya_toolbox::terminal::printer::Printer;
|
||||
use serde::{Serialize, Serializer};
|
||||
|
||||
use crate::{config::ImapConfig, imap::stream};
|
||||
|
||||
@@ -23,7 +28,7 @@ pub struct ListMailboxesCommand {
|
||||
}
|
||||
|
||||
impl ListMailboxesCommand {
|
||||
pub fn execute(self, _printer: &mut impl Printer, config: ImapConfig) -> Result<()> {
|
||||
pub fn execute(self, printer: &mut impl Printer, config: ImapConfig) -> Result<()> {
|
||||
let (context, mut stream) = stream::connect(config)?;
|
||||
|
||||
let mut arg = None;
|
||||
@@ -37,109 +42,186 @@ impl ListMailboxesCommand {
|
||||
}
|
||||
};
|
||||
|
||||
println!("mailboxes: {mailboxes:#?}");
|
||||
let table = MailboxesTable::from(mailboxes);
|
||||
|
||||
// TODO: list mailboxs
|
||||
|
||||
// let mailboxs = Mailboxs::from(backend.list_mailboxs().await?);
|
||||
// let table = MailboxsTable::from(mailboxs)
|
||||
// .with_some_width(self.table_max_width)
|
||||
// .with_some_preset(toml_account_config.mailbox_list_table_preset())
|
||||
// .with_some_name_color(toml_account_config.mailbox_list_table_name_color())
|
||||
// .with_some_desc_color(toml_account_config.mailbox_list_table_desc_color());
|
||||
|
||||
// printer.out(table)?;
|
||||
printer.out(table)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// #[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
|
||||
// #[serde(rename_all = "kebab-case")]
|
||||
// pub struct ListMailboxesTableConfig {
|
||||
// pub preset: Option<String>,
|
||||
// pub name_color: Option<Color>,
|
||||
// pub desc_color: Option<Color>,
|
||||
// }
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub struct ListMailboxesTableConfig {
|
||||
pub preset: Option<String>,
|
||||
pub name_color: Option<Color>,
|
||||
pub desc_color: Option<Color>,
|
||||
}
|
||||
|
||||
// impl ListMailboxesTableConfig {
|
||||
// pub fn preset(&self) -> &str {
|
||||
// self.preset.as_deref().unwrap_or(presets::ASCII_MARKDOWN)
|
||||
// }
|
||||
impl ListMailboxesTableConfig {
|
||||
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 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))
|
||||
// }
|
||||
// }
|
||||
pub fn desc_color(&self) -> comfy_table::Color {
|
||||
map_color(self.desc_color.unwrap_or(Color::Green))
|
||||
}
|
||||
}
|
||||
|
||||
// pub struct MailboxesTable {
|
||||
// mailboxes: Vec<Mailbox<'static>>,
|
||||
// width: Option<u16>,
|
||||
// config: ListMailboxesTableConfig,
|
||||
// }
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
pub struct Mailbox {
|
||||
pub name: String,
|
||||
pub delimiter: String,
|
||||
pub attributes: Vec<String>,
|
||||
}
|
||||
|
||||
// impl MailboxesTable {
|
||||
// pub fn with_some_width(mut self, width: Option<u16>) -> Self {
|
||||
// self.width = width;
|
||||
// self
|
||||
// }
|
||||
#[derive(Clone, Debug, Default, Serialize)]
|
||||
pub struct Mailboxes(Vec<Mailbox>);
|
||||
|
||||
// pub fn with_some_preset(mut self, preset: Option<String>) -> Self {
|
||||
// self.config.preset = preset;
|
||||
// self
|
||||
// }
|
||||
impl<T: IntoIterator<Item = Mailbox>> From<T> for Mailboxes {
|
||||
fn from(mboxes: T) -> Self {
|
||||
Self(mboxes.into_iter().collect())
|
||||
}
|
||||
}
|
||||
|
||||
// pub fn with_some_name_color(mut self, color: Option<Color>) -> Self {
|
||||
// self.config.name_color = color;
|
||||
// self
|
||||
// }
|
||||
impl Deref for Mailboxes {
|
||||
type Target = Vec<Mailbox>;
|
||||
|
||||
// pub fn with_some_desc_color(mut self, color: Option<Color>) -> Self {
|
||||
// self.config.desc_color = color;
|
||||
// self
|
||||
// }
|
||||
// }
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
// impl From<Mailboxes> for MailboxesTable {
|
||||
// fn from(mailboxes: Mailboxes) -> Self {
|
||||
// Self {
|
||||
// mailboxes,
|
||||
// width: None,
|
||||
// config: Default::default(),
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
pub struct MailboxesTable {
|
||||
mailboxes: Mailboxes,
|
||||
width: Option<u16>,
|
||||
config: ListMailboxesTableConfig,
|
||||
}
|
||||
|
||||
// impl fmt::Display for MailboxesTable {
|
||||
// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
// let mut table = Table::new();
|
||||
impl MailboxesTable {
|
||||
pub fn with_some_width(mut self, width: Option<u16>) -> Self {
|
||||
self.width = width;
|
||||
self
|
||||
}
|
||||
|
||||
// table
|
||||
// .load_preset(self.config.preset())
|
||||
// .set_content_arrangement(ContentArrangement::DynamicFullWidth)
|
||||
// .set_header(Row::from([Cell::new("NAME"), Cell::new("DESC")]))
|
||||
// .add_rows(
|
||||
// self.mailboxes
|
||||
// .iter()
|
||||
// .map(|mailbox| mailbox.to_row(&self.config)),
|
||||
// );
|
||||
pub fn with_some_preset(mut self, preset: Option<String>) -> Self {
|
||||
self.config.preset = preset;
|
||||
self
|
||||
}
|
||||
|
||||
// if let Some(width) = self.width {
|
||||
// table.set_width(width);
|
||||
// }
|
||||
pub fn with_some_name_color(mut self, color: Option<Color>) -> Self {
|
||||
self.config.name_color = color;
|
||||
self
|
||||
}
|
||||
|
||||
// writeln!(f)?;
|
||||
// write!(f, "{table}")?;
|
||||
// writeln!(f)?;
|
||||
// Ok(())
|
||||
// }
|
||||
// }
|
||||
pub fn with_some_desc_color(mut self, color: Option<Color>) -> Self {
|
||||
self.config.desc_color = color;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
// impl Serialize for MailboxesTable {
|
||||
// fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
// self.mailboxes.serialize(serializer)
|
||||
// }
|
||||
// }
|
||||
impl
|
||||
From<
|
||||
Vec<(
|
||||
io_imap::types::mailbox::Mailbox<'static>,
|
||||
Option<io_imap::types::core::QuotedChar>,
|
||||
Vec<io_imap::types::flag::FlagNameAttribute<'static>>,
|
||||
)>,
|
||||
> for MailboxesTable
|
||||
{
|
||||
fn from(
|
||||
mailboxes: Vec<(
|
||||
io_imap::types::mailbox::Mailbox<'static>,
|
||||
Option<io_imap::types::core::QuotedChar>,
|
||||
Vec<io_imap::types::flag::FlagNameAttribute<'static>>,
|
||||
)>,
|
||||
) -> Self {
|
||||
Self {
|
||||
mailboxes: mailboxes
|
||||
.into_iter()
|
||||
.map(|(mbox, delim, attrs)| Mailbox {
|
||||
name: match mbox {
|
||||
io_imap::types::mailbox::Mailbox::Inbox => "Inbox".into(),
|
||||
io_imap::types::mailbox::Mailbox::Other(mbox) => {
|
||||
String::from_utf8_lossy(mbox.inner().as_ref()).to_string()
|
||||
}
|
||||
},
|
||||
delimiter: match delim {
|
||||
Some(delim) => delim.inner().to_string(),
|
||||
None => String::new(),
|
||||
},
|
||||
attributes: attrs.into_iter().map(|attr| attr.to_string()).collect(),
|
||||
})
|
||||
.into(),
|
||||
width: None,
|
||||
config: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for MailboxesTable {
|
||||
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("DELIMITER"),
|
||||
Cell::new("ATTRIBUTES"),
|
||||
]))
|
||||
.add_rows(self.mailboxes.iter().map(|mbox| {
|
||||
let mut row = Row::new();
|
||||
row.max_height(1);
|
||||
|
||||
row.add_cell(Cell::new(&mbox.name).fg(self.config.name_color()));
|
||||
row.add_cell(Cell::new(&mbox.delimiter).fg(self.config.desc_color()));
|
||||
row.add_cell(Cell::new(&mbox.attributes.join(", ")).fg(self.config.desc_color()));
|
||||
|
||||
row
|
||||
}));
|
||||
|
||||
if let Some(width) = self.width {
|
||||
table.set_width(width);
|
||||
}
|
||||
|
||||
writeln!(f)?;
|
||||
write!(f, "{table}")?;
|
||||
writeln!(f)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for MailboxesTable {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
self.mailboxes.serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
fn map_color(color: Color) -> comfy_table::Color {
|
||||
match color {
|
||||
Color::Reset => comfy_table::Color::Reset,
|
||||
Color::Black => comfy_table::Color::Black,
|
||||
Color::DarkGrey => comfy_table::Color::DarkGrey,
|
||||
Color::Red => comfy_table::Color::Red,
|
||||
Color::DarkRed => comfy_table::Color::DarkRed,
|
||||
Color::Green => comfy_table::Color::Green,
|
||||
Color::DarkGreen => comfy_table::Color::DarkGreen,
|
||||
Color::Yellow => comfy_table::Color::Yellow,
|
||||
Color::DarkYellow => comfy_table::Color::DarkYellow,
|
||||
Color::Blue => comfy_table::Color::Blue,
|
||||
Color::DarkBlue => comfy_table::Color::DarkBlue,
|
||||
Color::Magenta => comfy_table::Color::Magenta,
|
||||
Color::DarkMagenta => comfy_table::Color::DarkMagenta,
|
||||
Color::Cyan => comfy_table::Color::Cyan,
|
||||
Color::DarkCyan => comfy_table::Color::DarkCyan,
|
||||
Color::White => comfy_table::Color::White,
|
||||
Color::Grey => comfy_table::Color::Grey,
|
||||
Color::Rgb { r, g, b } => comfy_table::Color::Rgb { r, g, b },
|
||||
Color::AnsiValue(n) => comfy_table::Color::AnsiValue(n),
|
||||
}
|
||||
}
|
||||
|
||||
+34
-23
@@ -1,9 +1,10 @@
|
||||
#[cfg(unix)]
|
||||
use std::os::unix::net::UnixStream;
|
||||
use std::{
|
||||
fs,
|
||||
io::{self, Read, Write},
|
||||
net::TcpStream,
|
||||
sync::Arc,
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
@@ -28,33 +29,25 @@ use rustls::{
|
||||
};
|
||||
#[cfg(any(feature = "rustls-aws", feature = "rustls-ring"))]
|
||||
use rustls_platform_verifier::{ConfigVerifierExt, Verifier};
|
||||
#[cfg(windows)]
|
||||
use uds_windows::UnixStream;
|
||||
|
||||
use crate::config::{ImapConfig, RustlsCryptoConfig, SaslMechanismConfig, TlsProviderConfig};
|
||||
|
||||
pub enum Stream {
|
||||
Plain(TcpStream),
|
||||
Tcp(TcpStream),
|
||||
Unix(UnixStream),
|
||||
#[cfg(any(feature = "rustls-aws", feature = "rustls-ring"))]
|
||||
Rustls(StreamOwned<ClientConnection, TcpStream>),
|
||||
#[cfg(feature = "native-tls")]
|
||||
NativeTls(native_tls::TlsStream<TcpStream>),
|
||||
}
|
||||
|
||||
impl Stream {
|
||||
pub fn set_read_timeout(&self, dur: Option<Duration>) -> io::Result<()> {
|
||||
match self {
|
||||
Self::Plain(s) => s.set_read_timeout(dur),
|
||||
#[cfg(any(feature = "rustls-aws", feature = "rustls-ring"))]
|
||||
Self::Rustls(s) => s.get_ref().set_read_timeout(dur),
|
||||
#[cfg(feature = "native-tls")]
|
||||
Self::NativeTls(s) => s.get_ref().set_read_timeout(dur),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Read for Stream {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
match self {
|
||||
Self::Plain(s) => s.read(buf),
|
||||
Self::Tcp(s) => s.read(buf),
|
||||
Self::Unix(s) => s.read(buf),
|
||||
#[cfg(any(feature = "rustls-aws", feature = "rustls-ring"))]
|
||||
Self::Rustls(s) => s.read(buf),
|
||||
#[cfg(feature = "native-tls")]
|
||||
@@ -66,7 +59,8 @@ impl Read for Stream {
|
||||
impl Write for Stream {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
match self {
|
||||
Self::Plain(s) => s.write(buf),
|
||||
Self::Tcp(s) => s.write(buf),
|
||||
Self::Unix(s) => s.write(buf),
|
||||
#[cfg(any(feature = "rustls-aws", feature = "rustls-ring"))]
|
||||
Self::Rustls(s) => s.write(buf),
|
||||
#[cfg(feature = "native-tls")]
|
||||
@@ -76,7 +70,8 @@ impl Write for Stream {
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
match self {
|
||||
Self::Plain(s) => s.flush(),
|
||||
Self::Tcp(s) => s.flush(),
|
||||
Self::Unix(s) => s.flush(),
|
||||
#[cfg(any(feature = "rustls-aws", feature = "rustls-ring"))]
|
||||
Self::Rustls(s) => s.flush(),
|
||||
#[cfg(feature = "native-tls")]
|
||||
@@ -109,11 +104,11 @@ pub fn connect(mut config: ImapConfig) -> Result<(ImapContext, Stream)> {
|
||||
}
|
||||
}
|
||||
|
||||
(context, Stream::Plain(stream))
|
||||
(context, Stream::Tcp(stream))
|
||||
}
|
||||
scheme if scheme.eq_ignore_ascii_case("imaps") => {
|
||||
let port = config.url.port().unwrap_or(993);
|
||||
let mut tcp = TcpStream::connect((host, port))?;
|
||||
let mut stream = TcpStream::connect((host, port))?;
|
||||
|
||||
if config.starttls {
|
||||
let mut coroutine = ImapStartTls::new(context);
|
||||
@@ -121,7 +116,7 @@ pub fn connect(mut config: ImapConfig) -> Result<(ImapContext, Stream)> {
|
||||
|
||||
loop {
|
||||
match coroutine.resume(arg.take()) {
|
||||
ImapStartTlsResult::Io(io) => arg = Some(handle(&mut tcp, io)?),
|
||||
ImapStartTlsResult::Io(io) => arg = Some(handle(&mut stream, io)?),
|
||||
ImapStartTlsResult::Ok { context: c } => break context = c,
|
||||
ImapStartTlsResult::Err { err, .. } => Err(err)?,
|
||||
}
|
||||
@@ -226,7 +221,7 @@ pub fn connect(mut config: ImapConfig) -> Result<(ImapContext, Stream)> {
|
||||
|
||||
let server_name = host.to_string().try_into()?;
|
||||
let conn = ClientConnection::new(Arc::new(config), server_name)?;
|
||||
Stream::Rustls(StreamOwned::new(conn, tcp))
|
||||
Stream::Rustls(StreamOwned::new(conn, stream))
|
||||
}
|
||||
#[cfg(feature = "native-tls")]
|
||||
TlsProviderConfig::NativeTls => {
|
||||
@@ -240,7 +235,7 @@ pub fn connect(mut config: ImapConfig) -> Result<(ImapContext, Stream)> {
|
||||
}
|
||||
|
||||
let connector = builder.build()?;
|
||||
Stream::NativeTls(connector.connect(host, tcp)?)
|
||||
Stream::NativeTls(connector.connect(host, stream)?)
|
||||
}
|
||||
#[allow(unreachable_patterns)]
|
||||
_ => unreachable!(),
|
||||
@@ -277,7 +272,23 @@ pub fn connect(mut config: ImapConfig) -> Result<(ImapContext, Stream)> {
|
||||
(context, stream)
|
||||
}
|
||||
scheme if scheme.eq_ignore_ascii_case("unix") => {
|
||||
todo!()
|
||||
let sock_path = config.url.path();
|
||||
let mut stream = UnixStream::connect(&sock_path)?;
|
||||
|
||||
let mut coroutine = GetImapGreetingWithCapability::new(context);
|
||||
let mut arg = None;
|
||||
|
||||
loop {
|
||||
match coroutine.resume(arg.take()) {
|
||||
GetImapGreetingWithCapabilityResult::Io(io) => {
|
||||
arg = Some(handle(&mut stream, io)?)
|
||||
}
|
||||
GetImapGreetingWithCapabilityResult::Ok { context: c } => break context = c,
|
||||
GetImapGreetingWithCapabilityResult::Err { err, .. } => Err(err)?,
|
||||
}
|
||||
}
|
||||
|
||||
(context, Stream::Unix(stream))
|
||||
}
|
||||
scheme => {
|
||||
bail!("Unknown scheme {scheme}, expected imap, imaps or unix");
|
||||
|
||||
Reference in New Issue
Block a user