Add jsonpath placeholder support

Signed-off-by: trivernis <trivernis@protonmail.com>
main
trivernis 3 years ago
parent 35c7004de3
commit a15a713a0c
Signed by: Trivernis
GPG Key ID: DFFFCC2C7A02DB45

163
Cargo.lock generated

@ -2,6 +2,21 @@
# It is not intended for manual editing. # It is not intended for manual editing.
version = 3 version = 3
[[package]]
name = "addr2line"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e61f2b7f93d2c7d2b08263acaa4a363b3e276806c68af6134c44f523bf1aacd"
dependencies = [
"gimli",
]
[[package]]
name = "adler"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]] [[package]]
name = "aho-corasick" name = "aho-corasick"
version = "0.7.18" version = "0.7.18"
@ -34,6 +49,21 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
[[package]]
name = "backtrace"
version = "0.3.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7a905d892734eea339e896738c14b9afce22b5318f64b951e70bf3844419b01"
dependencies = [
"addr2line",
"cc",
"cfg-if",
"libc",
"miniz_oxide",
"object",
"rustc-demangle",
]
[[package]] [[package]]
name = "base64" name = "base64"
version = "0.13.0" version = "0.13.0"
@ -77,6 +107,12 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040" checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040"
[[package]]
name = "cc"
version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e70cc2f62c6ce1868963827bd677764c62d07c3d9a3e1fb1177ee1a9ab199eb2"
[[package]] [[package]]
name = "cfg-if" name = "cfg-if"
version = "1.0.0" version = "1.0.0"
@ -161,6 +197,15 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "error-chain"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff511d5dc435d703f4971bc399647c9bc38e20cb41452e3b9feb4765419ed3f3"
dependencies = [
"backtrace",
]
[[package]] [[package]]
name = "fern" name = "fern"
version = "0.6.0" version = "0.6.0"
@ -281,6 +326,12 @@ dependencies = [
"wasi 0.10.2+wasi-snapshot-preview1", "wasi 0.10.2+wasi-snapshot-preview1",
] ]
[[package]]
name = "gimli"
version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0a01e0497841a3b2db4f8afa483cce65f7e96a3498bd6c541734792aeac8fe7"
[[package]] [[package]]
name = "glob" name = "glob"
version = "0.3.0" version = "0.3.0"
@ -440,6 +491,19 @@ version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736"
[[package]]
name = "jsonpath"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8061db09019f1879ba586685694fe18279f597b1b3a9dd308f35e596be6cdf7d"
dependencies = [
"error-chain",
"pest",
"pest_derive",
"serde 1.0.127",
"serde_json",
]
[[package]] [[package]]
name = "lazy_static" name = "lazy_static"
version = "1.4.0" version = "1.4.0"
@ -508,6 +572,16 @@ dependencies = [
"unicase", "unicase",
] ]
[[package]]
name = "miniz_oxide"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b"
dependencies = [
"adler",
"autocfg",
]
[[package]] [[package]]
name = "mio" name = "mio"
version = "0.7.13" version = "0.7.13"
@ -540,9 +614,12 @@ dependencies = [
"dirs", "dirs",
"fern", "fern",
"glob", "glob",
"jsonpath",
"lazy_static", "lazy_static",
"log", "log",
"regex",
"serde 1.0.127", "serde 1.0.127",
"serde_json",
"thiserror", "thiserror",
"tokio", "tokio",
"toml", "toml",
@ -625,6 +702,15 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "object"
version = "0.26.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c55827317fb4c08822499848a14237d2874d6f139828893017237e7ab93eb386"
dependencies = [
"memchr",
]
[[package]] [[package]]
name = "once_cell" name = "once_cell"
version = "1.8.0" version = "1.8.0"
@ -643,6 +729,23 @@ version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
[[package]]
name = "pest"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fce5d8b5cc33983fc74f78ad552b5522ab41442c4ca91606e4236eb4b5ceefc"
[[package]]
name = "pest_derive"
version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca3294f437119209b084c797604295f40227cffa35c57220b1e99a6ff3bf8ee4"
dependencies = [
"pest",
"quote 0.3.15",
"syn 0.11.11",
]
[[package]] [[package]]
name = "pin-project" name = "pin-project"
version = "1.0.8" version = "1.0.8"
@ -659,8 +762,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e8fe8163d14ce7f0cdac2e040116f22eac817edabff0be91e8aff7e9accf389" checksum = "6e8fe8163d14ce7f0cdac2e040116f22eac817edabff0be91e8aff7e9accf389"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote 1.0.9",
"syn", "syn 1.0.74",
] ]
[[package]] [[package]]
@ -687,7 +790,7 @@ version = "1.0.28"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c7ed8b8c7b886ea3ed7dde405212185f423ab44682667c8c6dd14aa1d9f6612" checksum = "5c7ed8b8c7b886ea3ed7dde405212185f423ab44682667c8c6dd14aa1d9f6612"
dependencies = [ dependencies = [
"unicode-xid", "unicode-xid 0.2.2",
] ]
[[package]] [[package]]
@ -696,6 +799,12 @@ version = "1.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
[[package]]
name = "quote"
version = "0.3.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a"
[[package]] [[package]]
name = "quote" name = "quote"
version = "1.0.9" version = "1.0.9"
@ -837,6 +946,12 @@ version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e52c148ef37f8c375d49d5a73aa70713125b7f19095948a923f80afdeb22ec2" checksum = "3e52c148ef37f8c375d49d5a73aa70713125b7f19095948a923f80afdeb22ec2"
[[package]]
name = "rustc-demangle"
version = "0.1.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dead70b0b5e03e9c814bcb6b01e03e68f7c57a80aa48c72ec92152ab3e818d49"
[[package]] [[package]]
name = "ryu" name = "ryu"
version = "1.0.5" version = "1.0.5"
@ -889,8 +1004,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a024926d3432516606328597e0f224a51355a493b49fdd67e9209187cbe55ecc" checksum = "a024926d3432516606328597e0f224a51355a493b49fdd67e9209187cbe55ecc"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote 1.0.9",
"syn", "syn 1.0.74",
] ]
[[package]] [[package]]
@ -960,6 +1075,17 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]]
name = "syn"
version = "0.11.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad"
dependencies = [
"quote 0.3.15",
"synom",
"unicode-xid 0.0.4",
]
[[package]] [[package]]
name = "syn" name = "syn"
version = "1.0.74" version = "1.0.74"
@ -967,8 +1093,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1873d832550d4588c3dbc20f01361ab00bfe741048f71e3fecf145a7cc18b29c" checksum = "1873d832550d4588c3dbc20f01361ab00bfe741048f71e3fecf145a7cc18b29c"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote 1.0.9",
"unicode-xid", "unicode-xid 0.2.2",
]
[[package]]
name = "synom"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6"
dependencies = [
"unicode-xid 0.0.4",
] ]
[[package]] [[package]]
@ -1001,8 +1136,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "060d69a0afe7796bf42e9e2ff91f5ee691fb15c53d38b4b62a9a53eb23164745" checksum = "060d69a0afe7796bf42e9e2ff91f5ee691fb15c53d38b4b62a9a53eb23164745"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote 1.0.9",
"syn", "syn 1.0.74",
] ]
[[package]] [[package]]
@ -1056,8 +1191,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54473be61f4ebe4efd09cec9bd5d16fa51d70ea0192213d754d2d500457db110" checksum = "54473be61f4ebe4efd09cec9bd5d16fa51d70ea0192213d754d2d500457db110"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote 1.0.9",
"syn", "syn 1.0.74",
] ]
[[package]] [[package]]
@ -1201,6 +1336,12 @@ dependencies = [
"tinyvec", "tinyvec",
] ]
[[package]]
name = "unicode-xid"
version = "0.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc"
[[package]] [[package]]
name = "unicode-xid" name = "unicode-xid"
version = "0.2.2" version = "0.2.2"

