Parse config file and act upon

All dependencies were updated.

An example configuration file `config.yml` is added to show the
configuration options. Following locations are checked:
- `/etc/webhookey/config.yml`
- `<config_dir>/webhookey/config.yml`
- `./config.yml`

Whereas `<config_dir>` is depending on the platform:
- Linux:   `$XDG_CONFIG_HOME` or `$HOME/.config`
- macOS:   `$HOME/Library/Application Support`
- Windows: `{FOLDERID_RoamingAppData}`

Each hook's action is executed if all of the specified filters match.
This commit is contained in:
finga 2021-03-03 15:24:46 +01:00
parent d8ca63ab37
commit c8505b27c5
4 changed files with 440 additions and 137 deletions

View file

@ -1,24 +1,121 @@
#![feature(proc_macro_hygiene, decl_macro)]
use rocket::{get, post, routes};
use anyhow::{anyhow, bail, Result};
use regex::Regex;
use rocket::{fairing::AdHoc, get, post, routes, State};
use rocket_contrib::json::Json;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::fs::File;
use std::io::BufReader;
use std::process::Command;
#[derive(Debug, Deserialize, Serialize)]
struct Data (serde_json::Value);
struct Config {
hooks: HashMap<String, Hook>,
}
#[derive(Debug, Deserialize, Serialize)]
struct Hook {
action: Option<String>,
filters: HashMap<String, Filter>,
}
#[derive(Debug, Deserialize, Serialize)]
struct Filter {
pointer: String,
regex: String,
}
#[derive(Debug, Deserialize, Serialize)]
struct Data(serde_json::Value);
#[get("/")]
fn index() -> &'static str {
"Hello, webhookey!"
}
#[post("/", format = "json", data = "<data>")]
fn receive_hook(data: Json<Data>) -> String {
format!("data: {:?}", data)
fn execute_hook(name: &str, hook: &Hook, data: &serde_json::Value) -> Result<()> {
println!(
"hook: {:?} action: {:?} filters: {:?}",
name, hook.action, hook.filters
);
for (filter_name, filter) in hook.filters.iter() {
println!("filter: {:?}", filter);
if let Some(value) = data.pointer(&filter.pointer) {
let regex = Regex::new(&filter.regex)?;
if let Some(value) = value.as_str() {
if !regex.is_match(value) {
return Ok(());
}
} else {
println!("Error could not parse pointer");
anyhow!(
"Could not parse pointer in hook `{}` from filter `{}`",
name,
filter_name
);
}
}
}
println!("execute action");
if let Some(action) = &hook.action {
println!("execute {}", action);
Command::new(action).spawn()?;
}
Ok(())
}
fn main() {
#[post("/", format = "json", data = "<data>")]
fn receive_hook(config: State<Config>, data: Json<Data>) -> Result<String> {
let data = serde_json::to_value(data.0)?;
for (hook_name, hook) in config.hooks.iter() {
execute_hook(&hook_name, &hook, &data)?;
}
Ok("Request received.".to_string())
}
fn get_config() -> Result<File> {
if let Ok(config) = File::open("/etc/webhookey/config.yml") {
return Ok(config);
}
if let Some(mut path) = dirs::config_dir() {
path.push("webhookey/config.yml");
if let Ok(config) = File::open(path) {
return Ok(config);
}
}
if let Ok(config) = File::open("config.yml") {
return Ok(config);
}
bail!("No configuration files found.");
}
fn main() -> Result<()> {
let config: Config = serde_yaml::from_reader(BufReader::new(get_config()?))?;
println!("{}", serde_yaml::to_string(&config)?);
rocket::ignite()
.mount("/", routes![index, receive_hook])
.attach(AdHoc::on_attach("webhookey config", move |rocket| {
Ok(rocket.manage(config))
}))
.launch();
Ok(())
}