what the heck

This commit is contained in:
Michael Zhang 2021-02-19 19:44:04 -06:00
parent dbe92e4962
commit f5d3a89641
Signed by: michael
GPG key ID: BDA47A31A3C8EE6B
9 changed files with 119 additions and 25 deletions

23
Cargo.lock generated
View file

@ -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",

View file

@ -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"

View file

@ -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:

View file

@ -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")?;

View file

@ -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;

View file

@ -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(())
}
}

View file

@ -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();
}
});

View file

@ -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;

View file

@ -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()?;