Add some documentation
This commit is contained in:
parent
371d6fb356
commit
e980c597f1
6 changed files with 54 additions and 22 deletions
2
Justfile
Normal file
2
Justfile
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
doc:
|
||||||
|
cargo doc --document-private-items
|
|
@ -1,3 +1,7 @@
|
||||||
|
//! Module for setting up config files and watchers.
|
||||||
|
//!
|
||||||
|
//! One of the primary goals of panorama is to be able to always hot-reload configuration files.
|
||||||
|
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::sync::mpsc::{self, Receiver};
|
use std::sync::mpsc::{self, Receiver};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
@ -9,14 +13,22 @@ use notify::{DebouncedEvent, RecommendedWatcher, RecursiveMode, Watcher};
|
||||||
use tokio::sync::watch;
|
use tokio::sync::watch;
|
||||||
use xdg::BaseDirectories;
|
use xdg::BaseDirectories;
|
||||||
|
|
||||||
|
/// Alias for a MailConfig receiver.
|
||||||
pub type ConfigWatcher = watch::Receiver<Option<MailConfig>>;
|
pub type ConfigWatcher = watch::Receiver<Option<MailConfig>>;
|
||||||
|
|
||||||
|
/// Configuration
|
||||||
#[derive(Default, Serialize, Deserialize, Clone, Debug)]
|
#[derive(Default, Serialize, Deserialize, Clone, Debug)]
|
||||||
pub struct MailConfig {
|
pub struct MailConfig {
|
||||||
|
/// Host of the IMAP server (needs to be hostname for TLS)
|
||||||
pub server: String,
|
pub server: String,
|
||||||
|
|
||||||
|
/// Port of the IMAP server
|
||||||
pub port: u16,
|
pub port: u16,
|
||||||
|
|
||||||
|
/// Username for authenticating to IMAP
|
||||||
pub username: String,
|
pub username: String,
|
||||||
|
|
||||||
|
/// Password for authenticating to IMAP
|
||||||
pub password: String,
|
pub password: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,6 +58,10 @@ async fn read_config(path: impl AsRef<Path>) -> Result<MailConfig> {
|
||||||
Ok(config)
|
Ok(config)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The inner loop of the watcher, which is responsible for taking events received by the watcher
|
||||||
|
/// and trying to parse and return the config.
|
||||||
|
///
|
||||||
|
/// This exists so all errors are able to be caught in one go.
|
||||||
async fn watcher_loop(
|
async fn watcher_loop(
|
||||||
fs_events: Receiver<DebouncedEvent>,
|
fs_events: Receiver<DebouncedEvent>,
|
||||||
config_tx: watch::Sender<Option<MailConfig>>,
|
config_tx: watch::Sender<Option<MailConfig>>,
|
||||||
|
@ -67,6 +83,8 @@ async fn watcher_loop(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Start the entire config watcher system, and return a [ConfigWatcher][self::ConfigWatcher],
|
||||||
|
/// which is a cloneable receiver of config update events.
|
||||||
pub fn spawn_config_watcher() -> Result<ConfigWatcher> {
|
pub fn spawn_config_watcher() -> Result<ConfigWatcher> {
|
||||||
let (_watcher, config_rx) = start_watcher()?;
|
let (_watcher, config_rx) = start_watcher()?;
|
||||||
let (config_tx, config_update) = watch::channel(None);
|
let (config_tx, config_update) = watch::channel(None);
|
||||||
|
|
20
src/lib.rs
Normal file
20
src/lib.rs
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
//! Panorama
|
||||||
|
//! ===
|
||||||
|
|
||||||
|
#![deny(missing_docs)]
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate anyhow;
|
||||||
|
#[macro_use]
|
||||||
|
extern crate crossterm;
|
||||||
|
#[macro_use]
|
||||||
|
extern crate log;
|
||||||
|
#[macro_use]
|
||||||
|
extern crate serde;
|
||||||
|
|
||||||
|
pub mod config;
|
||||||
|
pub mod mail;
|
||||||
|
pub mod ui;
|
||||||
|
|
||||||
|
/// A cloneable type that allows sending an exit-"signal" to stop the application.
|
||||||
|
pub type ExitSender = tokio::sync::oneshot::Sender<()>;
|
|
@ -1,3 +1,5 @@
|
||||||
|
//! Mail
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
@ -24,8 +26,12 @@ use tokio_util::codec::{Decoder, LinesCodec, LinesCodecError};
|
||||||
|
|
||||||
use crate::config::{MailConfig, ConfigWatcher};
|
use crate::config::{MailConfig, ConfigWatcher};
|
||||||
|
|
||||||
|
/// Command sent to the mail thread by something else (i.e. UI)
|
||||||
pub enum MailCommand {
|
pub enum MailCommand {
|
||||||
|
/// Refresh the list
|
||||||
Refresh,
|
Refresh,
|
||||||
|
|
||||||
|
/// Send a raw command
|
||||||
Raw(Command),
|
Raw(Command),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
27
src/main.rs
27
src/main.rs
|
@ -1,16 +1,3 @@
|
||||||
#[macro_use]
|
|
||||||
extern crate anyhow;
|
|
||||||
#[macro_use]
|
|
||||||
extern crate crossterm;
|
|
||||||
#[macro_use]
|
|
||||||
extern crate log;
|
|
||||||
#[macro_use]
|
|
||||||
extern crate serde;
|
|
||||||
|
|
||||||
mod config;
|
|
||||||
mod mail;
|
|
||||||
mod ui;
|
|
||||||
|
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
@ -20,10 +7,7 @@ use futures::future::TryFutureExt;
|
||||||
use structopt::StructOpt;
|
use structopt::StructOpt;
|
||||||
use tokio::sync::{mpsc, oneshot};
|
use tokio::sync::{mpsc, oneshot};
|
||||||
use xdg::BaseDirectories;
|
use xdg::BaseDirectories;
|
||||||
|
use panorama::config::{spawn_config_watcher, MailConfig};
|
||||||
use crate::config::{spawn_config_watcher, MailConfig};
|
|
||||||
|
|
||||||
type ExitSender = oneshot::Sender<()>;
|
|
||||||
|
|
||||||
#[derive(Debug, StructOpt)]
|
#[derive(Debug, StructOpt)]
|
||||||
#[structopt(author, about)]
|
#[structopt(author, about)]
|
||||||
|
@ -48,7 +32,6 @@ async fn main() -> Result<()> {
|
||||||
let xdg = BaseDirectories::new()?;
|
let xdg = BaseDirectories::new()?;
|
||||||
let config_update = spawn_config_watcher()?;
|
let config_update = spawn_config_watcher()?;
|
||||||
|
|
||||||
let config = MailConfig::default();
|
|
||||||
// let config: MailConfig = {
|
// let config: MailConfig = {
|
||||||
// let config_path = opt
|
// let config_path = opt
|
||||||
// .config_path
|
// .config_path
|
||||||
|
@ -74,10 +57,6 @@ async fn main() -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn report_err(err: anyhow::Error) {
|
|
||||||
error!("error: {:?}", err);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn setup_logger(opt: &Opt) -> Result<()> {
|
fn setup_logger(opt: &Opt) -> Result<()> {
|
||||||
let mut fern = fern::Dispatch::new()
|
let mut fern = fern::Dispatch::new()
|
||||||
.format(|out, message, record| {
|
.format(|out, message, record| {
|
||||||
|
@ -98,3 +77,7 @@ fn setup_logger(opt: &Opt) -> Result<()> {
|
||||||
fern.apply()?;
|
fern.apply()?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn report_err(err: anyhow::Error) {
|
||||||
|
log::error!("error: {:?}", err);
|
||||||
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
//! UI
|
||||||
|
|
||||||
mod table;
|
mod table;
|
||||||
|
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
@ -23,6 +25,7 @@ const FRAME: Duration = Duration::from_millis(20);
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
pub struct Rect(u16, u16, u16, u16);
|
pub struct Rect(u16, u16, u16, u16);
|
||||||
|
|
||||||
|
/// UI entrypoint.
|
||||||
pub async fn run_ui(mut w: impl Write, exit: ExitSender) -> Result<()> {
|
pub async fn run_ui(mut w: impl Write, exit: ExitSender) -> Result<()> {
|
||||||
execute!(w, cursor::Hide, terminal::EnterAlternateScreen)?;
|
execute!(w, cursor::Hide, terminal::EnterAlternateScreen)?;
|
||||||
terminal::enable_raw_mode()?;
|
terminal::enable_raw_mode()?;
|
||||||
|
|
Loading…
Reference in a new issue