@ -17,6 +17,9 @@ log = "0.4.14"
colored = "2.0.0" colored = "2.0.0"
chrono = "0.4.19" chrono = "0.4.19"
fern = "0.6.0" fern = "0.6.0"
serde_json = "1.0.66"
jsonpath = "0.1.1"
regex = "1.5.4"
[dependencies.serde] [dependencies.serde]
version = "1.0.127" version = "1.0.127"

@ -1,21 +1,25 @@
use crate::command_template::CommandTemplate;
use crate::error::MultihookResult; use crate::error::MultihookResult;
use std::path::{Path, PathBuf}; use serde_json::Value;
use std::fs::read_to_string;
use std::path::PathBuf;
use tokio::process::Command; use tokio::process::Command;
pub enum HookAction { pub struct HookAction {
Script(PathBuf), command: CommandTemplate,
Command(String),
} }
impl HookAction { impl HookAction {
pub async fn execute(&self, body: &str) -> MultihookResult<()> { pub fn new<S: ToString>(command: S) -> Self {
match self { Self {
HookAction::Script(s) => Self::execute_script(s, body).await, command: CommandTemplate::new(command),
HookAction::Command(c) => Self::execute_command(c, body).await,
} }
} }
async fn execute_command(command: &str, body: &str) -> MultihookResult<()> { pub async fn execute(&self, body: &str) -> MultihookResult<()> {
let json_body: Value = serde_json::from_str(body).unwrap_or_default();
let command = self.command.evaluate(&json_body);
let output = Command::new("sh") let output = Command::new("sh")
.env("HOOK_BODY", body) .env("HOOK_BODY", body)
.arg("-c") .arg("-c")
@ -32,28 +36,12 @@ impl HookAction {
Ok(()) Ok(())
} }
async fn execute_script(script: &PathBuf, body: &str) -> MultihookResult<()> {
let output = Command::new(script).env("HOOK_BODY", body).output().await?;
let stderr = String::from_utf8_lossy(&output.stderr[..]);
let stdout = String::from_utf8_lossy(&output.stdout[..]);
log::debug!("Script output is: {}", stdout);
if stderr.len() > 0 {
log::error!("Errors occurred during script execution: {}", stderr);
}
Ok(())
}
} }
impl From<String> for HookAction { impl From<String> for HookAction {
fn from(action: String) -> Self { fn from(action: String) -> Self {
let path = PathBuf::from(&action); let path = PathBuf::from(&action);
if Path::new(&path).exists() { let contents = read_to_string(path).unwrap_or(action);
Self::Script(path) Self::new(contents)
} else {
Self::Command(action)
}
} }
} }

@ -0,0 +1,72 @@
use jsonpath::Selector;
use lazy_static::lazy_static;
use regex::{Match, Regex};
use serde_json::Value;
pub struct CommandTemplate {
src: String,
matches: Vec<(usize, usize)>,
}
impl CommandTemplate {
pub fn new<S: ToString>(command: S) -> Self {
lazy_static! {
static ref PLACEHOLDER_REGEX: Regex = Regex::new(r"\{\{(.*?)\}\}").unwrap();
}
let command = command.to_string();
let matches = PLACEHOLDER_REGEX
.find_iter(&command)
.map(|m: Match| (m.start(), m.end()))
.collect();
Self {
src: command,
matches,
}
}
pub fn evaluate(&self, json: &Value) -> String {
let mut result_string = String::with_capacity(self.src.len());
let mut last_index = 0;
for (start, end) in &self.matches {
let before = &self.src[last_index..*start];
let query = &self.src[*start + 2..*end - 2];
result_string.push_str(before);
result_string.push_str(&evaluate_path(query, json).unwrap_or_default());
last_index = *end;
}
result_string.push_str(&self.src[last_index..]);
result_string
}
}
fn evaluate_path(query: &str, json: &Value) -> Option<String> {
let selector = Selector::new(query).ok()?;
let results = selector
.find(json)
.map(json_to_string)
.collect::<Vec<String>>();
Some(results.join("\n"))
}
fn json_to_string(value: &Value) -> String {
match value {
Value::Null => String::with_capacity(0),
Value::Bool(b) => b.to_string(),
Value::Number(n) => n.to_string(),
Value::String(s) => s.to_owned(),
Value::Array(a) => a
.iter()
.map(|v| json_to_string(v))
.collect::<Vec<String>>()
.join("\n"),
Value::Object(o) => o
.iter()
.map(|(k, v)| format!("{} = {}", k, json_to_string(v)))
.collect::<Vec<String>>()
.join("\n"),
}
}

@ -1,8 +1,10 @@
use crate::logging::init_logger; use crate::logging::init_logger;
use crate::server::HookServer; use crate::server::HookServer;
use crate::settings::get_settings; use crate::settings::get_settings;
use std::path::{Path, PathBuf};
mod action; mod action;
mod command_template;
mod error; mod error;
mod logging; mod logging;
mod server; mod server;
@ -11,6 +13,12 @@ mod settings;
#[tokio::main] #[tokio::main]
async fn main() { async fn main() {
init_logger(); init_logger();
let data_dir = dirs::data_dir()
.map(|d| d.join("multihook"))
.unwrap_or(PathBuf::from("."));
if !Path::new(&data_dir).exists() {
std::fs::create_dir(data_dir).expect("Failed to create data dir");
}
let settings = get_settings(); let settings = get_settings();
let mut server = HookServer::new(); let mut server = HookServer::new();
for (name, endpoint) in &settings.endpoints { for (name, endpoint) in &settings.endpoints {

@ -44,8 +44,8 @@ pub fn get_settings() -> &'static Settings {
fn load_settings() -> MultihookResult<Settings> { fn load_settings() -> MultihookResult<Settings> {
let config_dir = dirs::config_dir() let config_dir = dirs::config_dir()
.unwrap_or(PathBuf::from(".config")) .map(|c| c.join("multihook"))
.join("multihook"); .unwrap_or(PathBuf::from(".config"));
if !Path::new(&config_dir).exists() { if !Path::new(&config_dir).exists() {
fs::create_dir(&config_dir)?; fs::create_dir(&config_dir)?;
} }

Loading…
Cancel
Save