Skip to content

Instantly share code, notes, and snippets.

@nebgnahz
Created October 20, 2016 07:25
Show Gist options
  • Save nebgnahz/5793366eba80997e22396735bbd28dc0 to your computer and use it in GitHub Desktop.
Save nebgnahz/5793366eba80997e22396735bbd28dc0 to your computer and use it in GitHub Desktop.
A proof-of-concept varz interface that takes GET/PUT HTTP request and changes the internal value
extern crate iron;
use std::collections::HashMap;
use std::str::FromStr;
use std::io::Read;
use std::sync::RwLock;
use iron::prelude::*;
use iron::{status, Handler};
type Result<T> = ::std::result::Result<T, VarzError>;
#[derive(Debug)]
enum VarzError {
Parse,
Lock,
}
impl From<::std::num::ParseIntError> for VarzError {
fn from(_err: ::std::num::ParseIntError) -> VarzError {
VarzError::Parse
}
}
impl From<::std::string::ParseError> for VarzError {
fn from(_err: ::std::string::ParseError) -> VarzError {
VarzError::Parse
}
}
impl From<::std::str::ParseBoolError> for VarzError {
fn from(_err: ::std::str::ParseBoolError) -> VarzError {
VarzError::Parse
}
}
impl<T> From<::std::sync::PoisonError<T>> for VarzError {
fn from(_err: ::std::sync::PoisonError<T>) -> VarzError {
VarzError::Lock
}
}
trait GetSet {
fn get(&self) -> Option<String>;
fn set(&self, String) -> Result<()>;
}
struct HttpVarz {
maps: HashMap<String, Box<GetSet + Send + Sync>>,
}
impl HttpVarz {
fn new() -> HttpVarz {
HttpVarz { maps: HashMap::new() }
}
fn add<GS>(&mut self, name: String, variable: GS)
where GS: GetSet + Send + Sync + 'static
{
self.maps.insert(name, Box::new(variable));
}
fn to_html(&self) -> String {
self.maps.iter().fold(String::new(), |mut sum, (k, v)| {
if let Some(value) = v.get() {
sum.push_str(&format!("{}: {}\n", k, value));
}
sum
})
}
}
impl Handler for HttpVarz {
fn handle(&self, req: &mut Request) -> IronResult<Response> {
let name = req.url.path().join("/");
match self.maps.get(&name) {
Some(varz) => {
match req.method {
iron::method::Method::Get => {
if let Some(value) = varz.get() {
Ok(Response::with((status::Ok, format!("GET {} {}", name, value))))
} else {
Ok(Response::with(status::BadRequest))
}
}
iron::method::Method::Put => {
let mut string = String::new();
match req.body.read_to_string(&mut string) {
Ok(_) => {
match varz.set(string) {
Ok(_) => {
Ok(Response::with((status::Ok, format!("PUT {}", name))))
}
Err(e) => {
Ok(Response::with((status::BadRequest, format!("{:?}", e))))
}
}
}
Err(e) => Ok(Response::with((status::BadRequest, format!("{:?}", e)))),
}
}
_ => unreachable!(),
}
}
None => Ok(Response::with((status::Ok, self.to_html()))),
}
}
}
struct Varz<T, F: Fn()> {
val: RwLock<T>,
callback: F,
}
impl<T, F: Fn()> Varz<T, F> {
fn new(val: T, cb: F) -> Self {
Varz {
val: RwLock::new(val),
callback: cb,
}
}
}
impl<T, F> GetSet for Varz<T, F>
where T: ToString + FromStr,
F: Fn() + 'static,
VarzError: std::convert::From<<T as std::str::FromStr>::Err>
{
fn get(&self) -> Option<String> {
match self.val.read() {
Ok(v) => Some(v.to_string()),
Err(_) => None,
}
}
fn set(&self, x: String) -> Result<()> {
let new_val = try!(x.parse::<T>());
let mut val_ref = try!(self.val.write());
*val_ref = new_val;
(self.callback)();
Ok(())
}
}
fn main() {
let mut varz = HttpVarz::new();
varz.add("x".to_string(),
Varz::new(3u32, || println!("set x callback is invoked")));
varz.add("y".to_string(),
Varz::new(123usize, || println!("set y callback is invoked")));
varz.add("z".to_string(),
Varz::new(true, || println!("set z callback is invoked")));
varz.add("a".to_string(),
Varz::new("hello".to_string(),
|| println!("set a callback is invoked")));
println!("Hello, world!");
Iron::new(varz).http("localhost:3000").unwrap();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment