kalavara/
lib.rs

1//! A distributed persistent key value store that speaks http. Inspired by
2//! [minkeyvalue](https://github.com/geohot/minikeyvalue).
3//!
4//! ## Usage
5//!
6//! 1. insert a key-value
7//!
8//! ```sh
9//! curl -XPUT -L -d value http://localhost:6000/store/key
10//! ```
11//!
12//! 2. retrive value
13//!
14//! ```sh
15//! curl -XGET -L http://localhost:6000/store/key
16//! ```
17//!
18//! 3. delete a key
19//!
20//! ```sh
21//! curl -XDELETE -L http://localhost:6000/store/key
22//! ```
23//!
24//! 4. register a new volume server with master
25//!
26//! ```sh
27//! curl -XPOST -d "http://newvolume.server" http://localhost:6000/admin/add-volume
28//! ```
29
30use tiny_http::{Method, Request};
31
32use std::io::Read;
33
34const STORE_PREFIX: &str = "/store/";
35const ADMIN_PREFIX: &str = "/admin/";
36
37/// returns the key from url string by removing /store/ prefix and query params if any
38fn get_key(url: &str, prefix: &str) -> String {
39    let pfx_len = if url.starts_with(prefix) {
40        prefix.len()
41    } else {
42        0
43    };
44
45    // remove query params if any
46    match url.find('?') {
47        None => String::from(&url[pfx_len..]),
48        Some(indx) => String::from(&url[pfx_len..indx]),
49    }
50}
51
52#[test]
53fn test_get_key() {
54    let url = "/store/originalkey?q=this&that=that#foo";
55    assert_eq!(get_key(url, "/store/"), String::from("originalkey"));
56}
57
58/// Trait that send http response to a request
59/// ResponseKind Should implement this
60trait Respond {
61    fn respond(self, req: Request);
62}
63
64/// Kalavara Store service trait
65/// Defines methods that stores needs to implement
66trait Service: Sync + Send {
67    /// ResponseTye
68    /// Should know how to respond to a request
69    type Response: Respond + Default;
70
71    /// Get a key from store
72    fn get(&self, key: String) -> Self::Response;
73
74    /// Save/Update key in store
75    fn save(&self, key: String, value: impl Read) -> Self::Response;
76
77    /// Remove a key from store
78    fn delete(&self, key: String) -> Self::Response;
79
80    /// Dispatch a request to respective handler methods
81    fn dispatch(&self, mut req: Request) {
82        let key = get_key(req.url(), STORE_PREFIX);
83
84        let resp = match *req.method() {
85            Method::Get => self.get(key),
86            Method::Post | Method::Put => self.save(key, req.as_reader()),
87            Method::Delete => self.delete(key),
88            _ => Default::default(),
89        };
90
91        resp.respond(req);
92    }
93}
94
95#[macro_use]
96mod macros;
97pub mod master;
98pub mod volume;