what the heck
This commit is contained in:
parent
dbe92e4962
commit
f5d3a89641
9 changed files with 119 additions and 25 deletions
23
Cargo.lock
generated
23
Cargo.lock
generated
|
@ -247,6 +247,28 @@ version = "0.1.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
|
||||
|
||||
[[package]]
|
||||
name = "format-bytes"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cc35f5e45d6b31053cea13078ffc6fa52fa8617aa54b7ac2011720d9c009e04f"
|
||||
dependencies = [
|
||||
"format-bytes-macros",
|
||||
"proc-macro-hack",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "format-bytes-macros"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b05089e341a0460449e2210c3bf7b61597860b07f0deae58da38dbed0a4c6b6d"
|
||||
dependencies = [
|
||||
"proc-macro-hack",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "funty"
|
||||
version = "1.1.0"
|
||||
|
@ -645,6 +667,7 @@ dependencies = [
|
|||
"cfg-if",
|
||||
"chrono",
|
||||
"crossterm",
|
||||
"format-bytes",
|
||||
"futures",
|
||||
"inotify",
|
||||
"lettre",
|
||||
|
|
|
@ -19,6 +19,7 @@ async-trait = "0.1.42"
|
|||
cfg-if = "1.0.0"
|
||||
chrono = "0.4.19"
|
||||
crossterm = "0.19.0"
|
||||
format-bytes = "0.2.0"
|
||||
futures = "0.3.12"
|
||||
inotify = { version = "0.9.2", features = ["stream"] }
|
||||
lettre = "0.9.5"
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
panorama
|
||||
========
|
||||
|
||||
[![](https://tokei.rs/b1/github/iptq/panorama?category=lines)](https://github.com/XAMPPRocky/tokei).
|
||||
|
||||
Panorama is a terminal Personal Information Manager (PIM).
|
||||
|
||||
Goals:
|
||||
|
|
|
@ -90,6 +90,12 @@ async fn start_inotify_stream(
|
|||
let config_home = config_home.as_ref().to_path_buf();
|
||||
let config_path = config_home.join("panorama.toml");
|
||||
|
||||
// first shot
|
||||
{
|
||||
let config = read_config(&config_path).await?;
|
||||
config_tx.send(config)?;
|
||||
}
|
||||
|
||||
while let Some(v) = event_stream.next().await {
|
||||
let event = v.context("event")?;
|
||||
|
||||
|
|
|
@ -3,13 +3,15 @@
|
|||
|
||||
#![deny(missing_docs)]
|
||||
// TODO: get rid of this before any kind of public release
|
||||
#![allow(unused_imports)]
|
||||
#![allow(unused_imports, unused_variables)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate anyhow;
|
||||
#[macro_use]
|
||||
extern crate crossterm;
|
||||
#[macro_use]
|
||||
extern crate format_bytes;
|
||||
#[macro_use]
|
||||
extern crate serde;
|
||||
#[macro_use]
|
||||
extern crate tracing;
|
||||
|
|
|
@ -5,8 +5,9 @@ use std::sync::Arc;
|
|||
|
||||
use anyhow::Result;
|
||||
use futures::{
|
||||
future::{Future, TryFuture},
|
||||
stream::Stream,
|
||||
future::{self, BoxFuture, Future, FutureExt, TryFuture},
|
||||
sink::{Sink, SinkExt},
|
||||
stream::{Stream, StreamExt},
|
||||
};
|
||||
use panorama_imap::builders::command::Command;
|
||||
use parking_lot::Mutex;
|
||||
|
@ -16,7 +17,7 @@ use tokio::{
|
|||
sync::{oneshot, Notify},
|
||||
};
|
||||
use tokio_rustls::{rustls::ClientConfig, webpki::DNSNameRef, TlsConnector};
|
||||
use tokio_util::codec::{Framed, Decoder, LinesCodec};
|
||||
use tokio_util::codec::{Decoder, Framed, FramedRead, FramedWrite, LinesCodec, LinesCodecError};
|
||||
|
||||
use crate::config::{ImapConfig, TlsMethod};
|
||||
|
||||
|
@ -26,6 +27,7 @@ pub async fn open_imap_connection(config: ImapConfig) -> Result<()> {
|
|||
|
||||
let stream = TcpStream::connect((server, port)).await?;
|
||||
|
||||
debug!("hellosu");
|
||||
match config.tls {
|
||||
TlsMethod::Off => begin_authentication(config, stream).await,
|
||||
TlsMethod::On => {
|
||||
|
@ -33,13 +35,23 @@ pub async fn open_imap_connection(config: ImapConfig) -> Result<()> {
|
|||
begin_authentication(config, stream).await
|
||||
}
|
||||
TlsMethod::Starttls => {
|
||||
let cmd_mgr = CommandManager::new(stream);
|
||||
let (stream, cmd_mgr) = CommandManager::new(stream);
|
||||
let flights = cmd_mgr.flights();
|
||||
|
||||
// listen(stream, flights).await?;
|
||||
// async move {
|
||||
// let mut cmd_mgr = cmd_mgr;
|
||||
// cmd_mgr.capabilities().await;
|
||||
// }
|
||||
// .await;
|
||||
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Performs TLS negotiation, using the webpki_roots and verifying the server name
|
||||
#[instrument(skip(server_name, stream))]
|
||||
async fn perform_tls_negotiation(
|
||||
server_name: impl AsRef<str>,
|
||||
stream: impl AsyncRead + AsyncWrite + Unpin,
|
||||
|
@ -61,10 +73,10 @@ async fn fetch_capabilities(stream: impl AsyncRead + AsyncWrite) -> Result<Vec<S
|
|||
let codec = LinesCodec::new();
|
||||
let framed = codec.framed(stream);
|
||||
|
||||
// framed.send("a0 CAPABILITY");
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[instrument(skip(config, stream))]
|
||||
async fn begin_authentication(
|
||||
config: ImapConfig,
|
||||
stream: impl AsyncRead + AsyncWrite,
|
||||
|
@ -72,47 +84,94 @@ async fn begin_authentication(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
trait ImapStream: AsyncRead + AsyncWrite + Unpin {}
|
||||
impl<T: AsyncRead + AsyncWrite + Unpin> ImapStream for T {}
|
||||
pub async fn listen(
|
||||
mut stream: impl Stream<Item = Result<String, LinesCodecError>> + Unpin,
|
||||
in_flight: InFlight,
|
||||
) -> Result<()> {
|
||||
debug!("listening for messages from server");
|
||||
loop {
|
||||
let line = match stream.next().await {
|
||||
Some(v) => v?,
|
||||
None => break,
|
||||
};
|
||||
debug!("line: {:?}", line);
|
||||
|
||||
let mut parts = line.split(' ');
|
||||
let tag = parts.next().unwrap().parse()?; // TODO: handle empty
|
||||
|
||||
{
|
||||
let mut in_flight = in_flight.lock();
|
||||
if let Some(sender) = in_flight.remove(&tag) {
|
||||
sender.send(()).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// trait ImapStream: AsyncRead + AsyncWrite + Send + Unpin {}
|
||||
// impl<T: AsyncRead + AsyncWrite + Send + Unpin> ImapStream for T {}
|
||||
|
||||
trait ImapSink: Sink<String, Error = LinesCodecError> + Unpin {}
|
||||
impl<T: Sink<String, Error = LinesCodecError> + Unpin> ImapSink for T {}
|
||||
|
||||
type InFlightMap = HashMap<usize, oneshot::Sender<()>>;
|
||||
type InFlight = Arc<Mutex<InFlightMap>>;
|
||||
|
||||
struct CommandManager<'a> {
|
||||
id: usize,
|
||||
in_flight: Arc<Mutex<HashMap<usize, oneshot::Sender<()>>>>,
|
||||
stream: Framed<Box<dyn ImapStream + 'a>, LinesCodec>,
|
||||
sink: Box<dyn ImapSink + 'a>,
|
||||
}
|
||||
|
||||
impl<'a> CommandManager<'a> {
|
||||
pub fn new(stream: impl ImapStream + 'a) -> Self {
|
||||
pub fn new(
|
||||
stream: impl AsyncRead + AsyncWrite + 'a,
|
||||
) -> (impl Stream<Item = Result<String, LinesCodecError>>, Self) {
|
||||
let codec = LinesCodec::new();
|
||||
let framed = codec.framed(Box::new(stream) as Box<_>);
|
||||
let framed = codec.framed(stream);
|
||||
let (framed_sink, framed_stream) = framed.split();
|
||||
|
||||
CommandManager {
|
||||
let cmd_mgr = CommandManager {
|
||||
id: 0,
|
||||
in_flight: Arc::new(Mutex::new(HashMap::new())),
|
||||
stream: framed,
|
||||
}
|
||||
sink: Box::new(framed_sink),
|
||||
};
|
||||
(framed_stream, cmd_mgr)
|
||||
}
|
||||
|
||||
pub fn decompose(self) -> impl ImapStream + 'a {
|
||||
let parts = self.stream.into_parts();
|
||||
parts.io
|
||||
pub fn flights(&self) -> Arc<Mutex<HashMap<usize, oneshot::Sender<()>>>> {
|
||||
self.in_flight.clone()
|
||||
}
|
||||
|
||||
pub async fn listen(&self) {
|
||||
loop {
|
||||
}
|
||||
pub fn decompose(self) -> impl ImapSink + Unpin + 'a {
|
||||
self.sink
|
||||
}
|
||||
|
||||
pub fn run(&mut self, command: Command) -> impl TryFuture {
|
||||
pub async fn capabilities(&mut self) -> Result<Vec<String>> {
|
||||
self.exec(Command {
|
||||
args: b"CAPABILITY".to_vec(),
|
||||
next_state: None,
|
||||
})
|
||||
.await?;
|
||||
Ok(vec![])
|
||||
}
|
||||
|
||||
pub async fn exec(&mut self, command: Command) -> Result<()> {
|
||||
let id = self.id;
|
||||
self.id += 1;
|
||||
|
||||
let cmd_str = String::from_utf8(command.args)?;
|
||||
self.sink.send(cmd_str).await?;
|
||||
|
||||
let (tx, rx) = oneshot::channel();
|
||||
{
|
||||
let mut in_flight = self.in_flight.lock();
|
||||
in_flight.insert(id, tx);
|
||||
}
|
||||
|
||||
async { rx.await }
|
||||
rx.await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ use tokio_stream::wrappers::WatchStream;
|
|||
|
||||
use crate::config::{Config, ConfigWatcher};
|
||||
|
||||
use self::imap::open_imap_connection;
|
||||
use self::imap2::open_imap_connection;
|
||||
|
||||
/// Command sent to the mail thread by something else (i.e. UI)
|
||||
pub enum MailCommand {
|
||||
|
@ -47,6 +47,7 @@ pub async fn run_mail(
|
|||
|
||||
let handle = tokio::spawn(async {
|
||||
for acct in config.mail_accounts.into_iter() {
|
||||
debug!("opening imap connection for {:?}", acct);
|
||||
open_imap_connection(acct.imap).await.unwrap();
|
||||
}
|
||||
});
|
||||
|
|
|
@ -21,7 +21,7 @@ struct Opt {
|
|||
#[tokio::main(flavor = "multi_thread")]
|
||||
async fn main() -> Result<()> {
|
||||
// parse command line arguments into options struct
|
||||
let opt = Opt::from_args();
|
||||
let _opt = Opt::from_args();
|
||||
|
||||
// print logs to file as directed by command line options
|
||||
use tracing_subscriber::filter::LevelFilter;
|
||||
|
|
|
@ -27,7 +27,7 @@ const FRAME: Duration = Duration::from_millis(20);
|
|||
pub struct Rect(u16, u16, u16, u16);
|
||||
|
||||
/// UI entrypoint.
|
||||
#[instrument]
|
||||
#[instrument(skip(w, exit))]
|
||||
pub async fn run_ui(mut w: impl Write + Debug, exit: ExitSender) -> Result<()> {
|
||||
execute!(w, cursor::Hide, terminal::EnterAlternateScreen)?;
|
||||
terminal::enable_raw_mode()?;
|
||||
|
|
Loading…
Reference in a new issue