cargo clippy fixes + refactor
This commit is contained in:
parent
4ed955fb93
commit
706d397ad5
8 changed files with 108 additions and 83 deletions
|
@ -34,3 +34,6 @@ tokio-util = { version = "0.6.3", features = ["full"] }
|
|||
toml = "0.5.8"
|
||||
webpki-roots = "0.21.0"
|
||||
xdg = "2.2.0"
|
||||
|
||||
[features]
|
||||
clippy = []
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
use std::borrow::Cow;
|
||||
use std::fmt;
|
||||
use std::marker::PhantomData;
|
||||
use std::ops::{RangeFrom, RangeInclusive};
|
||||
use std::str;
|
||||
|
|
|
@ -29,14 +29,14 @@ pub struct Config {
|
|||
}
|
||||
|
||||
/// Configuration for a single mail account
|
||||
#[derive(Default, Serialize, Deserialize, Clone, Debug)]
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
pub struct MailAccountConfig {
|
||||
/// Imap
|
||||
pub imap: ImapConfig,
|
||||
}
|
||||
|
||||
/// Configuring an IMAP server
|
||||
#[derive(Default, Serialize, Deserialize, Clone, Debug)]
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
pub struct ImapConfig {
|
||||
/// Host of the IMAP server (needs to be hostname for TLS)
|
||||
pub server: String,
|
||||
|
@ -49,6 +49,25 @@ pub struct ImapConfig {
|
|||
|
||||
/// Password for authenticating to IMAP
|
||||
pub password: String,
|
||||
|
||||
/// TLS
|
||||
pub tls: TlsMethod,
|
||||
}
|
||||
|
||||
/// Describes when to perform the TLS handshake
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
pub enum TlsMethod {
|
||||
/// Perform TLS handshake immediately upon connection
|
||||
#[serde(rename = "on")]
|
||||
On,
|
||||
|
||||
/// Perform TLS handshake after issuing the STARTTLS command
|
||||
#[serde(rename = "starttls")]
|
||||
Starttls,
|
||||
|
||||
/// Don't perform TLS handshake at all (unsecured)
|
||||
#[serde(rename = "off")]
|
||||
Off,
|
||||
}
|
||||
|
||||
/// Spawns a notify::RecommendedWatcher to watch the XDG config directory. Whenever the config file
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
//! Mail
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::Display;
|
||||
use std::sync::Arc;
|
||||
|
@ -16,62 +14,13 @@ use panorama_imap::{
|
|||
parser::parse_response,
|
||||
types::{Capability, RequestId, Response, ResponseCode, State, Status},
|
||||
};
|
||||
use tokio::{
|
||||
net::TcpStream,
|
||||
sync::mpsc::{self, UnboundedReceiver},
|
||||
task::JoinHandle,
|
||||
};
|
||||
use tokio::{net::TcpStream, sync::mpsc};
|
||||
use tokio_rustls::{rustls::ClientConfig, webpki::DNSNameRef, TlsConnector};
|
||||
use tokio_stream::wrappers::WatchStream;
|
||||
use tokio_util::codec::{Decoder, LinesCodec, LinesCodecError};
|
||||
|
||||
use crate::config::{Config, ConfigWatcher, ImapConfig};
|
||||
use crate::config::ImapConfig;
|
||||
|
||||
/// Command sent to the mail thread by something else (i.e. UI)
|
||||
pub enum MailCommand {
|
||||
/// Refresh the list
|
||||
Refresh,
|
||||
|
||||
/// Send a raw command
|
||||
Raw(Command),
|
||||
}
|
||||
|
||||
/// Main entrypoint for the mail listener.
|
||||
pub async fn run_mail(
|
||||
config_watcher: ConfigWatcher,
|
||||
cmd_in: UnboundedReceiver<MailCommand>,
|
||||
) -> Result<()> {
|
||||
let mut curr_conn: Option<JoinHandle<_>> = None;
|
||||
|
||||
let mut config_watcher = WatchStream::new(config_watcher);
|
||||
loop {
|
||||
debug!("listening for configs");
|
||||
let a = config_watcher.next().await;
|
||||
debug!("got config {:?}", a);
|
||||
let config: Config = match a {
|
||||
Some(Some(v)) => v,
|
||||
_ => break,
|
||||
};
|
||||
|
||||
// TODO: gracefully shut down connection
|
||||
// just gonna drop the connection for now
|
||||
if let Some(mut curr_conn) = curr_conn.take() {
|
||||
debug!("dropping connection...");
|
||||
curr_conn.abort();
|
||||
}
|
||||
|
||||
let handle = tokio::spawn(async {
|
||||
for acct in config.mail_accounts.into_iter() {
|
||||
open_imap_connection(acct.imap);
|
||||
}
|
||||
});
|
||||
curr_conn = Some(handle);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn open_imap_connection(config: ImapConfig) -> Result<()> {
|
||||
pub async fn open_imap_connection(config: ImapConfig) -> Result<()> {
|
||||
debug!(
|
||||
"Opening imap connection to {}:{}",
|
||||
config.server, config.port
|
||||
|
@ -165,6 +114,7 @@ where
|
|||
debug!("<<< {:?}", resp);
|
||||
|
||||
match st {
|
||||
State::Authenticated => {}
|
||||
State::NotAuthenticated => match resp {
|
||||
Response::Data {
|
||||
status: Status::Ok,
|
||||
|
@ -194,7 +144,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
Response::Capabilities(caps) => {
|
||||
Response::Capabilities(_caps) => {
|
||||
if with_ssl {
|
||||
// send authentication information
|
||||
let cmd = Command {
|
||||
|
@ -226,10 +176,12 @@ where
|
|||
Ok(LoopExit::Closed)
|
||||
}
|
||||
|
||||
type InFlightFunc = Box<dyn Fn(Option<ResponseCode>) + Send>;
|
||||
|
||||
/// A struct in charge of managing multiple in-flight commands.
|
||||
struct CommandManager<S> {
|
||||
tag_idx: usize,
|
||||
in_flight: HashMap<String, Box<dyn Fn(Option<ResponseCode>) + Send>>,
|
||||
in_flight: HashMap<String, InFlightFunc>,
|
||||
sink: S,
|
||||
}
|
||||
|
57
src/mail/mod.rs
Normal file
57
src/mail/mod.rs
Normal file
|
@ -0,0 +1,57 @@
|
|||
//! Mail
|
||||
|
||||
mod imap;
|
||||
|
||||
use anyhow::Result;
|
||||
use futures::stream::StreamExt;
|
||||
use panorama_imap::builders::command::Command as ImapCommand;
|
||||
use tokio::{sync::mpsc::UnboundedReceiver, task::JoinHandle};
|
||||
use tokio_stream::wrappers::WatchStream;
|
||||
|
||||
use crate::config::{Config, ConfigWatcher};
|
||||
|
||||
use self::imap::open_imap_connection;
|
||||
|
||||
/// Command sent to the mail thread by something else (i.e. UI)
|
||||
pub enum MailCommand {
|
||||
/// Refresh the list
|
||||
Refresh,
|
||||
|
||||
/// Send a raw command
|
||||
Raw(ImapCommand),
|
||||
}
|
||||
|
||||
/// Main entrypoint for the mail listener.
|
||||
pub async fn run_mail(
|
||||
config_watcher: ConfigWatcher,
|
||||
_cmd_in: UnboundedReceiver<MailCommand>,
|
||||
) -> Result<()> {
|
||||
let mut curr_conn: Option<JoinHandle<_>> = None;
|
||||
|
||||
let mut config_watcher = WatchStream::new(config_watcher);
|
||||
loop {
|
||||
debug!("listening for configs");
|
||||
let a = config_watcher.next().await;
|
||||
debug!("got config {:?}", a);
|
||||
let config: Config = match a {
|
||||
Some(Some(v)) => v,
|
||||
_ => break,
|
||||
};
|
||||
|
||||
// TODO: gracefully shut down connection
|
||||
// just gonna drop the connection for now
|
||||
if let Some(curr_conn) = curr_conn.take() {
|
||||
debug!("dropping connection...");
|
||||
curr_conn.abort();
|
||||
}
|
||||
|
||||
let handle = tokio::spawn(async {
|
||||
for acct in config.mail_accounts.into_iter() {
|
||||
open_imap_connection(acct.imap).await.unwrap();
|
||||
}
|
||||
});
|
||||
curr_conn = Some(handle);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
15
src/main.rs
15
src/main.rs
|
@ -1,18 +1,13 @@
|
|||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use anyhow::Result;
|
||||
use futures::future::TryFutureExt;
|
||||
use panorama::{
|
||||
config::{spawn_config_watcher, Config},
|
||||
mail, ui,
|
||||
};
|
||||
use panorama::{config::spawn_config_watcher, mail, ui};
|
||||
use structopt::StructOpt;
|
||||
use tokio::sync::{mpsc, oneshot};
|
||||
use tokio::sync::mpsc;
|
||||
use xdg::BaseDirectories;
|
||||
|
||||
#[derive(Debug, StructOpt)]
|
||||
|
@ -35,14 +30,14 @@ async fn main() -> Result<()> {
|
|||
// print logs to file as directed by command line options
|
||||
setup_logger(&opt)?;
|
||||
|
||||
let xdg = BaseDirectories::new()?;
|
||||
let (config_thread, config_update) = spawn_config_watcher()?;
|
||||
let _xdg = BaseDirectories::new()?;
|
||||
let (_config_thread, config_update) = spawn_config_watcher()?;
|
||||
|
||||
// used to notify the runtime that the process should exit
|
||||
let (exit_tx, mut exit_rx) = mpsc::channel::<()>(1);
|
||||
|
||||
// used to send commands to the mail service
|
||||
let (mail_tx, mail_rx) = mpsc::unbounded_channel();
|
||||
let (_mail_tx, mail_rx) = mpsc::unbounded_channel();
|
||||
|
||||
tokio::spawn(mail::run_mail(config_update.clone(), mail_rx).unwrap_or_else(report_err));
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ use crossterm::{
|
|||
cursor,
|
||||
event::{self, Event, KeyCode, KeyEvent},
|
||||
style::{self, Color},
|
||||
terminal::{self, ClearType},
|
||||
terminal,
|
||||
};
|
||||
use tokio::time;
|
||||
|
||||
|
@ -58,12 +58,13 @@ pub async fn run_ui(mut w: impl Write, exit: ExitSender) -> Result<()> {
|
|||
if event::poll(FRAME)? {
|
||||
let event = event::read()?;
|
||||
table.update(&event);
|
||||
match event {
|
||||
Event::Key(KeyEvent {
|
||||
code: KeyCode::Char('q'),
|
||||
..
|
||||
}) => break,
|
||||
_ => {}
|
||||
|
||||
if let Event::Key(KeyEvent {
|
||||
code: KeyCode::Char('q'),
|
||||
..
|
||||
}) = event
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,8 +17,8 @@ pub struct Table {
|
|||
|
||||
impl Table {
|
||||
pub fn update(&mut self, event: &Event) {
|
||||
match event {
|
||||
Event::Key(KeyEvent { code, .. }) => match code {
|
||||
if let Event::Key(KeyEvent { code, .. }) = event {
|
||||
match code {
|
||||
KeyCode::Char('j') => {
|
||||
if let Some(selected_row) = &mut self.selected_row {
|
||||
*selected_row = (self.rows.len() as u16 - 1).min(*selected_row + 1);
|
||||
|
@ -27,13 +27,12 @@ impl Table {
|
|||
KeyCode::Char('k') => {
|
||||
if let Some(selected_row) = &mut self.selected_row {
|
||||
if *selected_row > 0 {
|
||||
*selected_row = *selected_row - 1;
|
||||
*selected_row -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -45,7 +44,7 @@ impl Table {
|
|||
let mut columns = Vec::new();
|
||||
for row in self.rows.iter() {
|
||||
for (i, cell) in row.iter().enumerate() {
|
||||
if columns.len() == 0 || columns.len() - 1 < i {
|
||||
if columns.is_empty() || columns.len() - 1 < i {
|
||||
columns.push(0);
|
||||
} else {
|
||||
columns[i] = cell.len().max(columns[i]);
|
||||
|
@ -105,7 +104,7 @@ impl Table {
|
|||
|
||||
pub fn push_row(&mut self, row: Vec<String>) {
|
||||
self.rows.push(row);
|
||||
if let None = self.selected_row {
|
||||
if self.selected_row.is_none() {
|
||||
self.selected_row = Some(0);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue