feat(machoke): add in-memory store

This commit was merged in pull request #4.
This commit is contained in:
2026-09-02 08:38:56 -04:00
parent 17d69e51e6
commit cfbd1dbebe
6 changed files with 241 additions and 12 deletions

View File

@@ -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"],
)

View File

@@ -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]

View File

@@ -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
View 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());
}

View File

@@ -1,3 +0,0 @@
fn main() {
println!("{}", machoke::greeting());
}