d
This commit is contained in:
parent
2893b22d03
commit
04bd3e62b6
11 changed files with 306 additions and 2459 deletions
2522
Cargo.lock
generated
2522
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -1,5 +1,4 @@
|
|||
[workspace]
|
||||
members = [
|
||||
"panorama-gui",
|
||||
"panorama-core",
|
||||
"core",
|
||||
]
|
||||
|
|
12
core/Cargo.toml
Normal file
12
core/Cargo.toml
Normal file
|
@ -0,0 +1,12 @@
|
|||
[package]
|
||||
name = "panorama-core"
|
||||
version = "0.1.0"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.42"
|
||||
serde = { version = "1.0.126", features = ["derive"] }
|
||||
tokio = { version = "1.9.0", features = ["full"] }
|
||||
clap = "3.0.0-beta.2"
|
||||
futures = "0.3.16"
|
||||
inotify = { version = "0.9.3", features = ["stream"] }
|
76
core/src/config/mod.rs
Normal file
76
core/src/config/mod.rs
Normal file
|
@ -0,0 +1,76 @@
|
|||
mod watcher;
|
||||
|
||||
use std::path::{PathBuf, Path};
|
||||
use std::collections::HashMap;
|
||||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
|
||||
use anyhow::Result;
|
||||
|
||||
/// Configuration
|
||||
#[derive(Default, Serialize, Deserialize, Clone, Debug)]
|
||||
pub struct Config {
|
||||
/// Version of the config to use
|
||||
/// (potentially for migration later?)
|
||||
pub version: String,
|
||||
/// Directory to store panorama-related data in
|
||||
pub data_dir: PathBuf,
|
||||
/// Mail accounts
|
||||
#[serde(rename = "mail")]
|
||||
pub mail_accounts: HashMap<String, MailAccountConfig>,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub async fn from_file(path: impl AsRef<Path>) -> Result<Self> {
|
||||
let mut file = File::open(path.as_ref())?;
|
||||
let mut contents = Vec::new();
|
||||
file.read_to_end(&mut contents)?;
|
||||
let config = toml::from_slice(&contents)?;
|
||||
Ok(config)
|
||||
}
|
||||
}
|
||||
|
||||
/// Configuration for a single mail account
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
pub struct MailAccountConfig {
|
||||
/// Imap
|
||||
pub imap: ImapConfig,
|
||||
}
|
||||
|
||||
/// Configuring an IMAP server
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
pub struct ImapConfig {
|
||||
/// Host of the IMAP server (needs to be hostname for TLS)
|
||||
pub server: String,
|
||||
/// Port of the IMAP server
|
||||
pub port: u16,
|
||||
/// TLS
|
||||
pub tls: TlsMethod,
|
||||
/// Auth
|
||||
#[serde(flatten)]
|
||||
pub auth: ImapAuth,
|
||||
}
|
||||
|
||||
/// Method of authentication for the IMAP server
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
#[serde(tag = "auth")]
|
||||
pub enum ImapAuth {
|
||||
/// Use plain username/password authentication
|
||||
#[serde(rename = "plain")]
|
||||
#[allow(missing_docs)]
|
||||
Plain { username: String, password: String },
|
||||
}
|
||||
|
||||
/// Describes when to perform the TLS handshake
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
pub enum TlsMethod {
|
||||
/// Perform TLS handshake immediately upon connection
|
||||
#[serde(rename = "on")]
|
||||
On,
|
||||
/// Perform TLS handshake after issuing the STARTTLS command
|
||||
#[serde(rename = "starttls")]
|
||||
Starttls,
|
||||
/// Don't perform TLS handshake at all (unsecured)
|
||||
#[serde(rename = "off")]
|
||||
Off,
|
||||
}
|
83
core/src/config/watcher.rs
Normal file
83
core/src/config/watcher.rs
Normal file
|
@ -0,0 +1,83 @@
|
|||
use std::path::{Path, PathBuf};
|
||||
|
||||
use anyhow::{Result, Context};
|
||||
use futures::{future::TryFutureExt, stream::StreamExt};
|
||||
use inotify::Inotify;
|
||||
use tokio::{task::JoinHandle, sync::watch};
|
||||
|
||||
use super::Config;
|
||||
|
||||
pub type ConfigWatcher = watch::Receiver<Config>;
|
||||
|
||||
/// 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_system() -> Result<(JoinHandle<()>, ConfigWatcher)> {
|
||||
let mut inotify = Inotify::init()?;
|
||||
let xdg = BaseDirectories::new()?;
|
||||
let config_home = xdg.get_config_home().join("panorama");
|
||||
if !config_home.exists() {
|
||||
fs::create_dir_all(&config_home)?;
|
||||
}
|
||||
inotify
|
||||
.add_watch(&config_home, WatchMask::CLOSE_WRITE)
|
||||
.context("adding watch for config home")?;
|
||||
// let config_file_path = config_home.join("panorama.toml");
|
||||
// if config_file_path.exists() {
|
||||
// inotify
|
||||
// .add_watch(config_file_path, WatchMask::ALL_EVENTS)
|
||||
// .context("adding watch for config file")?;
|
||||
// }
|
||||
debug!("watching {:?}", config_home);
|
||||
let (config_tx, config_update) = watch::channel(Config::default());
|
||||
let handle = tokio::spawn(
|
||||
start_inotify_stream(inotify, config_home, config_tx).unwrap_or_else(report_err),
|
||||
);
|
||||
Ok((handle, config_update))
|
||||
}
|
||||
|
||||
async fn start_inotify_stream(
|
||||
mut inotify: Inotify,
|
||||
config_home: impl AsRef<Path>,
|
||||
config_tx: watch::Sender<Config>,
|
||||
) -> Result<()> {
|
||||
let mut buffer = vec![0u8; 1024];
|
||||
let mut event_stream = inotify.event_stream(&mut buffer)?;
|
||||
let config_home = config_home.as_ref().to_path_buf();
|
||||
let config_path = config_home.join("panorama.toml");
|
||||
|
||||
// first shot
|
||||
{
|
||||
let config = Config::from_file(&config_path).await?;
|
||||
config_tx.send(config)?;
|
||||
}
|
||||
|
||||
debug!("listening for inotify events");
|
||||
while let Some(v) = event_stream.next().await {
|
||||
let event = v.context("event")?;
|
||||
debug!("inotify event: {:?}", event);
|
||||
if let Some(name) = event.name {
|
||||
let path = PathBuf::from(name);
|
||||
let path_c = config_home
|
||||
.clone()
|
||||
.join(path.clone())
|
||||
.canonicalize()
|
||||
.context("osu")?;
|
||||
if !path_c.exists() {
|
||||
debug!("path {:?} doesn't exist", path_c);
|
||||
continue;
|
||||
}
|
||||
// TODO: any better way to do this?
|
||||
let config_path_c = config_path.canonicalize().context("cfg_path")?;
|
||||
if config_path_c != path_c {
|
||||
debug!("did not match {:?} {:?}", config_path_c, path_c);
|
||||
continue;
|
||||
}
|
||||
|
||||
debug!("reading config from {:?}", path_c);
|
||||
let config = Config::from_file(path_c).await.context("read")?;
|
||||
// debug!("sending config {:?}", config);
|
||||
config_tx.send(config)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
0
core/src/imap.rs
Normal file
0
core/src/imap.rs
Normal file
37
core/src/main.rs
Normal file
37
core/src/main.rs
Normal file
|
@ -0,0 +1,37 @@
|
|||
#[macro_use]
|
||||
extern crate serde;
|
||||
extern crate futures;
|
||||
|
||||
mod config;
|
||||
mod imap;
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
use clap::Clap;
|
||||
use anyhow::Result;
|
||||
|
||||
use crate::config::Config;
|
||||
|
||||
/// Panorama core
|
||||
#[derive(Debug, Clap)]
|
||||
struct Options {
|
||||
/// Config file path (defaults to XDG)
|
||||
#[clap(long = "config", short = 'c')]
|
||||
config_file: Option<PathBuf>,
|
||||
}
|
||||
|
||||
async fn run_with_config(config: Config) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
let opts = Options::parse();
|
||||
println!("{:?}", opts);
|
||||
|
||||
let (tx, mut rx) = watch::channel(());
|
||||
|
||||
loop {
|
||||
|
||||
}
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
[package]
|
||||
name = "panorama-core"
|
||||
version = "0.1.0"
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
|
@ -1,5 +0,0 @@
|
|||
pub struct Panorama {}
|
||||
|
||||
impl Panorama {
|
||||
pub fn init() {}
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
[package]
|
||||
name = "panorama-gui"
|
||||
version = "0.1.0"
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.42"
|
||||
iced = "0.3.0"
|
||||
iced_native = "0.4.0"
|
||||
structopt = "0.3.22"
|
||||
tokio = { version = "1.8.2", features = ["full"] }
|
|
@ -1,6 +0,0 @@
|
|||
use anyhow::Result;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
Loading…
Reference in a new issue