Osuation
This commit is contained in:
parent
16545a4dd1
commit
b232bebed5
7 changed files with 38 additions and 202 deletions
29
src/app.rs
29
src/app.rs
|
@ -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> {}
|
|
@ -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(())
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
pub enum Event {}
|
|
@ -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 })
|
||||
}
|
||||
}
|
62
src/main.rs
62
src/main.rs
|
@ -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(())
|
||||
}
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
}
|
67
src/ui.rs
67
src/ui.rs
|
@ -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)?;
|
||||
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 })
|
||||
}
|
||||
|
||||
pub fn run(mut self) -> Result<()> {
|
||||
use crossterm::event::{Event, KeyCode, KeyEvent};
|
||||
|
||||
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?"),
|
||||
}
|
||||
execute!(w, cursor::MoveTo(0, 0))?;
|
||||
|
||||
// read events from the terminal
|
||||
let now = Local::now();
|
||||
println!("shiet {}", now);
|
||||
|
||||
// approx 60fps
|
||||
time::sleep(FRAME).await;
|
||||
|
||||
if event::poll(FRAME)? {
|
||||
match event::read()? {
|
||||
Event::Key(KeyEvent {
|
||||
code: KeyCode::Char('q'),
|
||||
..
|
||||
}) => {
|
||||
break;
|
||||
}
|
||||
}) => break,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
execute!(
|
||||
w,
|
||||
style::ResetColor,
|
||||
cursor::Show,
|
||||
terminal::LeaveAlternateScreen
|
||||
)?;
|
||||
terminal::disable_raw_mode()?;
|
||||
|
||||
exit.send(()).expect("fake news?");
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: Write> Drop for Ui<S> {
|
||||
fn drop(&mut self) {
|
||||
use crossterm::{cursor::Show, style::ResetColor, terminal::LeaveAlternateScreen};
|
||||
|
||||
execute!(self.screen, ResetColor, Show, LeaveAlternateScreen,).unwrap();
|
||||
terminal::disable_raw_mode().unwrap();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue