restructure configs
This commit is contained in:
parent
5ad3a844e1
commit
4ed955fb93
4 changed files with 62 additions and 26 deletions
11
notes.md
Normal file
11
notes.md
Normal file
|
@ -0,0 +1,11 @@
|
|||
imap routine
|
||||
---
|
||||
|
||||
- basic tcp connection is opened
|
||||
- if tls is "on", then immediately perform tls handshake with the server
|
||||
- if tls is "starttls", check starttls capability
|
||||
- if the server doesn't have starttls capability, die and report to the user
|
||||
- if the server _does_ have starttls, exit the read loop and perform tls handshake over current connection
|
||||
- at this point, tls should be figured out, so moving on to auth
|
||||
- check if the auth type that the user specified is in the list of auth types (prob support plain and oauth2?)
|
||||
|
|
@ -14,11 +14,30 @@ use tokio::{sync::watch, task::JoinHandle};
|
|||
use xdg::BaseDirectories;
|
||||
|
||||
/// Alias for a MailConfig receiver.
|
||||
pub type ConfigWatcher = watch::Receiver<Option<MailConfig>>;
|
||||
pub type ConfigWatcher = watch::Receiver<Option<Config>>;
|
||||
|
||||
/// Configuration
|
||||
#[derive(Default, Serialize, Deserialize, Clone, Debug)]
|
||||
pub struct MailConfig {
|
||||
pub struct Config {
|
||||
/// Version of the config to use
|
||||
/// (potentially for migration later?)
|
||||
pub version: String,
|
||||
|
||||
/// Mail accounts
|
||||
#[serde(rename = "mail")]
|
||||
pub mail_accounts: Vec<MailAccountConfig>,
|
||||
}
|
||||
|
||||
/// Configuration for a single mail account
|
||||
#[derive(Default, Serialize, Deserialize, Clone, Debug)]
|
||||
pub struct MailAccountConfig {
|
||||
/// Imap
|
||||
pub imap: ImapConfig,
|
||||
}
|
||||
|
||||
/// Configuring an IMAP server
|
||||
#[derive(Default, Serialize, Deserialize, Clone, Debug)]
|
||||
pub struct ImapConfig {
|
||||
/// Host of the IMAP server (needs to be hostname for TLS)
|
||||
pub server: String,
|
||||
|
||||
|
@ -48,7 +67,7 @@ fn start_watcher() -> Result<(RecommendedWatcher, Receiver<DebouncedEvent>)> {
|
|||
Ok((watcher, rx))
|
||||
}
|
||||
|
||||
async fn read_config(path: impl AsRef<Path>) -> Result<MailConfig> {
|
||||
async fn read_config(path: impl AsRef<Path>) -> Result<Config> {
|
||||
let mut file = File::open(path.as_ref())?;
|
||||
let mut contents = Vec::new();
|
||||
file.read_to_end(&mut contents)?;
|
||||
|
@ -63,20 +82,29 @@ async fn read_config(path: impl AsRef<Path>) -> Result<MailConfig> {
|
|||
/// This exists so all errors are able to be caught in one go.
|
||||
async fn watcher_loop(
|
||||
fs_events: Receiver<DebouncedEvent>,
|
||||
config_tx: watch::Sender<Option<MailConfig>>,
|
||||
config_tx: watch::Sender<Option<Config>>,
|
||||
) -> Result<()> {
|
||||
// first try opening the config file directly on load
|
||||
// first try opening the config file directly when the program is opened
|
||||
// (so the config isn't blank until the user touches the config file)
|
||||
let xdg = BaseDirectories::new()?;
|
||||
if let Some(config_path) = xdg.find_config_file("panorama/panorama.toml") {
|
||||
debug!("found config at {:?}", config_path);
|
||||
let config = read_config(config_path).await?;
|
||||
debug!("read config: {:?}, sending to output", config);
|
||||
config_tx.send(Some(config))?;
|
||||
}
|
||||
|
||||
// start listening for events from the notify::Watcher
|
||||
for event in fs_events {
|
||||
debug!("new event: {:?}", event);
|
||||
// config_tx.send(Some(config))?;
|
||||
use notify::DebouncedEvent::*;
|
||||
match event {
|
||||
NoticeWrite(path) | Write(path) => {
|
||||
let config = read_config(path).await?;
|
||||
config_tx.send(Some(config))?;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -93,7 +121,7 @@ pub fn spawn_config_watcher() -> Result<(JoinHandle<()>, ConfigWatcher)> {
|
|||
match watcher_loop(config_rx, config_tx).await {
|
||||
Ok(_) => {}
|
||||
Err(err) => {
|
||||
debug!("config watcher died: {:?}", err);
|
||||
debug!("config watcher bugged: {:?}", err);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
17
src/mail.rs
17
src/mail.rs
|
@ -25,7 +25,7 @@ use tokio_rustls::{rustls::ClientConfig, webpki::DNSNameRef, TlsConnector};
|
|||
use tokio_stream::wrappers::WatchStream;
|
||||
use tokio_util::codec::{Decoder, LinesCodec, LinesCodecError};
|
||||
|
||||
use crate::config::{ConfigWatcher, MailConfig};
|
||||
use crate::config::{Config, ConfigWatcher, ImapConfig};
|
||||
|
||||
/// Command sent to the mail thread by something else (i.e. UI)
|
||||
pub enum MailCommand {
|
||||
|
@ -45,7 +45,10 @@ pub async fn run_mail(
|
|||
|
||||
let mut config_watcher = WatchStream::new(config_watcher);
|
||||
loop {
|
||||
let config: MailConfig = match config_watcher.next().await {
|
||||
debug!("listening for configs");
|
||||
let a = config_watcher.next().await;
|
||||
debug!("got config {:?}", a);
|
||||
let config: Config = match a {
|
||||
Some(Some(v)) => v,
|
||||
_ => break,
|
||||
};
|
||||
|
@ -57,14 +60,18 @@ pub async fn run_mail(
|
|||
curr_conn.abort();
|
||||
}
|
||||
|
||||
let handle = tokio::spawn(open_imap_connection(config));
|
||||
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: MailConfig) -> Result<()> {
|
||||
async fn open_imap_connection(config: ImapConfig) -> Result<()> {
|
||||
debug!(
|
||||
"Opening imap connection to {}:{}",
|
||||
config.server, config.port
|
||||
|
@ -115,7 +122,7 @@ enum LoopExit<S, S2> {
|
|||
}
|
||||
|
||||
async fn listen_loop<S, S2>(
|
||||
config: MailConfig,
|
||||
config: ImapConfig,
|
||||
st: &mut State,
|
||||
sink: S2,
|
||||
mut stream: S,
|
||||
|
|
18
src/main.rs
18
src/main.rs
|
@ -8,7 +8,7 @@ use std::path::PathBuf;
|
|||
use anyhow::Result;
|
||||
use futures::future::TryFutureExt;
|
||||
use panorama::{
|
||||
config::{spawn_config_watcher, MailConfig},
|
||||
config::{spawn_config_watcher, Config},
|
||||
mail, ui,
|
||||
};
|
||||
use structopt::StructOpt;
|
||||
|
@ -38,26 +38,16 @@ async fn main() -> Result<()> {
|
|||
let xdg = BaseDirectories::new()?;
|
||||
let (config_thread, config_update) = spawn_config_watcher()?;
|
||||
|
||||
// let config: MailConfig = {
|
||||
// let config_path = opt
|
||||
// .config_path
|
||||
// .clone()
|
||||
// .unwrap_or_else(|| "config.toml".into());
|
||||
// let mut config_file = File::open(config_path)?;
|
||||
// let mut contents = Vec::new();
|
||||
// config_file.read_to_end(&mut contents)?;
|
||||
// toml::from_slice(&contents)?
|
||||
// };
|
||||
|
||||
// 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_thread = 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));
|
||||
|
||||
let stdout = std::io::stdout();
|
||||
let ui_thread = tokio::spawn(ui::run_ui(stdout, exit_tx).unwrap_or_else(report_err));
|
||||
tokio::spawn(ui::run_ui(stdout, exit_tx).unwrap_or_else(report_err));
|
||||
|
||||
exit_rx.recv().await;
|
||||
|
||||
|
|
Loading…
Reference in a new issue