Initial commit

This commit is contained in:
mycroft committed 2021-03-24 23:30:02 +01:00
commit e4aff3f39f
5 files changed
+1049

No files matched your search

+195
View File
@@ -0,0 +1,195 @@
/*
* metric2fs-rs
*/
use std::fs::{self, OpenOptions};
use std::collections::HashMap;
use std::convert::Infallible;
use std::net::SocketAddr;
use std::path::PathBuf;
use anyhow::Result;
use filetime::{FileTime,set_file_mtime};
use globset::Glob;
use hyper::{Body, Request, Response, Server, Method, StatusCode};
use hyper::body;
use hyper::service::{make_service_fn, service_fn};
extern crate pretty_env_logger;
#[macro_use] extern crate log;
fn write_metric(metric: String) {
let dir = metric.replace(".", "/");
let mut path = PathBuf::new();
path.push(&dir);
let res = fs::create_dir_all(&path);
if let Err(res) = res {
warn!("Could not create directory: {}", res);
return;
}
path.push(".md");
let res = OpenOptions::new().create(true).write(true).open(&path);
if let Err(res) = res {
warn!("Could not create metric: {}", res);
}
let res = set_file_mtime(&path, FileTime::now());
if let Err(res) = res {
warn!("Could not update metric's timestamp: {}", res);
}
debug!("Created metric: {}", metric)
}
fn fetch_metric(expr: String) -> Result<Vec<String>> {
let components = expr.split(".").collect::<Vec<&str>>();
let mut init_path = PathBuf::new();
init_path.push(".");
info!("Searching metrics for expr: {}", expr);
let mut pathes = vec![];
pathes.push(init_path);
for component in components.iter() {
let gs = Glob::new(component)?.compile_matcher();
let mut new_pathes = vec![];
for path in pathes.iter_mut() {
if !path.is_dir() {
continue;
}
for entry in fs::read_dir(path)? {
let dir = entry?;
if gs.is_match(dir.file_name()) {
new_pathes.push(dir.path());
}
}
}
pathes = new_pathes;
}
let mut metrics : Vec<String> = vec![];
for path in pathes {
let mut pb = PathBuf::new();
pb.push(path);
pb.push(".md");
let stat = fs::metadata(&pb);
if let Ok(_stat) = stat {
let metric_name = String::from(pb.to_str().unwrap());
let metric_name = metric_name.strip_prefix("./").unwrap();
let metric_name = metric_name.strip_suffix("/.md").unwrap();
let metric_name = metric_name.replace("/", ".");
debug!("Found metric: {}", metric_name);
metrics.push(metric_name.to_string())
}
}
info!("Got {} metric(s).", metrics.len());
Ok(metrics)
}
async fn handlers(req: Request<Body>) -> Result<Response<Body>, Infallible> {
let mut response = Response::new(Body::empty());
match (req.method(), req.uri().path()) {
(&Method::GET, "/metric") => {
let query = req.uri().query();
let mut hm = HashMap::new();
for q in query.unwrap().split("&") {
let parts = q.split("=").collect::<Vec<&str>>();
if parts.len() != 2 {
continue;
}
hm.insert(String::from(parts[0]), String::from(parts[1]));
}
let entry = hm.get(&String::from("q"));
if entry.is_none() {
*response.status_mut() = StatusCode::NOT_ACCEPTABLE;
return Ok(response);
}
let entry = entry.unwrap();
if let Ok(list) = fetch_metric(String::from(entry)) {
*response.body_mut() = list.join("\n").into();
}
},
(&Method::POST, "/metric") => {
let content = body::to_bytes(req.into_body()).await;
if let Err(_) = content {
*response.status_mut() = StatusCode::NOT_ACCEPTABLE;
return Ok(response);
}
let content = content.unwrap();
if !content.is_ascii() {
*response.status_mut() = StatusCode::NOT_ACCEPTABLE;
return Ok(response);
}
if let Ok(content) = String::from_utf8(content.to_vec()) {
let metrics = content.split("\n").collect::<Vec<&str>>();
for metric in metrics {
debug!("Creating metric {}", metric);
write_metric(String::from(metric));
}
}
},
_ => {
*response.status_mut() = StatusCode::NOT_FOUND;
},
};
Ok(response)
}
async fn shutdown_signal() {
// Wait for the CTRL+C signal
tokio::signal::ctrl_c()
.await
.expect("failed to install CTRL+C signal handler");
}
#[tokio::main]
async fn main() {
pretty_env_logger::init();
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
let make_svc = make_service_fn(|_conn| async {
Ok::<_, Infallible>(service_fn(handlers))
});
if let Err(res) = std::env::set_current_dir("./db/") {
panic!("{:?}", res);
}
let server = Server::bind(&addr).serve(make_svc);
let graceful = server.with_graceful_shutdown(shutdown_signal());
if let Err(e) = graceful.await {
eprintln!("server error: {}", e);
}
}