Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: marvindv/jsonlogic_rs
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: master
Choose a base ref
...
head repository: onsigntv/jsonlogic_rs
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: master
Choose a head ref
Able to merge. These branches can be automatically merged.
  • 6 commits
  • 39 files changed
  • 2 contributors

Commits on Apr 22, 2022

  1. Ensure compliance with json-logic-js

    Joao Carneiro Haas committed Apr 22, 2022
    Copy the full SHA
    75ce3b4 View commit details

Commits on Apr 25, 2022

  1. Use same test model as json-logic-js

    Ensure both json-logic projects use the same test-suite, and fix some
    issues that appeared with some of the json-logic-js tests.
    Joao Carneiro Haas committed Apr 25, 2022
    Copy the full SHA
    323e0b5 View commit details

Commits on May 11, 2022

  1. Fix issues with geo circle operation

    Joao Carneiro Haas committed May 11, 2022
    Copy the full SHA
    dfd63ca View commit details

Commits on Jun 27, 2022

  1. Fix issue with pattern matching

    Since adding a 'rad' key to a 'point' would match as a 'circle' with
    Rust's pattern matching, we're now being stricter when 'typing' the
    first argument of the geographic functions.
    joaohaas committed Jun 27, 2022
    Copy the full SHA
    17e7f38 View commit details

Commits on Aug 11, 2022

  1. Fix behavior when evaluating empty objects

    In order to match python behavior, we evaluate empty objects to false.
    joaohaas committed Aug 11, 2022
    Copy the full SHA
    384ca05 View commit details

