Browse Source

Use serde to parse the config instead of manually parsing it.

master
treyzania 2 years ago
parent
commit
99f8aca9fe
4 changed files with 62 additions and 112 deletions
  1. 3
    0
      Cargo.lock
  2. 1
    0
      Cargo.toml
  3. 57
    111
      src/config.rs
  4. 1
    1
      src/main.rs

+ 3
- 0
Cargo.lock View File

@@ -1,5 +1,7 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3

[[package]]
name = "ansi_term"
version = "0.12.1"
@@ -597,6 +599,7 @@ dependencies = [
"ruma-client-api",
"ruma-events",
"ruma-identifiers",
"serde",
"tokio 0.2.16",
"tokio-inotify",
"toml",

+ 1
- 0
Cargo.toml View File

@@ -16,6 +16,7 @@ ruma-client = "0.3.0"
ruma-client-api = "0.6.0"
ruma-events = "0.15.0"
ruma-identifiers = "0.14.1"
serde = { version = "1", features = ["derive"] }
tokio = { version = "0.2.16", features = ["fs", "io-driver", "net", "rt-core", "sync"] }
tokio-inotify = "0.4.1"
toml = "0.5.6"

+ 57
- 111
src/config.rs View File

@@ -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())
})
}

+ 1
- 1
src/main.rs View File

@@ -69,7 +69,7 @@ fn main() {
}

// Process configuration.
let config = match rt.block_on(parse_configs(confs)) {
let config = match rt.block_on(parse_configs(&confs)) {
Ok(c) => c,
Err(e) => {
println!("Error parsing configuration: {:?}", e);

Loading…
Cancel
Save