Skip to content

Commit f56872a

Browse files
authored
Adding support for state transition reasons (#7)
1 parent 09afbb6 commit f56872a

6 files changed

Lines changed: 130 additions & 114 deletions

File tree

commands.json

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"SM.GET": {
3-
"summary": "Gets a state machine, or the current state of a machine",
3+
"summary": "Gets a state machine, or the stored reason for the state transition",
44
"since": "0.1.0",
55
"complexity": "O(N)",
66
"group": "statemachine",
@@ -10,9 +10,18 @@
1010
"type": "key"
1111
},
1212
{
13-
"name": "state",
14-
"type": "string"
13+
"name": "codition",
14+
"type": "string",
15+
"optional": true,
16+
"arguments": [
17+
{
18+
"name": "reason",
19+
"type": "pure-token",
20+
"token": "reason"
21+
}
22+
]
1523
}
24+
1625
]
1726
},
1827
"SM.SET": {
@@ -103,10 +112,14 @@
103112
"type": "string"
104113
},
105114
{
106-
"name": "codition",
115+
"name": "condition",
107116
"type": "string",
108117
"optional": true,
109118
"arguments": [
119+
{
120+
"name": "reason",
121+
"type": "string"
122+
},
110123
{
111124
"name": "force",
112125
"type": "pure-token",

src/function_get.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,12 @@ use redis_module::{
55
};
66

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

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

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

2223
let rval = serde_json::to_string(&v)?;
23-
Ok(RedisValue::BulkString(rval))
24+
if fieldarg.is_err() {
25+
Ok(RedisValue::BulkString(rval))
26+
} else if fieldarg.unwrap().to_string().to_uppercase() == "REASON" {
27+
let sm = v.unwrap();
28+
Ok(RedisValue::SimpleString(sm.reason().to_string()))
29+
} else {
30+
Ok(RedisValue::Null)
31+
}
2432
}
2533

2634
pub(crate) fn template(_ctx: &Context, args: Vec<RedisString>) -> RedisResult {

src/function_state.rs

Lines changed: 22 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use redis_module::{
66
};
77

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

24-
// return value.map_or_else(
25-
// || Ok(RedisValue::Null),
26-
// |sm| Ok(RedisValue::SimpleString(sm.current().to_string())),
27-
// );
28-
// }
29-
3024
let sm = value.unwrap();
3125
let mut keys: Vec<RedisValue> = Vec::new();
32-
if ! list.is_err() {
26+
if list.is_ok() {
3327
for x in sm.map().keys() {
3428
keys.push(RedisValue::SimpleString(x.to_string()));
3529
}
36-
} else if list.unwrap().to_string().to_uppercase() == "LIST" {
37-
keys.push(RedisValue::SimpleString(sm.current().to_string()));
3830
} else {
39-
return Ok(RedisValue::Null);
31+
keys.push(RedisValue::SimpleString(sm.current().to_string()));
4032
}
4133
Ok(RedisValue::Array(keys))
42-
43-
// if let Some(sm) = value {
44-
// let mut keys: Vec<RedisValue> = Vec::new();
45-
// for x in sm.map().keys() {
46-
// keys.push(RedisValue::SimpleString(x.to_string()));
47-
// }
48-
// Ok(RedisValue::Array(keys))
49-
// } else {
50-
// Ok(RedisValue::Null)
51-
// }
52-
5334
}
5435

55-
// pub(crate) fn states(ctx: &Context, args: Vec<RedisString>) -> RedisResult {
56-
// if args.len() != 2 {
57-
// return Err(RedisError::WrongArity);
58-
// }
59-
// let mut args = args.into_iter().skip(1);
60-
// let key = args.next_arg()?;
61-
62-
// let rkey = RedisKey::open(ctx.ctx, &key);
63-
// let value = rkey.get_value::<StateMachine>(&REDIS_SM_TYPE)?;
64-
65-
// if let Some(sm) = value {
66-
// let mut keys: Vec<RedisValue> = Vec::new();
67-
// for x in sm.map().keys() {
68-
// keys.push(RedisValue::SimpleString(x.to_string()));
69-
// }
70-
// Ok(RedisValue::Array(keys))
71-
// } else {
72-
// Ok(RedisValue::Null)
73-
// }
74-
// }
75-
7636
pub(crate) fn mutate(ctx: &Context, args: Vec<RedisString>) -> RedisResult {
77-
if args.len() < 3 || args.len() > 5 {
37+
if args.len() < 3 || args.len() > 6 {
7838
return Err(RedisError::WrongArity);
7939
}
8040

8141
let mut args = args.into_iter().skip(1);
8242
let key = args.next_arg()?;
8343
let target = args.next_arg()?;
84-
let force = args.next_arg();
44+
45+
let maybe_reason = args.next_arg();
46+
let maybe_options = args.next_arg();
8547

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

9355
let sm = value.unwrap();
94-
if force.is_err() {
95-
let res = sm.check_transition(target.to_string());
96-
if !res {
97-
return Ok(RedisValue::Null);
56+
if !sm.is_valid_state(target.to_string()) {
57+
return Err(RedisError::String("Invaild state transition".to_string()));
58+
}
59+
60+
let res = sm.can_transition(target.to_string());
61+
62+
if let Ok(options) = maybe_options {
63+
if options.to_string().to_uppercase() != "FORCE" {
64+
return Err(RedisError::String("Invaild command option".to_string()));
9865
}
99-
sm.set_current(target.to_string());
100-
} else if force.unwrap().to_string().to_uppercase() == "FORCE" {
101-
sm.set_current(target.to_string());
102-
} else {
103-
return Ok(RedisValue::Null);
66+
} else if !res {
67+
return Err(RedisError::String("Invaild state transition".to_string()));
10468
}
10569

70+
sm.set_current(target.to_string());
71+
if let Ok(value) = maybe_reason {
72+
sm.set_reason(value.to_string());
73+
}
10674
REDIS_OK
10775
}

src/types.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ pub(crate) struct StateMachine {
88
current: String,
99
map: HashMap<String, Vec<String>>,
1010
initial: String,
11+
reason: String,
1112
// TODO store some way for this to never change
1213
}
1314

@@ -21,6 +22,7 @@ pub(crate) fn new() -> StateMachine {
2122
initial: String::from(""),
2223
current: String::from(""),
2324
map: m,
25+
reason: String::from(""),
2426
}
2527
}
2628

@@ -29,7 +31,11 @@ impl StateMachine {
2931
self.current = c;
3032
}
3133

32-
pub(crate) fn check_transition(&self, target: String) -> bool {
34+
pub(crate) fn set_reason(&mut self, c: String) {
35+
self.reason = c;
36+
}
37+
38+
pub(crate) fn can_transition(&self, target: String) -> bool {
3339
let current = String::from(self.current());
3440
let mapval = self.map.get(&current);
3541
if mapval.is_none() {
@@ -39,6 +45,10 @@ impl StateMachine {
3945
v.contains(&target)
4046
}
4147

48+
pub(crate) fn is_valid_state(&self, target: String) -> bool {
49+
self.map.contains_key(&target)
50+
}
51+
4252
pub(crate) fn current(&self) -> &str {
4353
&self.current
4454
}
@@ -50,4 +60,8 @@ impl StateMachine {
5060
pub(crate) const fn map(&self) -> &HashMap<String, Vec<String>> {
5161
&self.map
5262
}
63+
64+
pub(crate) fn reason(&self) -> &str {
65+
&self.reason
66+
}
5367
}

0 commit comments

Comments
 (0)