From 2143ec16b231f7aee5e0812679c4e990fac8c45c Mon Sep 17 00:00:00 2001 From: Guy Korland Date: Wed, 11 Sep 2019 11:22:59 +0300 Subject: [PATCH] fix #55 return the right type on json.num* call (#56) * fix #55 return the right type on json.num* call --- src/lib.rs | 56 ++++++++++++++++++++++++++++++--------------- test/pytest/test.py | 40 ++++++++++++++++---------------- 2 files changed, 57 insertions(+), 39 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 55a4528..61aeab9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -274,58 +274,76 @@ fn json_type(ctx: &Context, args: Vec) -> RedisResult { /// JSON.NUMINCRBY /// fn json_num_incrby(ctx: &Context, args: Vec) -> RedisResult { - json_num_op(ctx, args, |num1, num2| num1 + num2) + json_num_op(ctx, args, |i1, i2| i1 + i2, |f1, f2| f1 + f2) } /// /// JSON.NUMMULTBY /// fn json_num_multby(ctx: &Context, args: Vec) -> RedisResult { - json_num_op(ctx, args, |num1, num2| num1 * num2) + json_num_op(ctx, args, |i1, i2| i1 * i2, |f1, f2| f1 * f2) } /// /// JSON.NUMPOWBY /// fn json_num_powby(ctx: &Context, args: Vec) -> RedisResult { - json_num_op(ctx, args, |num1, num2| num1.powf(num2)) + json_num_op(ctx, args, |i1, i2| i1.pow(i2 as u32), |f1, f2| f1.powf(f2)) } -fn json_num_op(ctx: &Context, args: Vec, fun: F) -> RedisResult +fn json_num_op(ctx: &Context, args: Vec, op_i64: I, op_f64: F) -> RedisResult where + I: Fn(i64, i64) -> i64, F: Fn(f64, f64) -> f64, { let mut args = args.into_iter().skip(1); let key = args.next_string()?; let path = backwards_compat_path(args.next_string()?); - let number: f64 = args.next_string()?.parse()?; + let number = args.next_string()?; let key = ctx.open_key_writable(&key); key.get_value::(&REDIS_JSON_TYPE)? .ok_or_else(RedisError::nonexistent_key) .and_then(|doc| { - doc.value_op(&path, |value| do_json_num_op(&fun, number, value)) - .map(|v| v.to_string().into()) - .map_err(|e| e.into()) + doc.value_op(&path, |value| { + do_json_num_op(&number, value, &op_i64, &op_f64) + }) + .map(|v| v.to_string().into()) + .map_err(|e| e.into()) }) } -fn do_json_num_op(fun: F, number: f64, value: &Value) -> Result +fn do_json_num_op( + in_value: &str, + curr_value: &Value, + op_i64: I, + op_f64: F, +) -> Result where + I: FnOnce(i64, i64) -> i64, F: FnOnce(f64, f64) -> f64, { - value - .as_f64() - .ok_or_else(|| err_json(value, "number")) - .and_then(|curr_value| { - let res = fun(curr_value, number); - - Number::from_f64(res) - .ok_or(Error::from("ERR cannot represent result as Number")) - .map(Value::Number) - }) + if let Value::Number(curr_value) = curr_value { + let in_value = &serde_json::from_str(in_value)?; + if let Value::Number(in_value) = in_value { + let num_res = match (curr_value.as_i64(), in_value.as_i64()) { + (Some(num1), Some(num2)) => op_i64(num1, num2).into(), + _ => { + let num1 = curr_value.as_f64().unwrap(); + let num2 = in_value.as_f64().unwrap(); + Number::from_f64(op_f64(num1, num2)).unwrap() + } + }; + + Ok(Value::Number(num_res)) + } else { + Err(err_json(in_value, "number")) + } + } else { + Err(err_json(curr_value, "number")) + } } fn err_json(value: &Value, expected_value: &'static str) -> Error { diff --git a/test/pytest/test.py b/test/pytest/test.py index 945e6be..87ea728 100644 --- a/test/pytest/test.py +++ b/test/pytest/test.py @@ -599,10 +599,10 @@ def testNumIncrCommand(self): r.flushdb() self.assertOk(r.execute_command('JSON.SET', 'test', '.', '{ "foo": 0, "bar": "baz" }')) - # self.assertEqual('1', r.execute_command('JSON.NUMINCRBY', 'test', '.foo', 1)) - # self.assertEqual('1', r.execute_command('JSON.GET', 'test', '.foo')) - # self.assertEqual('3', r.execute_command('JSON.NUMINCRBY', 'test', '.foo', 2)) - # self.assertEqual('3.5', r.execute_command('JSON.NUMINCRBY', 'test', '.foo', .5)) + self.assertEqual('1', r.execute_command('JSON.NUMINCRBY', 'test', '.foo', 1)) + self.assertEqual('1', r.execute_command('JSON.GET', 'test', '.foo')) + self.assertEqual('3', r.execute_command('JSON.NUMINCRBY', 'test', '.foo', 2)) + self.assertEqual('3.5', r.execute_command('JSON.NUMINCRBY', 'test', '.foo', .5)) # test a wrong type with self.assertRaises(redis.exceptions.ResponseError) as cm: @@ -612,22 +612,22 @@ def testNumIncrCommand(self): # with self.assertRaises(redis.exceptions.ResponseError) as cm: # r.execute_command('JSON.NUMINCRBY', 'test', '.fuzz', 1) # - # # test issue #9 - # self.assertOk(r.execute_command('JSON.SET', 'num', '.', '0')) - # self.assertEqual('1', r.execute_command('JSON.NUMINCRBY', 'num', '.', 1)) - # self.assertEqual('2.5', r.execute_command('JSON.NUMINCRBY', 'num', '.', 1.5)) - # - # # test issue 55 - # self.assertOk(r.execute_command('JSON.SET', 'foo', '.', '{"foo":0,"bar":42}')) - # # Get the document once - # r.execute_command('JSON.GET', 'foo', '.') - # self.assertEqual('1', r.execute_command('JSON.NUMINCRBY', 'foo', 'foo', 1)) - # self.assertEqual('84', r.execute_command('JSON.NUMMULTBY', 'foo', 'bar', 2)) - # res = json.loads(r.execute_command('JSON.GET', 'foo', '.')) - # self.assertEqual(1, res['foo']) - # self.assertEqual(84, res['bar']) - # - # + # test issue #9 + self.assertOk(r.execute_command('JSON.SET', 'num', '.', '0')) + self.assertEqual('1', r.execute_command('JSON.NUMINCRBY', 'num', '.', 1)) + self.assertEqual('2.5', r.execute_command('JSON.NUMINCRBY', 'num', '.', 1.5)) + + # test issue 55 + self.assertOk(r.execute_command('JSON.SET', 'foo', '.', '{"foo":0,"bar":42}')) + # Get the document once + r.execute_command('JSON.GET', 'foo', '.') + self.assertEqual('1', r.execute_command('JSON.NUMINCRBY', 'foo', 'foo', 1)) + self.assertEqual('84', r.execute_command('JSON.NUMMULTBY', 'foo', 'bar', 2)) + res = json.loads(r.execute_command('JSON.GET', 'foo', '.')) + self.assertEqual(1, res['foo']) + self.assertEqual(84, res['bar']) + + def testStrCommands(self): """Test JSON.STRAPPEND and JSON.STRLEN commands"""