use std::collections::HashMap;
pub struct Ctx {
store: HashMap<String, String>,
log: Vec<String>,
}
impl Ctx {
pub fn new() -> Self {
Self {
store: HashMap::new(),
log: vec![],
}
}
pub fn set(&mut self, key: impl Into<String>, value: impl Into<String>) {
self.store.insert(key.into(), value.into());
}
pub fn get(&self, key: &str) -> Option<&str> {
self.store.get(key).map(|s| s.as_str())
}
pub fn remove(&mut self, key: &str) -> Option<String> {
self.store.remove(key)
}
pub fn log(&mut self, msg: impl Into<String>) {
self.log.push(msg.into());
}
pub fn logs(&self) -> &[String] {
&self.log
}
pub fn clear_logs(&mut self) {
self.log.clear();
}
pub fn clear(&mut self) {
self.store.clear();
self.log.clear();
}
}
impl Default for Ctx {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn set_then_get() {
let mut ctx = Ctx::new();
ctx.set("key", "value");
assert_eq!(ctx.get("key"), Some("value"));
}
#[test]
fn get_missing_key() {
let ctx = Ctx::new();
assert_eq!(ctx.get("nope"), None);
}
#[test]
fn set_overwrites() {
let mut ctx = Ctx::new();
ctx.set("key", "first");
ctx.set("key", "second");
assert_eq!(ctx.get("key"), Some("second"));
}
#[test]
fn remove_returns_value() {
let mut ctx = Ctx::new();
ctx.set("key", "value");
assert_eq!(ctx.remove("key"), Some("value".to_string()));
assert_eq!(ctx.get("key"), None);
}
#[test]
fn remove_missing_key() {
let mut ctx = Ctx::new();
assert_eq!(ctx.remove("nope"), None);
}
#[test]
fn log_appends_and_logs_returns_in_order() {
let mut ctx = Ctx::new();
ctx.log("first");
ctx.log("second");
ctx.log("third");
assert_eq!(ctx.logs(), &["first", "second", "third"]);
}
#[test]
fn clear_logs_preserves_store() {
let mut ctx = Ctx::new();
ctx.set("key", "value");
ctx.log("msg");
ctx.clear_logs();
assert!(ctx.logs().is_empty());
assert_eq!(ctx.get("key"), Some("value"));
}
#[test]
fn clear_empties_both() {
let mut ctx = Ctx::new();
ctx.set("key", "value");
ctx.log("msg");
ctx.clear();
assert!(ctx.logs().is_empty());
assert_eq!(ctx.get("key"), None);
}
#[test]
fn separate_contexts_have_independent_state() {
let mut a = Ctx::new();
let b = Ctx::new();
a.set("topic", "rust");
a.log("phase 1 done");
assert_eq!(a.get("topic"), Some("rust"));
assert_eq!(b.get("topic"), None);
assert_eq!(a.logs(), &["phase 1 done"]);
assert!(b.logs().is_empty());
}
}