{"id":21303014,"url":"https://github.com/GoPlasmatic/datalogic-rs","last_synced_at":"2025-07-11T20:31:55.736Z","repository":{"id":263525271,"uuid":"890313886","full_name":"GoPlasmatic/datalogic-rs","owner":"GoPlasmatic","description":"A fast, type-safe Rust implementation of JSONLogic for evaluating logical rules as JSON. Perfect for business rules engines and dynamic filtering in Rust applications.","archived":false,"fork":false,"pushed_at":"2025-06-28T07:29:44.000Z","size":4477,"stargazers_count":36,"open_issues_count":0,"forks_count":2,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-06-28T07:40:20.466Z","etag":null,"topics":["jsonlogic","rules-engine","rust"],"latest_commit_sha":null,"homepage":"https://goplasmatic.github.io/datalogic-rs/","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/GoPlasmatic.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2024-11-18T11:08:46.000Z","updated_at":"2025-06-28T07:29:47.000Z","dependencies_parsed_at":null,"dependency_job_id":"9fb8d5a3-7c52-4f87-bf1b-82adaf48c66b","html_url":"https://github.com/GoPlasmatic/datalogic-rs","commit_stats":null,"previous_names":["open-payments/datalogic-rs","json-logic/datalogic-rs","codetiger/datalogic-rs"],"tags_count":37,"template":false,"template_full_name":null,"purl":"pkg:github/GoPlasmatic/datalogic-rs","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GoPlasmatic%2Fdatalogic-rs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GoPlasmatic%2Fdatalogic-rs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GoPlasmatic%2Fdatalogic-rs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GoPlasmatic%2Fdatalogic-rs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/GoPlasmatic","download_url":"https://codeload.github.com/GoPlasmatic/datalogic-rs/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GoPlasmatic%2Fdatalogic-rs/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":264892369,"owners_count":23679279,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["jsonlogic","rules-engine","rust"],"created_at":"2024-11-21T15:58:37.542Z","updated_at":"2025-07-11T20:31:55.726Z","avatar_url":"https://github.com/GoPlasmatic.png","language":"Rust","funding_links":[],"categories":["Libraries"],"sub_categories":["Data processing"],"readme":"# datalogic-rs\n\n[![Release Crates](https://github.com/codetiger/datalogic-rs/actions/workflows/crate-publish.yml/badge.svg)](https://github.com/codetiger/datalogic-rs/actions?query=crate-publish)\n[![Documentation](https://docs.rs/datalogic-rs/badge.svg)](https://docs.rs/datalogic-rs)\n[![crates.io](https://img.shields.io/crates/v/datalogic-rs.svg)](https://crates.io/crates/datalogic-rs)\n[![Downloads](https://img.shields.io/crates/d/datalogic-rs)](https://crates.io/crates/datalogic-rs)\n\nA **lightweight, high-performance** Rust implementation of [JSONLogic](http://jsonlogic.com), optimized for **rule-based decision-making** and **dynamic expressions**.\n\n✨ **Why `datalogic-rs`?**\n- 🏆 **Fully JSONLogic-compliant** (100% test coverage)\n- 🚀 **Fast \u0026 lightweight**: Zero-copy JSON parsing, minimal allocations\n- 🔒 **Thread-safe**: Designed for parallel execution\n- ⚡ **Optimized for production**: Static dispatch and rule optimization\n- 🔌 **Extensible**: Support for custom operators\n- 🏗️ **Structured output**: Support for structured object preservation and templating\n\n## Overview\n\ndatalogic-rs provides a robust implementation of JSONLogic rules with arena-based memory management for optimal performance. The library features comprehensive operator support, optimizations for static rule components, and high test coverage.\n\n## Features\n\n- Arena-based memory management for optimal performance\n- Comprehensive JSONLogic operator support\n- Optimizations for static rule components\n- Zero copy rule creation and evaluation\n- High test coverage and compatibility with standard JSONLogic\n- Intuitive API for creating, parsing, and evaluating rules\n- Structured object preservation for powerful output templating\n\n## Installation\n\nAdd `datalogic-rs` to your `Cargo.toml`:\n\n```toml\n[dependencies]\ndatalogic-rs = \"3.0.12\"\n```\n\n## Core API Methods\n\ndatalogic-rs provides three primary API methods for evaluating rules, each suited for different use cases:\n\n### 1. `evaluate` - For reusing parsed rules and data\n\nBest for scenarios where the same rule will be evaluated against different data contexts, or vice versa.\n\n```rust\nuse datalogic_rs::DataLogic;\n\nlet dl = DataLogic::new();\n\n// Parse rule and data once\nlet rule = dl.parse_logic(r#\"{ \"\u003e\": [{\"var\": \"temp\"}, 100] }\"#, None).unwrap();\nlet data = dl.parse_data(r#\"{\"temp\": 110}\"#).unwrap();\n\n// Evaluate the rule against the data\nlet result = dl.evaluate(\u0026rule, \u0026data).unwrap();\nassert!(result.to_json().as_bool().unwrap());\n```\n\n### 2. `evaluate_str` - One-step parsing and evaluation\n\nIdeal for one-time evaluations or when rules are dynamically generated.\n\n```rust\nuse datalogic_rs::DataLogic;\n\nlet dl = DataLogic::new();\n\n// Parse and evaluate in one step\nlet result = dl.evaluate_str(\n    r#\"{ \"abs\": -42 }\"#,\n    r#\"{}\"#,\n    None\n).unwrap();\n\nassert_eq!(result.as_i64().unwrap(), 42);\n```\n\n### 3. `evaluate_json` - Work directly with JSON values\n\nPerfect when your application already has the rule and data as serde_json Values.\n\n```rust\nuse datalogic_rs::DataLogic;\nuse serde_json::json;\n\nlet dl = DataLogic::new();\n\n// Use serde_json values directly\nlet logic = json!({\n    \"if\": [\n        {\"\u003e\": [{\"var\": \"cart.total\"}, 100]},\n        \"Eligible for discount\",\n        \"No discount\"\n    ]\n});\nlet data = json!({\"cart\": {\"total\": 120}});\n\nlet result = dl.evaluate_json(\u0026logic, \u0026data, None).unwrap();\nassert_eq!(result.as_str().unwrap(), \"Eligible for discount\");\n```\n\n## Real-World Examples\n\n### 1. Complex Logical Rules (AND/OR)\n\n```rust\nuse datalogic_rs::DataLogic;\n\nlet dl = DataLogic::new();\nlet result = dl.evaluate_str(\n    r#\"{\n        \"and\": [\n            {\"\u003e=\": [{\"var\": \"age\"}, 18]},\n            {\"\u003c\": [{\"var\": \"age\"}, 65]},\n            {\"or\": [\n                {\"==\": [{\"var\": \"subscription\"}, \"premium\"]},\n                {\"\u003e=\": [{\"var\": \"purchases\"}, 5]}\n            ]}\n        ]\n    }\"#,\n    r#\"{\"age\": 25, \"subscription\": \"basic\", \"purchases\": 7}\"#,\n    None\n).unwrap();\n\nassert!(result.as_bool().unwrap());\n```\n\n### 2. Array Operations\n\n```rust\nuse datalogic_rs::DataLogic;\n\nlet dl = DataLogic::new();\nlet result = dl.evaluate_str(\n    r#\"{\n        \"map\": [\n            {\n                \"filter\": [\n                    {\"var\": \"users\"},\n                    {\"\u003e=\": [{\"var\": \"age\"}, 18]}\n                ]\n            },\n            {\"var\": \"name\"}\n        ]\n    }\"#,\n    r#\"{\n        \"users\": [\n            {\"name\": \"Alice\", \"age\": 20},\n            {\"name\": \"Bob\", \"age\": 15},\n            {\"name\": \"Charlie\", \"age\": 25}\n        ]\n    }\"#,\n    None\n).unwrap();\n\n// Returns [\"Alice\", \"Charlie\"]\nassert_eq!(result.as_array().unwrap().len(), 2);\n```\n\n### 3. String Processing\n\n```rust\nuse datalogic_rs::DataLogic;\n\nlet dl = DataLogic::new();\n\n// Replace text and split into words\nlet result = dl.evaluate_str(\n    r#\"{\n        \"split\": [\n            {\"replace\": [\n                {\"var\": \"message\"}, \n                \"hello\", \n                \"hi\"\n            ]}, \n            \" \"\n        ]\n    }\"#,\n    r#\"{\"message\": \"hello world hello there\"}\"#,\n    None\n).unwrap();\n\n// Returns [\"hi\", \"world\", \"hi\", \"there\"]\nassert_eq!(result.as_array().unwrap().len(), 4);\n```\n\n### 4. DateTime Operations\n\n```rust\nuse datalogic_rs::DataLogic;\n\nlet dl = DataLogic::new();\nlet result = dl.evaluate_str(\n    r#\"{\n        \"\u003e\": [\n            {\"+\": [\n                {\"datetime\": \"2023-07-15T08:30:00Z\"},\n                {\"timestamp\": \"2d\"}\n            ]},\n            {\"datetime\": \"2023-07-16T08:30:00Z\"}\n        ]\n    }\"#,\n    r#\"{}\"#,\n    None\n).unwrap();\n\nassert!(result.as_bool().unwrap());\n```\n\n### 5. Timezone Offset Extraction\n\n```rust\nuse datalogic_rs::DataLogic;\n\nlet dl = DataLogic::new();\n\n// Extract timezone offset from datetime with timezone info\nlet result = dl.evaluate_str(\n    r#\"{\n        \"format_date\": [\n            {\"datetime\": \"2022-07-06T13:20:06+05:00\"}, \n            \"z\"\n        ]\n    }\"#,\n    r#\"{}\"#,\n    None\n).unwrap();\n\nassert_eq!(result.as_str().unwrap(), \"+0500\");\n\n// Timezone-aware datetime operations preserve original timezone\nlet result = dl.evaluate_str(\n    r#\"{\n        \"format_date\": [\n            {\"datetime\": \"2022-07-06T13:20:06+05:00\"}, \n            \"yyyy-MM-ddTHH:mm:ssXXX\"\n        ]\n    }\"#,\n    r#\"{}\"#,\n    None\n).unwrap();\n\nassert_eq!(result.as_str().unwrap(), \"2022-07-06T13:20:06+05:00\");\n```\n\n### 6. Regex Extraction with Split\n\n```rust\nuse datalogic_rs::DataLogic;\n\nlet dl = DataLogic::new();\n\n// Extract structured data from IBAN using regex named groups\nlet result = dl.evaluate_str(\n    r#\"{\n        \"split\": [\n            \"SBININBB101\",\n            \"^(?P\u003cbank\u003e[A-Z]{4})(?P\u003ccountry\u003e[A-Z]{2})(?P\u003clocation\u003e[A-Z0-9]{2})(?P\u003cbranch\u003e[A-Z0-9]{3})?$\"\n        ]\n    }\"#,\n    r#\"{}\"#,\n    None\n).unwrap();\n\n// Returns: {\"bank\": \"SBIN\", \"country\": \"IN\", \"location\": \"BB\", \"branch\": \"101\"}\nlet obj = result.as_object().unwrap();\nassert_eq!(obj.get(\"bank\").unwrap().as_str().unwrap(), \"SBIN\");\nassert_eq!(obj.get(\"country\").unwrap().as_str().unwrap(), \"IN\");\n```\n\n### 7. Structured Object Preservation\n\nCreate structured output objects with non-operator keys:\n\n```rust\nuse datalogic_rs::DataLogic;\n\n// Enable structured object preservation\nlet dl = DataLogic::with_preserve_structure();\n\n// Create structured output with evaluated fields\nlet result = dl.evaluate_str(\n    r#\"{\n        \"result\": {\"==\": [1, 1]},\n        \"score\": {\"+\": [85, 10, 5]},\n        \"grade\": {\"if\": [\n            {\"\u003e\": [{\"var\": \"score\"}, 90]}, \n            \"A\", \n            \"B\"\n        ]}\n    }\"#,\n    r#\"{\"score\": 95}\"#,\n    None\n).unwrap();\n\n// Returns: {\"result\": true, \"score\": 100, \"grade\": \"A\"}\nlet obj = result.as_object().unwrap();\nassert_eq!(obj[\"result\"].as_bool().unwrap(), true);\nassert_eq!(obj[\"score\"].as_i64().unwrap(), 100);\nassert_eq!(obj[\"grade\"].as_str().unwrap(), \"A\");\n```\n\n## Custom Operators\n\nCreate domain-specific operators to extend the system:\n\n```rust\nuse datalogic_rs::{DataLogic, SimpleOperatorFn, DataValue};\nuse datalogic_rs::value::NumberValue;\n\n// Define a custom operator function - simple approach\nfn double\u003c'r\u003e(args: Vec\u003cDataValue\u003c'r\u003e\u003e, data: DataValue\u003c'r\u003e) -\u003e std::result::Result\u003cDataValue\u003c'r\u003e, String\u003e {\n    if args.is_empty() {\n        // If no arguments, try to use a value from data context\n        if let Some(obj) = data.as_object() {\n            for (key, val) in obj {\n                if *key == \"value\" \u0026\u0026 val.is_number() {\n                    if let Some(n) = val.as_f64() {\n                        return Ok(DataValue::Number(NumberValue::from_f64(n * 2.0)));\n                    }\n                }\n            }\n        }\n        return Err(\"double operator requires an argument or 'value' in data\".to_string());\n    }\n    \n    if let Some(n) = args[0].as_f64() {\n        return Ok(DataValue::Number(NumberValue::from_f64(n * 2.0)));\n    }\n    \n    Err(\"Argument must be a number\".to_string())\n}\n\nlet mut dl = DataLogic::new();\ndl.register_simple_operator(\"double\", double);\n\n// Using with an explicit argument\nlet result = dl.evaluate_str(\n    r#\"{\"double\": 4}\"#,\n    r#\"{}\"#,\n    None\n).unwrap();\n\nassert_eq!(result.as_f64().unwrap(), 8.0);\n\n// Using with data context\nlet result = dl.evaluate_str(\n    r#\"{\"double\": []}\"#,\n    r#\"{\"value\": 5}\"#,\n    None\n).unwrap();\n\nassert_eq!(result.as_f64().unwrap(), 10.0);\n```\n\nCustom operators can be combined with built-in operators for complex logic:\n\n```rust\nlet complex_rule = r#\"{\n    \"*\": [\n        2,\n        {\"double\": {\"var\": \"value\"}},\n        3\n    ]\n}\"#;\n\n// With data: {\"value\": 3}, evaluates to 2 * (3*2) * 3 = 2 * 6 * 3 = 36\n```\n\nFor more advanced use cases and complex data types, DataLogic-rs also provides an [advanced custom operator API](CUSTOM_OPERATORS.md).\n\n## Use Cases\n\n`datalogic-rs` excels in scenarios requiring runtime rule evaluation:\n\n### Feature Flagging\nControl feature access based on user attributes or context:\n\n```rust\nlet rule = r#\"{\n    \"and\": [\n        {\"==\": [{\"var\": \"user.country\"}, \"US\"]},\n        {\"or\": [\n            {\"==\": [{\"var\": \"user.role\"}, \"beta_tester\"]},\n            {\"\u003e=\": [{\"var\": \"user.account_age_days\"}, 30]}\n        ]}\n    ]\n}\"#;\n\n// Feature is available only to US users who are either beta testers or have accounts older than 30 days\nlet feature_enabled = dl.evaluate_str(rule, user_data_json, None).unwrap().as_bool().unwrap();\n```\n\n### Dynamic Pricing\nApply complex discount rules:\n\n```rust\nlet pricing_rule = r#\"{\n    \"if\": [\n        {\"\u003e=\": [{\"var\": \"cart.total\"}, 100]},\n        {\"-\": [{\"var\": \"cart.total\"}, {\"*\": [{\"var\": \"cart.total\"}, 0.1]}]},\n        {\"var\": \"cart.total\"}\n    ]\n}\"#;\n\n// 10% discount for orders over $100\nlet final_price = dl.evaluate_str(pricing_rule, order_data, None).unwrap().as_f64().unwrap();\n```\n\n### Fraud Detection\nEvaluate transaction risk:\n\n```rust\nlet fraud_check = r#\"{\n    \"or\": [\n        {\"and\": [\n            {\"!=\": [{\"var\": \"transaction.billing_country\"}, {\"var\": \"user.country\"}]},\n            {\"\u003e=\": [{\"var\": \"transaction.amount\"}, 1000]}\n        ]},\n        {\"and\": [\n            {\"\u003e=\": [{\"var\": \"transaction.attempts_last_hour\"}, 5]},\n            {\"\u003e\": [{\"var\": \"transaction.amount\"}, 500]}\n        ]}\n    ]\n}\"#;\n\nlet is_suspicious = dl.evaluate_str(fraud_check, transaction_data, None).unwrap().as_bool().unwrap();\n```\n\n### Authorization Rules\nImplement complex access control:\n\n```rust\nlet access_rule = r#\"{\n    \"or\": [\n        {\"==\": [{\"var\": \"user.role\"}, \"admin\"]},\n        {\"and\": [\n            {\"==\": [{\"var\": \"user.role\"}, \"editor\"]},\n            {\"in\": [{\"var\": \"resource.project_id\"}, {\"var\": \"user.projects\"}]}\n        ]}\n    ]\n}\"#;\n\nlet has_access = dl.evaluate_str(access_rule, access_context, None).unwrap().as_bool().unwrap();\n```\n\n### Form Validation\nCheck field dependencies dynamically:\n\n```rust\nlet validation_rule = r#\"{\n    \"if\": [\n        {\"==\": [{\"var\": \"shipping_method\"}, \"international\"]},\n        {\"and\": [\n            {\"!\": {\"missing\": \"postal_code\"}},\n            {\"!\": {\"missing\": \"country\"}}\n        ]},\n        true\n    ]\n}\"#;\n\nlet is_valid = dl.evaluate_str(validation_rule, form_data, None).unwrap().as_bool().unwrap();\n```\n\n### Data Extraction and Parsing\nExtract structured data using regex patterns:\n\n```rust\nlet extraction_rule = r#\"{\n    \"split\": [\n        {\"var\": \"iban\"},\n        \"^(?P\u003cbank\u003e[A-Z]{4})(?P\u003ccountry\u003e[A-Z]{2})(?P\u003clocation\u003e[A-Z0-9]{2})(?P\u003cbranch\u003e[A-Z0-9]{3})?$\"\n    ]\n}\"#;\n\nlet data = r#\"{\"iban\": \"SBININBB101\"}\"#;\nlet parsed_iban = dl.evaluate_str(extraction_rule, data, None).unwrap();\n// Returns: {\"bank\": \"SBIN\", \"country\": \"IN\", \"location\": \"BB\", \"branch\": \"101\"}\n\n// Use extracted data for further validation\nlet validation_rule = r#\"{\n    \"and\": [\n        {\"==\": [{\"var\": \"result.country\"}, \"IN\"]},\n        {\"in\": [{\"var\": \"result.bank\"}, [\"SBIN\", \"ICIC\", \"HDFC\"]]}\n    ]\n}\"#;\n\nlet validation_data = format!(r#\"{{\"result\": {}}}\"#, parsed_iban);\nlet is_valid_bank = dl.evaluate_str(validation_rule, \u0026validation_data, None).unwrap().as_bool().unwrap();\n```\n\n## Supported Operations\n\n| Category | Operators |\n|----------|-----------|\n| **Comparison** | `==` (equal), `===` (strict equal), `!=` (not equal), `!==` (strict not equal), `\u003e` (greater than), `\u003e=` (greater than or equal), `\u003c` (less than), `\u003c=` (less than or equal) |\n| **Logic** | `and`, `or`, `!` (not), `!!` (double negation) |\n| **Arithmetic** | `+` (addition), `-` (subtraction), `*` (multiplication), `/` (division), `%` (modulo), `min`, `max`, `abs` (absolute value), `ceil` (round up), `floor` (round down) |\n| **Control Flow** | `if` (conditional), `?:` (ternary), `??` (nullish coalescing) |\n| **Arrays** | `map`, `filter`, `reduce`, `all`, `some`, `none`, `merge`, `in` (contains), `length`, `slice`, `sort` |\n| **Strings** | `cat` (concatenate), `substr`, `starts_with`, `ends_with`, `upper`, `lower`, `trim`, `replace`, `split` (with regex extraction) |\n| **Data Access** | `var` (variable access), `val` (value access), `exists`, `missing`, `missing_some` |\n| **DateTime** | `datetime`, `timestamp`, `now`, `parse_date`, `format_date` (with timezone offset support), `date_diff` |\n| **Error Handling** | `throw`, `try` |\n| **Custom** | Support for user-defined operators |\n\n## Performance\n\n**Benchmark results show** `datalogic-rs` is **30% faster** than the next fastest JSONLogic implementations, thanks to:\n- Arena-based memory management\n- Static operator dispatch\n- Zero-copy deserialization\n- Optimized rule compilation\n\n### Benchmark Metrics (Apple M2 Pro)\n\n| Implementation | Execution Time | Relative Performance |\n|----------------|---------------|---------------------|\n| **datalogic-rs** | **380ms** | **1.0x (baseline)** |\n| json-logic-engine (pre-compiled) | 417ms | 1.1x slower |\n| json-logic-engine (interpreted) | 986.064ms | 2.6x slower |\n| json-logic-js | 5,755ms | 15.1x slower |\n\nThese benchmarks represent execution time for the same standard suite of JSONLogic tests, demonstrating datalogic-rs's superior performance profile across common expression patterns.\n\n## Contributing\n\nWe welcome contributions! See the [CONTRIBUTING.md](./CONTRIBUTING.md) for details.\n\n## License\n\nLicensed under Apache License, Version 2.0\n\n---\n\n### Next Steps\n✅ Try out `datalogic-rs` today!  \n📖 Check out the [API documentation](./API.md) for detailed usage instructions  \n📚 See the [docs.rs documentation](https://docs.rs/datalogic-rs) for comprehensive reference  \n📝 Learn how to implement [custom operators](./CUSTOM_OPERATORS.md) to extend the engine  \n⭐ Star the [GitHub repository](https://github.com/codetiger/datalogic-rs) if you find it useful!","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FGoPlasmatic%2Fdatalogic-rs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FGoPlasmatic%2Fdatalogic-rs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FGoPlasmatic%2Fdatalogic-rs/lists"}