webhook-catcher 0.2.0

A simple runner that waits for a webhook
Documentation
use std::{process::Command, sync::Arc};

use axum::{
    Json, Router,
    extract::{Path, State},
    routing::get,
};
use serde::{Deserialize, Serialize};
use tokio::net::TcpListener;

#[derive(Debug, Deserialize, Serialize)]
pub struct Config {
    #[serde(default = "default_webhook_listen_address")]
    addr: String,
    hooks: Vec<Entry>,
}

fn default_webhook_listen_address() -> String {
    format!("0.0.0.0:1234")
}

#[derive(Debug, Deserialize, Serialize)]
pub struct Entry {
    path: String,
    command: String,
    args: Vec<String>,
}

pub async fn run_server(cfg: Config) {
    let listener = TcpListener::bind(cfg.addr.clone()).await.unwrap();
    let state = Arc::new(cfg);
    let app: Router = Router::new()
        .route("/{*key}", get(handle_url))
        .with_state(state);
    axum::serve(listener, app).await.unwrap();
}

#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
enum Response {
    Success,
    Failed,
}

#[axum::debug_handler]
async fn handle_url(Path(key): Path<String>, State(cfg): State<Arc<Config>>) -> Json<Response> {
    if let Some(entry) = cfg.hooks.iter().find(|it| it.path == key) {
        println!("Running entry {}", key);
        let mut command = Command::new(&entry.command);
        command.args(entry.args.iter());
        let res = command.status();

        match res {
            Ok(o) if o.success() => Json(Response::Success),
            _ => Json(Response::Failed),
        }
    } else {
        Json(Response::Failed)
    }
}