Skip to content

Commit 324269f

Browse files
committed
Removed force, added transition
1 parent 011706e commit 324269f

7 files changed

Lines changed: 107 additions & 55 deletions

File tree

commands.json

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -79,38 +79,50 @@
7979
}
8080
]
8181
},
82-
"SM.FORCE": {
83-
"summary": "Forces the state machine into a named state",
82+
"SM.CREATE": {
83+
"summary": "Creates a blank state machine and stores it in the named key",
8484
"since": "0.1.0",
8585
"complexity": "O(N)",
8686
"group": "statemachine",
8787
"arguments": [
8888
{
8989
"name": "key",
9090
"type": "key"
91-
},
92-
{
93-
"name": "state",
94-
"type": "string"
9591
}
9692
]
9793
},
98-
"SM.CREATE": {
99-
"summary": "Creates a blank state machine and stores it in the named key",
94+
"SM.TEMPLATE": {
95+
"summary": "Return the json template used for constructing a state machine",
10096
"since": "0.1.0",
101-
"complexity": "O(N)",
97+
"complexity": "O(1)",
98+
"group": "statemachine"
99+
},
100+
"SM.TRANSITION": {
101+
"summary": "Transition the state machine to the specific state",
102+
"since": "0.1.0",
103+
"complexity": "O(1)",
102104
"group": "statemachine",
103105
"arguments": [
104106
{
105107
"name": "key",
106108
"type": "key"
109+
},
110+
{
111+
"name": "state",
112+
"type": "string"
113+
},
114+
{
115+
"name": "codition",
116+
"type": "string",
117+
"optional": true,
118+
"arguments": [
119+
{
120+
"name": "force",
121+
"type": "pure-token",
122+
"token": "F"
123+
}
124+
]
107125
}
108126
]
109-
},
110-
"SM.TEMPLATE": {
111-
"summary": "Return the json template used for constructing a state machine",
112-
"since": "0.1.0",
113-
"complexity": "O(1)",
114-
"group": "statemachine"
115127
}
116128
}

src/function_set.rs

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ pub(crate) fn set(ctx: &Context, args: Vec<RedisString>) -> RedisResult {
2222
let mut rval: StateMachine = new_from_redisstring(val)?;
2323

2424
if !current.is_empty() {
25-
rval.set_current_from_redisstring(current);
25+
rval.set_current(current.to_string());
2626
}
2727
if rval.current().is_empty() {
2828
rval.set_current(rval.initial().to_string());
@@ -66,23 +66,3 @@ pub(crate) fn reset(ctx: &Context, args: Vec<RedisString>) -> RedisResult {
6666
Ok(RedisValue::Null)
6767
}
6868
}
69-
70-
// Force set the named state machine to a value
71-
pub(crate) fn force_set(ctx: &Context, args: Vec<RedisString>) -> RedisResult {
72-
if args.len() != 3 {
73-
return Err(RedisError::WrongArity);
74-
}
75-
76-
let mut args = args.into_iter().skip(1);
77-
let key = args.next_arg()?;
78-
let state = args.next_arg()?;
79-
80-
let rkey = RedisKeyWritable::open(ctx.ctx, &key);
81-
let v = rkey.get_value::<StateMachine>(&REDIS_SM_TYPE)?;
82-
if let Some(rval) = v {
83-
rval.set_current_from_redisstring(state);
84-
REDIS_OK
85-
} else {
86-
Ok(RedisValue::Null)
87-
}
88-
}

src/function_state.rs

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
use crate::types::StateMachine;
22
use crate::REDIS_SM_TYPE;
33
use redis_module::{
4-
key::RedisKey, Context, NextArg, RedisError, RedisResult, RedisString, RedisValue,
4+
key::{RedisKey, RedisKeyWritable},
5+
Context, NextArg, RedisError, RedisResult, RedisString, RedisValue, REDIS_OK,
56
};
67

78
pub(crate) fn current_state(ctx: &Context, args: Vec<RedisString>) -> RedisResult {
@@ -41,3 +42,36 @@ pub(crate) fn states(ctx: &Context, args: Vec<RedisString>) -> RedisResult {
4142
Ok(RedisValue::Null)
4243
}
4344
}
45+
46+
pub(crate) fn transition(ctx: &Context, args: Vec<RedisString>) -> RedisResult {
47+
if args.len() < 3 || args.len() > 5 {
48+
return Err(RedisError::WrongArity);
49+
}
50+
51+
let mut args = args.into_iter().skip(1);
52+
let key = args.next_arg()?;
53+
let target = args.next_arg()?;
54+
let force = args.next_arg();
55+
56+
let rkey = RedisKeyWritable::open(ctx.ctx, &key);
57+
let value = rkey.get_value::<StateMachine>(&REDIS_SM_TYPE)?;
58+
59+
if value.is_none() {
60+
return Ok(RedisValue::Null);
61+
}
62+
63+
let sm = value.unwrap();
64+
if force.is_err() {
65+
let res = sm.check_transition(target.to_string());
66+
if !res {
67+
return Ok(RedisValue::Null);
68+
}
69+
sm.set_current(target.to_string());
70+
} else if force.unwrap().to_string() == "F" {
71+
sm.set_current(target.to_string());
72+
} else {
73+
return Ok(RedisValue::Null);
74+
}
75+
76+
REDIS_OK
77+
}

src/lib.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,8 @@ redis_module! {
2626
["SM.DEL", function_delete::delete, "write", 1, 1, 1],
2727
["SM.CREATE", function_set::create, "write deny-oom", 1, 1, 1],
2828
["SM.TEMPLATE", function_get::template, "readonly", 0, 0, 0],
29-
["SM.FORCE", function_set::force_set, "write deny-oom", 1, 1, 1],
29+
// ["SM.FORCE", function_set::force_set, "write deny-oom", 1, 1, 1],
3030
["SM.RESET", function_set::reset, "write deny-oom", 1, 1, 1],
31-
32-
// ["SM.GO", function_state::go "write deny-oom", 1, 1, 1],
31+
["SM.TRANSITION", function_state::transition, "write deny-oom", 1, 1, 1],
3332
],
3433
}

src/rdb.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ unsafe extern "C" fn free(value: *mut c_void) {
6060
if value.is_null() {
6161
return;
6262
}
63-
let sm = value as *mut StateMachine ;
63+
let sm = value as *mut StateMachine;
6464
Box::from_raw(sm);
6565
}
6666

@@ -70,7 +70,7 @@ unsafe extern "C" fn copy(
7070
tokey: *mut raw::RedisModuleString,
7171
value: *const c_void,
7272
) -> *mut c_void {
73-
let sm = &*(value as *mut StateMachine) ;
73+
let sm = &*(value as *mut StateMachine);
7474
let newSm = sm.clone();
7575
Box::into_raw(Box::new(newSm)).cast::<c_void>()
7676
}

src/types.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,14 @@ impl StateMachine {
2929
self.current = c;
3030
}
3131

32-
pub(crate) fn set_current_from_redisstring(&mut self, c: RedisString) {
33-
self.current = c.to_string();
32+
pub(crate) fn check_transition(&self, target: String) -> bool {
33+
let current = String::from(self.current());
34+
let mapval = self.map.get(&current);
35+
if mapval.is_none() {
36+
return false;
37+
}
38+
let v = mapval.unwrap();
39+
v.contains(&target)
3440
}
3541

3642
pub(crate) fn current(&self) -> &str {

tests/test_commands.py

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -98,18 +98,18 @@ def test_reset(r):
9898
assert r.execute_command("SM.CURRENT", "foostates") == initial
9999

100100

101-
def test_force_set(r):
102-
r.flushdb()
103-
initial = "begin"
104-
mapstates = {
105-
"a": ["this", "maps", "states"],
106-
"b": ["this", "too", "maps", "somewhere"],
107-
}
108-
validmap = {"initial": initial, "map": mapstates, "current": "maps"}
109-
assert r.execute_command("SM.SET", "foostates", json.dumps(validmap))
110-
assert r.execute_command("SM.FORCE", "foostates", "too")
101+
# def test_force_set(r):
102+
# r.flushdb()
103+
# initial = "begin"
104+
# mapstates = {
105+
# "a": ["this", "maps", "states"],
106+
# "b": ["this", "too", "maps", "somewhere"],
107+
# }
108+
# validmap = {"initial": initial, "map": mapstates, "current": "maps"}
109+
# assert r.execute_command("SM.SET", "foostates", json.dumps(validmap))
110+
# assert r.execute_command("SM.FORCE", "foostates", "too")
111111

112-
assert r.execute_command("SM.CURRENT", "foostates") == "too"
112+
# assert r.execute_command("SM.CURRENT", "foostates") == "too"
113113

114114

115115
def test_create(r):
@@ -133,3 +133,24 @@ def test_template(r):
133133
assert val["initial"] == ""
134134
assert val["map"] == {}
135135
assert val["current"] == ""
136+
137+
def test_transition(r):
138+
r.flushdb()
139+
initial = "begin"
140+
current = "begin"
141+
mapstates = {
142+
"a": ["this", "maps", "states"],
143+
"b": ["this", "too", "maps", "somewhere"],
144+
"begin": ["too", "maps"],
145+
}
146+
147+
valid_with_current = {"initial": initial, "map": mapstates, "current": current}
148+
assert r.execute_command("SM.SET", "bar", json.dumps(valid_with_current))
149+
assert r.execute_command("SM.TRANSITION", "bar", "smurfy") == None
150+
assert r.execute_command("SM.TRANSITION", "bar", "too")
151+
152+
# force state
153+
r.flushdb()
154+
assert r.execute_command("SM.SET", "bar", json.dumps(valid_with_current))
155+
assert r.execute_command("SM.TRANSITION", "bar", "banna", "foo") == None
156+
assert r.execute_command("SM.TRANSITION", "bar", "banna", "F")

0 commit comments

Comments
 (0)