feat(machoke): add in-memory store
This commit is contained in:
@@ -37,3 +37,5 @@
|
||||
- After an explicit squash or rewrite request on a pull request, subsequent pull-request updates still use follow-up commits unless the user explicitly asks for another rewrite.
|
||||
- Starlark top-level rule and extension declarations cannot reference implementation functions before those functions are assigned.
|
||||
Keep executable public interface declarations after their implementation symbols, or validate before committing a top-down rewrite.
|
||||
- `rules_rust` `rust_test` targets cannot set both `crate` and `srcs`.
|
||||
Use one input style per test target.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
load("@rules_rust//rust:defs.bzl", "rust_binary", "rust_library")
|
||||
load("@rules_rust//rust:defs.bzl", "rust_library", "rust_test")
|
||||
|
||||
rust_library(
|
||||
name = "machoke_lib",
|
||||
@@ -6,8 +6,8 @@ rust_library(
|
||||
srcs = ["lib.rs"],
|
||||
)
|
||||
|
||||
rust_binary(
|
||||
name = "machoke",
|
||||
srcs = ["main.rs"],
|
||||
rust_test(
|
||||
name = "machoke_test",
|
||||
srcs = ["lib_test.rs"],
|
||||
deps = [":machoke_lib"],
|
||||
)
|
||||
|
||||
@@ -6,8 +6,8 @@ edition = "2024"
|
||||
[lib]
|
||||
path = "lib.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "machoke"
|
||||
path = "main.rs"
|
||||
[[test]]
|
||||
name = "lib_test"
|
||||
path = "lib_test.rs"
|
||||
|
||||
[dependencies]
|
||||
|
||||
@@ -1,3 +1,86 @@
|
||||
pub fn greeting() -> &'static str {
|
||||
"Hello from machoke"
|
||||
use std::collections::HashMap;
|
||||
use std::path::PathBuf;
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Machoke<T> {
|
||||
entries: HashMap<String, T>,
|
||||
_options: Options,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Options {
|
||||
pub backend: Backend,
|
||||
pub path: PathBuf,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub enum Backend {
|
||||
#[default]
|
||||
Simple,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct Entry<'a, T> {
|
||||
pub key: &'a str,
|
||||
pub value: &'a T,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum Error {
|
||||
NotFound { key: String },
|
||||
InvalidKey { key: String },
|
||||
Backend { message: String },
|
||||
}
|
||||
|
||||
impl<T> Machoke<T> {
|
||||
pub fn new(options: Options) -> Self {
|
||||
Self {
|
||||
entries: HashMap::new(),
|
||||
_options: options,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get(&self, key: &str) -> Result<&T> {
|
||||
validate_key(key)?;
|
||||
|
||||
self.entries.get(key).ok_or_else(|| Error::NotFound {
|
||||
key: key.to_string(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn set(&mut self, key: String, value: T) -> Result<()> {
|
||||
validate_key(&key)?;
|
||||
|
||||
self.entries.insert(key, value);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn delete(&mut self, key: &str) -> Result<bool> {
|
||||
validate_key(key)?;
|
||||
|
||||
Ok(self.entries.remove(key).is_some())
|
||||
}
|
||||
|
||||
pub fn list(&self) -> Result<Vec<Entry<'_, T>>> {
|
||||
Ok(self
|
||||
.entries
|
||||
.iter()
|
||||
.map(|(key, value)| Entry {
|
||||
key: key.as_str(),
|
||||
value,
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
}
|
||||
|
||||
fn validate_key(key: &str) -> Result<()> {
|
||||
if key.is_empty() {
|
||||
return Err(Error::InvalidKey {
|
||||
key: key.to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
147
src/machoke/lib_test.rs
Normal file
147
src/machoke/lib_test.rs
Normal file
@@ -0,0 +1,147 @@
|
||||
use machoke::*;
|
||||
|
||||
#[test]
|
||||
fn get_existing_key_returns_stored_value() {
|
||||
let mut machoke = Machoke::new(Options::default());
|
||||
machoke.set("home".to_string(), "plex".to_string()).unwrap();
|
||||
|
||||
let value = machoke.get("home").unwrap();
|
||||
|
||||
assert_eq!(value, "plex");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_missing_key_returns_not_found() {
|
||||
let machoke: Machoke<String> = Machoke::new(Options::default());
|
||||
|
||||
let result = machoke.get("home");
|
||||
|
||||
assert_eq!(
|
||||
result,
|
||||
Err(Error::NotFound {
|
||||
key: "home".to_string(),
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_empty_key_returns_invalid_key() {
|
||||
let machoke: Machoke<String> = Machoke::new(Options::default());
|
||||
|
||||
let result = machoke.get("");
|
||||
|
||||
assert_eq!(
|
||||
result,
|
||||
Err(Error::InvalidKey {
|
||||
key: "".to_string(),
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_new_key_stores_value() {
|
||||
let mut machoke = Machoke::new(Options::default());
|
||||
|
||||
machoke.set("home".to_string(), "plex".to_string()).unwrap();
|
||||
|
||||
assert_eq!(machoke.get("home").unwrap(), "plex");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_existing_key_overwrites_value() {
|
||||
let mut machoke = Machoke::new(Options::default());
|
||||
machoke
|
||||
.set("home".to_string(), "jellyfin".to_string())
|
||||
.unwrap();
|
||||
|
||||
machoke.set("home".to_string(), "plex".to_string()).unwrap();
|
||||
|
||||
assert_eq!(machoke.get("home").unwrap(), "plex");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_empty_key_returns_invalid_key() {
|
||||
let mut machoke = Machoke::new(Options::default());
|
||||
|
||||
let result = machoke.set("".to_string(), "plex".to_string());
|
||||
|
||||
assert_eq!(
|
||||
result,
|
||||
Err(Error::InvalidKey {
|
||||
key: "".to_string(),
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn delete_existing_key_removes_value() {
|
||||
let mut machoke = Machoke::new(Options::default());
|
||||
machoke.set("home".to_string(), "plex".to_string()).unwrap();
|
||||
|
||||
let deleted = machoke.delete("home").unwrap();
|
||||
|
||||
assert!(deleted);
|
||||
assert_eq!(
|
||||
machoke.get("home"),
|
||||
Err(Error::NotFound {
|
||||
key: "home".to_string(),
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn delete_missing_key_returns_false() {
|
||||
let mut machoke: Machoke<String> = Machoke::new(Options::default());
|
||||
|
||||
let deleted = machoke.delete("home").unwrap();
|
||||
|
||||
assert!(!deleted);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn delete_empty_key_returns_invalid_key() {
|
||||
let mut machoke: Machoke<String> = Machoke::new(Options::default());
|
||||
|
||||
let result = machoke.delete("");
|
||||
|
||||
assert_eq!(
|
||||
result,
|
||||
Err(Error::InvalidKey {
|
||||
key: "".to_string(),
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_populated_store_returns_all_entries() {
|
||||
let mut machoke = Machoke::new(Options::default());
|
||||
machoke.set("home".to_string(), "plex".to_string()).unwrap();
|
||||
machoke
|
||||
.set("docs".to_string(), "readme".to_string())
|
||||
.unwrap();
|
||||
|
||||
let mut entries = machoke
|
||||
.list()
|
||||
.unwrap()
|
||||
.into_iter()
|
||||
.map(|entry| (entry.key.to_string(), entry.value.clone()))
|
||||
.collect::<Vec<_>>();
|
||||
entries.sort();
|
||||
|
||||
assert_eq!(
|
||||
entries,
|
||||
vec![
|
||||
("docs".to_string(), "readme".to_string()),
|
||||
("home".to_string(), "plex".to_string()),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_empty_store_returns_empty_entries() {
|
||||
let machoke: Machoke<String> = Machoke::new(Options::default());
|
||||
|
||||
let entries = machoke.list().unwrap();
|
||||
|
||||
assert!(entries.is_empty());
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
fn main() {
|
||||
println!("{}", machoke::greeting());
|
||||
}
|
||||
Reference in New Issue
Block a user