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] #[macro_use]
extern crate async_trait;
#[macro_use]
extern crate cfg_if;
#[macro_use]
extern crate crossterm; 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; mod ui;
use std::io;
use std::sync::mpsc::channel;
use std::thread;
use anyhow::Result; use anyhow::Result;
use tokio::runtime::Runtime; use lettre::SmtpClient;
use tokio::sync::oneshot;
use crate::panorama::Panorama; type ExitSender = oneshot::Sender<()>;
use crate::config::watch_config;
use crate::ui::Ui;
fn main() -> Result<()> { #[tokio::main]
fern::Dispatch::new() async fn main() -> Result<()> {
.format(|out, message, record| { SmtpClient::new_simple("");
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()?;
let runtime = Runtime::new()?; let (exit_tx, exit_rx) = oneshot::channel::<()>();
thread::spawn(move || {
let panorama = Panorama::new().unwrap();
runtime.block_on(panorama.run());
});
let stdout = io::stdout(); let stdout = std::io::stdout();
let (evts_tx, evts_rx) = channel(); tokio::spawn(ui::run_ui(stdout, exit_tx));
// 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()?;
exit_rx.await?;
Ok(()) 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::io::Write;
use std::sync::mpsc::Receiver; use std::time::Duration;
use anyhow::Result; use anyhow::Result;
use chrono::Local;
use crossterm::{ use crossterm::{
event, cursor,
terminal::{self, EnterAlternateScreen}, event::{self, Event, KeyCode, KeyEvent},
style, terminal,
}; };
use tokio::time;
use crate::event::Event; use crate::ExitSender;
pub struct Ui<S: Write> { const FRAME: Duration = Duration::from_millis(16);
screen: S,
evts: Receiver<Event>,
}
impl<S: Write> Ui<S> { pub async fn run_ui(mut w: impl Write, exit: ExitSender) -> Result<()> {
pub fn init(mut screen: S, evts: Receiver<Event>) -> Result<Self> { execute!(w, cursor::Hide, terminal::EnterAlternateScreen)?;
execute!(screen, EnterAlternateScreen)?; terminal::enable_raw_mode()?;
terminal::enable_raw_mode()?;
Ok(Ui { screen, evts }) loop {
} execute!(w, cursor::MoveTo(0, 0))?;
pub fn run(mut self) -> Result<()> { let now = Local::now();
use crossterm::event::{Event, KeyCode, KeyEvent}; println!("shiet {}", now);
loop { // approx 60fps
// check for new events time::sleep(FRAME).await;
use std::sync::mpsc::TryRecvError;
match self.evts.try_recv() {
Ok(evt) => {}
Err(TryRecvError::Empty) => {} // skip
Err(TryRecvError::Disconnected) => todo!("impossible?"),
}
// read events from the terminal if event::poll(FRAME)? {
match event::read()? { match event::read()? {
Event::Key(KeyEvent { Event::Key(KeyEvent {
code: KeyCode::Char('q'), code: KeyCode::Char('q'),
.. ..
}) => { }) => break,
break;
}
_ => {} _ => {}
} }
} }
Ok(())
} }
}
impl<S: Write> Drop for Ui<S> { execute!(
fn drop(&mut self) { w,
use crossterm::{cursor::Show, style::ResetColor, terminal::LeaveAlternateScreen}; style::ResetColor,
cursor::Show,
terminal::LeaveAlternateScreen
)?;
terminal::disable_raw_mode()?;
execute!(self.screen, ResetColor, Show, LeaveAlternateScreen,).unwrap(); exit.send(()).expect("fake news?");
terminal::disable_raw_mode().unwrap(); Ok(())
}
} }