Commits on Jun 19, 2023

  1. Fix reduce null treatment

    joaohaas committed Jun 19, 2023
    Copy the full SHA
    77ecd4d View commit details
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.DS_Store
/target
**/*.rs.bk
Cargo.lock
6 changes: 4 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
[package]
name = "jsonlogic"
version = "0.5.1"
version = "0.6.0"
authors = ["Marvin Davieds <marvin.davieds@gmail.com>"]
edition = "2018"
edition = "2021"
license = "MIT"
description = "A JsonLogic implementation in Rust"
repository = "https://github.com/marvindv/jsonlogic_rs"
@@ -13,4 +13,6 @@ readme = "README.md"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
regex = "1.5"
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -42,6 +42,10 @@ See the [`examples`](https://github.com/marvindv/jsonlogic_rs/tree/master/exampl
**jsonlogic_rs** supports all JsonLogic operations. For detailed informations about all operations and their arguments, head over to [Supported Operations](http://jsonlogic.com/operations.html) on
[jsonlogic.com](http://jsonlogic.com/).

It does differ quite a bit in how null values are handled though.
Regardless of null or missing input, we never return a null value from an operation.
Most of these changes can be checked through the [null tests file](tests/null.rs).

For Rust usage examples and edge cases have a look at the linked tests for each operator below.

* Accessing Data
@@ -75,3 +79,19 @@ For Rust usage examples and edge cases have a look at the linked tests for each
- [`substr`](https://github.com/marvindv/jsonlogic_rs/blob/master/tests/string.rs#L35)
* Miscellaneous
- [`log`](https://github.com/marvindv/jsonlogic_rs/blob/master/tests/misc.rs#L5)

## Additional Operators

Besides the default operators, this repo also implements a few new operators:

- `><`: Receives a geo-coordinate and a list of region objects, and checks whether the coordinate is inside the any region.
- `>.<`: Receives a geo-coordinate and a region object, and checks whether the coordinate is inside the region.
- `>t<`: Receives a point, in virtual coordinates from 0 to 100_000 and a rectangle array of [x, y, width, height], to determine wether the point
is within the rectangle. Third parameter is optional for mapping the rectangle to a different area of the plane. Returns a boolean.
- `tsrep`: Receives the current unix timestamp, a timestamp from the start of the day and a repetition value to determine wether the current
timestamp is a repeating point within the day. Returns a boolean.
- `match`: Checks whether the first parameter matches a regular expression in the second parameter. Returns the match array or `null`.
- `*=`: Checks whether the first argument starts with the second argument
- `=*`: Checks whether the first argument ends with the second argument

You can find examples on how to use these operators on the test files [`geo`](tests/geo.rs), [`time`](tests/time.rs) and [`string_extra`](tests/string_extra.rs).
2 changes: 0 additions & 2 deletions rustfmt.toml
Original file line number Diff line number Diff line change
@@ -1,2 +0,0 @@
unstable_features = true
wrap_comments = true
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -9,6 +9,8 @@ use std::collections::HashSet;

use data::Data;

pub use operators::logic::is_truthy;

/// Applies the given JsonLogic rule to the specified data.
/// If the rule does not use any variables, you may pass `&Value::Null` as the second argument.
///
11 changes: 5 additions & 6 deletions src/operators/addition.rs
Original file line number Diff line number Diff line change
@@ -6,14 +6,13 @@ use super::{logic, Data, Expression};
/// will be cast to a number. Returns `Value::Null` if one argument cannot be coerced into a
/// number.
pub fn compute(args: &[Expression], data: &Data) -> Value {
let mut result = 0f64;
let mut result = 0.0;

for arg in args.iter() {
// Use parseFloat like in the javascript implementation.
// parseFloat(null) is NaN, whereas coerce_to_f64 would return 0.
match logic::parse_float(&arg.compute(data)) {
Some(num) => result += num,
None => return Value::Null,
if let Some(num) = logic::coerce_to_f64(&arg.compute(data)) {
result += num;
}
}

@@ -29,8 +28,8 @@ mod tests {
#[test]
fn test() {
assert_eq!(compute_const!(), json!(0.0));
assert_eq!(compute_const!(Value::Null), Value::Null);
assert_eq!(compute_const!(json!("foo")), Value::Null);
assert_eq!(compute_const!(json!(null)), json!(0.0));
assert_eq!(compute_const!(json!("foo")), json!(0.0));
assert_eq!(compute_const!(json!("6")), json!(6.0));
assert_eq!(compute_const!(json!(4), json!(2)), json!(6.0));
assert_eq!(
35 changes: 17 additions & 18 deletions src/operators/division.rs
Original file line number Diff line number Diff line change
@@ -1,31 +1,30 @@
use serde_json::{Number, Value};
use serde_json::{json, Value};

use super::{logic, Data, Expression};

/// "/", takes two arguments that are coerced into numbers. Returns `Value::Null` if the divisor is
/// coerced to `0` or one argument cannot be coerced into a number.
pub fn compute(args: &[Expression], data: &Data) -> Value {
let a = match args
let mut result = match args
.get(0)
.map(|arg| arg.compute(data))
.and_then(|a| logic::coerce_to_f64(&a))
{
Some(a) => a,
None => return Value::Null,
None => return json!(0.0),
};

let b = match args
.get(1)
.map(|arg| arg.compute(data))
.and_then(|b| logic::coerce_to_f64(&b))
{
Some(b) => b,
None => return Value::Null,
};
for arg in args.iter().skip(1) {
match logic::coerce_to_f64(&arg.compute(data)) {
Some(num) => result /= num,
None => return json!(0.0),
}
}

match Number::from_f64(a / b) {
Some(num) => Value::Number(num),
None => Value::Null,
if result.is_finite() {
json!(result)
} else {
json!(0.0)
}
}

@@ -37,10 +36,10 @@ mod tests {

#[test]
fn null() {
assert_eq!(compute_const!(), Value::Null);
assert_eq!(compute_const!(json!("a")), Value::Null);
assert_eq!(compute_const!(json!(1)), Value::Null);
assert_eq!(compute_const!(json!(1), json!(0)), Value::Null);
assert_eq!(compute_const!(), json!(0.0));
assert_eq!(compute_const!(json!("a")), json!(0.0));
assert_eq!(compute_const!(json!(1)), json!(1.0));
assert_eq!(compute_const!(json!(1), json!(0)), json!(0.0));

assert_eq!(compute_const!(json!(1), json!(2)), json!(0.5));
}
5 changes: 3 additions & 2 deletions src/operators/double_negation.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use serde_json::{json, Value};
use serde_json::Value;

use super::{logic, Data, Expression};

@@ -7,7 +7,7 @@ pub fn compute(args: &[Expression], data: &Data) -> Value {
let a = args
.get(0)
.map(|arg| arg.compute(data))
.unwrap_or(json!(null));
.unwrap_or(Value::Null);

Value::Bool(logic::is_truthy(&a))
}
@@ -16,6 +16,7 @@ pub fn compute(args: &[Expression], data: &Data) -> Value {
mod tests {
use super::*;
use crate::compute_const;
use serde_json::json;

#[test]
fn test() {
20 changes: 20 additions & 0 deletions src/operators/ends_with.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
use serde_json::Value;

use super::{logic, Data, Expression};

pub fn compute(args: &[Expression], data: &Data) -> Value {
let a = match args.get(0) {
Some(arg) => arg.compute(data),
None => return Value::Bool(false),
};

let b = match args.get(1) {
Some(arg) => arg.compute(data),
None => return Value::Bool(false),
};

match (a, b) {
(Value::Null, _) | (_, Value::Null) => Value::Bool(false),
(a, b) => Value::Bool(logic::coerce_to_str(&a).ends_with(&logic::coerce_to_str(&b))),
}
}
17 changes: 17 additions & 0 deletions src/operators/geo_any_in.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
use serde_json::Value;

use super::{logic_geo, Data, Expression};

pub fn compute(args: &[Expression], data: &Data) -> Value {
let a = match args.get(0) {
Some(arg) => arg.compute(data),
None => return Value::Bool(false),
};

let b = match args.get(1) {
Some(arg) => arg.compute(data),
None => return Value::Bool(false),
};

Value::Bool(logic_geo::is_within_regions(a, b))
}
17 changes: 17 additions & 0 deletions src/operators/geo_in.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
use serde_json::Value;

use super::{logic_geo, Data, Expression};

pub fn compute(args: &[Expression], data: &Data) -> Value {
let a = match args.get(0) {
Some(arg) => arg.compute(data),
None => return Value::Bool(false),
};

let b = match args.get(1) {
Some(arg) => arg.compute(data),
None => return Value::Bool(false),
};

Value::Bool(logic_geo::is_within_region(a, b))
}
5 changes: 4 additions & 1 deletion src/operators/greater_equal_than.rs
Original file line number Diff line number Diff line change
@@ -13,5 +13,8 @@ pub fn compute(args: &[Expression], data: &Data) -> Value {
None => return Value::Bool(false),
};

Value::Bool(logic::greater_equal_than(&a, &b))
match (a, b) {
(Value::Null, _) | (_, Value::Null) => Value::Bool(false),
(a, b) => Value::Bool(logic::greater_equal_than(&a, &b)),
}
}
5 changes: 4 additions & 1 deletion src/operators/greater_than.rs
Original file line number Diff line number Diff line change
@@ -13,5 +13,8 @@ pub fn compute(args: &[Expression], data: &Data) -> Value {
None => return Value::Bool(false),
};

Value::Bool(logic::greater_than(&a, &b))
match (a, b) {
(Value::Null, _) | (_, Value::Null) => Value::Bool(false),
(a, b) => Value::Bool(logic::greater_than(&a, &b)),
}
}
5 changes: 3 additions & 2 deletions src/operators/is_in.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use serde_json::{json, Value};
use serde_json::Value;

use super::{logic, Data, Expression};

@@ -10,7 +10,7 @@ use super::{logic, Data, Expression};
pub fn compute(args: &[Expression], data: &Data) -> Value {
let a = match args.get(0) {
Some(arg) => arg.compute(data),
None => return json!(false),
None => return Value::Bool(false),
};

let result = match args.get(1).map(|arg| arg.compute(data)) {
@@ -28,6 +28,7 @@ pub fn compute(args: &[Expression], data: &Data) -> Value {
mod tests {
use super::*;
use crate::compute_const;
use serde_json::json;

#[test]
fn basic() {
5 changes: 4 additions & 1 deletion src/operators/less_equal_than.rs
Original file line number Diff line number Diff line change
@@ -15,7 +15,10 @@ pub fn compute(args: &[Expression], data: &Data) -> Value {

let result = match args.get(2) {
Some(c) => compute_between_inclusive(&a, &b, &c.compute(data)),
None => compute_less_equal_than(&a, &b),
None => match (a, b) {
(Value::Null, _) | (_, Value::Null) => false,
(a, b) => compute_less_equal_than(&a, &b),
},
};

Value::Bool(result)
5 changes: 4 additions & 1 deletion src/operators/less_than.rs
Original file line number Diff line number Diff line change
@@ -15,7 +15,10 @@ pub fn compute(args: &[Expression], data: &Data) -> Value {

let result = match args.get(2) {
Some(c) => compute_between_exclusive(&a, &b, &c.compute(data)),
None => compute_less_than(&a, &b),
None => match (a, b) {
(Value::Null, _) | (_, Value::Null) => false,
(a, b) => compute_less_than(&a, &b),
},
};

Value::Bool(result)
Loading