cargo clippy fixes + refactor

This commit is contained in:
Michael Zhang 2021-02-15 04:36:06 -06:00
parent 4ed955fb93
commit 706d397ad5
Signed by: michael
GPG key ID: BDA47A31A3C8EE6B
8 changed files with 108 additions and 83 deletions

View file

@ -34,3 +34,6 @@ tokio-util = { version = "0.6.3", features = ["full"] }
toml = "0.5.8" toml = "0.5.8"
webpki-roots = "0.21.0" webpki-roots = "0.21.0"
xdg = "2.2.0" xdg = "2.2.0"
[features]
clippy = []

View file

@ -1,5 +1,4 @@
use std::borrow::Cow; use std::borrow::Cow;
use std::fmt;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::ops::{RangeFrom, RangeInclusive}; use std::ops::{RangeFrom, RangeInclusive};
use std::str; use std::str;

View file

@ -29,14 +29,14 @@ pub struct Config {
} }
/// Configuration for a single mail account /// Configuration for a single mail account
#[derive(Default, Serialize, Deserialize, Clone, Debug)] #[derive(Serialize, Deserialize, Clone, Debug)]
pub struct MailAccountConfig { pub struct MailAccountConfig {
/// Imap /// Imap
pub imap: ImapConfig, pub imap: ImapConfig,
} }
/// Configuring an IMAP server /// Configuring an IMAP server
#[derive(Default, Serialize, Deserialize, Clone, Debug)] #[derive(Serialize, Deserialize, Clone, Debug)]
pub struct ImapConfig { pub struct ImapConfig {
/// Host of the IMAP server (needs to be hostname for TLS) /// Host of the IMAP server (needs to be hostname for TLS)
pub server: String, pub server: String,
@ -49,6 +49,25 @@ pub struct ImapConfig {
/// Password for authenticating to IMAP /// Password for authenticating to IMAP
pub password: String, 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 /// Spawns a notify::RecommendedWatcher to watch the XDG config directory. Whenever the config file

View file

@ -1,5 +1,3 @@
//! Mail
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt::Display; use std::fmt::Display;
use std::sync::Arc; use std::sync::Arc;
@ -16,62 +14,13 @@ use panorama_imap::{
parser::parse_response, parser::parse_response,
types::{Capability, RequestId, Response, ResponseCode, State, Status}, types::{Capability, RequestId, Response, ResponseCode, State, Status},
}; };
use tokio::{ use tokio::{net::TcpStream, sync::mpsc};
net::TcpStream,
sync::mpsc::{self, UnboundedReceiver},
task::JoinHandle,
};
use tokio_rustls::{rustls::ClientConfig, webpki::DNSNameRef, TlsConnector}; use tokio_rustls::{rustls::ClientConfig, webpki::DNSNameRef, TlsConnector};
use tokio_stream::wrappers::WatchStream;
use tokio_util::codec::{Decoder, LinesCodec, LinesCodecError}; 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 async fn open_imap_connection(config: ImapConfig) -> Result<()> {
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<()> {
debug!( debug!(
"Opening imap connection to {}:{}", "Opening imap connection to {}:{}",
config.server, config.port config.server, config.port
@ -165,6 +114,7 @@ where
debug!("<<< {:?}", resp); debug!("<<< {:?}", resp);
match st { match st {
State::Authenticated => {}
State::NotAuthenticated => match resp { State::NotAuthenticated => match resp {
Response::Data { Response::Data {
status: Status::Ok, status: Status::Ok,
@ -194,7 +144,7 @@ where
} }
} }
Response::Capabilities(caps) => { Response::Capabilities(_caps) => {
if with_ssl { if with_ssl {
// send authentication information // send authentication information
let cmd = Command { let cmd = Command {
@ -226,10 +176,12 @@ where
Ok(LoopExit::Closed) Ok(LoopExit::Closed)
} }
type InFlightFunc = Box<dyn Fn(Option<ResponseCode>) + Send>;
/// A struct in charge of managing multiple in-flight commands. /// A struct in charge of managing multiple in-flight commands.
struct CommandManager<S> { struct CommandManager<S> {
tag_idx: usize, tag_idx: usize,
in_flight: HashMap<String, Box<dyn Fn(Option<ResponseCode>) + Send>>, in_flight: HashMap<String, InFlightFunc>,
sink: S, sink: S,
} }

57
src/mail/mod.rs Normal file
View 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(())
}

View file

@ -1,18 +1,13 @@
#[macro_use] #[macro_use]
extern crate log; extern crate log;
use std::fs::File;
use std::io::Read;
use std::path::PathBuf; use std::path::PathBuf;
use anyhow::Result; use anyhow::Result;
use futures::future::TryFutureExt; use futures::future::TryFutureExt;
use panorama::{ use panorama::{config::spawn_config_watcher, mail, ui};
config::{spawn_config_watcher, Config},
mail, ui,
};
use structopt::StructOpt; use structopt::StructOpt;
use tokio::sync::{mpsc, oneshot}; use tokio::sync::mpsc;
use xdg::BaseDirectories; use xdg::BaseDirectories;
#[derive(Debug, StructOpt)] #[derive(Debug, StructOpt)]
@ -35,14 +30,14 @@ async fn main() -> Result<()> {
// print logs to file as directed by command line options // print logs to file as directed by command line options
setup_logger(&opt)?; setup_logger(&opt)?;
let xdg = BaseDirectories::new()?; let _xdg = BaseDirectories::new()?;
let (config_thread, config_update) = spawn_config_watcher()?; let (_config_thread, config_update) = spawn_config_watcher()?;
// used to notify the runtime that the process should exit // used to notify the runtime that the process should exit
let (exit_tx, mut exit_rx) = mpsc::channel::<()>(1); let (exit_tx, mut exit_rx) = mpsc::channel::<()>(1);
// used to send commands to the mail service // 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)); tokio::spawn(mail::run_mail(config_update.clone(), mail_rx).unwrap_or_else(report_err));

View file

@ -11,7 +11,7 @@ use crossterm::{
cursor, cursor,
event::{self, Event, KeyCode, KeyEvent}, event::{self, Event, KeyCode, KeyEvent},
style::{self, Color}, style::{self, Color},
terminal::{self, ClearType}, terminal,
}; };
use tokio::time; use tokio::time;
@ -58,12 +58,13 @@ pub async fn run_ui(mut w: impl Write, exit: ExitSender) -> Result<()> {
if event::poll(FRAME)? { if event::poll(FRAME)? {
let event = event::read()?; let event = event::read()?;
table.update(&event); table.update(&event);
match event {
Event::Key(KeyEvent { if let Event::Key(KeyEvent {
code: KeyCode::Char('q'), code: KeyCode::Char('q'),
.. ..
}) => break, }) = event
_ => {} {
break;
} }
} }
} }

View file

@ -17,8 +17,8 @@ pub struct Table {
impl Table { impl Table {
pub fn update(&mut self, event: &Event) { pub fn update(&mut self, event: &Event) {
match event { if let Event::Key(KeyEvent { code, .. }) = event {
Event::Key(KeyEvent { code, .. }) => match code { match code {
KeyCode::Char('j') => { KeyCode::Char('j') => {
if let Some(selected_row) = &mut self.selected_row { if let Some(selected_row) = &mut self.selected_row {
*selected_row = (self.rows.len() as u16 - 1).min(*selected_row + 1); *selected_row = (self.rows.len() as u16 - 1).min(*selected_row + 1);
@ -27,13 +27,12 @@ impl Table {
KeyCode::Char('k') => { KeyCode::Char('k') => {
if let Some(selected_row) = &mut self.selected_row { if let Some(selected_row) = &mut self.selected_row {
if *selected_row > 0 { if *selected_row > 0 {
*selected_row = *selected_row - 1; *selected_row -= 1;
} }
} }
} }
_ => {} _ => {}
}, }
_ => {}
} }
} }
@ -45,7 +44,7 @@ impl Table {
let mut columns = Vec::new(); let mut columns = Vec::new();
for row in self.rows.iter() { for row in self.rows.iter() {
for (i, cell) in row.iter().enumerate() { 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); columns.push(0);
} else { } else {
columns[i] = cell.len().max(columns[i]); columns[i] = cell.len().max(columns[i]);
@ -105,7 +104,7 @@ impl Table {
pub fn push_row(&mut self, row: Vec<String>) { pub fn push_row(&mut self, row: Vec<String>) {
self.rows.push(row); self.rows.push(row);
if let None = self.selected_row { if self.selected_row.is_none() {
self.selected_row = Some(0); self.selected_row = Some(0);
} }
} }