|
|
@@ -13,13 +13,14 @@ use tokio::prelude::*; |
|
|
|
|
|
|
|
use toml; |
|
|
|
|
|
|
|
use serde::Deserialize; |
|
|
|
|
|
|
|
// 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), |
|
|
|
} |
|
|
@@ -63,6 +64,27 @@ pub struct SpoolDir { |
|
|
|
pub dest_room_id: RoomId, |
|
|
|
} |
|
|
|
|
|
|
|
#[derive(Clone, Debug, Deserialize)] |
|
|
|
pub struct ConfigFile { |
|
|
|
acct: Vec<ConfigAccount>, |
|
|
|
watch: Vec<ConfigWatch>, |
|
|
|
} |
|
|
|
|
|
|
|
#[derive(Clone, Debug, Deserialize)] |
|
|
|
pub struct ConfigAccount { |
|
|
|
label: String, |
|
|
|
homeserver: String, |
|
|
|
username: String, |
|
|
|
password: String, |
|
|
|
} |
|
|
|
|
|
|
|
#[derive(Clone, Debug, Deserialize)] |
|
|
|
pub struct ConfigWatch { |
|
|
|
sender: String, |
|
|
|
path: String, |
|
|
|
destroom: String, |
|
|
|
} |
|
|
|
|
|
|
|
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 |
|
|
@@ -77,135 +99,59 @@ pub async fn find_configs(search_dir: &PathBuf) -> Result<Vec<PathBuf>, Error> { |
|
|
|
.collect()) |
|
|
|
} |
|
|
|
|
|
|
|
pub async fn parse_configs(paths: Vec<PathBuf>) -> Result<Config, Error> { |
|
|
|
pub async fn parse_configs(paths: &Vec<PathBuf>) -> Result<Config, Error> { |
|
|
|
let mut conf = Config::default(); |
|
|
|
|
|
|
|
for p in &paths { |
|
|
|
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"); |
|
|
|
println!("warning: error loading config, skipping: {:?}", p); |
|
|
|
continue; |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
match parse_toml(&val) { |
|
|
|
Ok((accts, spools)) => { |
|
|
|
conf.accounts.extend(accts); |
|
|
|
conf.spool_dirs.extend(spools); |
|
|
|
} |
|
|
|
Err(_) => { |
|
|
|
println!("Error processing config, skipping"); |
|
|
|
// Ingest the accounts. |
|
|
|
for a in val.acct { |
|
|
|
if conf.accounts.contains_key(&a.label) { |
|
|
|
eprintln!("warning: ignoring duplicate account entry for {}", a.label); |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
let acct = Account { |
|
|
|
homeserver: a.homeserver, |
|
|
|
auth: Auth::UsernamePass(a.username, a.password), |
|
|
|
device_id: None, |
|
|
|
display: None, |
|
|
|
}; |
|
|
|
|
|
|
|
conf.accounts.insert(a.label, acct); |
|
|
|
} |
|
|
|
|
|
|
|
// Ingest the watches. |
|
|
|
for s in val.watch { |
|
|
|
let sd = SpoolDir { |
|
|
|
path: PathBuf::from(s.path), |
|
|
|
send_delay_sec: 0, |
|
|
|
sender_acct_label: s.sender, |
|
|
|
dest_room_id: RoomId::try_from(s.destroom.as_str()) |
|
|
|
.map_err(|_| Error::ParseIdentifier)?, |
|
|
|
}; |
|
|
|
|
|
|
|
conf.spool_dirs.push(sd); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
Ok(conf) |
|
|
|
} |
|
|
|
|
|
|
|
async fn load_toml(path: &PathBuf) -> Result<toml::Value, Error> { |
|
|
|
async fn load_toml(path: &PathBuf) -> Result<ConfigFile, 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), |
|
|
|
} |
|
|
|
toml::from_slice(&buf).map_err(|e| { |
|
|
|
eprintln!("warning: parsing file {:?}", e); |
|
|
|
Error::ParseFile(path.clone()) |
|
|
|
}) |
|
|
|
} |