Skip to content

Commit

Permalink
refactor(context): reimplement context as an kv store
Browse files Browse the repository at this point in the history
  • Loading branch information
willyrgf committed Jan 21, 2024
1 parent c57eb4b commit 4d0d6c3
Show file tree
Hide file tree
Showing 9 changed files with 320 additions and 272 deletions.
15 changes: 1 addition & 14 deletions mfm_core/src/contexts/mod.rs
Original file line number Diff line number Diff line change
@@ -1,28 +1,15 @@
use crate::config::Config;
use anyhow::{anyhow, Error};
use mfm_machine::state::context::Context;
use serde_derive::{Deserialize, Serialize};
use serde_json::Value;

#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum ConfigSource {
TomlFile(String),
}

impl Context for ConfigSource {
fn read(&self) -> Result<Value, Error> {
serde_json::to_value(self).map_err(|e| anyhow!(e))
}

fn write(&mut self, _: &Value) -> Result<(), Error> {
// do nothing
Ok(())
}
}

#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub struct ReadConfig {
pub config_source: ConfigSource,
pub config: Config,
}
pub const READ_CONFIG: &str = "read_config";
24 changes: 19 additions & 5 deletions mfm_core/src/states/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use mfm_machine::state::{
Tag,
};

use crate::contexts;
use crate::contexts::{self, READ_CONFIG};

#[derive(Debug, Clone, PartialEq, StateMetadataReqs)]
pub struct ReadConfig {
Expand All @@ -28,7 +28,12 @@ impl ReadConfig {

impl StateHandler for ReadConfig {
fn handler(&self, context: ContextWrapper) -> StateResult {
let value = context.lock().unwrap().read().unwrap();
let value = context
.lock()
.unwrap()
.read(READ_CONFIG.to_string())
.unwrap();

let data: contexts::ConfigSource = serde_json::from_value(value).unwrap();
println!("data: {:?}", data);
Ok(())
Expand All @@ -37,16 +42,25 @@ impl StateHandler for ReadConfig {

#[cfg(test)]
mod test {
use mfm_machine::state::{context::wrap_context, StateHandler};
use std::collections::HashMap;

use mfm_machine::state::{
context::{wrap_context, Local},
StateHandler,
};
use serde_json::json;

use crate::contexts::ConfigSource;
use crate::contexts::{ConfigSource, READ_CONFIG};

use super::ReadConfig;

#[test]
fn test_readconfig_from_source_file() {
let state = ReadConfig::new();
let ctx_input = wrap_context(ConfigSource::File("test_config.toml".to_string()));
let ctx_input = wrap_context(Local::new(HashMap::from([(
READ_CONFIG.to_string(),
json!(ConfigSource::TomlFile("test_config.toml".to_string())),
)])));
let result = state.handler(ctx_input);
assert!(result.is_ok())
}
Expand Down
86 changes: 49 additions & 37 deletions mfm_machine/src/state/context.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,51 @@
use std::sync::{Arc, Mutex};
use std::{
collections::HashMap,
sync::{Arc, Mutex},
};

use anyhow::Error;
use serde_json::Value;
use anyhow::{anyhow, Error};
use serde_derive::{Deserialize, Serialize};
use serde_json::{json, Value};

pub type ContextWrapper = Arc<Mutex<Box<dyn Context>>>;

// TODO: rethink this implementation of kv store context;
// we should be able to express context constraints for each state
// at the type system level;
#[derive(Default, Serialize, Deserialize)]
pub struct Local {
map: HashMap<String, Value>,
}

impl Local {
pub fn new(map: HashMap<String, Value>) -> Self {
Self { map }
}
}

impl Context for Local {
fn read(&self, key: String) -> Result<Value, Error> {
Ok(self
.map
.get(&key)
.ok_or_else(|| anyhow!("key not found"))?
.clone())
}

fn write(&mut self, key: String, value: &Value) -> Result<(), Error> {
self.map.insert(key, value.clone());
Ok(())
}

fn dump(&self) -> Result<Value, Error> {
Ok(json!(self))
}
}

pub trait Context {
fn read(&self) -> Result<Value, Error>;
fn write(&mut self, value: &Value) -> Result<(), Error>;
fn read(&self, key: String) -> Result<Value, Error>;
fn write(&mut self, key: String, value: &Value) -> Result<(), Error>;
fn dump(&self) -> Result<Value, Error>;
}

pub fn wrap_context<C: Context + 'static>(context: C) -> ContextWrapper {
Expand All @@ -17,45 +55,19 @@ pub fn wrap_context<C: Context + 'static>(context: C) -> ContextWrapper {

#[cfg(test)]
mod test {
use anyhow::anyhow;
use serde_derive::{Deserialize, Serialize};
use serde_json::json;

use super::*;

#[derive(Serialize, Deserialize)]
struct ContextA {
a: String,
b: u64,
}

impl ContextA {
fn _new(a: String, b: u64) -> Self {
Self { a, b }
}
}

impl Context for ContextA {
fn read(&self) -> Result<Value, Error> {
serde_json::to_value(self).map_err(|e| anyhow!(e))
}

fn write(&mut self, value: &Value) -> Result<(), Error> {
let ctx: ContextA = serde_json::from_value(value.clone()).map_err(|e| anyhow!(e))?;
self.a = ctx.a;
self.b = ctx.b;
Ok(())
}
}

#[test]
fn test_read_write() {
let context_a: &mut dyn Context = &mut ContextA::_new(String::from("hello"), 7);
let context_b: &dyn Context = &ContextA::_new(String::from("hellow"), 9);
let context_a: &mut dyn Context = &mut Local::default();

assert_ne!(context_a.read().unwrap(), context_b.read().unwrap());
let body = json!({"b1": "test1"});
let key = "key1".to_string();

context_a.write(&context_b.read().unwrap()).unwrap();
context_a.write(key.clone(), &body).unwrap();

assert_eq!(context_a.read().unwrap(), context_b.read().unwrap());
assert_eq!(context_a.read(key).unwrap(), body);
}
}
Loading

0 comments on commit 4d0d6c3

Please sign in to comment.