Skip to content

Commit

Permalink
Adding support for state transition reasons (#7)
Browse files Browse the repository at this point in the history
  • Loading branch information
chayim authored Mar 20, 2023
1 parent 09afbb6 commit f56872a
Show file tree
Hide file tree
Showing 6 changed files with 130 additions and 114 deletions.
21 changes: 17 additions & 4 deletions commands.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"SM.GET": {
"summary": "Gets a state machine, or the current state of a machine",
"summary": "Gets a state machine, or the stored reason for the state transition",
"since": "0.1.0",
"complexity": "O(N)",
"group": "statemachine",
Expand All @@ -10,9 +10,18 @@
"type": "key"
},
{
"name": "state",
"type": "string"
"name": "codition",
"type": "string",
"optional": true,
"arguments": [
{
"name": "reason",
"type": "pure-token",
"token": "reason"
}
]
}

]
},
"SM.SET": {
Expand Down Expand Up @@ -103,10 +112,14 @@
"type": "string"
},
{
"name": "codition",
"name": "condition",
"type": "string",
"optional": true,
"arguments": [
{
"name": "reason",
"type": "string"
},
{
"name": "force",
"type": "pure-token",
Expand Down
12 changes: 10 additions & 2 deletions src/function_get.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ use redis_module::{
};

pub(crate) fn get(ctx: &Context, args: Vec<RedisString>) -> RedisResult {
if args.len() < 2 {
if args.len() < 2 || args.len() > 4 {
return Err(RedisError::WrongArity);
}
let mut args = args.into_iter().skip(1);
let key = args.next_arg()?;
let fieldarg = args.next_arg();

let rkey = RedisKey::open(ctx.ctx, &key);

Expand All @@ -20,7 +21,14 @@ pub(crate) fn get(ctx: &Context, args: Vec<RedisString>) -> RedisResult {
}

let rval = serde_json::to_string(&v)?;
Ok(RedisValue::BulkString(rval))
if fieldarg.is_err() {
Ok(RedisValue::BulkString(rval))
} else if fieldarg.unwrap().to_string().to_uppercase() == "REASON" {
let sm = v.unwrap();
Ok(RedisValue::SimpleString(sm.reason().to_string()))
} else {
Ok(RedisValue::Null)
}
}

pub(crate) fn template(_ctx: &Context, args: Vec<RedisString>) -> RedisResult {
Expand Down
76 changes: 22 additions & 54 deletions src/function_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use redis_module::{
};

pub(crate) fn state(ctx: &Context, args: Vec<RedisString>) -> RedisResult {
if args.len() < 2 || args.len() > 3{
if args.len() < 2 || args.len() > 3 {
return Err(RedisError::WrongArity);
}
let mut args = args.into_iter().skip(1);
Expand All @@ -21,67 +21,29 @@ pub(crate) fn state(ctx: &Context, args: Vec<RedisString>) -> RedisResult {
return Ok(RedisValue::Null);
}

// return value.map_or_else(
// || Ok(RedisValue::Null),
// |sm| Ok(RedisValue::SimpleString(sm.current().to_string())),
// );
// }

let sm = value.unwrap();
let mut keys: Vec<RedisValue> = Vec::new();
if ! list.is_err() {
if list.is_ok() {
for x in sm.map().keys() {
keys.push(RedisValue::SimpleString(x.to_string()));
}
} else if list.unwrap().to_string().to_uppercase() == "LIST" {
keys.push(RedisValue::SimpleString(sm.current().to_string()));
} else {
return Ok(RedisValue::Null);
keys.push(RedisValue::SimpleString(sm.current().to_string()));
}
Ok(RedisValue::Array(keys))

// if let Some(sm) = value {
// let mut keys: Vec<RedisValue> = Vec::new();
// for x in sm.map().keys() {
// keys.push(RedisValue::SimpleString(x.to_string()));
// }
// Ok(RedisValue::Array(keys))
// } else {
// Ok(RedisValue::Null)
// }

}

// pub(crate) fn states(ctx: &Context, args: Vec<RedisString>) -> RedisResult {
// if args.len() != 2 {
// return Err(RedisError::WrongArity);
// }
// let mut args = args.into_iter().skip(1);
// let key = args.next_arg()?;

// let rkey = RedisKey::open(ctx.ctx, &key);
// let value = rkey.get_value::<StateMachine>(&REDIS_SM_TYPE)?;

// if let Some(sm) = value {
// let mut keys: Vec<RedisValue> = Vec::new();
// for x in sm.map().keys() {
// keys.push(RedisValue::SimpleString(x.to_string()));
// }
// Ok(RedisValue::Array(keys))
// } else {
// Ok(RedisValue::Null)
// }
// }

pub(crate) fn mutate(ctx: &Context, args: Vec<RedisString>) -> RedisResult {
if args.len() < 3 || args.len() > 5 {
if args.len() < 3 || args.len() > 6 {
return Err(RedisError::WrongArity);
}

let mut args = args.into_iter().skip(1);
let key = args.next_arg()?;
let target = args.next_arg()?;
let force = args.next_arg();

let maybe_reason = args.next_arg();
let maybe_options = args.next_arg();

let rkey = RedisKeyWritable::open(ctx.ctx, &key);
let value = rkey.get_value::<StateMachine>(&REDIS_SM_TYPE)?;
Expand All @@ -91,17 +53,23 @@ pub(crate) fn mutate(ctx: &Context, args: Vec<RedisString>) -> RedisResult {
}

let sm = value.unwrap();
if force.is_err() {
let res = sm.check_transition(target.to_string());
if !res {
return Ok(RedisValue::Null);
if !sm.is_valid_state(target.to_string()) {
return Err(RedisError::String("Invaild state transition".to_string()));
}

let res = sm.can_transition(target.to_string());

if let Ok(options) = maybe_options {
if options.to_string().to_uppercase() != "FORCE" {
return Err(RedisError::String("Invaild command option".to_string()));
}
sm.set_current(target.to_string());
} else if force.unwrap().to_string().to_uppercase() == "FORCE" {
sm.set_current(target.to_string());
} else {
return Ok(RedisValue::Null);
} else if !res {
return Err(RedisError::String("Invaild state transition".to_string()));
}

sm.set_current(target.to_string());
if let Ok(value) = maybe_reason {
sm.set_reason(value.to_string());
}
REDIS_OK
}
16 changes: 15 additions & 1 deletion src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ pub(crate) struct StateMachine {
current: String,
map: HashMap<String, Vec<String>>,
initial: String,
reason: String,
// TODO store some way for this to never change
}

Expand All @@ -21,6 +22,7 @@ pub(crate) fn new() -> StateMachine {
initial: String::from(""),
current: String::from(""),
map: m,
reason: String::from(""),
}
}

Expand All @@ -29,7 +31,11 @@ impl StateMachine {
self.current = c;
}

pub(crate) fn check_transition(&self, target: String) -> bool {
pub(crate) fn set_reason(&mut self, c: String) {
self.reason = c;
}

pub(crate) fn can_transition(&self, target: String) -> bool {
let current = String::from(self.current());
let mapval = self.map.get(&current);
if mapval.is_none() {
Expand All @@ -39,6 +45,10 @@ impl StateMachine {
v.contains(&target)
}

pub(crate) fn is_valid_state(&self, target: String) -> bool {
self.map.contains_key(&target)
}

pub(crate) fn current(&self) -> &str {
&self.current
}
Expand All @@ -50,4 +60,8 @@ impl StateMachine {
pub(crate) const fn map(&self) -> &HashMap<String, Vec<String>> {
&self.map
}

pub(crate) fn reason(&self) -> &str {
&self.reason
}
}
Loading

0 comments on commit f56872a

Please sign in to comment.