This commit is contained in:
Michael Zhang 2021-02-12 02:54:19 -06:00
parent 16545a4dd1
commit b232bebed5
Signed by: michael
GPG key ID: BDA47A31A3C8EE6B
7 changed files with 38 additions and 202 deletions

View file

@ -1,29 +0,0 @@
use std::ops::Deref;
use crate::config::ConfigWatcher;
#[async_trait]
pub trait AnyApp {
fn say_hello(&self) {
debug!("hello from app");
}
}
/// An App is usually associated with a particular separate service or piece of functionality.
pub struct App<A: AppI> {
inner: A,
// config_watcher: ConfigWatcher<A::Config>,
}
impl<A: AppI> App<A> {
pub fn new(app: A) -> Self {
App { inner: app }
}
}
/// The interface that anything that wants to become an App must implement
pub trait AppI: Sized {
type Config;
}
impl<A: AppI> AnyApp for App<A> {}

View file

@ -1,25 +0,0 @@
use std::marker::PhantomData;
use std::sync::mpsc::channel;
use std::time::Duration;
use anyhow::Result;
use notify::{DebouncedEvent, RecursiveMode, Watcher};
use xdg::BaseDirectories;
pub struct ConfigWatcher<C> {
_ty: PhantomData<C>,
}
pub fn watch_config() -> Result<()> {
let (tx, rx) = channel();
let xdg = BaseDirectories::new()?;
let config_home = xdg.get_config_home();
let mut watcher = notify::watcher(tx, Duration::from_secs(5))?;
watcher.watch(config_home, RecursiveMode::Recursive)?;
loop {
let evt = rx.recv()?;
}
Ok(())
}

View file

@ -1 +0,0 @@
pub enum Event {}

View file

@ -1,21 +0,0 @@
use std::collections::HashMap;
use lettre::SmtpClient;
use anyhow::Result;
use crate::app::AppI;
pub struct MailApp {
client: SmtpClient,
}
impl AppI for MailApp {
type Config = ();
}
impl MailApp {
pub fn new(domain: impl AsRef<str>) -> Result<Self> {
let client = SmtpClient::new_simple(domain.as_ref())?;
Ok(MailApp { client })
}
}

View file

@ -1,65 +1,23 @@
#[macro_use]
extern crate async_trait;
#[macro_use]
extern crate cfg_if;
#[macro_use]
extern crate crossterm;
#[macro_use]
extern crate log;
#[macro_use]
extern crate pin_project;
mod app;
mod config;
mod event;
mod mailapp;
mod panorama;
mod ui;
use std::io;
use std::sync::mpsc::channel;
use std::thread;
use anyhow::Result;
use tokio::runtime::Runtime;
use lettre::SmtpClient;
use tokio::sync::oneshot;
use crate::panorama::Panorama;
use crate::config::watch_config;
use crate::ui::Ui;
type ExitSender = oneshot::Sender<()>;
fn main() -> Result<()> {
fern::Dispatch::new()
.format(|out, message, record| {
out.finish(format_args!(
"{}[{}][{}] {}",
chrono::Local::now().format("[%Y-%m-%d][%H:%M:%S]"),
record.target(),
record.level(),
message
))
})
.level(log::LevelFilter::Debug)
.chain(fern::log_file("output.log")?)
.apply()?;
#[tokio::main]
async fn main() -> Result<()> {
SmtpClient::new_simple("");
let runtime = Runtime::new()?;
thread::spawn(move || {
let panorama = Panorama::new().unwrap();
runtime.block_on(panorama.run());
});
let (exit_tx, exit_rx) = oneshot::channel::<()>();
let stdout = io::stdout();
let (evts_tx, evts_rx) = channel();
// spawn a thread for listening to configuration changes
thread::spawn(move || {
watch_config();
});
info!("poggers");
// run the ui on the main thread
let ui = Ui::init(stdout, evts_rx)?;
ui.run()?;
let stdout = std::io::stdout();
tokio::spawn(ui::run_ui(stdout, exit_tx));
exit_rx.await?;
Ok(())
}

View file

@ -1,37 +0,0 @@
use std::any::{Any, TypeId};
use std::collections::HashMap;
use anyhow::Result;
use crate::mailapp::MailApp;
use crate::app::{AnyApp, App};
pub struct Panorama {
apps: Vec<Box<dyn AnyApp>>,
}
impl Panorama {
pub fn new() -> Result<Panorama> {
let mut apps = Vec::new();
let mail = MailApp::new("mzhang.io")?;
let mail = Box::new(App::new(mail)) as Box<dyn AnyApp>;
apps.push(mail);
Ok(Panorama {
apps,
})
}
pub async fn run(&self) -> Result<()> {
debug!("starting all apps...");
loop {
self.apps.iter().map(|app| {
app.say_hello();
});
}
Ok(())
}
}

View file

@ -1,60 +1,51 @@
use std::io::Write;
use std::sync::mpsc::Receiver;
use std::time::Duration;
use anyhow::Result;
use chrono::Local;
use crossterm::{
event,
terminal::{self, EnterAlternateScreen},
cursor,
event::{self, Event, KeyCode, KeyEvent},
style, terminal,
};
use tokio::time;
use crate::event::Event;
use crate::ExitSender;
pub struct Ui<S: Write> {
screen: S,
evts: Receiver<Event>,
}
const FRAME: Duration = Duration::from_millis(16);
impl<S: Write> Ui<S> {
pub fn init(mut screen: S, evts: Receiver<Event>) -> Result<Self> {
execute!(screen, EnterAlternateScreen)?;
terminal::enable_raw_mode()?;
pub async fn run_ui(mut w: impl Write, exit: ExitSender) -> Result<()> {
execute!(w, cursor::Hide, terminal::EnterAlternateScreen)?;
terminal::enable_raw_mode()?;
Ok(Ui { screen, evts })
}
loop {
execute!(w, cursor::MoveTo(0, 0))?;
pub fn run(mut self) -> Result<()> {
use crossterm::event::{Event, KeyCode, KeyEvent};
let now = Local::now();
println!("shiet {}", now);
loop {
// check for new events
use std::sync::mpsc::TryRecvError;
match self.evts.try_recv() {
Ok(evt) => {}
Err(TryRecvError::Empty) => {} // skip
Err(TryRecvError::Disconnected) => todo!("impossible?"),
}
// approx 60fps
time::sleep(FRAME).await;
// read events from the terminal
if event::poll(FRAME)? {
match event::read()? {
Event::Key(KeyEvent {
code: KeyCode::Char('q'),
..
}) => {
break;
}
}) => break,
_ => {}
}
}
Ok(())
}
}
impl<S: Write> Drop for Ui<S> {
fn drop(&mut self) {
use crossterm::{cursor::Show, style::ResetColor, terminal::LeaveAlternateScreen};
execute!(
w,
style::ResetColor,
cursor::Show,
terminal::LeaveAlternateScreen
)?;
terminal::disable_raw_mode()?;
execute!(self.screen, ResetColor, Show, LeaveAlternateScreen,).unwrap();
terminal::disable_raw_mode().unwrap();
}
exit.send(()).expect("fake news?");
Ok(())
}