123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211 |
- use std::collections::*;
- use std::convert::TryFrom;
- use std::default::Default;
- use std::io;
- use std::path::PathBuf;
-
- use futures::prelude::*;
-
- use ruma_identifiers::{self, RoomId};
-
- use tokio::fs as tokiofs;
- use tokio::prelude::*;
-
- use toml;
-
- // FIXME Some of these error types are only used in the main module.
- #[derive(Debug)]
- pub enum Error {
- // TODO Reconcile these different parse errors.
- ParseFile(PathBuf),
- ParseToml(toml::de::Error),
- ParseMalformed,
- ParseIdentifier,
- Io(io::Error),
- }
-
- impl From<io::Error> for Error {
- fn from(f: io::Error) -> Self {
- Self::Io(f)
- }
- }
-
- impl From<toml::de::Error> for Error {
- fn from(f: toml::de::Error) -> Self {
- Self::ParseToml(f)
- }
- }
-
- #[derive(Default, Debug)]
- pub struct Config {
- pub accounts: HashMap<String, Account>,
- pub spool_dirs: Vec<SpoolDir>,
- }
-
- #[derive(Clone, Debug)]
- pub struct Account {
- pub homeserver: String,
- pub display: Option<String>,
- pub device_id: Option<String>,
- pub auth: Auth,
- }
-
- #[derive(Clone, Debug)]
- pub enum Auth {
- UsernamePass(String, String),
- }
-
- #[derive(Debug)]
- pub struct SpoolDir {
- pub path: PathBuf,
- pub send_delay_sec: u32,
- pub sender_acct_label: String,
- pub dest_room_id: RoomId,
- }
-
- pub async fn find_configs(search_dir: &PathBuf) -> Result<Vec<PathBuf>, Error> {
- let items: Vec<tokiofs::DirEntry> = tokiofs::read_dir(search_dir).await?.try_collect().await?;
- Ok(items
- .into_iter()
- .filter(|de| {
- de.file_name()
- .to_str()
- .map(|s| s.ends_with(".toml"))
- .unwrap_or(false)
- })
- .map(|e| e.path())
- .collect())
- }
-
- pub async fn parse_configs(paths: Vec<PathBuf>) -> Result<Config, Error> {
- let mut conf = Config::default();
-
- for p in &paths {
- println!("Reading config: {}", p.to_str().unwrap_or("[non-UTF-8]"));
- let val = match load_toml(p).await {
- Ok(t) => t,
- Err(_) => {
- println!("Error loading config, skipping");
- continue;
- }
- };
-
- match parse_toml(&val) {
- Ok((accts, spools)) => {
- conf.accounts.extend(accts);
- conf.spool_dirs.extend(spools);
- }
- Err(_) => {
- println!("Error processing config, skipping");
- continue;
- }
- }
- }
-
- Ok(conf)
- }
-
- async fn load_toml(path: &PathBuf) -> Result<toml::Value, Error> {
- let mut buf = Vec::new();
- let mut f = tokiofs::File::open(path).await?;
- let _ = f.read_to_end(&mut buf).await?;
- toml::de::from_slice(buf.as_slice()).map_err(|_| Error::ParseFile(path.clone()))
- }
-
- fn parse_toml(val: &toml::Value) -> Result<(Vec<(String, Account)>, Vec<SpoolDir>), Error> {
- use toml::Value::*;
-
- let mut accts = Vec::new();
- let mut spools = Vec::new();
-
- match val {
- Table(tab) => {
- let acct_ents = tab.get("acct").cloned();
- let watch_ents = tab.get("watch").cloned();
-
- match acct_ents {
- Some(Array(entries)) => {
- for acct in &entries {
- accts.push(parse_acct_entry(&acct)?);
- }
- }
- // TODO
- Some(_) => {}
- _ => {}
- }
-
- match watch_ents {
- Some(Array(entries)) => {
- for wd in &entries {
- spools.push(parse_watch_entry(&wd)?);
- }
- }
- // TODO
- Some(_) => {}
- _ => {}
- }
- }
- _ => {}
- }
-
- Ok((accts, spools))
- }
-
- fn parse_acct_entry(ent: &toml::Value) -> Result<(String, Account), Error> {
- use toml::Value::*;
- type StdString = ::std::string::String;
-
- let label = ent.get("label");
- let homeserver = ent.get("homeserver");
- let display = ent.get("display");
- let dev_id = ent.get("deviceid");
- let username = ent.get("username");
- let password = ent.get("password");
-
- // This is gross and I don't like it, but ok.
- match (label, homeserver, username, password) {
- (Some(String(l)), Some(String(s)), Some(String(u)), Some(String(p))) => {
- let auth = Auth::UsernamePass(u.clone(), p.clone());
- Ok((
- l.clone(),
- Account {
- homeserver: s.clone(),
- display: display
- .cloned()
- .map(|v| v.try_into::<StdString>())
- .transpose()?,
- device_id: dev_id
- .cloned()
- .map(|v| v.try_into::<StdString>())
- .transpose()?,
- auth: auth,
- },
- ))
- }
- _ => Err(Error::ParseMalformed),
- }
- }
-
- fn parse_watch_entry(ent: &toml::Value) -> Result<SpoolDir, Error> {
- use toml::Value::*;
-
- let sender = ent.get("sender");
- let path = ent.get("path");
- let dest = ent.get("destroom");
- let delay = ent.get("delay");
-
- // Again this is gross, but whatever.
- match (path, sender, dest) {
- (Some(String(p)), Some(String(s)), Some(String(d))) => Ok(SpoolDir {
- path: PathBuf::from(p.clone()),
- send_delay_sec: delay
- .cloned()
- .map(|v| v.try_into::<u32>())
- .transpose()?
- .unwrap_or(0),
- sender_acct_label: s.clone(),
- dest_room_id: RoomId::try_from(d.as_str()).map_err(|_| Error::ParseIdentifier)?,
- }),
- _ => Err(Error::ParseMalformed),
- }
- }
|