Compare commits

..

7 Commits

Author SHA1 Message Date
copilot-swe-agent[bot]
eb6e156e8a fix: cover outer-scope bracket key handling
Agent-Logs-Url: https://github.com/microsoft/regorus/sessions/992f5462-7cc4-4e4f-bd7f-799308063765

Co-authored-by: anakrish <35780660+anakrish@users.noreply.github.com>
2026-05-07 03:08:59 +00:00
copilot-swe-agent[bot]
5ae1d8abf2 chore: clarify bracket-head rule semantics
Agent-Logs-Url: https://github.com/microsoft/regorus/sessions/272a971a-ae52-45ae-8cb3-714e747599c4

Co-authored-by: anakrish <35780660+anakrish@users.noreply.github.com>
2026-05-06 22:31:09 +00:00
copilot-swe-agent[bot]
03c4275855 fix: classify constant-key bracket rules precisely
Agent-Logs-Url: https://github.com/microsoft/regorus/sessions/272a971a-ae52-45ae-8cb3-714e747599c4

Co-authored-by: anakrish <35780660+anakrish@users.noreply.github.com>
2026-05-06 22:27:07 +00:00
copilot-swe-agent[bot]
78f226f957 fix: refine constant-key bracket rule handling
Agent-Logs-Url: https://github.com/microsoft/regorus/sessions/272a971a-ae52-45ae-8cb3-714e747599c4

Co-authored-by: anakrish <35780660+anakrish@users.noreply.github.com>
2026-05-06 22:19:56 +00:00
copilot-swe-agent[bot]
bec159a580 test: align VM ObjectSet collision expectations
Agent-Logs-Url: https://github.com/microsoft/regorus/sessions/34a4e1b3-d364-46c4-9998-b00780f5d339

Co-authored-by: anakrish <35780660+anakrish@users.noreply.github.com>
2026-05-06 19:38:42 +00:00
copilot-swe-agent[bot]
117671d959 fix: preserve all bindings for partial object iteration
Agent-Logs-Url: https://github.com/microsoft/regorus/sessions/34a4e1b3-d364-46c4-9998-b00780f5d339

Co-authored-by: anakrish <35780660+anakrish@users.noreply.github.com>
2026-05-06 19:34:41 +00:00
copilot-swe-agent[bot]
8617c79da5 Initial plan 2026-05-06 19:25:36 +00:00
124 changed files with 3356 additions and 9596 deletions

View File

@@ -8,5 +8,3 @@ steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 0 # full history needed for git diff against main
- run: git fetch origin main:refs/remotes/origin/main
name: Ensure origin/main ref is available for diff computation

View File

@@ -25,21 +25,15 @@ Key constraints (details in copilot-instructions.md):
## Step 1: Get the Diff
```bash
# Primary: use gh pr diff (works in cloud agent + any PR context).
# Fallback: git merge-base for local non-PR usage.
if gh pr diff --name-only >/dev/null 2>&1; then
echo "---STAT---"
gh pr diff --name-only
echo "---DIFF---"
gh pr diff
else
BASE=$(git merge-base upstream/main HEAD 2>/dev/null \
|| git merge-base origin/main HEAD 2>/dev/null \
|| git merge-base main HEAD 2>/dev/null)
echo "Reviewing changes since: $BASE"
git diff "$BASE"..HEAD --stat
git diff "$BASE"..HEAD
BASE=$(git merge-base upstream/main HEAD 2>/dev/null \
|| git merge-base origin/main HEAD 2>/dev/null)
if [ -z "$BASE" ]; then
echo "ERROR: Cannot find upstream/main or origin/main. Cannot determine review scope."
exit 1
fi
echo "Reviewing changes since: $BASE"
git diff "$BASE"..HEAD --stat
git diff "$BASE"..HEAD -- '*.rs' '*.toml' 'examples/'
```
If the diff is empty, stop and report: "No changes found to review."
@@ -202,9 +196,3 @@ one pass. If any were skipped, note them and briefly assess.
### Summary
X findings (N critical, N high, N medium, N low). One sentence overall assessment.
### Output
After generating the report above, write the COMPLETE report to `/tmp/code-review-report.md`
using the `create` tool or shell. This ensures the full report is preserved even if
display output is truncated.

View File

@@ -40,25 +40,22 @@ Use `read_agent` with `wait: true` to wait for each background agent.
## Step 1: Get the Diff and Build Inventory
```bash
# Primary: use gh pr diff (works in cloud agent + any PR context).
# Fallback: git merge-base for local non-PR usage.
if gh pr diff --name-only >/dev/null 2>&1; then
echo "---STAT---"
gh pr diff --name-only
echo "---DIFF---"
gh pr diff
else
BASE=$(git merge-base upstream/main HEAD 2>/dev/null \
|| git merge-base origin/main HEAD 2>/dev/null \
|| git merge-base main HEAD 2>/dev/null)
echo "Reviewing changes since: $BASE"
git diff "$BASE"..HEAD --stat
git diff "$BASE"..HEAD
BASE=$(git merge-base upstream/main HEAD 2>/dev/null \
|| git merge-base origin/main HEAD 2>/dev/null)
if [ -z "$BASE" ]; then
echo "ERROR: Cannot find upstream/main or origin/main."
exit 1
fi
echo "Reviewing changes since: $BASE"
git diff "$BASE"..HEAD --stat
git diff "$BASE"..HEAD -- '*.rs' '*.toml' 'examples/' | head -2000
```
If the diff is empty, stop and report: "No changes found to review."
**Scope rule:** Focus on code files (`*.rs`, `*.toml`, examples). Do NOT pass
docs/config diffs to agents.
**Build a risk-classified inventory.** List every changed function, struct,
impl, trait, pub item, and significant code block. Number them and tag with
risk predicates:
@@ -109,10 +106,8 @@ Use `model: "gpt-5.4"` in the task tool call (provides model diversity).
> Get the diff:
> ```
> BASE=$(git merge-base upstream/main HEAD 2>/dev/null \
> || git merge-base origin/main HEAD 2>/dev/null \
> || git merge-base main HEAD 2>/dev/null)
> # If no merge-base, use: gh pr diff
> git diff "$BASE"..HEAD # or: gh pr diff
> || git merge-base origin/main HEAD 2>/dev/null)
> git diff "$BASE"..HEAD -- '*.rs' '*.toml' 'examples/'
> ```
>
> Key regorus constraints:
@@ -166,10 +161,8 @@ Use `model: "claude-opus-4.6"` in the task tool call.
> Get the diff AND read full source files for context:
> ```
> BASE=$(git merge-base upstream/main HEAD 2>/dev/null \
> || git merge-base origin/main HEAD 2>/dev/null \
> || git merge-base main HEAD 2>/dev/null)
> # If no merge-base, use: gh pr diff
> git diff "$BASE"..HEAD # or: gh pr diff
> || git merge-base origin/main HEAD 2>/dev/null)
> git diff "$BASE"..HEAD -- '*.rs' '*.toml' 'examples/'
> ```
> Then use `view` to read the full source files that were changed.
>
@@ -226,10 +219,8 @@ Use the default model (no `model` parameter).
> Get the diff:
> ```
> BASE=$(git merge-base upstream/main HEAD 2>/dev/null \
> || git merge-base origin/main HEAD 2>/dev/null \
> || git merge-base main HEAD 2>/dev/null)
> # If no merge-base, use: gh pr diff
> git diff "$BASE"..HEAD # or: gh pr diff
> || git merge-base origin/main HEAD 2>/dev/null)
> git diff "$BASE"..HEAD -- '*.rs' '*.toml' 'examples/'
> ```
> Use `view` to read surrounding context.
>
@@ -448,10 +439,8 @@ Launch **1 general-purpose agent in background mode**.
> Get the diff:
> ```
> BASE=$(git merge-base upstream/main HEAD 2>/dev/null \
> || git merge-base origin/main HEAD 2>/dev/null \
> || git merge-base main HEAD 2>/dev/null)
> # If no merge-base, use: gh pr diff
> git diff "$BASE"..HEAD # or: gh pr diff
> || git merge-base origin/main HEAD 2>/dev/null)
> git diff "$BASE"..HEAD -- '*.rs' '*.toml' 'examples/'
> ```
> Use `view` to read full source files.
>
@@ -492,8 +481,8 @@ Launch **1 general-purpose agent in background mode**.
## Step 5: Synthesize and Report
**CRITICAL:** Write the report to `/tmp/deep-review-report.md` FIRST, then display it.
Use a shell command to write the file before any other output in this step.
**IMPORTANT:** This is the primary output. Everything above was preparation.
Keep the report COMPACT — one finding per block, no filler prose.
Apply verdicts from the adversarial verifier:
- **CONFIRMED**: keep at stated severity
@@ -533,9 +522,3 @@ would catch it. If not, name the minimal test that should exist.
X findings (N critical, N high, N medium, N low). Y "likely" findings.
Z dropped (one-line reasons).
Risk assessment in one sentence.
---
**Remember:** The report above MUST be written to `/tmp/deep-review-report.md` at the
START of Step 5 (before displaying it). Use shell: `cat > /tmp/deep-review-report.md << 'REPORT_EOF'`
... report content ... `REPORT_EOF`

View File

@@ -6,50 +6,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
## [0.11.0](https://github.com/microsoft/regorus/compare/regorus-v0.10.1...regorus-v0.11.0) - 2026-07-21
### Added
- *(compiler)* support registered host-await builtins for natural function call syntax ([#667](https://github.com/microsoft/regorus/pull/667))
- *(value)* introduce Set storage abstraction ([#740](https://github.com/microsoft/regorus/pull/740))
### Fixed
- *(rvm)* assert every-quantifier results so failing cases don't pass ([#765](https://github.com/microsoft/regorus/pull/765))
- `Engine::add_data` now deep-merges nested data documents instead of only merging top-level keys. Adding `{ "a": { "x": 1 } }` followed by `{ "a": { "y": 2 } }` now yields `{ "a": { "x": 1, "y": 2 } }` (matching OPA's data-document merge). Nested sets under a shared key are unioned. Only genuine leaf conflicts (the same path holding two different values) are reported as errors. ([#760](https://github.com/microsoft/regorus/pull/760))
- A zero-arg function producing two different complete values (e.g. `f() := { "a": 1 }` and `f() := { "b": 2 }`) is now reported as a conflict, matching OPA's complete-rule semantics, instead of silently combining the outputs.
### Security
- `Engine::add_data` now rejects data nested beyond 128 levels instead of risking a stack overflow on adversarially deep input.
### Other
- *(deps)* bump the rust-dependencies group across 5 directories with 11 updates ([#764](https://github.com/microsoft/regorus/pull/764))
- Expand keyword-in-ref coverage for complex parser edge cases (interpreter + RVM) ([#744](https://github.com/microsoft/regorus/pull/744))
- *(deps)* bump the rust-dependencies group across 5 directories with 4 updates ([#754](https://github.com/microsoft/regorus/pull/754))
- *(deps)* bump the rust-dependencies group across 5 directories with 6 updates ([#750](https://github.com/microsoft/regorus/pull/750))
- *(value)* migrate Value::Object to Object storage abstraction ([#736](https://github.com/microsoft/regorus/pull/736))
- normalize path separators in folder filter on Windows ([#742](https://github.com/microsoft/regorus/pull/742))
- Introduce Object storage abstraction ([#735](https://github.com/microsoft/regorus/pull/735))
- *(rvm)* add debug-mode invariant assertions ([#737](https://github.com/microsoft/regorus/pull/737))
- *(deps)* bump the rust-dependencies group across 5 directories with 5 updates ([#734](https://github.com/microsoft/regorus/pull/734))
## [0.10.1](https://github.com/microsoft/regorus/compare/regorus-v0.10.0...regorus-v0.10.1) - 2026-05-22
### Fixed
- *(ffi)* eliminate aliasing UB + add Azure Policy JSON compilation FFI ([#727](https://github.com/microsoft/regorus/pull/727))
- *(interpreter,rvm)* correct partial object rule iteration and classification ([#718](https://github.com/microsoft/regorus/pull/718))
- *(copilot)* robust diff computation for cloud agent environments ([#709](https://github.com/microsoft/regorus/pull/709))
### Other
- *(azure_policy)* reduce AliasRegistry allocations via Rc sharing ([#725](https://github.com/microsoft/regorus/pull/725))
- *(normalizer)* use Rc<str> interning to reduce alias resolution allocations ([#726](https://github.com/microsoft/regorus/pull/726))
- *(deps)* bump the rust-dependencies group across 5 directories with 2 updates ([#724](https://github.com/microsoft/regorus/pull/724))
- *(deps)* bump the rust-dependencies group across 5 directories with 4 updates ([#717](https://github.com/microsoft/regorus/pull/717))
## [0.10.0] - 2026-05-05
### Added

537
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -8,7 +8,7 @@ members = [
[package]
name = "regorus"
description = "A fast, lightweight Rego (OPA policy language) interpreter"
version = "0.11.0"
version = "0.10.0"
edition = "2021"
license = "MIT AND Apache-2.0 AND BSD-3-Clause"
repository = "https://github.com/microsoft/regorus"
@@ -98,23 +98,23 @@ rand = ["dep:rand"]
[dependencies]
anyhow = { version = "1.0.102", default-features = false }
serde = {version = "1.0.150", default-features = false, features = ["derive", "rc", "alloc"] }
serde_json = { version = "1.0.150", default-features = false, features = ["alloc"] }
hashbrown = { version = "0.17", default-features = false, features = ["default-hasher"], optional = true }
serde_json = { version = "1.0.89", default-features = false, features = ["alloc"] }
hashbrown = { version = "0.16", default-features = false, features = ["default-hasher"], optional = true }
lazy_static = { version = "1.4.0", default-features = false }
thiserror = { version = "2.0", default-features = false }
data-encoding = { version = "2.8.0", optional = true, default-features=false, features = ["alloc"] }
num-bigint = { version = "0.5", default-features = false }
num-bigint = { version = "0.4", default-features = false }
num-traits = { version = "0.2", default-features = false }
parking_lot = { version = "0.12", optional = true }
spin = { version = "0.12.0", default-features = false, features = ["mutex", "spin_mutex"] }
spin = { version = "0.10.0", default-features = false, features = ["mutex", "spin_mutex"] }
globset = { version = "0.4.16", features = ["simd-accel"], default-features = false, optional = true }
regex = {version = "1.12.3", optional = true, default-features = false }
semver = {version = "1.0.28", optional = true, default-features = false }
url = { version = "2.5.4", optional = true }
uuid = { version = "1.22.0", default-features = false, features = ["v4", "fast-rng"], optional = true }
jsonschema = { version = "0.47.0", default-features = false, optional = true }
jsonschema = { version = "0.45.1", default-features = false, optional = true }
chrono = { version = "0.4.44", optional = true }
chrono-tz = { version = "0.10.1", optional = true }
ipnet = { version = "2.12.0", optional = true, default-features = false }
@@ -127,8 +127,8 @@ rand = { version = "0.10.0", default-features = false, features = ["thread_rng"]
# Causes the project to link with the Spectre-mitigated CRT and libs.
msvc_spectre_libs = { version = "0.1", features = ["error"], optional = true }
dashmap = { version = "6.1", default-features = false, optional = true }
lru = { version = "0.18", default-features = false, optional = true }
mimalloc = { package = "regorus-mimalloc", path = "mimalloc", version = "2.2.7", optional = true }
lru = { version = "0.16", default-features = false, optional = true }
mimalloc = { package = "regorus-mimalloc", path = "mimalloc", version = "2.2.6", optional = true }
# rvm related deps
indexmap = { version = "2.13.1", default-features = false, features = ["serde"], optional = true }

View File

@@ -1,7 +1,7 @@
<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
<RegorusPackageVersion>0.11.0</RegorusPackageVersion>
<RegorusPackageVersion>0.10.0</RegorusPackageVersion>
<RegorusPackageVersionSuffix Condition="'$(VersionSuffix)' != ''">-$(VersionSuffix)</RegorusPackageVersionSuffix>
</PropertyGroup>

View File

@@ -150,76 +150,3 @@ const string ContextJson = """
var allowed = RbacEngine.EvaluateCondition(Condition, ContextJson);
Console.WriteLine($"RBAC condition allowed: {allowed}");
```
## Azure Policy JSON Evaluation
Compile and evaluate Azure Policy JSON `policyRule` definitions directly — no Rego translation required.
The `AzurePolicyCompiler` compiles JSON policy rules into RVM programs that can be executed with the `Rvm` engine.
```csharp
using Regorus;
// 1. Load alias definitions for the resource provider
const string AliasesJson = """
[{
"namespace": "Microsoft.Storage",
"resourceTypes": [{
"resourceType": "storageAccounts",
"aliases": [{
"name": "Microsoft.Storage/storageAccounts/supportsHttpsTrafficOnly",
"defaultPath": "properties.supportsHttpsTrafficOnly",
"paths": []
}]
}]
}]
""";
using var registry = AliasRegistry.FromJson(AliasesJson);
// 2. Compile a JSON policy rule (the native Azure Policy language)
const string PolicyRule = """
{
"if": {
"allOf": [
{ "field": "type", "equals": "Microsoft.Storage/storageAccounts" },
{ "field": "Microsoft.Storage/storageAccounts/supportsHttpsTrafficOnly", "equals": false }
]
},
"then": { "effect": "deny" }
}
""";
using var program = AzurePolicyCompiler.CompilePolicyRule(registry, PolicyRule);
// 3. Normalize an ARM resource and evaluate
var armResource = """
{
"type": "Microsoft.Storage/storageAccounts",
"name": "mystorage",
"properties": { "supportsHttpsTrafficOnly": false }
}
""";
var envelope = registry.NormalizeAndWrap(armResource);
using var vm = new Rvm();
vm.LoadProgram(program);
vm.SetInputJson(envelope!);
var result = vm.ExecuteEntryPoint("main");
// result: {"effect": "deny"} for non-compliant, "<undefined>" for compliant
Console.WriteLine($"Policy result: {result}");
```
**Context-dependent policies:** If your policy uses context functions like
`subscription()`, `resourceGroup()`, or `requestContext()`, you must also set
the VM context separately:
```csharp
// The context JSON from NormalizeAndWrap is in the input envelope,
// but must also be provided to the VM's ambient context:
vm.SetContextJson(contextJson);
```
You can also compile full policy definitions (with parameters) using
`AzurePolicyCompiler.CompilePolicyDefinition()`. See
`bindings/csharp/Regorus.Tests/AzurePolicyCompilerTests.cs` for comprehensive examples.

View File

@@ -43,28 +43,31 @@ public class AliasRegistryTests
[TestMethod]
public void Create_and_dispose_succeeds()
{
using var registry = AliasRegistry.Empty();
using var registry = new AliasRegistry();
Assert.AreEqual(0, registry.Length);
}
[TestMethod]
public void LoadJson_populates_registry()
{
using var registry = AliasRegistry.FromJson(AliasesJson);
using var registry = new AliasRegistry();
registry.LoadJson(AliasesJson);
Assert.AreEqual(1, registry.Length);
}
[TestMethod]
public void LoadManifest_populates_registry()
{
using var registry = AliasRegistry.FromManifest(ManifestJson);
using var registry = new AliasRegistry();
registry.LoadManifest(ManifestJson);
Assert.AreEqual(1, registry.Length);
}
[TestMethod]
public void NormalizeAndWrap_produces_envelope()
{
using var registry = AliasRegistry.FromJson(AliasesJson);
using var registry = new AliasRegistry();
registry.LoadJson(AliasesJson);
var resource = @"{
""name"": ""acct1"",
@@ -90,7 +93,8 @@ public class AliasRegistryTests
[TestMethod]
public void NormalizeAndWrap_with_context_and_parameters()
{
using var registry = AliasRegistry.FromJson(AliasesJson);
using var registry = new AliasRegistry();
registry.LoadJson(AliasesJson);
var resource = @"{
""name"": ""acct1"",
@@ -111,7 +115,8 @@ public class AliasRegistryTests
[TestMethod]
public void Denormalize_restores_properties()
{
using var registry = AliasRegistry.FromJson(AliasesJson);
using var registry = new AliasRegistry();
registry.LoadJson(AliasesJson);
var normalized = @"{
""name"": ""acct1"",
@@ -132,7 +137,8 @@ public class AliasRegistryTests
[TestMethod]
public void Round_trip_normalize_then_denormalize()
{
using var registry = AliasRegistry.FromJson(AliasesJson);
using var registry = new AliasRegistry();
registry.LoadJson(AliasesJson);
var resource = @"{
""name"": ""acct1"",
@@ -160,7 +166,8 @@ public class AliasRegistryTests
[TestMethod]
public void DataPlane_manifest_normalize()
{
using var registry = AliasRegistry.FromManifest(ManifestJson);
using var registry = new AliasRegistry();
registry.LoadManifest(ManifestJson);
var resource = @"{
""type"": ""Microsoft.KeyVault.Data/vaults/certificates"",
@@ -178,7 +185,7 @@ public class AliasRegistryTests
[ExpectedException(typeof(InvalidOperationException))]
public void LoadJson_invalid_throws()
{
using var builder = new AliasRegistryBuilder();
builder.LoadJson("not valid json");
using var registry = new AliasRegistry();
registry.LoadJson("not valid json");
}
}

View File

@@ -1,436 +0,0 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System;
using System.Text.Json.Nodes;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Regorus;
namespace Regorus.Tests;
/// <summary>
/// Tests for <see cref="AzurePolicyCompiler"/> — compiling Azure Policy JSON
/// policyRule and policyDefinition into RVM programs and evaluating them.
/// </summary>
[TestClass]
public class AzurePolicyCompilerTests
{
// -----------------------------------------------------------------------
// Test data
// -----------------------------------------------------------------------
private const string StorageAliasesJson = @"[{
""namespace"": ""Microsoft.Storage"",
""resourceTypes"": [{
""resourceType"": ""storageAccounts"",
""capabilities"": ""SupportsTags, SupportsLocation"",
""aliases"": [
{
""name"": ""Microsoft.Storage/storageAccounts/supportsHttpsTrafficOnly"",
""defaultPath"": ""properties.supportsHttpsTrafficOnly"",
""paths"": []
},
{
""name"": ""Microsoft.Storage/storageAccounts/minimumTlsVersion"",
""defaultPath"": ""properties.minimumTlsVersion"",
""paths"": []
}
]
}]
}]";
/// <summary>Simple policy rule that checks the resource type.</summary>
private const string SimpleAuditRule = @"{
""if"": {
""field"": ""type"",
""equals"": ""Microsoft.Storage/storageAccounts""
},
""then"": { ""effect"": ""audit"" }
}";
/// <summary>Policy rule that uses an alias to check HTTPS-only.</summary>
private const string HttpsDenyRule = @"{
""if"": {
""allOf"": [
{ ""field"": ""type"", ""equals"": ""Microsoft.Storage/storageAccounts"" },
{ ""field"": ""Microsoft.Storage/storageAccounts/supportsHttpsTrafficOnly"", ""equals"": false }
]
},
""then"": { ""effect"": ""deny"" }
}";
/// <summary>Full policy definition with parameters.</summary>
private const string PolicyDefinitionWithParams = @"{
""displayName"": ""Require HTTPS for storage accounts"",
""policyType"": ""Custom"",
""mode"": ""Indexed"",
""parameters"": {
""effect"": {
""type"": ""String"",
""defaultValue"": ""deny""
}
},
""policyRule"": {
""if"": {
""allOf"": [
{ ""field"": ""type"", ""equals"": ""Microsoft.Storage/storageAccounts"" },
{ ""field"": ""Microsoft.Storage/storageAccounts/supportsHttpsTrafficOnly"", ""equals"": false }
]
},
""then"": { ""effect"": ""[parameters('effect')]"" }
}
}";
// -----------------------------------------------------------------------
// Helper
// -----------------------------------------------------------------------
/// <summary>
/// Wrap a normalized resource JSON and parameters into the input envelope
/// expected by compiled Azure Policy RVM programs.
/// </summary>
private static string WrapInput(string resourceJson, string parametersJson = "{}")
{
return $@"{{""resource"": {resourceJson}, ""parameters"": {parametersJson}}}";
}
/// <summary>
/// Compile a policy rule, load it into an RVM, set input, and execute.
/// Returns the result string from <c>ExecuteEntryPoint("main")</c>.
/// </summary>
private static string? CompileAndEval(
AliasRegistry? registry,
string policyRuleJson,
string inputJson)
{
using var program = AzurePolicyCompiler.CompilePolicyRule(registry, policyRuleJson);
using var vm = new Rvm();
vm.LoadProgram(program);
vm.SetInputJson(inputJson);
return vm.ExecuteEntryPoint("main");
}
// -----------------------------------------------------------------------
// CompilePolicyRule tests
// -----------------------------------------------------------------------
[TestMethod]
public void CompilePolicyRule_no_aliases_succeeds()
{
using var program = AzurePolicyCompiler.CompilePolicyRule(null, SimpleAuditRule);
Assert.IsNotNull(program);
}
[TestMethod]
public void CompilePolicyRule_with_aliases_succeeds()
{
using var registry = AliasRegistry.FromJson(StorageAliasesJson);
using var program = AzurePolicyCompiler.CompilePolicyRule(registry, HttpsDenyRule);
Assert.IsNotNull(program);
}
[TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void CompilePolicyRule_null_json_throws()
{
AzurePolicyCompiler.CompilePolicyRule(null, null!);
}
[TestMethod]
[ExpectedException(typeof(InvalidOperationException))]
public void CompilePolicyRule_invalid_json_throws()
{
AzurePolicyCompiler.CompilePolicyRule(null, "not valid json");
}
// -----------------------------------------------------------------------
// CompilePolicyDefinition tests
// -----------------------------------------------------------------------
[TestMethod]
public void CompilePolicyDefinition_no_aliases_succeeds()
{
using var program = AzurePolicyCompiler.CompilePolicyDefinition(null, PolicyDefinitionWithParams);
Assert.IsNotNull(program);
}
[TestMethod]
public void CompilePolicyDefinition_with_aliases_succeeds()
{
using var registry = AliasRegistry.FromJson(StorageAliasesJson);
using var program = AzurePolicyCompiler.CompilePolicyDefinition(registry, PolicyDefinitionWithParams);
Assert.IsNotNull(program);
}
[TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void CompilePolicyDefinition_null_json_throws()
{
AzurePolicyCompiler.CompilePolicyDefinition(null, null!);
}
[TestMethod]
[ExpectedException(typeof(InvalidOperationException))]
public void CompilePolicyDefinition_invalid_json_throws()
{
AzurePolicyCompiler.CompilePolicyDefinition(null, @"{""not"": ""a definition""}");
}
// -----------------------------------------------------------------------
// End-to-end evaluation tests
// -----------------------------------------------------------------------
[TestMethod]
public void Eval_simple_rule_matching_resource_returns_effect()
{
var input = WrapInput(
@"{""type"": ""microsoft.storage/storageaccounts""}");
var result = CompileAndEval(null, SimpleAuditRule, input);
Assert.IsNotNull(result, "expected a result for matching resource");
var doc = JsonNode.Parse(result!)!;
Assert.AreEqual("audit", doc["effect"]?.GetValue<string>(),
$"expected 'audit' effect, got: {result}");
}
[TestMethod]
public void Eval_simple_rule_non_matching_resource_returns_undefined()
{
var input = WrapInput(
@"{""type"": ""microsoft.compute/virtualmachines""}");
var result = CompileAndEval(null, SimpleAuditRule, input);
Assert.IsNotNull(result);
StringAssert.Contains(result!, "undefined",
"expected undefined for non-matching resource type");
}
[TestMethod]
public void Eval_alias_rule_non_compliant_returns_deny()
{
using var registry = AliasRegistry.FromJson(StorageAliasesJson);
// Non-compliant: HTTPS not enabled (normalized/lowercased form)
var input = WrapInput(
@"{""type"": ""microsoft.storage/storageaccounts"", ""supportshttpstrafficonly"": false}");
using var program = AzurePolicyCompiler.CompilePolicyRule(registry, HttpsDenyRule);
using var vm = new Rvm();
vm.LoadProgram(program);
vm.SetInputJson(input);
var result = vm.ExecuteEntryPoint("main");
Assert.IsNotNull(result);
var doc = JsonNode.Parse(result!)!;
Assert.AreEqual("deny", doc["effect"]?.GetValue<string>(),
$"expected 'deny' for non-compliant resource, got: {result}");
}
[TestMethod]
public void Eval_alias_rule_compliant_returns_undefined()
{
using var registry = AliasRegistry.FromJson(StorageAliasesJson);
// Compliant: HTTPS enabled
var input = WrapInput(
@"{""type"": ""microsoft.storage/storageaccounts"", ""supportshttpstrafficonly"": true}");
using var program = AzurePolicyCompiler.CompilePolicyRule(registry, HttpsDenyRule);
using var vm = new Rvm();
vm.LoadProgram(program);
vm.SetInputJson(input);
var result = vm.ExecuteEntryPoint("main");
Assert.IsNotNull(result);
StringAssert.Contains(result!, "undefined",
"expected undefined for compliant resource");
}
[TestMethod]
public void Eval_definition_with_default_parameters()
{
using var registry = AliasRegistry.FromJson(StorageAliasesJson);
using var program = AzurePolicyCompiler.CompilePolicyDefinition(
registry, PolicyDefinitionWithParams);
using var vm = new Rvm();
vm.LoadProgram(program);
// Non-compliant resource
var input = WrapInput(
@"{""type"": ""microsoft.storage/storageaccounts"", ""supportshttpstrafficonly"": false}");
vm.SetInputJson(input);
var result = vm.ExecuteEntryPoint("main");
Assert.IsNotNull(result);
var doc = JsonNode.Parse(result!)!;
// Default parameter value is "deny"
Assert.AreEqual("deny", doc["effect"]?.GetValue<string>(),
$"expected default 'deny' effect, got: {result}");
}
[TestMethod]
public void Eval_with_normalized_arm_resource_end_to_end()
{
using var registry = AliasRegistry.FromJson(StorageAliasesJson);
// Simulate the full production flow:
// 1. Start with an ARM resource
var armResource = @"{
""type"": ""Microsoft.Storage/storageAccounts"",
""name"": ""mystorage"",
""location"": ""eastus"",
""properties"": {
""supportsHttpsTrafficOnly"": false,
""minimumTlsVersion"": ""TLS1_0""
}
}";
// 2. Normalize via AliasRegistry
var normalizedEnvelope = registry.NormalizeAndWrap(
armResource,
apiVersion: null,
contextJson: "{}",
parametersJson: "{}");
Assert.IsNotNull(normalizedEnvelope);
// 3. Compile the policy rule
using var program = AzurePolicyCompiler.CompilePolicyRule(registry, HttpsDenyRule);
// 4. Execute
using var vm = new Rvm();
vm.LoadProgram(program);
vm.SetInputJson(normalizedEnvelope!);
var result = vm.ExecuteEntryPoint("main");
Assert.IsNotNull(result);
var doc = JsonNode.Parse(result!)!;
Assert.AreEqual("deny", doc["effect"]?.GetValue<string>(),
$"expected 'deny' for non-HTTPS storage account, got: {result}");
}
[TestMethod]
public void Eval_normalized_compliant_resource_end_to_end()
{
using var registry = AliasRegistry.FromJson(StorageAliasesJson);
var armResource = @"{
""type"": ""Microsoft.Storage/storageAccounts"",
""name"": ""secureastorage"",
""location"": ""westus"",
""properties"": {
""supportsHttpsTrafficOnly"": true,
""minimumTlsVersion"": ""TLS1_2""
}
}";
var normalizedEnvelope = registry.NormalizeAndWrap(
armResource,
apiVersion: null,
contextJson: "{}",
parametersJson: "{}");
Assert.IsNotNull(normalizedEnvelope);
using var program = AzurePolicyCompiler.CompilePolicyRule(registry, HttpsDenyRule);
using var vm = new Rvm();
vm.LoadProgram(program);
vm.SetInputJson(normalizedEnvelope!);
var result = vm.ExecuteEntryPoint("main");
Assert.IsNotNull(result);
StringAssert.Contains(result!, "undefined",
"expected undefined for compliant HTTPS storage account");
}
[TestMethod]
public void Program_can_be_serialized_and_reloaded()
{
using var program = AzurePolicyCompiler.CompilePolicyRule(null, SimpleAuditRule);
// Serialize to binary
var binary = program.SerializeBinary();
Assert.IsTrue(binary.Length > 0, "serialized program should not be empty");
// Deserialize and run
using var restored = Program.DeserializeBinary(binary, out var isPartial);
Assert.IsFalse(isPartial, "program should not be partial");
using var vm = new Rvm();
vm.LoadProgram(restored);
var input = WrapInput(@"{""type"": ""microsoft.storage/storageaccounts""}");
vm.SetInputJson(input);
var result = vm.ExecuteEntryPoint("main");
Assert.IsNotNull(result);
var doc = JsonNode.Parse(result!)!;
Assert.AreEqual("audit", doc["effect"]?.GetValue<string>());
}
[TestMethod]
public void Program_generates_listing()
{
using var program = AzurePolicyCompiler.CompilePolicyRule(null, SimpleAuditRule);
var listing = program.GenerateListing();
Assert.IsFalse(string.IsNullOrWhiteSpace(listing),
"generated listing should not be empty");
}
// -----------------------------------------------------------------------
// Context-dependent policy tests
// -----------------------------------------------------------------------
/// Policy rule that uses subscription() context function.
private const string ContextPolicyRule = @"{
""if"": {
""allOf"": [
{ ""field"": ""type"", ""equals"": ""Microsoft.Storage/storageAccounts"" },
{ ""value"": ""[subscription().subscriptionId]"", ""equals"": ""sub-123"" }
]
},
""then"": { ""effect"": ""deny"" }
}";
[TestMethod]
public void Eval_context_policy_with_set_context_returns_effect()
{
using var program = AzurePolicyCompiler.CompilePolicyRule(null, ContextPolicyRule);
using var vm = new Rvm();
vm.LoadProgram(program);
vm.SetContextJson(@"{""subscription"": {""subscriptionId"": ""sub-123""}}");
var input = WrapInput(
@"{""type"": ""microsoft.storage/storageaccounts""}");
vm.SetInputJson(input);
var result = vm.ExecuteEntryPoint("main");
Assert.IsNotNull(result);
var doc = JsonNode.Parse(result!)!;
Assert.AreEqual("deny", doc["effect"]?.GetValue<string>(),
$"expected 'deny' with matching context, got: {result}");
}
[TestMethod]
public void Eval_context_policy_without_context_returns_undefined()
{
using var program = AzurePolicyCompiler.CompilePolicyRule(null, ContextPolicyRule);
using var vm = new Rvm();
vm.LoadProgram(program);
// No context set — subscription() will be undefined
var input = WrapInput(
@"{""type"": ""microsoft.storage/storageaccounts""}");
vm.SetInputJson(input);
var result = vm.ExecuteEntryPoint("main");
Assert.IsNotNull(result);
StringAssert.Contains(result!, "undefined",
"expected undefined without context set");
}
}

View File

@@ -62,7 +62,8 @@ public class AzurePolicyTests
[TestMethod]
public void AliasRegistry_NormalizeAndWrap_produces_input_envelope()
{
using var registry = AliasRegistry.FromJson(StorageAliasesJson);
using var registry = new AliasRegistry();
registry.LoadJson(StorageAliasesJson);
var result = registry.NormalizeAndWrap(
StorageResourceJson,
@@ -83,7 +84,8 @@ public class AzurePolicyTests
[TestMethod]
public void AliasRegistry_NormalizeAndWrap_flattens_properties()
{
using var registry = AliasRegistry.FromJson(StorageAliasesJson);
using var registry = new AliasRegistry();
registry.LoadJson(StorageAliasesJson);
var result = registry.NormalizeAndWrap(StorageResourceJson);
Assert.IsNotNull(result);
@@ -105,7 +107,8 @@ public class AzurePolicyTests
[TestMethod]
public void AliasRegistry_NormalizeAndWrap_preserves_type_field()
{
using var registry = AliasRegistry.FromJson(StorageAliasesJson);
using var registry = new AliasRegistry();
registry.LoadJson(StorageAliasesJson);
var result = registry.NormalizeAndWrap(StorageResourceJson);
var doc = JsonNode.Parse(result!);
@@ -122,7 +125,8 @@ public class AzurePolicyTests
[TestMethod]
public void AliasRegistry_NormalizeAndWrap_includes_parameters()
{
using var registry = AliasRegistry.FromJson(StorageAliasesJson);
using var registry = new AliasRegistry();
registry.LoadJson(StorageAliasesJson);
var parametersJson = @"{ ""effect"": ""Deny"" }";
var result = registry.NormalizeAndWrap(
@@ -139,7 +143,8 @@ public class AzurePolicyTests
[TestMethod]
public void AliasRegistry_Denormalize_roundtrips_correctly()
{
using var registry = AliasRegistry.FromJson(StorageAliasesJson);
using var registry = new AliasRegistry();
registry.LoadJson(StorageAliasesJson);
// Normalize the ARM resource.
var envelope = registry.NormalizeAndWrap(StorageResourceJson);
@@ -172,7 +177,8 @@ public class AzurePolicyTests
}
var aliasesJson = File.ReadAllText(aliasesPath);
using var registry = AliasRegistry.FromJson(aliasesJson);
using var registry = new AliasRegistry();
registry.LoadJson(aliasesJson);
// The test_aliases.json file contains multiple providers.
Assert.IsTrue(registry.Length > 0,

View File

@@ -8,43 +8,51 @@ using Regorus.Internal;
namespace Regorus
{
/// <summary>
/// Immutable Azure Policy alias registry used for resource normalization
/// Manages Azure Policy alias definitions used for resource normalization
/// and policy compilation.
/// </summary>
public unsafe sealed class AliasRegistry : SafeHandleWrapper
{
internal AliasRegistry(RegorusAliasRegistryHandle handle)
: base(handle, nameof(AliasRegistry))
/// <summary>
/// Create an empty alias registry.
/// </summary>
public AliasRegistry()
: base(RegorusAliasRegistryHandle.Create(), nameof(AliasRegistry))
{
}
/// <summary>
/// Create an empty immutable alias registry.
/// Load control-plane alias data (array of ProviderAliases) from a JSON string.
/// </summary>
public static AliasRegistry Empty()
/// <param name="json">JSON array of ProviderAliases (e.g. from Get-AzPolicyAlias or ResourceTypesAndAliases.json)</param>
public void LoadJson(string json)
{
using var builder = new AliasRegistryBuilder();
return builder.Build();
Utf8Marshaller.WithUtf8(json, jsonPtr =>
{
UseHandle(regPtr =>
{
CheckAndDropResult(API.regorus_alias_registry_load_json(
(RegorusAliasRegistry*)regPtr, (byte*)jsonPtr));
return 0;
});
});
}
/// <summary>
/// Create an immutable alias registry from control-plane alias JSON.
/// Load a data-plane policy manifest from a JSON string.
/// </summary>
public static AliasRegistry FromJson(string json)
/// <param name="json">JSON object containing a DataPolicyManifest</param>
public void LoadManifest(string json)
{
using var builder = new AliasRegistryBuilder();
builder.LoadJson(json);
return builder.Build();
}
/// <summary>
/// Create an immutable alias registry from a data-plane manifest JSON document.
/// </summary>
public static AliasRegistry FromManifest(string json)
{
using var builder = new AliasRegistryBuilder();
builder.LoadManifest(json);
return builder.Build();
Utf8Marshaller.WithUtf8(json, jsonPtr =>
{
UseHandle(regPtr =>
{
CheckAndDropResult(API.regorus_alias_registry_load_manifest(
(RegorusAliasRegistry*)regPtr, (byte*)jsonPtr));
return 0;
});
});
}
/// <summary>
@@ -66,6 +74,11 @@ namespace Regorus
/// Normalize an ARM resource JSON and wrap it into the standard input envelope
/// expected by a compiled Azure Policy program.
/// </summary>
/// <param name="resourceJson">Raw ARM resource JSON</param>
/// <param name="apiVersion">API version string (e.g. "2023-01-01"), or null to use default alias paths</param>
/// <param name="contextJson">Additional context JSON object (pass "{}" if none)</param>
/// <param name="parametersJson">Policy parameter values JSON (pass "{}" if none)</param>
/// <returns>JSON string: { "resource": &lt;normalized&gt;, "context": &lt;context&gt;, "parameters": &lt;params&gt; }</returns>
public string? NormalizeAndWrap(string resourceJson, string? apiVersion = null, string contextJson = "{}", string parametersJson = "{}")
{
return Utf8Marshaller.WithUtf8(resourceJson, resPtr =>
@@ -83,22 +96,27 @@ namespace Regorus
(byte*)ctxPtr, (byte*)paramsPtr));
});
}
return Utf8Marshaller.WithUtf8(apiVersion, apiPtr =>
UseHandle(regPtr =>
{
return ResultHelpers.GetStringResult(
API.regorus_alias_registry_normalize_and_wrap(
(RegorusAliasRegistry*)regPtr,
(byte*)resPtr, (byte*)apiPtr,
(byte*)ctxPtr, (byte*)paramsPtr));
}));
else
{
return Utf8Marshaller.WithUtf8(apiVersion, apiPtr =>
UseHandle(regPtr =>
{
return ResultHelpers.GetStringResult(
API.regorus_alias_registry_normalize_and_wrap(
(RegorusAliasRegistry*)regPtr,
(byte*)resPtr, (byte*)apiPtr,
(byte*)ctxPtr, (byte*)paramsPtr));
}));
}
})));
}
/// <summary>
/// Denormalize a previously-normalized resource JSON back to ARM format.
/// </summary>
/// <param name="normalizedJson">The normalized resource JSON</param>
/// <param name="apiVersion">API version string, or null to use default alias paths</param>
/// <returns>Denormalized ARM JSON string</returns>
public string? Denormalize(string normalizedJson, string? apiVersion = null)
{
return Utf8Marshaller.WithUtf8(normalizedJson, normPtr =>
@@ -113,16 +131,23 @@ namespace Regorus
(byte*)normPtr, null));
});
}
return Utf8Marshaller.WithUtf8(apiVersion, apiPtr =>
UseHandle(regPtr =>
{
return ResultHelpers.GetStringResult(
API.regorus_alias_registry_denormalize(
(RegorusAliasRegistry*)regPtr,
(byte*)normPtr, (byte*)apiPtr));
}));
else
{
return Utf8Marshaller.WithUtf8(apiVersion, apiPtr =>
UseHandle(regPtr =>
{
return ResultHelpers.GetStringResult(
API.regorus_alias_registry_denormalize(
(RegorusAliasRegistry*)regPtr,
(byte*)normPtr, (byte*)apiPtr));
}));
}
});
}
private static string? CheckAndDropResult(RegorusResult result)
{
return ResultHelpers.GetStringResult(result);
}
}
}

View File

@@ -1,69 +0,0 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System;
using Regorus.Internal;
#nullable enable
namespace Regorus
{
/// <summary>
/// Mutable, single-threaded builder for <see cref="AliasRegistry"/>.
/// Load alias data, then call <see cref="Build"/> to freeze the registry.
/// </summary>
public unsafe sealed class AliasRegistryBuilder : SafeHandleWrapper
{
/// <summary>
/// Create an empty alias registry builder.
/// </summary>
public AliasRegistryBuilder()
: base(RegorusAliasRegistryBuilderHandle.Create(), nameof(AliasRegistryBuilder))
{
}
/// <summary>
/// Load control-plane alias data (array of ProviderAliases) from a JSON string.
/// </summary>
public void LoadJson(string json)
{
Utf8Marshaller.WithUtf8(json, jsonPtr =>
{
UseHandle(builderPtr =>
{
ResultHelpers.GetStringResult(API.regorus_alias_registry_builder_load_json(
(RegorusAliasRegistryBuilder*)builderPtr,
(byte*)jsonPtr));
});
});
}
/// <summary>
/// Load a data-plane policy manifest from a JSON string.
/// </summary>
public void LoadManifest(string json)
{
Utf8Marshaller.WithUtf8(json, jsonPtr =>
{
UseHandle(builderPtr =>
{
ResultHelpers.GetStringResult(API.regorus_alias_registry_builder_load_manifest(
(RegorusAliasRegistryBuilder*)builderPtr,
(byte*)jsonPtr));
});
});
}
/// <summary>
/// Freeze the builder into an immutable, thread-safe alias registry.
/// </summary>
public AliasRegistry Build()
{
return UseHandle(builderPtr =>
{
var registryPtr = ResultHelpers.GetPointerResult(
API.regorus_alias_registry_builder_build((RegorusAliasRegistryBuilder*)builderPtr));
return new AliasRegistry(RegorusAliasRegistryHandle.FromPointer(registryPtr));
});
}
}
}

View File

@@ -1,183 +0,0 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System;
using Regorus.Internal;
#nullable enable
namespace Regorus
{
/// <summary>
/// Provides static methods for compiling Azure Policy JSON definitions
/// into RVM programs that can be executed by <see cref="Rvm"/>.
/// </summary>
/// <remarks>
/// <para>
/// This class bridges the gap between Azure Policy JSON (the native
/// Azure policy language with <c>policyRule</c>, <c>field</c>,
/// <c>equals</c>, etc.) and Regorus's RVM execution engine.
/// </para>
///
/// <para>
/// <b>Typical workflow:</b>
/// </para>
/// <list type="number">
/// <item>Load alias definitions with <see cref="AliasRegistryBuilder"/> and freeze them into an <see cref="AliasRegistry"/>.</item>
/// <item>Normalize the ARM resource via <see cref="AliasRegistry.NormalizeAndWrap"/>.</item>
/// <item>Compile the JSON policyRule with <see cref="CompilePolicyRule"/> or the
/// full definition with <see cref="CompilePolicyDefinition"/>.</item>
/// <item>Execute the resulting <see cref="Program"/> in an <see cref="Rvm"/>
/// instance with the normalized input.</item>
/// </list>
///
/// <para>
/// <b>Context-dependent policies:</b> Policies that use context functions
/// such as <c>subscription()</c>, <c>resourceGroup()</c>, or
/// <c>requestContext()</c> require the VM context to be set separately via
/// <see cref="Rvm.SetContextJson"/> before execution. The context JSON
/// returned by <see cref="AliasRegistry.NormalizeAndWrap"/> is passed as
/// <c>input.context</c> but is <b>not</b> automatically wired into the VM's
/// ambient context — the caller must do both:
/// <c>vm.SetInputJson(envelope)</c> and <c>vm.SetContextJson(contextJson)</c>.
/// </para>
/// </remarks>
public static unsafe class AzurePolicyCompiler
{
/// <summary>
/// Compile an Azure Policy JSON policy rule into an RVM <see cref="Program"/>.
/// </summary>
/// <param name="aliasRegistry">
/// Alias registry for resolving fully-qualified alias names in field
/// references. Pass <c>null</c> if no alias resolution is needed.
/// <para>
/// <b>Warning:</b> When <c>null</c>, alias field references compile as raw
/// property paths and will silently produce incorrect evaluation results for
/// policies that use aliases. Modify/Append effect policies will also skip
/// the compile-time modifiability validation. Only pass <c>null</c> when the
/// policy is known to contain no alias references (e.g. simple type/location
/// checks or unit-test scenarios).
/// </para>
/// </param>
/// <param name="policyRuleJson">
/// JSON string containing the policyRule object, e.g.
/// <c>{ "if": { "field": "type", "equals": "..." }, "then": { "effect": "deny" } }</c>
/// </param>
/// <returns>
/// A compiled <see cref="Program"/> ready to be loaded into an
/// <see cref="Rvm"/> instance.
/// </returns>
/// <exception cref="ArgumentNullException">
/// Thrown when <paramref name="policyRuleJson"/> is <c>null</c>.
/// </exception>
/// <exception cref="Exception">
/// Thrown when parsing or compilation fails.
/// </exception>
public static Program CompilePolicyRule(AliasRegistry? aliasRegistry, string policyRuleJson)
{
if (policyRuleJson is null)
{
throw new ArgumentNullException(nameof(policyRuleJson));
}
return Utf8Marshaller.WithUtf8(policyRuleJson, rulePtr =>
{
if (aliasRegistry is null)
{
var result = API.regorus_compile_azure_policy_rule(
null, (byte*)rulePtr);
return GetProgramResult(result);
}
else
{
return aliasRegistry.UseHandleForInterop(regPtr =>
{
var result = API.regorus_compile_azure_policy_rule(
(RegorusAliasRegistry*)regPtr, (byte*)rulePtr);
return GetProgramResult(result);
});
}
});
}
/// <summary>
/// Compile a full Azure Policy definition JSON into an RVM <see cref="Program"/>.
/// </summary>
/// <param name="aliasRegistry">
/// Alias registry for resolving fully-qualified alias names in field
/// references. Pass <c>null</c> if no alias resolution is needed.
/// <para>
/// <b>Warning:</b> When <c>null</c>, alias field references compile as raw
/// property paths and will silently produce incorrect evaluation results for
/// policies that use aliases. Modify/Append effect policies will also skip
/// the compile-time modifiability validation. Only pass <c>null</c> when the
/// policy is known to contain no alias references (e.g. simple type/location
/// checks or unit-test scenarios).
/// </para>
/// </param>
/// <param name="policyDefinitionJson">
/// JSON string containing the full policy definition, which includes
/// <c>policyRule</c>, <c>parameters</c>, <c>displayName</c>, etc.
/// Accepted in both wrapped and unwrapped forms.
/// </param>
/// <returns>
/// A compiled <see cref="Program"/> ready to be loaded into an
/// <see cref="Rvm"/> instance.
/// </returns>
/// <exception cref="ArgumentNullException">
/// Thrown when <paramref name="policyDefinitionJson"/> is <c>null</c>.
/// </exception>
/// <exception cref="Exception">
/// Thrown when parsing or compilation fails.
/// </exception>
public static Program CompilePolicyDefinition(AliasRegistry? aliasRegistry, string policyDefinitionJson)
{
if (policyDefinitionJson is null)
{
throw new ArgumentNullException(nameof(policyDefinitionJson));
}
return Utf8Marshaller.WithUtf8(policyDefinitionJson, defnPtr =>
{
if (aliasRegistry is null)
{
var result = API.regorus_compile_azure_policy_definition(
null, (byte*)defnPtr);
return GetProgramResult(result);
}
else
{
return aliasRegistry.UseHandleForInterop(regPtr =>
{
var result = API.regorus_compile_azure_policy_definition(
(RegorusAliasRegistry*)regPtr, (byte*)defnPtr);
return GetProgramResult(result);
});
}
});
}
private static Program GetProgramResult(RegorusResult result)
{
try
{
if (result.status != RegorusStatus.Ok)
{
var message = Utf8Marshaller.FromUtf8(result.error_message);
throw result.status.CreateException(message);
}
if (result.data_type != RegorusDataType.Pointer || result.pointer_value == null)
{
throw new Exception("Expected program pointer but got different data type");
}
var handle = RegorusProgramHandle.FromPointer((IntPtr)result.pointer_value);
return new Program(handle);
}
finally
{
API.regorus_result_drop(result);
}
}
}
}

View File

@@ -178,14 +178,6 @@ namespace Regorus.Internal
[DllImport(LibraryName, EntryPoint = "regorus_rvm_set_input", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusResult regorus_rvm_set_input(RegorusRvm* vm, byte* input_json);
/// <summary>
/// Set the context document for the RVM.
/// The context provides host-supplied ambient data (e.g. resourceGroup(), subscription())
/// that Azure Policy functions can access.
/// </summary>
[DllImport(LibraryName, EntryPoint = "regorus_rvm_set_context", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusResult regorus_rvm_set_context(RegorusRvm* vm, byte* context_json);
/// <summary>
/// Execute the program.
/// </summary>
@@ -498,20 +490,6 @@ namespace Regorus.Internal
[DllImport(LibraryName, EntryPoint = "regorus_compile_policy_for_target", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusResult regorus_compile_policy_for_target(byte* data_json, RegorusPolicyModule* modules, UIntPtr modules_len);
/// <summary>
/// Compile an Azure Policy JSON policy rule into an RVM program.
/// </summary>
[DllImport(LibraryName, EntryPoint = "regorus_compile_azure_policy_rule", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusResult regorus_compile_azure_policy_rule(
RegorusAliasRegistry* registry, byte* policy_rule_json);
/// <summary>
/// Compile a full Azure Policy definition JSON into an RVM program.
/// </summary>
[DllImport(LibraryName, EntryPoint = "regorus_compile_azure_policy_definition", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusResult regorus_compile_azure_policy_definition(
RegorusAliasRegistry* registry, byte* policy_definition_json);
#endregion
#region Compiled Policy Methods
@@ -695,34 +673,10 @@ namespace Regorus.Internal
#region Alias Registry Methods
/// <summary>
/// Create a new alias registry builder.
/// Create a new, empty AliasRegistry.
/// </summary>
[DllImport(LibraryName, EntryPoint = "regorus_alias_registry_builder_new", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusAliasRegistryBuilder* regorus_alias_registry_builder_new();
/// <summary>
/// Drop an alias registry builder.
/// </summary>
[DllImport(LibraryName, EntryPoint = "regorus_alias_registry_builder_drop", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern void regorus_alias_registry_builder_drop(RegorusAliasRegistryBuilder* builder);
/// <summary>
/// Load control-plane alias data into the builder.
/// </summary>
[DllImport(LibraryName, EntryPoint = "regorus_alias_registry_builder_load_json", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusResult regorus_alias_registry_builder_load_json(RegorusAliasRegistryBuilder* builder, byte* json);
/// <summary>
/// Load a data-plane policy manifest into the builder.
/// </summary>
[DllImport(LibraryName, EntryPoint = "regorus_alias_registry_builder_load_manifest", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusResult regorus_alias_registry_builder_load_manifest(RegorusAliasRegistryBuilder* builder, byte* json);
/// <summary>
/// Freeze a builder into an immutable alias registry.
/// </summary>
[DllImport(LibraryName, EntryPoint = "regorus_alias_registry_builder_build", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusResult regorus_alias_registry_builder_build(RegorusAliasRegistryBuilder* builder);
[DllImport(LibraryName, EntryPoint = "regorus_alias_registry_new", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusAliasRegistry* regorus_alias_registry_new();
/// <summary>
/// Drop an AliasRegistry.
@@ -730,6 +684,18 @@ namespace Regorus.Internal
[DllImport(LibraryName, EntryPoint = "regorus_alias_registry_drop", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern void regorus_alias_registry_drop(RegorusAliasRegistry* registry);
/// <summary>
/// Load control-plane alias data (array of ProviderAliases) into the registry.
/// </summary>
[DllImport(LibraryName, EntryPoint = "regorus_alias_registry_load_json", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusResult regorus_alias_registry_load_json(RegorusAliasRegistry* registry, byte* json);
/// <summary>
/// Load a data-plane policy manifest into the registry.
/// </summary>
[DllImport(LibraryName, EntryPoint = "regorus_alias_registry_load_manifest", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
internal static extern RegorusResult regorus_alias_registry_load_manifest(RegorusAliasRegistry* registry, byte* json);
/// <summary>
/// Return the number of resource types loaded in the alias registry.
/// </summary>
@@ -957,14 +923,6 @@ namespace Regorus.Internal
public byte* content;
}
/// <summary>
/// Wrapper for AliasRegistryBuilder.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
internal unsafe partial struct RegorusAliasRegistryBuilder
{
}
/// <summary>
/// Wrapper for AliasRegistry.
/// </summary>

View File

@@ -15,7 +15,7 @@ namespace Regorus
/// </summary>
public unsafe sealed class Program : SafeHandleWrapper
{
internal Program(RegorusProgramHandle handle)
private Program(RegorusProgramHandle handle)
: base(handle, nameof(Program))
{
}

View File

@@ -69,29 +69,5 @@ namespace Regorus.Internal
API.regorus_result_drop(result);
}
}
internal static IntPtr GetPointerResult(RegorusResult result)
{
try
{
if (result.status != RegorusStatus.Ok)
{
var message = Utf8Marshaller.FromUtf8(result.error_message);
throw result.status.CreateException(message);
}
if (result.data_type != RegorusDataType.Pointer || result.pointer_value == null)
{
throw new InvalidOperationException("Expected pointer result.");
}
return (IntPtr)result.pointer_value;
}
finally
{
API.regorus_result_drop(result);
}
}
}
}

View File

@@ -106,24 +106,6 @@ namespace Regorus
});
}
/// <summary>
/// Set the context document for the VM.
/// The context provides host-supplied ambient data (e.g. resourceGroup(),
/// subscription()) that Azure Policy functions can access via LoadContext
/// instructions.
/// </summary>
public void SetContextJson(string contextJson)
{
Utf8Marshaller.WithUtf8(contextJson, contextPtr =>
{
UseHandle(vmPtr =>
{
CheckAndDropResult(API.regorus_rvm_set_context((RegorusRvm*)vmPtr, (byte*)contextPtr));
return 0;
});
});
}
/// <summary>
/// Set the execution mode (0 = run-to-completion, 1 = suspendable).
/// </summary>

View File

@@ -184,48 +184,28 @@ namespace Regorus
}
}
internal sealed class RegorusAliasRegistryBuilderHandle : SafeHandleZeroOrMinusOneIsInvalid
{
private RegorusAliasRegistryBuilderHandle() : base(ownsHandle: true)
{
}
internal static RegorusAliasRegistryBuilderHandle Create()
{
unsafe
{
var raw = Internal.API.regorus_alias_registry_builder_new();
if (raw is null)
{
throw new InvalidOperationException("Failed to create Regorus alias registry builder.");
}
var handle = new RegorusAliasRegistryBuilderHandle();
handle.SetHandle((IntPtr)raw);
return handle;
}
}
protected override bool ReleaseHandle()
{
if (!IsInvalid)
{
unsafe
{
Internal.API.regorus_alias_registry_builder_drop((Internal.RegorusAliasRegistryBuilder*)handle);
}
SetHandle(IntPtr.Zero);
}
return true;
}
}
internal sealed class RegorusAliasRegistryHandle : SafeHandleZeroOrMinusOneIsInvalid
{
private RegorusAliasRegistryHandle() : base(ownsHandle: true)
{
}
internal static RegorusAliasRegistryHandle Create()
{
unsafe
{
var raw = Internal.API.regorus_alias_registry_new();
if (raw is null)
{
throw new InvalidOperationException("Failed to create Regorus alias registry.");
}
var handle = new RegorusAliasRegistryHandle();
handle.SetHandle((IntPtr)raw);
return handle;
}
}
internal static RegorusAliasRegistryHandle FromPointer(IntPtr pointer)
{
if (pointer == IntPtr.Zero)

View File

@@ -232,9 +232,6 @@ allow if {
Console.WriteLine("\n8. RVM host await (suspend/resume):");
DemonstrateRvmHostAwait();
Console.WriteLine("\n9. Azure Policy JSON compilation:");
DemonstrateAzurePolicyJsonCompilation();
}
static void DemonstrateConcurrentEvaluation(Regorus.CompiledPolicy compiledPolicy)
@@ -495,80 +492,4 @@ allow if {
var resumed = vm.Resume("{\"tier\":\"gold\"}");
Console.WriteLine($"HostAwait resumed result: {resumed}");
}
// Azure Policy JSON constants
private const string STORAGE_ALIASES_JSON = @"[{
""namespace"": ""Microsoft.Storage"",
""resourceTypes"": [{
""resourceType"": ""storageAccounts"",
""capabilities"": ""SupportsTags, SupportsLocation"",
""aliases"": [
{
""name"": ""Microsoft.Storage/storageAccounts/supportsHttpsTrafficOnly"",
""defaultPath"": ""properties.supportsHttpsTrafficOnly"",
""paths"": []
}
]
}]
}]";
private const string HTTPS_DENY_RULE = @"{
""if"": {
""allOf"": [
{ ""field"": ""type"", ""equals"": ""Microsoft.Storage/storageAccounts"" },
{ ""field"": ""Microsoft.Storage/storageAccounts/supportsHttpsTrafficOnly"", ""equals"": false }
]
},
""then"": { ""effect"": ""deny"" }
}";
static void DemonstrateAzurePolicyJsonCompilation()
{
// 1. Set up alias registry
using var registry = Regorus.AliasRegistry.FromJson(STORAGE_ALIASES_JSON);
Console.WriteLine("Loaded storage account aliases");
// 2. Compile the JSON policy rule directly (no Rego needed)
using var program = Regorus.AzurePolicyCompiler.CompilePolicyRule(registry, HTTPS_DENY_RULE);
Console.WriteLine("Compiled Azure Policy JSON rule to RVM program");
// 3. Normalize an ARM resource
var armResource = @"{
""type"": ""Microsoft.Storage/storageAccounts"",
""name"": ""insecurestorage"",
""location"": ""eastus"",
""properties"": { ""supportsHttpsTrafficOnly"": false }
}";
var envelope = registry.NormalizeAndWrap(armResource, apiVersion: null, contextJson: "{}", parametersJson: "{}");
Console.WriteLine($"Normalized ARM resource to evaluation envelope");
// 4. Execute in the RVM
// Note: For policies using context functions (subscription(), resourceGroup()),
// call vm.SetContextJson(contextJson) before execution. The context from
// NormalizeAndWrap is in the envelope but must also be set on the VM separately.
using var vm = new Regorus.Rvm();
vm.LoadProgram(program);
vm.SetInputJson(envelope!);
// vm.SetContextJson(contextJson); // ← required for context-dependent policies
var result = vm.ExecuteEntryPoint("main");
Console.WriteLine($"Evaluation result (non-compliant): {result}");
// 5. Test with a compliant resource
var compliantResource = @"{
""type"": ""Microsoft.Storage/storageAccounts"",
""name"": ""securestorage"",
""location"": ""eastus"",
""properties"": { ""supportsHttpsTrafficOnly"": true }
}";
var compliantEnvelope = registry.NormalizeAndWrap(compliantResource, apiVersion: null, contextJson: "{}", parametersJson: "{}");
using var vm2 = new Regorus.Rvm();
vm2.LoadProgram(program);
vm2.SetInputJson(compliantEnvelope!);
var compliantResult = vm2.ExecuteEntryPoint("main");
Console.WriteLine($"Evaluation result (compliant): {compliantResult}");
// 6. Demonstrate program serialization
var binary = program.SerializeBinary();
Console.WriteLine($"Serialized program size: {binary.Length} bytes");
}
}

449
bindings/ffi/Cargo.lock generated
View File

@@ -92,15 +92,15 @@ dependencies = [
[[package]]
name = "anyhow"
version = "1.0.103"
version = "1.0.102"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a4385e2e34eb35d6b3efe798b9eb88096925d87726c0798709bf56d9ed84af3"
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
[[package]]
name = "autocfg"
version = "1.5.1"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
[[package]]
name = "bit-set"
@@ -119,9 +119,9 @@ checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7"
[[package]]
name = "bitflags"
version = "2.13.1"
version = "2.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da"
checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3"
[[package]]
name = "borrow-or-share"
@@ -131,19 +131,19 @@ checksum = "dc0b364ead1874514c8c2855ab558056ebfeb775653e7ae45ff72f28f8f3166c"
[[package]]
name = "bstr"
version = "1.13.0"
version = "1.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f7dc094d718f2e1c1559ad110e27eeaae14a5465d3d56dd6dbd793079fbd530"
checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab"
dependencies = [
"memchr",
"serde_core",
"serde",
]
[[package]]
name = "bumpalo"
version = "3.20.3"
version = "3.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649"
checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb"
[[package]]
name = "bytecount"
@@ -153,9 +153,9 @@ checksum = "175812e0be2bccb6abe50bb8d566126198344f707e304f45c648fd8f2cc0365e"
[[package]]
name = "cbindgen"
version = "0.29.4"
version = "0.29.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2ecb53484c9c167ba674026b656d8a27d7657a58e6066aa902bfb1a4aa00ae20"
checksum = "befbfd072a8e81c02f8c507aefce431fe5e7d051f83d48a23ffc9b9fe5a11799"
dependencies = [
"clap",
"heck",
@@ -172,9 +172,9 @@ dependencies = [
[[package]]
name = "cc"
version = "1.2.67"
version = "1.2.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e17dd265a7d0f31ef544e1b20e03add05d3b45b491b633b10d67145d2acc1a38"
checksum = "d16d90359e986641506914ba71350897565610e87ce0ad9e6f28569db3dd5c6d"
dependencies = [
"find-msvc-tools",
"shlex",
@@ -188,9 +188,9 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "chacha20"
version = "0.10.1"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d524456ba66e72eb8b115ff89e01e497f8e6d11d78b70b1aa13c0fbd97540a81"
checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601"
dependencies = [
"cfg-if",
"cpufeatures",
@@ -199,9 +199,9 @@ dependencies = [
[[package]]
name = "chrono"
version = "0.4.45"
version = "0.4.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327"
checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0"
dependencies = [
"iana-time-zone",
"js-sys",
@@ -222,18 +222,18 @@ dependencies = [
[[package]]
name = "clap"
version = "4.6.2"
version = "4.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd059f9da4f5c36b3787f65d38ccaab1cc315f07b01f89abc8359ee6a8205011"
checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51"
dependencies = [
"clap_builder",
]
[[package]]
name = "clap_builder"
version = "4.6.2"
version = "4.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f09628afdcc538b57f3c6341e9c8e9970f18e4a481690a64974d7023bd33548b"
checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f"
dependencies = [
"anstream",
"anstyle",
@@ -279,15 +279,15 @@ dependencies = [
[[package]]
name = "crossbeam-utils"
version = "0.8.22"
version = "0.8.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61803da095bee82a81bb1a452ecc25d3b2f1416d1897eb86430c6159ef717c17"
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
[[package]]
name = "dashmap"
version = "6.2.1"
version = "6.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6361d5c062261c78a176addb82d4c821ae42bed6089de0e12603cd25de2059c"
checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf"
dependencies = [
"cfg-if",
"crossbeam-utils",
@@ -305,9 +305,9 @@ checksum = "a4ae5f15dda3c708c0ade84bfee31ccab44a3da4f88015ed22f63732abe300c8"
[[package]]
name = "displaydoc"
version = "0.2.6"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f"
checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
dependencies = [
"proc-macro2",
"quote",
@@ -353,9 +353,9 @@ dependencies = [
[[package]]
name = "fancy-regex"
version = "0.18.0"
version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1e1dacd0d2082dfcf1351c4bdd566bbe89a2b263235a2b50058f1e130a47277"
checksum = "72cf461f865c862bb7dc573f643dd6a2b6842f7c30b07882b56bd148cc2761b8"
dependencies = [
"bit-set",
"regex-automata",
@@ -385,6 +385,12 @@ dependencies = [
"serde",
]
[[package]]
name = "foldhash"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
[[package]]
name = "foldhash"
version = "0.2.0"
@@ -450,21 +456,23 @@ dependencies = [
[[package]]
name = "getrandom"
version = "0.4.3"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099"
checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555"
dependencies = [
"cfg-if",
"libc",
"r-efi 6.0.0",
"rand_core",
"wasip2",
"wasip3",
]
[[package]]
name = "globset"
version = "0.4.19"
version = "0.4.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e47d37d2ae4464254884b60ab7071be2b876a9c35b696bd018ddcc76847309cd"
checksum = "52dfc19153a48bde0cbd630453615c8151bce3a5adfac7a0aebfbf0a1e1f57e3"
dependencies = [
"aho-corasick",
"bstr",
@@ -480,15 +488,30 @@ checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
[[package]]
name = "hashbrown"
version = "0.17.1"
version = "0.15.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
dependencies = [
"foldhash 0.1.5",
]
[[package]]
name = "hashbrown"
version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
dependencies = [
"allocator-api2",
"equivalent",
"foldhash",
"foldhash 0.2.0",
]
[[package]]
name = "hashbrown"
version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51"
[[package]]
name = "heck"
version = "0.5.0"
@@ -627,6 +650,12 @@ dependencies = [
"zerovec",
]
[[package]]
name = "id-arena"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954"
[[package]]
name = "idna"
version = "1.1.0"
@@ -655,7 +684,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9"
dependencies = [
"equivalent",
"hashbrown 0.17.1",
"hashbrown 0.17.0",
"serde",
"serde_core",
]
@@ -680,20 +709,21 @@ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "js-sys"
version = "0.3.103"
version = "0.3.97"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53b44bfcdb3f8d5837a46dae1ca9660a837176eee74a28b229bc626816589102"
checksum = "a1840c94c045fbcf8ba2812c95db44499f7c64910a912551aaaa541decebcacf"
dependencies = [
"cfg-if",
"futures-util",
"once_cell",
"wasm-bindgen",
]
[[package]]
name = "jsonschema"
version = "0.47.0"
version = "0.45.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "281c43ff06dcb331e9356d30e38853d559ce3d0a3f693e0b0e102667dec14fb1"
checksum = "257eb0e588b76827bbddc9e73945a9743693dd2adeaee9da26420f93cfedb798"
dependencies = [
"ahash",
"bytecount",
@@ -704,33 +734,30 @@ dependencies = [
"getrandom 0.3.4",
"idna",
"itoa",
"jsonschema-regex",
"num-cmp",
"num-traits",
"percent-encoding",
"referencing",
"regex",
"regex-syntax",
"serde",
"serde_json",
"unicode-general-category",
"uuid-simd",
]
[[package]]
name = "jsonschema-regex"
version = "0.47.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee0b351864e7ffbc5db9273daf7fa1b4d5177b0946713d667ca571b83c0b4045"
dependencies = [
"regex-syntax",
]
[[package]]
name = "lazy_static"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]]
name = "leb128fmt"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
[[package]]
name = "libc"
version = "0.2.186"
@@ -760,27 +787,21 @@ dependencies = [
[[package]]
name = "log"
version = "0.4.33"
version = "0.4.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad"
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
[[package]]
name = "lru"
version = "0.18.1"
version = "0.16.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b6180140927ee907000b0aa540091f6ea512ead4447c92b8fc35bc72788a5a6"
checksum = "7f66e8d5d03f609abc3a39e6f08e4164ebf1447a732906d39eb9b99b7919ef39"
[[package]]
name = "memchr"
version = "2.8.3"
version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
[[package]]
name = "micromap"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2a86d3146ed3995b5913c414f6664344b9617457320782e64f0bb44afd49d74"
checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
[[package]]
name = "msvc_spectre_libs"
@@ -797,7 +818,7 @@ version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23"
dependencies = [
"num-bigint 0.4.8",
"num-bigint",
"num-complex",
"num-integer",
"num-iter",
@@ -807,19 +828,9 @@ dependencies = [
[[package]]
name = "num-bigint"
version = "0.4.8"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c89e69e7e0f03bea5ef08013795c25018e101932225a656383bd384495ecc367"
dependencies = [
"num-integer",
"num-traits",
]
[[package]]
name = "num-bigint"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93e7820bc0a80a0238e650327316f929ba18d5be054b647490a3a6a339f3e7c0"
checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
dependencies = [
"num-integer",
"num-traits",
@@ -851,10 +862,11 @@ dependencies = [
[[package]]
name = "num-iter"
version = "0.1.46"
version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c92800bd69a1eac91786bcfe9da64a897eb72911b8dc3095decbd07429e8048b"
checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
]
@@ -865,7 +877,7 @@ version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824"
dependencies = [
"num-bigint 0.4.8",
"num-bigint",
"num-integer",
"num-traits",
]
@@ -973,6 +985,16 @@ dependencies = [
"zerovec",
]
[[package]]
name = "prettyplease"
version = "0.2.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
dependencies = [
"proc-macro2",
"syn",
]
[[package]]
name = "proc-macro2"
version = "1.0.106"
@@ -984,9 +1006,9 @@ dependencies = [
[[package]]
name = "quote"
version = "1.0.46"
version = "1.0.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368"
checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
dependencies = [
"proc-macro2",
]
@@ -1005,12 +1027,12 @@ checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
[[package]]
name = "rand"
version = "0.10.2"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7f5fa3a058cd35567ef9bfa5e75732bee0f9e4c55fa90477bef2dfcdbc4be80"
checksum = "d2e8e8bcc7961af1fdac401278c6a831614941f6164ee3bf4ce61b7edb162207"
dependencies = [
"chacha20",
"getrandom 0.4.3",
"getrandom 0.4.2",
"rand_core",
]
@@ -1051,16 +1073,14 @@ dependencies = [
[[package]]
name = "referencing"
version = "0.47.0"
version = "0.45.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "348e860aeb0b7bd035778fd11dd9cd5290d32e4aed3b8f2274a00287a9fd362b"
checksum = "e2f38748ceca8d0b0013e60f534d94a6e23dfd89fd2a88318fc5a2d04fda1010"
dependencies = [
"ahash",
"fluent-uri",
"getrandom 0.3.4",
"hashbrown 0.17.1",
"itoa",
"micromap",
"hashbrown 0.16.1",
"parking_lot",
"percent-encoding",
"serde_json",
@@ -1068,9 +1088,9 @@ dependencies = [
[[package]]
name = "regex"
version = "1.13.1"
version = "1.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d"
checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276"
dependencies = [
"aho-corasick",
"memchr",
@@ -1080,9 +1100,9 @@ dependencies = [
[[package]]
name = "regex-automata"
version = "0.4.16"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fcfdb36bda0c880c5931cdc7a2bcdc8ba4556847b9d912bca70bc94708711ad"
checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f"
dependencies = [
"aho-corasick",
"memchr",
@@ -1091,13 +1111,13 @@ dependencies = [
[[package]]
name = "regex-syntax"
version = "0.8.11"
version = "0.8.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4"
checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a"
[[package]]
name = "regorus"
version = "0.11.0"
version = "0.10.0"
dependencies = [
"anyhow",
"chrono",
@@ -1105,7 +1125,7 @@ dependencies = [
"dashmap",
"data-encoding",
"globset",
"hashbrown 0.17.1",
"hashbrown 0.16.1",
"icu_casemap",
"indexmap",
"ipnet",
@@ -1113,7 +1133,7 @@ dependencies = [
"lazy_static",
"lru",
"msvc_spectre_libs",
"num-bigint 0.5.1",
"num-bigint",
"num-traits",
"parking_lot",
"postcard",
@@ -1132,7 +1152,7 @@ dependencies = [
[[package]]
name = "regorus-ffi"
version = "0.11.0"
version = "0.10.0"
dependencies = [
"anyhow",
"cbindgen",
@@ -1143,7 +1163,7 @@ dependencies = [
[[package]]
name = "regorus-mimalloc"
version = "2.2.7"
version = "2.2.6"
dependencies = [
"regorus-mimalloc-sys",
]
@@ -1170,9 +1190,9 @@ dependencies = [
[[package]]
name = "rustversion"
version = "1.0.23"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f"
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
[[package]]
name = "ryu"
@@ -1224,9 +1244,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.150"
version = "1.0.149"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9"
checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86"
dependencies = [
"itoa",
"memchr",
@@ -1259,15 +1279,15 @@ dependencies = [
[[package]]
name = "shlex"
version = "2.0.1"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "siphasher"
version = "1.0.3"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649"
checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e"
[[package]]
name = "slab"
@@ -1277,15 +1297,15 @@ checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
[[package]]
name = "smallvec"
version = "1.15.2"
version = "1.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90"
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
[[package]]
name = "spin"
version = "0.12.2"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8abadc99fd9c7bbb7d0ca2b31d72a067d0c0dcd7aad25ab8cac71ba91417694b"
checksum = "d5fe4ccb98d9c292d56fec89a5e07da7fc4cf0dc11e156b41793132775d3e591"
[[package]]
name = "stable_deref_trait"
@@ -1301,9 +1321,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "syn"
version = "2.0.119"
version = "2.0.117"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297"
checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
dependencies = [
"proc-macro2",
"quote",
@@ -1328,7 +1348,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd"
dependencies = [
"fastrand",
"getrandom 0.4.3",
"getrandom 0.4.2",
"once_cell",
"rustix",
"windows-sys",
@@ -1395,14 +1415,14 @@ version = "1.1.2+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526"
dependencies = [
"winnow 1.0.4",
"winnow 1.0.2",
]
[[package]]
name = "toml_writer"
version = "1.1.2+spec-1.1.0"
version = "1.1.1+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d56353a2a665ad0f41a421187180aab746c8c325620617ad883a99a1cbe66d2"
checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db"
[[package]]
name = "unicode-general-category"
@@ -1416,6 +1436,12 @@ version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
[[package]]
name = "unicode-xid"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
[[package]]
name = "unsafe-libyaml"
version = "0.2.11"
@@ -1448,11 +1474,11 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "uuid"
version = "1.24.0"
version = "1.23.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf3923a6f5c4c6382e0b653c4117f48d631ea17f38ed86e2a828e6f7412f5239"
checksum = "ddd74a9687298c6858e9b88ec8935ec45d22e8fd5e6394fa1bd4e99a87789c76"
dependencies = [
"getrandom 0.4.3",
"getrandom 0.4.2",
"rand",
]
@@ -1480,18 +1506,27 @@ checksum = "5c3082ca00d5a5ef149bb8b555a72ae84c9c59f7250f013ac822ac2e49b19c64"
[[package]]
name = "wasip2"
version = "1.0.4+wasi-0.2.12"
version = "1.0.3+wasi-0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487"
checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6"
dependencies = [
"wit-bindgen",
"wit-bindgen 0.57.1",
]
[[package]]
name = "wasip3"
version = "0.4.0+wasi-0.3.0-rc-2026-01-06"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5"
dependencies = [
"wit-bindgen 0.51.0",
]
[[package]]
name = "wasm-bindgen"
version = "0.2.126"
version = "0.2.120"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b067c0c11094aef6b7a801c1e34a26affafdf3d051dba08456b868789aaf9a4"
checksum = "df52b6d9b87e0c74c9edfa1eb2d9bf85e5d63515474513aa50fa181b3c4f5db1"
dependencies = [
"cfg-if",
"once_cell",
@@ -1502,9 +1537,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.126"
version = "0.2.120"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "167ce5e579f6bcf889c4f7175a8a5a585de84e8ff93976ce393efa5f2837aab1"
checksum = "78b1041f495fb322e64aca85f5756b2172e35cd459376e67f2a6c9dffcedb103"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@@ -1512,9 +1547,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.126"
version = "0.2.120"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3997c7839262f4ef12cf90b818d6340c18e80f263f1a94bf157d0ec4420380e"
checksum = "9dcd0ff20416988a18ac686d4d4d0f6aae9ebf08a389ff5d29012b05af2a1b41"
dependencies = [
"bumpalo",
"proc-macro2",
@@ -1525,13 +1560,47 @@ dependencies = [
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.126"
version = "0.2.120"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc1b4cb0cc549fcf58d7dfc081778139b3d283a081644e833e84682ad71cea24"
checksum = "49757b3c82ebf16c57d69365a142940b384176c24df52a087fb748e2085359ea"
dependencies = [
"unicode-ident",
]
[[package]]
name = "wasm-encoder"
version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319"
dependencies = [
"leb128fmt",
"wasmparser",
]
[[package]]
name = "wasm-metadata"
version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909"
dependencies = [
"anyhow",
"indexmap",
"wasm-encoder",
"wasmparser",
]
[[package]]
name = "wasmparser"
version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe"
dependencies = [
"bitflags",
"hashbrown 0.15.5",
"indexmap",
"semver",
]
[[package]]
name = "windows-core"
version = "0.62.2"
@@ -1608,9 +1677,18 @@ checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945"
[[package]]
name = "winnow"
version = "1.0.4"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23b97319f7b8343df12cc98938e5c3eb436064524c8d2b4e30a1d3a36eecdf81"
checksum = "2ee1708bef14716a11bae175f579062d4554d95be2c6829f518df847b7b3fdd0"
[[package]]
name = "wit-bindgen"
version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5"
dependencies = [
"wit-bindgen-rust-macro",
]
[[package]]
name = "wit-bindgen"
@@ -1618,6 +1696,85 @@ version = "0.57.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e"
[[package]]
name = "wit-bindgen-core"
version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc"
dependencies = [
"anyhow",
"heck",
"wit-parser",
]
[[package]]
name = "wit-bindgen-rust"
version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21"
dependencies = [
"anyhow",
"heck",
"indexmap",
"prettyplease",
"syn",
"wasm-metadata",
"wit-bindgen-core",
"wit-component",
]
[[package]]
name = "wit-bindgen-rust-macro"
version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a"
dependencies = [
"anyhow",
"prettyplease",
"proc-macro2",
"quote",
"syn",
"wit-bindgen-core",
"wit-bindgen-rust",
]
[[package]]
name = "wit-component"
version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2"
dependencies = [
"anyhow",
"bitflags",
"indexmap",
"log",
"serde",
"serde_derive",
"serde_json",
"wasm-encoder",
"wasm-metadata",
"wasmparser",
"wit-parser",
]
[[package]]
name = "wit-parser"
version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736"
dependencies = [
"anyhow",
"id-arena",
"indexmap",
"log",
"semver",
"serde",
"serde_derive",
"serde_json",
"unicode-xid",
"wasmparser",
]
[[package]]
name = "writeable"
version = "0.6.3"
@@ -1626,9 +1783,9 @@ checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4"
[[package]]
name = "yoke"
version = "0.8.3"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "709fe23a0424b6a435d82152b1bd3fdfb0833487d5fa90d05d42762a9891fef5"
checksum = "abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca"
dependencies = [
"stable_deref_trait",
"yoke-derive",
@@ -1649,18 +1806,18 @@ dependencies = [
[[package]]
name = "zerocopy"
version = "0.8.54"
version = "0.8.48"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7cbbc0a705a0fd05cc3676525980d2bf5a9bc4adac6d6475209a7887cf59d19"
checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.8.54"
version = "0.8.48"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2e817b7b52d0c7358d3246da9d69935ebb18116b2b102b4230dac079b4862f5"
checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4"
dependencies = [
"proc-macro2",
"quote",
@@ -1669,9 +1826,9 @@ dependencies = [
[[package]]
name = "zerofrom"
version = "0.1.8"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272"
checksum = "69faa1f2a1ea75661980b013019ed6687ed0e83d069bc1114e2cc74c6c04c4df"
dependencies = [
"zerofrom-derive",
]
@@ -1725,6 +1882,6 @@ dependencies = [
[[package]]
name = "zmij"
version = "1.0.23"
version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b"
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"

View File

@@ -2,7 +2,7 @@
[package]
name = "regorus-ffi"
version = "0.11.0"
version = "0.10.0"
edition = "2021"
license = "MIT AND Apache-2.0 AND BSD-3-Clause"
@@ -13,7 +13,7 @@ crate-type = ["cdylib", "staticlib"]
[dependencies]
anyhow = "1.0"
regorus = { path = "../..", default-features = false }
serde_json = "1.0.150"
serde_json = "1.0.140"
parking_lot = { version = "0.12", optional = true }
[profile.release]

View File

@@ -5,108 +5,66 @@
#![cfg(feature = "azure_policy")]
use crate::common::{from_c_str, to_ref, to_shared_ref, RegorusResult, RegorusStatus};
use crate::common::{from_c_str, to_ref, RegorusResult, RegorusStatus};
use crate::panic_guard::with_unwind_guard;
use alloc::boxed::Box;
use alloc::format;
use alloc::string::String;
use alloc::sync::Arc;
use anyhow::{anyhow, Result};
use core::ffi::{c_char, c_void};
use core::{mem, ptr};
use anyhow::Result;
use core::ffi::c_char;
use core::ptr;
use regorus::languages::azure_policy::aliases::AliasRegistry;
/// Mutable builder for `AliasRegistry`.
///
/// This handle is intentionally single-threaded and must not be used
/// concurrently. Callers should finish loading alias data and then freeze it
/// into a `RegorusAliasRegistry` via `regorus_alias_registry_builder_build`.
pub struct RegorusAliasRegistryBuilder {
registry: AliasRegistry,
built: bool,
}
impl RegorusAliasRegistryBuilder {
fn new() -> Self {
Self {
registry: AliasRegistry::new(),
built: false,
}
}
fn registry_mut(&mut self) -> Result<&mut AliasRegistry> {
if self.built {
return Err(anyhow!("alias registry builder has already been built"));
}
Ok(&mut self.registry)
}
fn build(&mut self) -> Result<RegorusAliasRegistry> {
if self.built {
return Err(anyhow!("alias registry builder has already been built"));
}
self.built = true;
Ok(RegorusAliasRegistry {
registry: Arc::new(mem::replace(&mut self.registry, AliasRegistry::new())),
})
}
}
/// Frozen, immutable alias registry.
/// Opaque wrapper for `AliasRegistry`.
pub struct RegorusAliasRegistry {
registry: Arc<AliasRegistry>,
}
impl RegorusAliasRegistry {
/// Return a shared reference to the inner registry for use by the compiler.
pub(crate) fn inner(&self) -> Arc<AliasRegistry> {
Arc::clone(&self.registry)
}
registry: AliasRegistry,
}
// ---------------------------------------------------------------------------
// Builder lifecycle
// Lifecycle
// ---------------------------------------------------------------------------
/// Create a new, empty `AliasRegistry` builder.
/// Create a new, empty `AliasRegistry`.
///
/// The caller must eventually call `regorus_alias_registry_builder_drop`.
/// The caller must eventually call `regorus_alias_registry_drop` to free the handle.
#[no_mangle]
pub extern "C" fn regorus_alias_registry_builder_new() -> *mut RegorusAliasRegistryBuilder {
Box::into_raw(Box::new(RegorusAliasRegistryBuilder::new()))
pub extern "C" fn regorus_alias_registry_new() -> *mut RegorusAliasRegistry {
let wrapper = RegorusAliasRegistry {
registry: AliasRegistry::new(),
};
Box::into_raw(Box::new(wrapper))
}
/// Drop a `RegorusAliasRegistryBuilder`.
/// Drop a `RegorusAliasRegistry`.
#[no_mangle]
pub extern "C" fn regorus_alias_registry_builder_drop(builder: *mut RegorusAliasRegistryBuilder) {
if let Ok(builder) = to_ref(builder) {
pub extern "C" fn regorus_alias_registry_drop(registry: *mut RegorusAliasRegistry) {
if let Ok(r) = to_ref(registry) {
unsafe {
let _ = Box::from_raw(ptr::from_mut(builder));
let _ = Box::from_raw(ptr::from_mut(r));
}
}
}
// ---------------------------------------------------------------------------
// Builder loading
// Loading
// ---------------------------------------------------------------------------
/// Load control-plane alias data (array of `ProviderAliases`) into the builder.
/// Load control-plane alias data (array of `ProviderAliases`) into the registry.
///
/// `json` must be a valid null-terminated UTF-8 string containing the JSON
/// array returned by `Get-AzPolicyAlias` or the static
/// `ResourceTypesAndAliases.json` file.
#[no_mangle]
pub extern "C" fn regorus_alias_registry_builder_load_json(
builder: *mut RegorusAliasRegistryBuilder,
pub extern "C" fn regorus_alias_registry_load_json(
registry: *mut RegorusAliasRegistry,
json: *const c_char,
) -> RegorusResult {
with_unwind_guard(|| {
let output = || -> Result<()> {
let json_str = from_c_str(json)?;
to_ref(builder)?.registry_mut()?.load_from_json(&json_str)?;
to_ref(registry)?.registry.load_from_json(&json_str)?;
Ok(())
}();
@@ -120,20 +78,20 @@ pub extern "C" fn regorus_alias_registry_builder_load_json(
})
}
/// Load a data-plane policy manifest into the builder.
/// Load a data-plane policy manifest into the registry.
///
/// `json` must be a valid null-terminated UTF-8 string containing a single
/// `DataPolicyManifest` JSON object.
#[no_mangle]
pub extern "C" fn regorus_alias_registry_builder_load_manifest(
builder: *mut RegorusAliasRegistryBuilder,
pub extern "C" fn regorus_alias_registry_load_manifest(
registry: *mut RegorusAliasRegistry,
json: *const c_char,
) -> RegorusResult {
with_unwind_guard(|| {
let output = || -> Result<()> {
let json_str = from_c_str(json)?;
to_ref(builder)?
.registry_mut()?
to_ref(registry)?
.registry
.load_data_policy_manifest_json(&json_str)?;
Ok(())
}();
@@ -148,52 +106,16 @@ pub extern "C" fn regorus_alias_registry_builder_load_manifest(
})
}
/// Freeze a builder into an immutable `RegorusAliasRegistry`.
#[no_mangle]
pub extern "C" fn regorus_alias_registry_builder_build(
builder: *mut RegorusAliasRegistryBuilder,
) -> RegorusResult {
with_unwind_guard(|| {
let output = || -> Result<*mut RegorusAliasRegistry> {
let registry = to_ref(builder)?.build()?;
Ok(Box::into_raw(Box::new(registry)))
}();
match output {
Ok(registry) => RegorusResult::ok_pointer(registry as *mut c_void),
Err(e) => {
RegorusResult::err_with_message(RegorusStatus::InvalidArgument, format!("{e}"))
}
}
})
}
// ---------------------------------------------------------------------------
// Frozen registry lifecycle
// ---------------------------------------------------------------------------
/// Drop a `RegorusAliasRegistry`.
#[no_mangle]
pub extern "C" fn regorus_alias_registry_drop(registry: *mut RegorusAliasRegistry) {
if let Ok(registry) = to_ref(registry) {
unsafe {
let _ = Box::from_raw(ptr::from_mut(registry));
}
}
}
// ---------------------------------------------------------------------------
// Frozen registry queries
// Queries
// ---------------------------------------------------------------------------
/// Return the number of resource types loaded in the alias registry.
#[no_mangle]
pub extern "C" fn regorus_alias_registry_len(
registry: *const RegorusAliasRegistry,
) -> RegorusResult {
pub extern "C" fn regorus_alias_registry_len(registry: *mut RegorusAliasRegistry) -> RegorusResult {
with_unwind_guard(|| {
let output = || -> Result<i64> {
let len = to_shared_ref(registry)?.registry.len();
let len = to_ref(registry)?.registry.len();
Ok(len as i64)
}();
@@ -212,9 +134,15 @@ pub extern "C" fn regorus_alias_registry_len(
///
/// Returns a JSON string:
/// `{ "resource": <normalized>, "context": <context>, "parameters": <params> }`.
///
/// * `resource_json` raw ARM resource JSON
/// * `api_version` API version string (e.g. `"2023-01-01"`), or null to use
/// the default alias paths
/// * `context_json` JSON object for additional context (pass `"{}"` if none)
/// * `parameters_json` JSON object of policy parameter values (pass `"{}"` if none)
#[no_mangle]
pub extern "C" fn regorus_alias_registry_normalize_and_wrap(
registry: *const RegorusAliasRegistry,
registry: *mut RegorusAliasRegistry,
resource_json: *const c_char,
api_version: *const c_char,
context_json: *const c_char,
@@ -240,7 +168,7 @@ pub extern "C" fn regorus_alias_registry_normalize_and_wrap(
let context = regorus::Value::from_json_str(&context_str)?;
let params = regorus::Value::from_json_str(&params_str)?;
let wrapped = to_shared_ref(registry)?.registry.normalize_and_wrap(
let wrapped = to_ref(registry)?.registry.normalize_and_wrap(
&resource,
api_ver.as_deref(),
Some(context),
@@ -257,9 +185,14 @@ pub extern "C" fn regorus_alias_registry_normalize_and_wrap(
}
/// Denormalize a previously-normalized resource JSON back to ARM format.
///
/// * `normalized_json` the normalized resource JSON
/// * `api_version` API version string, or null to use the default alias paths
///
/// Returns the denormalized ARM JSON string.
#[no_mangle]
pub extern "C" fn regorus_alias_registry_denormalize(
registry: *const RegorusAliasRegistry,
registry: *mut RegorusAliasRegistry,
normalized_json: *const c_char,
api_version: *const c_char,
) -> RegorusResult {
@@ -279,7 +212,7 @@ pub extern "C" fn regorus_alias_registry_denormalize(
let normalized = regorus::Value::from_json_str(&normalized_str)?;
let result = to_shared_ref(registry)?
let result = to_ref(registry)?
.registry
.denormalize(&normalized, api_ver.as_deref());
result.to_json_str()
@@ -299,10 +232,12 @@ mod tests {
use core::ffi::CStr;
use std::ffi::CString;
/// Helper: create a C string from a Rust &str.
fn c(s: &str) -> CString {
CString::new(s).expect("CString::new failed")
}
/// Helper: assert a RegorusResult has Ok status and extract string output.
fn assert_ok_string(r: &RegorusResult) -> String {
assert_eq!(r.status, RegorusStatus::Ok, "expected Ok status");
assert!(!r.output.is_null(), "expected non-null output");
@@ -313,51 +248,12 @@ mod tests {
s
}
/// Helper: assert a RegorusResult has Ok status with integer output.
fn assert_ok_int(r: &RegorusResult) -> i64 {
assert_eq!(r.status, RegorusStatus::Ok, "expected Ok status");
r.int_value
}
fn assert_ok_pointer(r: &RegorusResult) -> *mut c_void {
assert_eq!(r.status, RegorusStatus::Ok, "expected Ok status");
assert!(matches!(
r.data_type,
crate::common::RegorusDataType::Pointer
));
assert!(!r.pointer_value.is_null());
r.pointer_value
}
fn build_registry_with_json(json: &str) -> *mut RegorusAliasRegistry {
let builder = regorus_alias_registry_builder_new();
let json = c(json);
let r = regorus_alias_registry_builder_load_json(builder, json.as_ptr());
assert_eq!(r.status, RegorusStatus::Ok);
regorus_result_drop(r);
let r = regorus_alias_registry_builder_build(builder);
let registry = assert_ok_pointer(&r) as *mut RegorusAliasRegistry;
regorus_result_drop(r);
regorus_alias_registry_builder_drop(builder);
registry
}
fn build_registry_with_manifest(json: &str) -> *mut RegorusAliasRegistry {
let builder = regorus_alias_registry_builder_new();
let json = c(json);
let r = regorus_alias_registry_builder_load_manifest(builder, json.as_ptr());
assert_eq!(r.status, RegorusStatus::Ok);
regorus_result_drop(r);
let r = regorus_alias_registry_builder_build(builder);
let registry = assert_ok_pointer(&r) as *mut RegorusAliasRegistry;
regorus_result_drop(r);
regorus_alias_registry_builder_drop(builder);
registry
}
const ALIASES: &str = r#"[{
"namespace": "Microsoft.Storage",
"resourceTypes": [{
@@ -383,21 +279,20 @@ mod tests {
}"#;
#[test]
fn lifecycle_builder_build_and_drop() {
let builder = regorus_alias_registry_builder_new();
assert!(!builder.is_null());
let r = regorus_alias_registry_builder_build(builder);
let registry = assert_ok_pointer(&r) as *mut RegorusAliasRegistry;
regorus_result_drop(r);
regorus_alias_registry_builder_drop(builder);
regorus_alias_registry_drop(registry);
fn lifecycle_new_and_drop() {
let reg = regorus_alias_registry_new();
assert!(!reg.is_null());
regorus_alias_registry_drop(reg);
}
#[test]
fn load_json_and_check_len() {
let reg = build_registry_with_json(ALIASES);
let reg = regorus_alias_registry_new();
let json = c(ALIASES);
let r = regorus_alias_registry_load_json(reg, json.as_ptr());
assert_eq!(r.status, RegorusStatus::Ok);
regorus_result_drop(r);
let r = regorus_alias_registry_len(reg);
assert_eq!(assert_ok_int(&r), 1);
@@ -408,7 +303,12 @@ mod tests {
#[test]
fn load_manifest_and_check_len() {
let reg = build_registry_with_manifest(MANIFEST);
let reg = regorus_alias_registry_new();
let json = c(MANIFEST);
let r = regorus_alias_registry_load_manifest(reg, json.as_ptr());
assert_eq!(r.status, RegorusStatus::Ok);
regorus_result_drop(r);
let r = regorus_alias_registry_len(reg);
assert_eq!(assert_ok_int(&r), 1);
@@ -419,39 +319,23 @@ mod tests {
#[test]
fn load_invalid_json_returns_error() {
let builder = regorus_alias_registry_builder_new();
let reg = regorus_alias_registry_new();
let bad = c("not valid json");
let r = regorus_alias_registry_builder_load_json(builder, bad.as_ptr());
let r = regorus_alias_registry_load_json(reg, bad.as_ptr());
assert_ne!(r.status, RegorusStatus::Ok);
regorus_result_drop(r);
regorus_alias_registry_builder_drop(builder);
}
#[test]
fn builder_cannot_be_reused_after_build() {
let builder = regorus_alias_registry_builder_new();
let r = regorus_alias_registry_builder_build(builder);
let registry = assert_ok_pointer(&r) as *mut RegorusAliasRegistry;
regorus_result_drop(r);
let aliases = c(ALIASES);
let r = regorus_alias_registry_builder_load_json(builder, aliases.as_ptr());
assert_ne!(r.status, RegorusStatus::Ok);
regorus_result_drop(r);
let r = regorus_alias_registry_builder_build(builder);
assert_ne!(r.status, RegorusStatus::Ok);
regorus_result_drop(r);
regorus_alias_registry_builder_drop(builder);
regorus_alias_registry_drop(registry);
regorus_alias_registry_drop(reg);
}
#[test]
fn normalize_and_wrap_round_trip() {
let reg = build_registry_with_json(ALIASES);
let reg = regorus_alias_registry_new();
let aliases = c(ALIASES);
let r = regorus_alias_registry_load_json(reg, aliases.as_ptr());
assert_eq!(r.status, RegorusStatus::Ok);
regorus_result_drop(r);
let resource = c(r#"{
"name": "acct1",
@@ -462,6 +346,7 @@ mod tests {
let ctx = c(r#"{"resourceGroup": {"name": "rg1"}}"#);
let params = c(r#"{"env": "prod"}"#);
// Normalize
let r = regorus_alias_registry_normalize_and_wrap(
reg,
resource.as_ptr(),
@@ -472,6 +357,7 @@ mod tests {
let envelope_json = assert_ok_string(&r);
regorus_result_drop(r);
// Parse and verify structure
let envelope: serde_json::Value =
serde_json::from_str(&envelope_json).expect("invalid JSON output");
assert!(
@@ -487,13 +373,16 @@ mod tests {
"envelope missing 'context'"
);
// The normalized resource should have lowercased alias fields
let res = &envelope["resource"];
assert_eq!(res["supportshttpstrafficonly"], true);
assert_eq!(res["name"], "acct1");
// Context and parameters should be passed through
assert_eq!(envelope["context"]["resourceGroup"]["name"], "rg1");
assert_eq!(envelope["parameters"]["env"], "prod");
// Denormalize the resource portion
let resource_json = serde_json::to_string(&res).expect("serialize resource");
let norm_cstr = c(&resource_json);
@@ -503,6 +392,7 @@ mod tests {
let denorm: serde_json::Value =
serde_json::from_str(&denorm_json).expect("invalid denorm JSON");
// Should be back under properties with restored casing
assert_eq!(
denorm["properties"]["supportsHttpsTrafficOnly"], true,
"expected restored casing under properties"
@@ -513,7 +403,11 @@ mod tests {
#[test]
fn denormalize_invalid_json_returns_error() {
let reg = build_registry_with_json(ALIASES);
let reg = regorus_alias_registry_new();
let aliases = c(ALIASES);
let r = regorus_alias_registry_load_json(reg, aliases.as_ptr());
assert_eq!(r.status, RegorusStatus::Ok);
regorus_result_drop(r);
let bad = c("not json");
let api = c("2023-01-01");
@@ -526,7 +420,11 @@ mod tests {
#[test]
fn normalize_data_plane_manifest() {
let reg = build_registry_with_manifest(MANIFEST);
let reg = regorus_alias_registry_new();
let manifest = c(MANIFEST);
let r = regorus_alias_registry_load_manifest(reg, manifest.as_ptr());
assert_eq!(r.status, RegorusStatus::Ok);
regorus_result_drop(r);
let resource = c(r#"{
"type": "Microsoft.KeyVault.Data/vaults/certificates",
@@ -555,12 +453,7 @@ mod tests {
#[test]
fn empty_registry_normalize() {
let builder = regorus_alias_registry_builder_new();
let r = regorus_alias_registry_builder_build(builder);
let reg = assert_ok_pointer(&r) as *mut RegorusAliasRegistry;
regorus_result_drop(r);
regorus_alias_registry_builder_drop(builder);
let reg = regorus_alias_registry_new();
let resource = c(r#"{"name": "test", "type": "Unknown/type", "properties": {"foo": 1}}"#);
let api = c("");
let ctx = c("{}");
@@ -577,6 +470,7 @@ mod tests {
regorus_result_drop(r);
let envelope: serde_json::Value = serde_json::from_str(&json).expect("invalid JSON");
// Without aliases, properties should still be flattened
assert_eq!(envelope["resource"]["foo"], 1);
assert_eq!(envelope["resource"]["name"], "test");

View File

@@ -236,10 +236,6 @@ pub(crate) fn to_ref<'a, T>(t: *mut T) -> Result<&'a mut T> {
unsafe { t.as_mut().ok_or_else(|| anyhow!("null pointer")) }
}
pub(crate) fn to_shared_ref<'a, T>(t: *const T) -> Result<&'a T> {
unsafe { t.as_ref().ok_or_else(|| anyhow!("null pointer")) }
}
pub(crate) fn to_regorus_result(r: Result<()>) -> RegorusResult {
match r {
Ok(()) => RegorusResult::ok_void(),

View File

@@ -1,6 +1,6 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
use crate::common::{from_c_str, to_shared_ref, RegorusResult, RegorusStatus};
use crate::common::{from_c_str, RegorusResult, RegorusStatus};
use crate::compiled_policy::RegorusCompiledPolicy;
use crate::panic_guard::with_unwind_guard;
use alloc::boxed::Box;
@@ -208,220 +208,6 @@ fn convert_c_modules_to_rust(
Ok(policy_modules)
}
// ---------------------------------------------------------------------------
// Azure Policy JSON compilation
// ---------------------------------------------------------------------------
/// Compile an Azure Policy JSON policy rule into an RVM program.
///
/// Parses the JSON `policyRule` (the `{ "if": ..., "then": ... }` object),
/// resolves aliases using the provided registry, and compiles the result
/// into an RVM [`Program`] that can be loaded into a [`RegorusRvm`].
///
/// # Parameters
/// * `registry` - Alias registry handle, or null.
/// * `policy_rule_json` - JSON string containing the policyRule object
///
/// # Null registry behavior
///
/// When `registry` is null, compilation proceeds **without alias resolution**.
/// Field references that correspond to Azure resource provider aliases
/// (e.g. `Microsoft.Storage/storageAccounts/supportsHttpsTrafficOnly`) will
/// be compiled as raw property paths rather than being resolved to their
/// short forms. This means:
///
/// - Policies that rely on aliases will **silently produce incorrect
/// evaluation results** because the field paths won't match the
/// normalized resource structure.
/// - **Modify / Append** effect policies will **skip the modifiability
/// validation** that normally rejects writes to non-modifiable aliases
/// at compile time.
///
/// Pass null only when the policy is known to contain no alias references
/// (e.g. simple `type` / `location` checks, or in unit-test scenarios).
///
/// # Returns
/// Returns a `RegorusResult` containing a `RegorusProgram` pointer on success.
///
/// # Safety
/// `policy_rule_json` must be a valid null-terminated UTF-8 string.
/// If `registry` is non-null it must be a valid `RegorusAliasRegistry` pointer.
/// The caller must eventually call `regorus_program_drop` on the returned handle.
#[cfg(all(feature = "azure_policy", feature = "rvm"))]
#[no_mangle]
pub extern "C" fn regorus_compile_azure_policy_rule(
registry: *const crate::alias_registry::RegorusAliasRegistry,
policy_rule_json: *const c_char,
) -> RegorusResult {
use crate::alias_registry::RegorusAliasRegistry;
use crate::rvm::RegorusProgram;
use alloc::sync::Arc;
use regorus::languages::azure_policy::{compiler, parser};
use regorus::Rc;
use regorus::Source;
with_unwind_guard(|| {
let result = || -> Result<RegorusProgram, (RegorusStatus, alloc::string::String)> {
let json_str = from_c_str(policy_rule_json).map_err(|e| {
(
RegorusStatus::InvalidDataFormat,
format!("Invalid policy rule JSON string: {e}"),
)
})?;
let source = Source::from_contents("policy_rule".into(), json_str).map_err(|e| {
(
RegorusStatus::InvalidDataFormat,
format!("Failed to create source: {e}"),
)
})?;
let ast = parser::parse_policy_rule(&source).map_err(|e| {
(
RegorusStatus::InvalidPolicy,
format!("Failed to parse policy rule: {e}"),
)
})?;
let program = if registry.is_null() {
compiler::compile_policy_rule(&ast)
} else {
let reg: &RegorusAliasRegistry = to_shared_ref(registry).map_err(|e| {
(
RegorusStatus::InvalidArgument,
format!("Invalid alias registry: {e}"),
)
})?;
compiler::compile_policy_rule_with_aliases(&ast, reg.inner())
};
program
.map(|p| RegorusProgram {
program: Arc::new(Rc::try_unwrap(p).unwrap_or_else(|rc| (*rc).clone())),
})
.map_err(|e| {
(
RegorusStatus::CompilationFailed,
format!("Failed to compile policy rule: {e}"),
)
})
}();
match result {
Ok(program) => {
RegorusResult::ok_pointer(Box::into_raw(Box::new(program)) as *mut c_void)
}
Err((status, msg)) => RegorusResult::err_with_message(status, msg),
}
})
}
/// Compile a full Azure Policy definition JSON into an RVM program.
///
/// Parses the JSON policy definition (which includes `policyRule`, `parameters`,
/// `displayName`, etc.), resolves aliases using the provided registry, and
/// compiles the result into an RVM [`Program`].
///
/// The definition JSON may be in either wrapped or unwrapped form:
/// - **Wrapped**: `{ "properties": { "policyRule": ..., "parameters": ... }, "id": ... }`
/// - **Unwrapped**: `{ "policyRule": ..., "parameters": ..., "displayName": ... }`
///
/// # Parameters
/// * `registry` - Alias registry handle, or null.
/// * `policy_definition_json` - JSON string containing the full policy definition
///
/// # Null registry behavior
///
/// When `registry` is null, compilation proceeds **without alias resolution**.
/// Field references that correspond to Azure resource provider aliases will
/// be compiled as raw property paths rather than being resolved. This means:
///
/// - Policies that rely on aliases will **silently produce incorrect
/// evaluation results**.
/// - **Modify / Append** effect policies will **skip the modifiability
/// validation** that normally rejects writes to non-modifiable aliases
/// at compile time.
///
/// Pass null only when the policy is known to contain no alias references
/// (e.g. simple `type` / `location` checks, or in unit-test scenarios).
///
/// # Returns
/// Returns a `RegorusResult` containing a `RegorusProgram` pointer on success.
///
/// # Safety
/// `policy_definition_json` must be a valid null-terminated UTF-8 string.
/// If `registry` is non-null it must be a valid `RegorusAliasRegistry` pointer.
/// The caller must eventually call `regorus_program_drop` on the returned handle.
#[cfg(all(feature = "azure_policy", feature = "rvm"))]
#[no_mangle]
pub extern "C" fn regorus_compile_azure_policy_definition(
registry: *const crate::alias_registry::RegorusAliasRegistry,
policy_definition_json: *const c_char,
) -> RegorusResult {
use crate::alias_registry::RegorusAliasRegistry;
use crate::rvm::RegorusProgram;
use alloc::sync::Arc;
use regorus::languages::azure_policy::{compiler, parser};
use regorus::Rc;
use regorus::Source;
with_unwind_guard(|| {
let result = || -> Result<RegorusProgram, (RegorusStatus, alloc::string::String)> {
let json_str = from_c_str(policy_definition_json).map_err(|e| {
(
RegorusStatus::InvalidDataFormat,
format!("Invalid policy definition JSON string: {e}"),
)
})?;
let source =
Source::from_contents("policy_definition".into(), json_str).map_err(|e| {
(
RegorusStatus::InvalidDataFormat,
format!("Failed to create source: {e}"),
)
})?;
let defn = parser::parse_policy_definition(&source).map_err(|e| {
(
RegorusStatus::InvalidPolicy,
format!("Failed to parse policy definition: {e}"),
)
})?;
let program = if registry.is_null() {
compiler::compile_policy_definition(&defn)
} else {
let reg: &RegorusAliasRegistry = to_shared_ref(registry).map_err(|e| {
(
RegorusStatus::InvalidArgument,
format!("Invalid alias registry: {e}"),
)
})?;
compiler::compile_policy_definition_with_aliases(&defn, reg.inner())
};
program
.map(|p| RegorusProgram {
program: Arc::new(Rc::try_unwrap(p).unwrap_or_else(|rc| (*rc).clone())),
})
.map_err(|e| {
(
RegorusStatus::CompilationFailed,
format!("Failed to compile policy definition: {e}"),
)
})
}();
match result {
Ok(program) => {
RegorusResult::ok_pointer(Box::into_raw(Box::new(program)) as *mut c_void)
}
Err((status, msg)) => RegorusResult::err_with_message(status, msg),
}
})
}
#[cfg(feature = "std")]
fn report_module_error(index: usize, kind: &str, err: &anyhow::Error) {
eprintln!("Invalid {} at index {}: {}", kind, index, err);
@@ -429,402 +215,3 @@ fn report_module_error(index: usize, kind: &str, err: &anyhow::Error) {
#[cfg(not(feature = "std"))]
fn report_module_error(_index: usize, _kind: &str, _err: &anyhow::Error) {}
// ---------------------------------------------------------------------------
// Tests
// ---------------------------------------------------------------------------
#[cfg(test)]
mod tests {
use super::*;
use crate::common::regorus_result_drop;
use core::ffi::CStr;
use std::ffi::CString;
fn c(s: &str) -> CString {
CString::new(s).expect("CString::new failed")
}
fn assert_ok_pointer(r: &RegorusResult) -> *mut c_void {
assert_eq!(
r.status,
RegorusStatus::Ok,
"expected Ok, got {:?}",
r.status
);
assert!(!r.pointer_value.is_null(), "expected non-null pointer");
r.pointer_value
}
#[cfg(all(feature = "azure_policy", feature = "rvm"))]
mod azure_policy_json {
use super::*;
use crate::alias_registry::regorus_alias_registry_drop;
use crate::rvm::{
regorus_program_drop, regorus_rvm_drop, regorus_rvm_execute_entry_point_by_name,
regorus_rvm_load_program, regorus_rvm_new, regorus_rvm_set_context,
regorus_rvm_set_input, RegorusProgram,
};
const ALIASES: &str = r#"[{
"namespace": "Microsoft.Storage",
"resourceTypes": [{
"resourceType": "storageAccounts",
"aliases": [{
"name": "Microsoft.Storage/storageAccounts/supportsHttpsTrafficOnly",
"defaultPath": "properties.supportsHttpsTrafficOnly",
"paths": []
}, {
"name": "Microsoft.Storage/storageAccounts/minimumTlsVersion",
"defaultPath": "properties.minimumTlsVersion",
"paths": []
}]
}]
}]"#;
const SIMPLE_POLICY_RULE: &str = r#"{
"if": {
"field": "type",
"equals": "Microsoft.Storage/storageAccounts"
},
"then": { "effect": "audit" }
}"#;
const ALIAS_POLICY_RULE: &str = r#"{
"if": {
"allOf": [
{ "field": "type", "equals": "Microsoft.Storage/storageAccounts" },
{ "field": "Microsoft.Storage/storageAccounts/supportsHttpsTrafficOnly", "equals": false }
]
},
"then": { "effect": "deny" }
}"#;
const POLICY_DEFINITION: &str = r#"{
"displayName": "Require HTTPS for storage accounts",
"policyType": "Custom",
"mode": "Indexed",
"parameters": {
"effect": {
"type": "String",
"defaultValue": "deny"
}
},
"policyRule": {
"if": {
"allOf": [
{ "field": "type", "equals": "Microsoft.Storage/storageAccounts" },
{ "field": "Microsoft.Storage/storageAccounts/supportsHttpsTrafficOnly", "equals": false }
]
},
"then": { "effect": "[parameters('effect')]" }
}
}"#;
/// Wrap a normalized resource JSON into the input envelope expected by
/// the compiled Azure Policy RVM program.
fn wrap_input(resource_json: &str, parameters_json: &str) -> String {
format!(r#"{{"resource": {resource_json}, "parameters": {parameters_json}}}"#)
}
fn build_registry_with_json(
json: &str,
) -> *mut crate::alias_registry::RegorusAliasRegistry {
let builder = crate::alias_registry::regorus_alias_registry_builder_new();
let json_c = c(json);
let r = crate::alias_registry::regorus_alias_registry_builder_load_json(
builder,
json_c.as_ptr(),
);
assert_eq!(r.status, RegorusStatus::Ok);
regorus_result_drop(r);
let r = crate::alias_registry::regorus_alias_registry_builder_build(builder);
let registry =
assert_ok_pointer(&r) as *mut crate::alias_registry::RegorusAliasRegistry;
regorus_result_drop(r);
crate::alias_registry::regorus_alias_registry_builder_drop(builder);
registry
}
/// Helper: compile a policy rule, execute it with input, and return the
/// result string.
unsafe fn compile_and_eval_rule(
registry: *const crate::alias_registry::RegorusAliasRegistry,
policy_rule: &str,
input_json: &str,
) -> String {
let rule_c = c(policy_rule);
let r = regorus_compile_azure_policy_rule(registry, rule_c.as_ptr());
let program_ptr = assert_ok_pointer(&r) as *mut RegorusProgram;
regorus_result_drop(r);
let vm = regorus_rvm_new();
assert!(!vm.is_null());
let r = regorus_rvm_load_program(vm, program_ptr);
assert_eq!(r.status, RegorusStatus::Ok);
regorus_result_drop(r);
let input_c = c(input_json);
let r = regorus_rvm_set_input(vm, input_c.as_ptr());
assert_eq!(r.status, RegorusStatus::Ok);
regorus_result_drop(r);
let entry = c("main");
let r = regorus_rvm_execute_entry_point_by_name(vm, entry.as_ptr());
assert_eq!(r.status, RegorusStatus::Ok, "execute failed");
let output = CStr::from_ptr(r.output)
.to_str()
.expect("invalid UTF-8")
.to_string();
regorus_result_drop(r);
regorus_rvm_drop(vm);
regorus_program_drop(program_ptr);
output
}
#[test]
fn compile_simple_rule_no_aliases() {
let rule_c = c(SIMPLE_POLICY_RULE);
let r = regorus_compile_azure_policy_rule(core::ptr::null_mut(), rule_c.as_ptr());
let ptr = assert_ok_pointer(&r);
regorus_result_drop(r);
regorus_program_drop(ptr as *mut RegorusProgram);
}
#[test]
fn compile_rule_with_aliases() {
let reg = build_registry_with_json(ALIASES);
let rule_c = c(ALIAS_POLICY_RULE);
let r = regorus_compile_azure_policy_rule(reg, rule_c.as_ptr());
let ptr = assert_ok_pointer(&r);
regorus_result_drop(r);
regorus_program_drop(ptr as *mut RegorusProgram);
regorus_alias_registry_drop(reg);
}
#[test]
fn compile_and_eval_simple_rule_matching() {
let input = wrap_input(r#"{"type":"microsoft.storage/storageaccounts"}"#, "{}");
let result =
unsafe { compile_and_eval_rule(core::ptr::null_mut(), SIMPLE_POLICY_RULE, &input) };
let parsed: serde_json::Value =
serde_json::from_str(&result).expect("result should be valid JSON");
assert_eq!(
parsed["effect"], "audit",
"expected audit effect, got: {result}"
);
}
#[test]
fn compile_and_eval_simple_rule_not_matching() {
let input = wrap_input(r#"{"type":"microsoft.compute/virtualmachines"}"#, "{}");
let result =
unsafe { compile_and_eval_rule(core::ptr::null_mut(), SIMPLE_POLICY_RULE, &input) };
// When the "if" condition doesn't match, the result should be undefined
assert!(
result.contains("undefined"),
"expected undefined for non-matching input, got: {result}"
);
}
#[test]
fn compile_and_eval_alias_rule_deny() {
let reg = build_registry_with_json(ALIASES);
// Non-compliant resource: HTTPS not enabled (normalized form)
let input = wrap_input(
r#"{"type": "microsoft.storage/storageaccounts", "supportshttpstrafficonly": false}"#,
"{}",
);
let result = unsafe { compile_and_eval_rule(reg, ALIAS_POLICY_RULE, &input) };
let parsed: serde_json::Value = serde_json::from_str(&result).expect("valid JSON");
assert_eq!(parsed["effect"], "deny", "expected deny, got: {result}");
regorus_alias_registry_drop(reg);
}
#[test]
fn compile_and_eval_alias_rule_compliant() {
let reg = build_registry_with_json(ALIASES);
// Compliant resource: HTTPS enabled (normalized form)
let input = wrap_input(
r#"{"type": "microsoft.storage/storageaccounts", "supportshttpstrafficonly": true}"#,
"{}",
);
let result = unsafe { compile_and_eval_rule(reg, ALIAS_POLICY_RULE, &input) };
assert!(
result.contains("undefined"),
"expected undefined for compliant resource, got: {result}"
);
regorus_alias_registry_drop(reg);
}
#[test]
fn compile_definition_no_aliases() {
let defn_c = c(POLICY_DEFINITION);
let r = regorus_compile_azure_policy_definition(core::ptr::null_mut(), defn_c.as_ptr());
let ptr = assert_ok_pointer(&r);
regorus_result_drop(r);
regorus_program_drop(ptr as *mut RegorusProgram);
}
#[test]
fn compile_definition_with_aliases_and_eval() {
let reg = build_registry_with_json(ALIASES);
let defn_c = c(POLICY_DEFINITION);
let r = regorus_compile_azure_policy_definition(reg, defn_c.as_ptr());
let program_ptr = assert_ok_pointer(&r) as *mut RegorusProgram;
regorus_result_drop(r);
// Evaluate with a non-compliant resource (normalized form, wrapped in envelope)
unsafe {
let vm = regorus_rvm_new();
let r = regorus_rvm_load_program(vm, program_ptr);
assert_eq!(r.status, RegorusStatus::Ok);
regorus_result_drop(r);
let input_json = wrap_input(
r#"{"type": "microsoft.storage/storageaccounts", "supportshttpstrafficonly": false}"#,
"{}",
);
let input = c(&input_json);
let r = regorus_rvm_set_input(vm, input.as_ptr());
assert_eq!(r.status, RegorusStatus::Ok);
regorus_result_drop(r);
let entry = c("main");
let r = regorus_rvm_execute_entry_point_by_name(vm, entry.as_ptr());
assert_eq!(r.status, RegorusStatus::Ok);
let result = CStr::from_ptr(r.output)
.to_str()
.expect("UTF-8")
.to_string();
regorus_result_drop(r);
let parsed: serde_json::Value = serde_json::from_str(&result).unwrap();
// The default parameter value is "deny"
assert_eq!(parsed["effect"], "deny", "got: {result}");
regorus_rvm_drop(vm);
regorus_program_drop(program_ptr);
}
regorus_alias_registry_drop(reg);
}
#[test]
fn invalid_json_returns_error() {
let bad = c("not valid json");
let r = regorus_compile_azure_policy_rule(core::ptr::null_mut(), bad.as_ptr());
assert_ne!(r.status, RegorusStatus::Ok);
regorus_result_drop(r);
}
#[test]
fn invalid_definition_returns_error() {
let bad = c(r#"{"not": "a policy definition"}"#);
let r = regorus_compile_azure_policy_definition(core::ptr::null_mut(), bad.as_ptr());
assert_ne!(r.status, RegorusStatus::Ok);
regorus_result_drop(r);
}
/// Policy rule that uses a context function (subscription()).
const CONTEXT_POLICY_RULE: &str = r#"{
"if": {
"allOf": [
{ "field": "type", "equals": "Microsoft.Storage/storageAccounts" },
{ "value": "[subscription().subscriptionId]", "equals": "sub-123" }
]
},
"then": { "effect": "deny" }
}"#;
#[test]
fn context_policy_evaluates_with_set_context() {
let rule_c = c(CONTEXT_POLICY_RULE);
let r = regorus_compile_azure_policy_rule(core::ptr::null_mut(), rule_c.as_ptr());
let program = assert_ok_pointer(&r) as *mut RegorusProgram;
regorus_result_drop(r);
let vm = regorus_rvm_new();
assert!(!vm.is_null());
let r = regorus_rvm_load_program(vm, program);
assert_eq!(r.status, RegorusStatus::Ok);
regorus_result_drop(r);
// Set the context with subscription info
let context = c(r#"{"subscription": {"subscriptionId": "sub-123"}}"#);
let r = regorus_rvm_set_context(vm, context.as_ptr());
assert_eq!(r.status, RegorusStatus::Ok);
regorus_result_drop(r);
// Set matching input
let input = c(&wrap_input(
r#"{"type": "microsoft.storage/storageaccounts"}"#,
"{}",
));
let r = regorus_rvm_set_input(vm, input.as_ptr());
assert_eq!(r.status, RegorusStatus::Ok);
regorus_result_drop(r);
let entry = c("main");
let r = regorus_rvm_execute_entry_point_by_name(vm, entry.as_ptr());
assert_eq!(r.status, RegorusStatus::Ok);
let output = unsafe { CStr::from_ptr(r.output) }.to_str().unwrap();
assert!(
output.contains("deny"),
"expected deny effect with matching context, got: {output}"
);
regorus_result_drop(r);
regorus_rvm_drop(vm);
regorus_program_drop(program);
}
#[test]
fn context_policy_undefined_without_context() {
let rule_c = c(CONTEXT_POLICY_RULE);
let r = regorus_compile_azure_policy_rule(core::ptr::null_mut(), rule_c.as_ptr());
let program = assert_ok_pointer(&r) as *mut RegorusProgram;
regorus_result_drop(r);
let vm = regorus_rvm_new();
assert!(!vm.is_null());
let r = regorus_rvm_load_program(vm, program);
assert_eq!(r.status, RegorusStatus::Ok);
regorus_result_drop(r);
// No context set — subscription() will be undefined
let input = c(&wrap_input(
r#"{"type": "microsoft.storage/storageaccounts"}"#,
"{}",
));
let r = regorus_rvm_set_input(vm, input.as_ptr());
assert_eq!(r.status, RegorusStatus::Ok);
regorus_result_drop(r);
let entry = c("main");
let r = regorus_rvm_execute_entry_point_by_name(vm, entry.as_ptr());
assert_eq!(r.status, RegorusStatus::Ok);
let output = unsafe { CStr::from_ptr(r.output) }.to_str().unwrap();
assert!(
output.contains("undefined"),
"expected undefined without context, got: {output}"
);
regorus_result_drop(r);
regorus_rvm_drop(vm);
regorus_program_drop(program);
}
}
}

View File

@@ -39,7 +39,7 @@ pub extern "C" fn regorus_compiled_policy_eval_with_input(
with_unwind_guard(|| {
let output = || -> Result<String> {
let input_value = regorus::Value::from_json_str(&from_c_str(input)?)?;
let result = to_shared_ref(compiled_policy as *const RegorusCompiledPolicy)?
let result = to_ref(compiled_policy)?
.compiled_policy
.eval_with_input(input_value)?;
result.to_json_str()
@@ -65,9 +65,7 @@ pub extern "C" fn regorus_compiled_policy_get_policy_info(
) -> RegorusResult {
with_unwind_guard(|| {
let output = || -> Result<String> {
let info = to_shared_ref(compiled_policy as *const RegorusCompiledPolicy)?
.compiled_policy
.get_policy_info()?;
let info = to_ref(compiled_policy)?.compiled_policy.get_policy_info()?;
serde_json::to_string(&info)
.map_err(|e| anyhow::anyhow!("Failed to serialize policy info: {}", e))
}();

View File

@@ -2,8 +2,7 @@
// Licensed under the MIT License.
use crate::common::{
from_c_str, to_ref, to_regorus_result, to_regorus_string_result, to_shared_ref, RegorusResult,
RegorusStatus,
from_c_str, to_ref, to_regorus_result, to_regorus_string_result, RegorusResult, RegorusStatus,
};
use crate::compiled_policy::RegorusCompiledPolicy;
use crate::limits::RegorusExecutionTimerConfig;
@@ -194,7 +193,7 @@ pub extern "C" fn regorus_engine_new() -> *mut RegorusEngine {
///
#[no_mangle]
pub extern "C" fn regorus_engine_clone(engine: *mut RegorusEngine) -> *mut RegorusEngine {
match to_shared_ref(engine as *const RegorusEngine) {
match to_ref(engine) {
Ok(e) => Box::into_raw(Box::new(e.clone())),
_ => ptr::null_mut(),
}
@@ -224,7 +223,7 @@ pub extern "C" fn regorus_engine_add_policy(
) -> RegorusResult {
with_unwind_guard(|| {
to_regorus_string_result(|| -> Result<String> {
let engine = to_shared_ref(engine as *const RegorusEngine)?;
let engine = to_ref(engine)?;
let mut guard = engine.try_write()?;
guard.add_policy(from_c_str(path)?, from_c_str(rego)?)
}())
@@ -239,7 +238,7 @@ pub extern "C" fn regorus_engine_add_policy_from_file(
) -> RegorusResult {
with_unwind_guard(|| {
to_regorus_string_result(|| -> Result<String> {
let engine = to_shared_ref(engine as *const RegorusEngine)?;
let engine = to_ref(engine)?;
let mut guard = engine.try_write()?;
guard.add_policy_from_file(from_c_str(path)?)
}())
@@ -257,7 +256,7 @@ pub extern "C" fn regorus_engine_add_data_json(
) -> RegorusResult {
with_unwind_guard(|| {
to_regorus_result(|| -> Result<()> {
let engine = to_shared_ref(engine as *const RegorusEngine)?;
let engine = to_ref(engine)?;
let mut guard = engine.try_write()?;
guard.add_data(regorus::Value::from_json_str(&from_c_str(data)?)?)
}())
@@ -271,7 +270,7 @@ pub extern "C" fn regorus_engine_add_data_json(
pub extern "C" fn regorus_engine_get_packages(engine: *mut RegorusEngine) -> RegorusResult {
with_unwind_guard(|| {
to_regorus_string_result(|| -> Result<String> {
let engine = to_shared_ref(engine as *const RegorusEngine)?;
let engine = to_ref(engine)?;
let guard = engine.try_read()?;
serde_json::to_string_pretty(&guard.get_packages()?).map_err(anyhow::Error::msg)
}())
@@ -285,7 +284,7 @@ pub extern "C" fn regorus_engine_get_packages(engine: *mut RegorusEngine) -> Reg
pub extern "C" fn regorus_engine_get_policies(engine: *mut RegorusEngine) -> RegorusResult {
with_unwind_guard(|| {
to_regorus_string_result(|| -> Result<String> {
let engine = to_shared_ref(engine as *const RegorusEngine)?;
let engine = to_ref(engine)?;
let guard = engine.try_read()?;
guard.get_policies_as_json()
}())
@@ -300,7 +299,7 @@ pub extern "C" fn regorus_engine_add_data_from_json_file(
) -> RegorusResult {
with_unwind_guard(|| {
to_regorus_result(|| -> Result<()> {
let engine = to_shared_ref(engine as *const RegorusEngine)?;
let engine = to_ref(engine)?;
let mut guard = engine.try_write()?;
guard.add_data(regorus::Value::from_json_file(from_c_str(path)?)?)
}())
@@ -314,7 +313,7 @@ pub extern "C" fn regorus_engine_add_data_from_json_file(
pub extern "C" fn regorus_engine_clear_data(engine: *mut RegorusEngine) -> RegorusResult {
with_unwind_guard(|| {
to_regorus_result(|| -> Result<()> {
let engine = to_shared_ref(engine as *const RegorusEngine)?;
let engine = to_ref(engine)?;
let mut guard = engine.try_write()?;
guard.clear_data();
Ok(())
@@ -333,7 +332,7 @@ pub extern "C" fn regorus_engine_set_input_json(
) -> RegorusResult {
with_unwind_guard(|| {
to_regorus_result(|| -> Result<()> {
let engine = to_shared_ref(engine as *const RegorusEngine)?;
let engine = to_ref(engine)?;
let mut guard = engine.try_write()?;
guard.set_input(regorus::Value::from_json_str(&from_c_str(input)?)?);
Ok(())
@@ -349,7 +348,7 @@ pub extern "C" fn regorus_engine_set_input_from_json_file(
) -> RegorusResult {
with_unwind_guard(|| {
to_regorus_result(|| -> Result<()> {
let engine = to_shared_ref(engine as *const RegorusEngine)?;
let engine = to_ref(engine)?;
let mut guard = engine.try_write()?;
guard.set_input(regorus::Value::from_json_file(from_c_str(path)?)?);
Ok(())
@@ -368,7 +367,7 @@ pub extern "C" fn regorus_engine_eval_query(
) -> RegorusResult {
with_unwind_guard(|| {
let output = || -> Result<String> {
let engine = to_shared_ref(engine as *const RegorusEngine)?;
let engine = to_ref(engine)?;
let mut guard = engine.try_write()?;
let results = guard.eval_query(from_c_str(query)?, false)?;
Ok(serde_json::to_string_pretty(&results)?)
@@ -391,7 +390,7 @@ pub extern "C" fn regorus_engine_eval_rule(
) -> RegorusResult {
with_unwind_guard(|| {
let output = || -> Result<String> {
let engine = to_shared_ref(engine as *const RegorusEngine)?;
let engine = to_ref(engine)?;
let mut guard = engine.try_write()?;
guard.eval_rule(from_c_str(rule)?)?.to_json_str()
}();
@@ -414,7 +413,7 @@ pub extern "C" fn regorus_engine_set_enable_coverage(
) -> RegorusResult {
with_unwind_guard(|| {
to_regorus_result(|| -> Result<()> {
let engine = to_shared_ref(engine as *const RegorusEngine)?;
let engine = to_ref(engine)?;
let mut guard = engine.try_write()?;
guard.set_enable_coverage(enable);
Ok(())
@@ -430,7 +429,7 @@ pub extern "C" fn regorus_engine_set_enable_coverage(
pub extern "C" fn regorus_engine_get_coverage_report(engine: *mut RegorusEngine) -> RegorusResult {
with_unwind_guard(|| {
let output = || -> Result<String> {
let engine = to_shared_ref(engine as *const RegorusEngine)?;
let engine = to_ref(engine)?;
let guard = engine.try_read()?;
Ok(serde_json::to_string_pretty(&guard.get_coverage_report()?)?)
}();
@@ -452,7 +451,7 @@ pub extern "C" fn regorus_engine_set_strict_builtin_errors(
) -> RegorusResult {
with_unwind_guard(|| {
to_regorus_result(|| -> Result<()> {
let engine = to_shared_ref(engine as *const RegorusEngine)?;
let engine = to_ref(engine)?;
let mut guard = engine.try_write()?;
guard.set_strict_builtin_errors(strict);
Ok(())
@@ -466,20 +465,18 @@ pub extern "C" fn regorus_engine_set_execution_timer_config(
engine: *mut RegorusEngine,
config: *const RegorusExecutionTimerConfig,
) -> RegorusResult {
with_unwind_guard(|| {
to_regorus_result(|| -> Result<()> {
let engine = to_shared_ref(engine as *const RegorusEngine)?;
let config = unsafe {
config
.as_ref()
.copied()
.ok_or_else(|| anyhow!("execution timer config pointer is null"))?
};
let mut guard = engine.try_write()?;
guard.set_execution_timer_config(config.to_execution_timer_config()?);
Ok(())
}())
})
to_regorus_result(|| -> Result<()> {
let engine = to_ref(engine)?;
let config = unsafe {
config
.as_ref()
.copied()
.ok_or_else(|| anyhow!("execution timer config pointer is null"))?
};
let mut guard = engine.try_write()?;
guard.set_execution_timer_config(config.to_execution_timer_config()?);
Ok(())
}())
}
#[no_mangle]
@@ -487,14 +484,12 @@ pub extern "C" fn regorus_engine_set_execution_timer_config(
pub extern "C" fn regorus_engine_clear_execution_timer_config(
engine: *mut RegorusEngine,
) -> RegorusResult {
with_unwind_guard(|| {
to_regorus_result(|| -> Result<()> {
let engine = to_shared_ref(engine as *const RegorusEngine)?;
let mut guard = engine.try_write()?;
guard.clear_execution_timer_config();
Ok(())
}())
})
to_regorus_result(|| -> Result<()> {
let engine = to_ref(engine)?;
let mut guard = engine.try_write()?;
guard.clear_execution_timer_config();
Ok(())
}())
}
/// Set the policy length limits used when loading policies.
@@ -505,7 +500,7 @@ pub extern "C" fn regorus_engine_set_policy_length_config(
) -> RegorusResult {
with_unwind_guard(|| {
to_regorus_result(|| -> Result<()> {
let engine = to_shared_ref(engine as *const RegorusEngine)?;
let engine = to_ref(engine)?;
let mut guard = engine.try_write()?;
guard.set_policy_length_config(config.to_policy_length_config()?);
Ok(())
@@ -520,7 +515,7 @@ pub extern "C" fn regorus_engine_clear_policy_length_config(
) -> RegorusResult {
with_unwind_guard(|| {
to_regorus_result(|| -> Result<()> {
let engine = to_shared_ref(engine as *const RegorusEngine)?;
let engine = to_ref(engine)?;
let mut guard = engine.try_write()?;
guard.clear_policy_length_config();
Ok(())
@@ -538,7 +533,7 @@ pub extern "C" fn regorus_engine_get_coverage_report_pretty(
) -> RegorusResult {
with_unwind_guard(|| {
let output = || -> Result<String> {
let engine = to_shared_ref(engine as *const RegorusEngine)?;
let engine = to_ref(engine)?;
let guard = engine.try_read()?;
guard.get_coverage_report()?.to_string_pretty()
}();
@@ -557,7 +552,7 @@ pub extern "C" fn regorus_engine_get_coverage_report_pretty(
pub extern "C" fn regorus_engine_clear_coverage_data(engine: *mut RegorusEngine) -> RegorusResult {
with_unwind_guard(|| {
to_regorus_result(|| -> Result<()> {
let engine = to_shared_ref(engine as *const RegorusEngine)?;
let engine = to_ref(engine)?;
let mut guard = engine.try_write()?;
guard.clear_coverage_data();
Ok(())
@@ -576,7 +571,7 @@ pub extern "C" fn regorus_engine_set_gather_prints(
) -> RegorusResult {
with_unwind_guard(|| {
to_regorus_result(|| -> Result<()> {
let engine = to_shared_ref(engine as *const RegorusEngine)?;
let engine = to_ref(engine)?;
let mut guard = engine.try_write()?;
guard.set_gather_prints(enable);
Ok(())
@@ -591,7 +586,7 @@ pub extern "C" fn regorus_engine_set_gather_prints(
pub extern "C" fn regorus_engine_take_prints(engine: *mut RegorusEngine) -> RegorusResult {
with_unwind_guard(|| {
let output = || -> Result<String> {
let engine = to_shared_ref(engine as *const RegorusEngine)?;
let engine = to_ref(engine)?;
let mut guard = engine.try_write()?;
Ok(serde_json::to_string_pretty(&guard.take_prints()?)?)
}();
@@ -610,7 +605,7 @@ pub extern "C" fn regorus_engine_take_prints(engine: *mut RegorusEngine) -> Rego
pub extern "C" fn regorus_engine_get_ast_as_json(engine: *mut RegorusEngine) -> RegorusResult {
with_unwind_guard(|| {
let output = || -> Result<String> {
let engine = to_shared_ref(engine as *const RegorusEngine)?;
let engine = to_ref(engine)?;
let guard = engine.try_read()?;
guard.get_ast_as_json()
}();
@@ -631,7 +626,7 @@ pub extern "C" fn regorus_engine_get_policy_package_names(
) -> RegorusResult {
with_unwind_guard(|| {
let output = || -> Result<String> {
let engine = to_shared_ref(engine as *const RegorusEngine)?;
let engine = to_ref(engine)?;
let guard = engine.try_read()?;
serde_json::to_string_pretty(&guard.get_policy_package_names()?)
.map_err(anyhow::Error::msg)
@@ -653,7 +648,7 @@ pub extern "C" fn regorus_engine_get_policy_parameters(
) -> RegorusResult {
with_unwind_guard(|| {
let output = || -> Result<String> {
let engine = to_shared_ref(engine as *const RegorusEngine)?;
let engine = to_ref(engine)?;
let guard = engine.try_read()?;
serde_json::to_string_pretty(&guard.get_policy_parameters()?)
.map_err(anyhow::Error::msg)
@@ -675,7 +670,7 @@ pub extern "C" fn regorus_engine_set_rego_v0(
) -> RegorusResult {
with_unwind_guard(|| {
let output = || -> Result<()> {
let engine = to_shared_ref(engine as *const RegorusEngine)?;
let engine = to_ref(engine)?;
let mut guard = engine.try_write()?;
guard.set_rego_v0(enable);
Ok(())
@@ -697,7 +692,7 @@ pub extern "C" fn regorus_engine_set_rego_v0(
#[cfg(feature = "azure_policy")]
pub extern "C" fn regorus_engine_compile_for_target(engine: *mut RegorusEngine) -> RegorusResult {
with_unwind_guard(|| {
let engine = match to_shared_ref(engine as *const RegorusEngine) {
let engine = match to_ref(engine) {
Ok(engine) => engine,
Err(e) => {
return RegorusResult::err_with_message(
@@ -746,7 +741,7 @@ pub extern "C" fn regorus_engine_compile_with_entrypoint(
let result = || -> Result<RegorusCompiledPolicy> {
let rule_str = from_c_str(rule)?;
let rule_rc: regorus::Rc<str> = rule_str.into();
let engine = to_shared_ref(engine as *const RegorusEngine)?;
let engine = to_ref(engine)?;
let mut guard = engine.try_write()?;
let compiled_policy = guard.compile_with_entrypoint(&rule_rc)?;
Ok(RegorusCompiledPolicy { compiled_policy })
@@ -805,7 +800,7 @@ pub extern "C" fn regorus_engine_compile_program_with_entrypoints(
.ok_or_else(|| anyhow!("entry_points must contain at least one entry"))?;
let rule_rc: regorus::Rc<str> = (*rule).into();
let engine = to_shared_ref(engine as *const RegorusEngine)?;
let engine = to_ref(engine)?;
let mut guard = engine.try_write()?;
let compiled_policy = guard.compile_with_entrypoint(&rule_rc)?;

View File

@@ -2,8 +2,7 @@
// Licensed under the MIT License.
use crate::common::{
from_c_str, to_ref, to_regorus_result, to_shared_ref, RegorusBuffer, RegorusResult,
RegorusStatus,
from_c_str, to_ref, to_regorus_result, RegorusBuffer, RegorusResult, RegorusStatus,
};
use crate::compile::RegorusPolicyModule;
use crate::compiled_policy::RegorusCompiledPolicy;
@@ -107,8 +106,7 @@ pub extern "C" fn regorus_program_compile_from_policy(
let entry_points_ref: Vec<&str> = entry_points_vec.iter().map(|s| s.as_str()).collect();
let compiled_policy =
&to_shared_ref(compiled_policy as *const RegorusCompiledPolicy)?.compiled_policy;
let compiled_policy = &to_ref(compiled_policy)?.compiled_policy;
let program = Compiler::compile_from_policy(compiled_policy, &entry_points_ref)?;
Ok(Box::into_raw(Box::new(RegorusProgram { program })))
}();
@@ -189,7 +187,7 @@ pub extern "C" fn regorus_program_new() -> *mut RegorusProgram {
pub extern "C" fn regorus_program_serialize_binary(program: *mut RegorusProgram) -> RegorusResult {
with_unwind_guard(|| {
let output = || -> Result<*mut RegorusBuffer> {
let program = &to_shared_ref(program as *const RegorusProgram)?.program;
let program = &to_ref(program)?.program;
let bytes = program.serialize_binary().map_err(|e| anyhow!(e))?;
Ok(RegorusBuffer::from_vec(bytes))
}();
@@ -213,10 +211,7 @@ pub extern "C" fn regorus_program_deserialize_binary(
) -> RegorusResult {
with_unwind_guard(|| {
let output = || -> Result<(*mut RegorusProgram, bool)> {
if data.is_null() {
if len > 0 {
return Err(anyhow!("null data pointer with non-zero length"));
}
if data.is_null() && len > 0 {
return Err(anyhow!("null data pointer"));
}
let data = unsafe { core::slice::from_raw_parts(data, len) };
@@ -254,7 +249,7 @@ pub extern "C" fn regorus_program_deserialize_binary(
pub extern "C" fn regorus_program_generate_listing(program: *mut RegorusProgram) -> RegorusResult {
with_unwind_guard(|| {
let output = || -> Result<String> {
let program = &to_shared_ref(program as *const RegorusProgram)?.program;
let program = &to_ref(program)?.program;
Ok(generate_assembly_listing(
program,
&AssemblyListingConfig::default(),
@@ -275,7 +270,7 @@ pub extern "C" fn regorus_program_generate_tabular_listing(
) -> RegorusResult {
with_unwind_guard(|| {
let output = || -> Result<String> {
let program = &to_shared_ref(program as *const RegorusProgram)?.program;
let program = &to_ref(program)?.program;
Ok(generate_tabular_assembly_listing(
program,
&AssemblyListingConfig::default(),
@@ -302,9 +297,7 @@ pub extern "C" fn regorus_rvm_new_with_policy(
) -> RegorusResult {
with_unwind_guard(|| {
let output = || -> Result<*mut RegorusRvm> {
let policy = to_shared_ref(compiled_policy as *const RegorusCompiledPolicy)?
.compiled_policy
.clone();
let policy = to_ref(compiled_policy)?.compiled_policy.clone();
Ok(Box::into_raw(Box::new(RegorusRvm::new(
RegoVM::new_with_policy(policy),
))))
@@ -325,11 +318,9 @@ pub extern "C" fn regorus_rvm_load_program(
) -> RegorusResult {
with_unwind_guard(|| {
to_regorus_result(|| -> Result<()> {
let vm = to_shared_ref(vm as *const RegorusRvm)?;
let vm = to_ref(vm)?;
let mut guard = vm.try_write()?;
let program = to_shared_ref(program as *const RegorusProgram)?
.program
.clone();
let program = to_ref(program)?.program.clone();
guard.load_program(program);
Ok(())
}())
@@ -341,7 +332,7 @@ pub extern "C" fn regorus_rvm_load_program(
pub extern "C" fn regorus_rvm_set_data(vm: *mut RegorusRvm, data: *const c_char) -> RegorusResult {
with_unwind_guard(|| {
to_regorus_result(|| -> Result<()> {
let vm = to_shared_ref(vm as *const RegorusRvm)?;
let vm = to_ref(vm)?;
let mut guard = vm.try_write()?;
let data_value = Value::from_json_str(&from_c_str(data)?)?;
guard.set_data(data_value)?;
@@ -358,7 +349,7 @@ pub extern "C" fn regorus_rvm_set_input(
) -> RegorusResult {
with_unwind_guard(|| {
to_regorus_result(|| -> Result<()> {
let vm = to_shared_ref(vm as *const RegorusRvm)?;
let vm = to_ref(vm)?;
let mut guard = vm.try_write()?;
let input_value = Value::from_json_str(&from_c_str(input)?)?;
guard.set_input(input_value);
@@ -367,33 +358,6 @@ pub extern "C" fn regorus_rvm_set_input(
})
}
/// Set the VM context document from JSON.
///
/// The context provides host-supplied ambient data (e.g. `resourceGroup()`,
/// `subscription()`) that Azure Policy functions can access via `LoadContext`
/// instructions. This must be called before `regorus_rvm_execute` when
/// evaluating policies that reference context functions.
///
/// # Safety
/// - `vm` must be a valid pointer to a `RegorusRvm` created by `regorus_rvm_new`.
/// - `context_json` must be a valid null-terminated UTF-8 string.
#[cfg(feature = "azure_policy")]
#[no_mangle]
pub extern "C" fn regorus_rvm_set_context(
vm: *mut RegorusRvm,
context_json: *const c_char,
) -> RegorusResult {
with_unwind_guard(|| {
to_regorus_result(|| -> Result<()> {
let vm = to_shared_ref(vm as *const RegorusRvm)?;
let mut guard = vm.try_write()?;
let context_value = Value::from_json_str(&from_c_str(context_json)?)?;
guard.set_context(context_value);
Ok(())
}())
})
}
/// Set the maximum number of instructions that can execute.
#[no_mangle]
pub extern "C" fn regorus_rvm_set_max_instructions(
@@ -402,7 +366,7 @@ pub extern "C" fn regorus_rvm_set_max_instructions(
) -> RegorusResult {
with_unwind_guard(|| {
to_regorus_result(|| -> Result<()> {
let vm = to_shared_ref(vm as *const RegorusRvm)?;
let vm = to_ref(vm)?;
let mut guard = vm.try_write()?;
guard.set_max_instructions(max_instructions);
Ok(())
@@ -418,7 +382,7 @@ pub extern "C" fn regorus_rvm_set_strict_builtin_errors(
) -> RegorusResult {
with_unwind_guard(|| {
to_regorus_result(|| -> Result<()> {
let vm = to_shared_ref(vm as *const RegorusRvm)?;
let vm = to_ref(vm)?;
let mut guard = vm.try_write()?;
guard.set_strict_builtin_errors(strict);
Ok(())
@@ -431,7 +395,7 @@ pub extern "C" fn regorus_rvm_set_strict_builtin_errors(
pub extern "C" fn regorus_rvm_set_execution_mode(vm: *mut RegorusRvm, mode: u8) -> RegorusResult {
with_unwind_guard(|| {
to_regorus_result(|| -> Result<()> {
let vm = to_shared_ref(vm as *const RegorusRvm)?;
let vm = to_ref(vm)?;
let mut guard = vm.try_write()?;
let mode = match mode {
0 => ExecutionMode::RunToCompletion,
@@ -449,7 +413,7 @@ pub extern "C" fn regorus_rvm_set_execution_mode(vm: *mut RegorusRvm, mode: u8)
pub extern "C" fn regorus_rvm_set_step_mode(vm: *mut RegorusRvm, enabled: bool) -> RegorusResult {
with_unwind_guard(|| {
to_regorus_result(|| -> Result<()> {
let vm = to_shared_ref(vm as *const RegorusRvm)?;
let vm = to_ref(vm)?;
let mut guard = vm.try_write()?;
guard.set_step_mode(enabled);
Ok(())
@@ -466,7 +430,7 @@ pub extern "C" fn regorus_rvm_set_execution_timer_config(
) -> RegorusResult {
with_unwind_guard(|| {
to_regorus_result(|| -> Result<()> {
let vm = to_shared_ref(vm as *const RegorusRvm)?;
let vm = to_ref(vm)?;
let mut guard = vm.try_write()?;
if has_config {
guard.set_execution_timer_config(Some(config.to_execution_timer_config()?));
@@ -483,7 +447,7 @@ pub extern "C" fn regorus_rvm_set_execution_timer_config(
pub extern "C" fn regorus_rvm_execute(vm: *mut RegorusRvm) -> RegorusResult {
with_unwind_guard(|| {
let output = || -> Result<String> {
let vm = to_shared_ref(vm as *const RegorusRvm)?;
let vm = to_ref(vm)?;
let mut guard = vm.try_write()?;
let result = guard.execute()?;
result.to_json_str()
@@ -504,7 +468,7 @@ pub extern "C" fn regorus_rvm_execute_entry_point_by_name(
) -> RegorusResult {
with_unwind_guard(|| {
let output = || -> Result<String> {
let vm = to_shared_ref(vm as *const RegorusRvm)?;
let vm = to_ref(vm)?;
let mut guard = vm.try_write()?;
let name = from_c_str(entry_point)?;
let result = guard.execute_entry_point_by_name(&name)?;
@@ -526,7 +490,7 @@ pub extern "C" fn regorus_rvm_execute_entry_point_by_index(
) -> RegorusResult {
with_unwind_guard(|| {
let output = || -> Result<String> {
let vm = to_shared_ref(vm as *const RegorusRvm)?;
let vm = to_ref(vm)?;
let mut guard = vm.try_write()?;
let result = guard.execute_entry_point_by_index(index)?;
result.to_json_str()
@@ -548,7 +512,7 @@ pub extern "C" fn regorus_rvm_resume(
) -> RegorusResult {
with_unwind_guard(|| {
let output = || -> Result<String> {
let vm = to_shared_ref(vm as *const RegorusRvm)?;
let vm = to_ref(vm)?;
let mut guard = vm.try_write()?;
let value = if has_value {
Some(Value::from_json_str(&from_c_str(resume_value_json)?)?)
@@ -571,7 +535,7 @@ pub extern "C" fn regorus_rvm_resume(
pub extern "C" fn regorus_rvm_get_execution_state(vm: *mut RegorusRvm) -> RegorusResult {
with_unwind_guard(|| {
let output = || -> Result<String> {
let vm = to_shared_ref(vm as *const RegorusRvm)?;
let vm = to_ref(vm)?;
let guard = vm.try_read()?;
let state: ExecutionState = guard.execution_state().clone();
Ok(format!("{:?}", state))

429
bindings/java/Cargo.lock generated
View File

@@ -42,15 +42,15 @@ dependencies = [
[[package]]
name = "anyhow"
version = "1.0.103"
version = "1.0.102"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a4385e2e34eb35d6b3efe798b9eb88096925d87726c0798709bf56d9ed84af3"
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
[[package]]
name = "autocfg"
version = "1.5.1"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
[[package]]
name = "bit-set"
@@ -69,9 +69,9 @@ checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7"
[[package]]
name = "bitflags"
version = "2.13.1"
version = "2.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da"
checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3"
[[package]]
name = "borrow-or-share"
@@ -81,19 +81,19 @@ checksum = "dc0b364ead1874514c8c2855ab558056ebfeb775653e7ae45ff72f28f8f3166c"
[[package]]
name = "bstr"
version = "1.13.0"
version = "1.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f7dc094d718f2e1c1559ad110e27eeaae14a5465d3d56dd6dbd793079fbd530"
checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab"
dependencies = [
"memchr",
"serde_core",
"serde",
]
[[package]]
name = "bumpalo"
version = "3.20.3"
version = "3.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649"
checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb"
[[package]]
name = "bytecount"
@@ -103,15 +103,15 @@ checksum = "175812e0be2bccb6abe50bb8d566126198344f707e304f45c648fd8f2cc0365e"
[[package]]
name = "bytes"
version = "1.12.1"
version = "1.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04"
checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33"
[[package]]
name = "cc"
version = "1.2.67"
version = "1.2.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e17dd265a7d0f31ef544e1b20e03add05d3b45b491b633b10d67145d2acc1a38"
checksum = "d16d90359e986641506914ba71350897565610e87ce0ad9e6f28569db3dd5c6d"
dependencies = [
"find-msvc-tools",
"shlex",
@@ -125,9 +125,9 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "chacha20"
version = "0.10.1"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d524456ba66e72eb8b115ff89e01e497f8e6d11d78b70b1aa13c0fbd97540a81"
checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601"
dependencies = [
"cfg-if",
"cpufeatures",
@@ -136,9 +136,9 @@ dependencies = [
[[package]]
name = "chrono"
version = "0.4.45"
version = "0.4.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327"
checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0"
dependencies = [
"iana-time-zone",
"js-sys",
@@ -199,9 +199,9 @@ checksum = "a4ae5f15dda3c708c0ade84bfee31ccab44a3da4f88015ed22f63732abe300c8"
[[package]]
name = "displaydoc"
version = "0.2.6"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f"
checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
dependencies = [
"proc-macro2",
"quote",
@@ -237,9 +237,9 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "fancy-regex"
version = "0.18.0"
version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1e1dacd0d2082dfcf1351c4bdd566bbe89a2b263235a2b50058f1e130a47277"
checksum = "72cf461f865c862bb7dc573f643dd6a2b6842f7c30b07882b56bd148cc2761b8"
dependencies = [
"bit-set",
"regex-automata",
@@ -263,6 +263,12 @@ dependencies = [
"serde",
]
[[package]]
name = "foldhash"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
[[package]]
name = "foldhash"
version = "0.2.0"
@@ -328,21 +334,23 @@ dependencies = [
[[package]]
name = "getrandom"
version = "0.4.3"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099"
checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555"
dependencies = [
"cfg-if",
"libc",
"r-efi 6.0.0",
"rand_core",
"wasip2",
"wasip3",
]
[[package]]
name = "globset"
version = "0.4.19"
version = "0.4.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e47d37d2ae4464254884b60ab7071be2b876a9c35b696bd018ddcc76847309cd"
checksum = "52dfc19153a48bde0cbd630453615c8151bce3a5adfac7a0aebfbf0a1e1f57e3"
dependencies = [
"aho-corasick",
"bstr",
@@ -352,15 +360,36 @@ dependencies = [
[[package]]
name = "hashbrown"
version = "0.17.1"
version = "0.15.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
dependencies = [
"foldhash 0.1.5",
]
[[package]]
name = "hashbrown"
version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
dependencies = [
"allocator-api2",
"equivalent",
"foldhash",
"foldhash 0.2.0",
]
[[package]]
name = "hashbrown"
version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51"
[[package]]
name = "heck"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "iana-time-zone"
version = "0.1.65"
@@ -467,6 +496,12 @@ dependencies = [
"zerovec",
]
[[package]]
name = "id-arena"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954"
[[package]]
name = "idna"
version = "1.1.0"
@@ -495,7 +530,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9"
dependencies = [
"equivalent",
"hashbrown",
"hashbrown 0.17.0",
"serde",
"serde_core",
]
@@ -563,20 +598,21 @@ dependencies = [
[[package]]
name = "js-sys"
version = "0.3.103"
version = "0.3.97"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53b44bfcdb3f8d5837a46dae1ca9660a837176eee74a28b229bc626816589102"
checksum = "a1840c94c045fbcf8ba2812c95db44499f7c64910a912551aaaa541decebcacf"
dependencies = [
"cfg-if",
"futures-util",
"once_cell",
"wasm-bindgen",
]
[[package]]
name = "jsonschema"
version = "0.47.0"
version = "0.45.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "281c43ff06dcb331e9356d30e38853d559ce3d0a3f693e0b0e102667dec14fb1"
checksum = "257eb0e588b76827bbddc9e73945a9743693dd2adeaee9da26420f93cfedb798"
dependencies = [
"ahash",
"bytecount",
@@ -587,33 +623,30 @@ dependencies = [
"getrandom 0.3.4",
"idna",
"itoa",
"jsonschema-regex",
"num-cmp",
"num-traits",
"percent-encoding",
"referencing",
"regex",
"regex-syntax",
"serde",
"serde_json",
"unicode-general-category",
"uuid-simd",
]
[[package]]
name = "jsonschema-regex"
version = "0.47.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee0b351864e7ffbc5db9273daf7fa1b4d5177b0946713d667ca571b83c0b4045"
dependencies = [
"regex-syntax",
]
[[package]]
name = "lazy_static"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]]
name = "leb128fmt"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
[[package]]
name = "libc"
version = "0.2.186"
@@ -637,27 +670,21 @@ dependencies = [
[[package]]
name = "log"
version = "0.4.33"
version = "0.4.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad"
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
[[package]]
name = "lru"
version = "0.18.1"
version = "0.16.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b6180140927ee907000b0aa540091f6ea512ead4447c92b8fc35bc72788a5a6"
checksum = "7f66e8d5d03f609abc3a39e6f08e4164ebf1447a732906d39eb9b99b7919ef39"
[[package]]
name = "memchr"
version = "2.8.3"
version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
[[package]]
name = "micromap"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2a86d3146ed3995b5913c414f6664344b9617457320782e64f0bb44afd49d74"
checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
[[package]]
name = "msvc_spectre_libs"
@@ -674,7 +701,7 @@ version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23"
dependencies = [
"num-bigint 0.4.8",
"num-bigint",
"num-complex",
"num-integer",
"num-iter",
@@ -684,19 +711,9 @@ dependencies = [
[[package]]
name = "num-bigint"
version = "0.4.8"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c89e69e7e0f03bea5ef08013795c25018e101932225a656383bd384495ecc367"
dependencies = [
"num-integer",
"num-traits",
]
[[package]]
name = "num-bigint"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93e7820bc0a80a0238e650327316f929ba18d5be054b647490a3a6a339f3e7c0"
checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
dependencies = [
"num-integer",
"num-traits",
@@ -728,10 +745,11 @@ dependencies = [
[[package]]
name = "num-iter"
version = "0.1.46"
version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c92800bd69a1eac91786bcfe9da64a897eb72911b8dc3095decbd07429e8048b"
checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
]
@@ -742,7 +760,7 @@ version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824"
dependencies = [
"num-bigint 0.4.8",
"num-bigint",
"num-integer",
"num-traits",
]
@@ -842,6 +860,16 @@ dependencies = [
"zerovec",
]
[[package]]
name = "prettyplease"
version = "0.2.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
dependencies = [
"proc-macro2",
"syn",
]
[[package]]
name = "proc-macro2"
version = "1.0.106"
@@ -853,9 +881,9 @@ dependencies = [
[[package]]
name = "quote"
version = "1.0.46"
version = "1.0.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368"
checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
dependencies = [
"proc-macro2",
]
@@ -874,12 +902,12 @@ checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
[[package]]
name = "rand"
version = "0.10.2"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7f5fa3a058cd35567ef9bfa5e75732bee0f9e4c55fa90477bef2dfcdbc4be80"
checksum = "d2e8e8bcc7961af1fdac401278c6a831614941f6164ee3bf4ce61b7edb162207"
dependencies = [
"chacha20",
"getrandom 0.4.3",
"getrandom 0.4.2",
"rand_core",
]
@@ -920,16 +948,14 @@ dependencies = [
[[package]]
name = "referencing"
version = "0.47.0"
version = "0.45.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "348e860aeb0b7bd035778fd11dd9cd5290d32e4aed3b8f2274a00287a9fd362b"
checksum = "e2f38748ceca8d0b0013e60f534d94a6e23dfd89fd2a88318fc5a2d04fda1010"
dependencies = [
"ahash",
"fluent-uri",
"getrandom 0.3.4",
"hashbrown",
"itoa",
"micromap",
"hashbrown 0.16.1",
"parking_lot",
"percent-encoding",
"serde_json",
@@ -937,9 +963,9 @@ dependencies = [
[[package]]
name = "regex"
version = "1.13.1"
version = "1.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d"
checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276"
dependencies = [
"aho-corasick",
"memchr",
@@ -949,9 +975,9 @@ dependencies = [
[[package]]
name = "regex-automata"
version = "0.4.16"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fcfdb36bda0c880c5931cdc7a2bcdc8ba4556847b9d912bca70bc94708711ad"
checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f"
dependencies = [
"aho-corasick",
"memchr",
@@ -960,13 +986,13 @@ dependencies = [
[[package]]
name = "regex-syntax"
version = "0.8.11"
version = "0.8.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4"
checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a"
[[package]]
name = "regorus"
version = "0.11.0"
version = "0.10.0"
dependencies = [
"anyhow",
"chrono",
@@ -979,7 +1005,7 @@ dependencies = [
"lazy_static",
"lru",
"msvc_spectre_libs",
"num-bigint 0.5.1",
"num-bigint",
"num-traits",
"parking_lot",
"postcard",
@@ -998,7 +1024,7 @@ dependencies = [
[[package]]
name = "regorus-java"
version = "0.11.0"
version = "0.10.0"
dependencies = [
"anyhow",
"jni",
@@ -1008,7 +1034,7 @@ dependencies = [
[[package]]
name = "regorus-mimalloc"
version = "2.2.7"
version = "2.2.6"
dependencies = [
"regorus-mimalloc-sys",
]
@@ -1031,9 +1057,9 @@ dependencies = [
[[package]]
name = "rustversion"
version = "1.0.23"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f"
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
[[package]]
name = "ryu"
@@ -1094,9 +1120,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.150"
version = "1.0.149"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9"
checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86"
dependencies = [
"itoa",
"memchr",
@@ -1120,15 +1146,15 @@ dependencies = [
[[package]]
name = "shlex"
version = "2.0.1"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "simd_cesu8"
version = "1.2.0"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11031e251abf8611c80f460e19dbdeb54a66db918e49c65a7065b46ac7aec520"
checksum = "94f90157bb87cddf702797c5dadfa0be7d266cdf49e22da2fcaa32eff75b2c33"
dependencies = [
"rustc_version",
"simdutf8",
@@ -1142,9 +1168,9 @@ checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e"
[[package]]
name = "siphasher"
version = "1.0.3"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649"
checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e"
[[package]]
name = "slab"
@@ -1154,15 +1180,15 @@ checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
[[package]]
name = "smallvec"
version = "1.15.2"
version = "1.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90"
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
[[package]]
name = "spin"
version = "0.12.2"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8abadc99fd9c7bbb7d0ca2b31d72a067d0c0dcd7aad25ab8cac71ba91417694b"
checksum = "d5fe4ccb98d9c292d56fec89a5e07da7fc4cf0dc11e156b41793132775d3e591"
[[package]]
name = "stable_deref_trait"
@@ -1172,9 +1198,9 @@ checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
[[package]]
name = "syn"
version = "2.0.119"
version = "2.0.117"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297"
checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
dependencies = [
"proc-macro2",
"quote",
@@ -1234,6 +1260,12 @@ version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
[[package]]
name = "unicode-xid"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
[[package]]
name = "unsafe-libyaml"
version = "0.2.11"
@@ -1260,11 +1292,11 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
[[package]]
name = "uuid"
version = "1.24.0"
version = "1.23.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf3923a6f5c4c6382e0b653c4117f48d631ea17f38ed86e2a828e6f7412f5239"
checksum = "ddd74a9687298c6858e9b88ec8935ec45d22e8fd5e6394fa1bd4e99a87789c76"
dependencies = [
"getrandom 0.4.3",
"getrandom 0.4.2",
"rand",
]
@@ -1302,18 +1334,27 @@ dependencies = [
[[package]]
name = "wasip2"
version = "1.0.4+wasi-0.2.12"
version = "1.0.3+wasi-0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487"
checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6"
dependencies = [
"wit-bindgen",
"wit-bindgen 0.57.1",
]
[[package]]
name = "wasip3"
version = "0.4.0+wasi-0.3.0-rc-2026-01-06"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5"
dependencies = [
"wit-bindgen 0.51.0",
]
[[package]]
name = "wasm-bindgen"
version = "0.2.126"
version = "0.2.120"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b067c0c11094aef6b7a801c1e34a26affafdf3d051dba08456b868789aaf9a4"
checksum = "df52b6d9b87e0c74c9edfa1eb2d9bf85e5d63515474513aa50fa181b3c4f5db1"
dependencies = [
"cfg-if",
"once_cell",
@@ -1324,9 +1365,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.126"
version = "0.2.120"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "167ce5e579f6bcf889c4f7175a8a5a585de84e8ff93976ce393efa5f2837aab1"
checksum = "78b1041f495fb322e64aca85f5756b2172e35cd459376e67f2a6c9dffcedb103"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@@ -1334,9 +1375,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.126"
version = "0.2.120"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3997c7839262f4ef12cf90b818d6340c18e80f263f1a94bf157d0ec4420380e"
checksum = "9dcd0ff20416988a18ac686d4d4d0f6aae9ebf08a389ff5d29012b05af2a1b41"
dependencies = [
"bumpalo",
"proc-macro2",
@@ -1347,13 +1388,47 @@ dependencies = [
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.126"
version = "0.2.120"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc1b4cb0cc549fcf58d7dfc081778139b3d283a081644e833e84682ad71cea24"
checksum = "49757b3c82ebf16c57d69365a142940b384176c24df52a087fb748e2085359ea"
dependencies = [
"unicode-ident",
]
[[package]]
name = "wasm-encoder"
version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319"
dependencies = [
"leb128fmt",
"wasmparser",
]
[[package]]
name = "wasm-metadata"
version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909"
dependencies = [
"anyhow",
"indexmap",
"wasm-encoder",
"wasmparser",
]
[[package]]
name = "wasmparser"
version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe"
dependencies = [
"bitflags",
"hashbrown 0.15.5",
"indexmap",
"semver",
]
[[package]]
name = "winapi-util"
version = "0.1.11"
@@ -1431,12 +1506,100 @@ dependencies = [
"windows-link",
]
[[package]]
name = "wit-bindgen"
version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5"
dependencies = [
"wit-bindgen-rust-macro",
]
[[package]]
name = "wit-bindgen"
version = "0.57.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e"
[[package]]
name = "wit-bindgen-core"
version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc"
dependencies = [
"anyhow",
"heck",
"wit-parser",
]
[[package]]
name = "wit-bindgen-rust"
version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21"
dependencies = [
"anyhow",
"heck",
"indexmap",
"prettyplease",
"syn",
"wasm-metadata",
"wit-bindgen-core",
"wit-component",
]
[[package]]
name = "wit-bindgen-rust-macro"
version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a"
dependencies = [
"anyhow",
"prettyplease",
"proc-macro2",
"quote",
"syn",
"wit-bindgen-core",
"wit-bindgen-rust",
]
[[package]]
name = "wit-component"
version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2"
dependencies = [
"anyhow",
"bitflags",
"indexmap",
"log",
"serde",
"serde_derive",
"serde_json",
"wasm-encoder",
"wasm-metadata",
"wasmparser",
"wit-parser",
]
[[package]]
name = "wit-parser"
version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736"
dependencies = [
"anyhow",
"id-arena",
"indexmap",
"log",
"semver",
"serde",
"serde_derive",
"serde_json",
"unicode-xid",
"wasmparser",
]
[[package]]
name = "writeable"
version = "0.6.3"
@@ -1445,9 +1608,9 @@ checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4"
[[package]]
name = "yoke"
version = "0.8.3"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "709fe23a0424b6a435d82152b1bd3fdfb0833487d5fa90d05d42762a9891fef5"
checksum = "abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca"
dependencies = [
"stable_deref_trait",
"yoke-derive",
@@ -1468,18 +1631,18 @@ dependencies = [
[[package]]
name = "zerocopy"
version = "0.8.54"
version = "0.8.48"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7cbbc0a705a0fd05cc3676525980d2bf5a9bc4adac6d6475209a7887cf59d19"
checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.8.54"
version = "0.8.48"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2e817b7b52d0c7358d3246da9d69935ebb18116b2b102b4230dac079b4862f5"
checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4"
dependencies = [
"proc-macro2",
"quote",
@@ -1488,9 +1651,9 @@ dependencies = [
[[package]]
name = "zerofrom"
version = "0.1.8"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272"
checksum = "69faa1f2a1ea75661980b013019ed6687ed0e83d069bc1114e2cc74c6c04c4df"
dependencies = [
"zerofrom-derive",
]
@@ -1542,6 +1705,6 @@ dependencies = [
[[package]]
name = "zmij"
version = "1.0.23"
version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b"
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"

View File

@@ -2,7 +2,7 @@
[package]
name = "regorus-java"
version = "0.11.0"
version = "0.10.0"
edition = "2021"
repository = "https://github.com/microsoft/regorus/bindings/java"
description = "Java bindings for Regorus - a fast, lightweight Rego interpreter written in Rust"
@@ -21,6 +21,6 @@ cache = ["regorus/cache"]
[dependencies]
anyhow = "1.0"
serde_json = "1.0.150"
serde_json = "1.0.112"
jni = "0.22.4"
regorus = { path = "../..", default-features = false, features = ["arc", "rvm"] }

View File

@@ -9,7 +9,7 @@
<groupId>com.microsoft.regorus</groupId>
<artifactId>regorus-java</artifactId>
<version>0.11.0</version>
<version>0.10.0</version>
<name>Regorus Java</name>
<description>Java bindings for Regorus - a fast, lightweight Rego interpreter written in Rust</description>
@@ -97,7 +97,7 @@
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.5.6</version>
<version>3.5.5</version>
<configuration>
<!-- Add debug build to Java path, so it's discoverable by JVM. This is only for tests. -->
<argLine>-Djava.library.path=${project.basedir}/target/debug:${java.library.path}</argLine>

View File

@@ -462,7 +462,7 @@ pub extern "system" fn Java_com_microsoft_regorus_Program_nativeCompileFromModul
}
let mut modules = Vec::with_capacity(ids.len());
for (id, content) in ids.into_iter().zip(contents) {
for (id, content) in ids.into_iter().zip(contents.into_iter()) {
modules.push(PolicyModule {
id: Rc::from(id.as_str()),
content: Rc::from(content.as_str()),

View File

@@ -42,15 +42,15 @@ dependencies = [
[[package]]
name = "anyhow"
version = "1.0.103"
version = "1.0.102"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a4385e2e34eb35d6b3efe798b9eb88096925d87726c0798709bf56d9ed84af3"
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
[[package]]
name = "autocfg"
version = "1.5.1"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
[[package]]
name = "bit-set"
@@ -69,9 +69,9 @@ checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7"
[[package]]
name = "bitflags"
version = "2.13.1"
version = "2.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da"
checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3"
[[package]]
name = "borrow-or-share"
@@ -81,19 +81,19 @@ checksum = "dc0b364ead1874514c8c2855ab558056ebfeb775653e7ae45ff72f28f8f3166c"
[[package]]
name = "bstr"
version = "1.13.0"
version = "1.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f7dc094d718f2e1c1559ad110e27eeaae14a5465d3d56dd6dbd793079fbd530"
checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab"
dependencies = [
"memchr",
"serde_core",
"serde",
]
[[package]]
name = "bumpalo"
version = "3.20.3"
version = "3.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649"
checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb"
[[package]]
name = "bytecount"
@@ -103,9 +103,9 @@ checksum = "175812e0be2bccb6abe50bb8d566126198344f707e304f45c648fd8f2cc0365e"
[[package]]
name = "cc"
version = "1.2.67"
version = "1.2.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e17dd265a7d0f31ef544e1b20e03add05d3b45b491b633b10d67145d2acc1a38"
checksum = "d16d90359e986641506914ba71350897565610e87ce0ad9e6f28569db3dd5c6d"
dependencies = [
"find-msvc-tools",
"shlex",
@@ -119,9 +119,9 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "chacha20"
version = "0.10.1"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d524456ba66e72eb8b115ff89e01e497f8e6d11d78b70b1aa13c0fbd97540a81"
checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601"
dependencies = [
"cfg-if",
"cpufeatures",
@@ -130,9 +130,9 @@ dependencies = [
[[package]]
name = "chrono"
version = "0.4.45"
version = "0.4.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327"
checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0"
dependencies = [
"iana-time-zone",
"js-sys",
@@ -183,9 +183,9 @@ checksum = "a4ae5f15dda3c708c0ade84bfee31ccab44a3da4f88015ed22f63732abe300c8"
[[package]]
name = "displaydoc"
version = "0.2.6"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f"
checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
dependencies = [
"proc-macro2",
"quote",
@@ -221,9 +221,9 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "fancy-regex"
version = "0.18.0"
version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1e1dacd0d2082dfcf1351c4bdd566bbe89a2b263235a2b50058f1e130a47277"
checksum = "72cf461f865c862bb7dc573f643dd6a2b6842f7c30b07882b56bd148cc2761b8"
dependencies = [
"bit-set",
"regex-automata",
@@ -247,6 +247,12 @@ dependencies = [
"serde",
]
[[package]]
name = "foldhash"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
[[package]]
name = "foldhash"
version = "0.2.0"
@@ -312,21 +318,23 @@ dependencies = [
[[package]]
name = "getrandom"
version = "0.4.3"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099"
checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555"
dependencies = [
"cfg-if",
"libc",
"r-efi 6.0.0",
"rand_core",
"wasip2",
"wasip3",
]
[[package]]
name = "globset"
version = "0.4.19"
version = "0.4.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e47d37d2ae4464254884b60ab7071be2b876a9c35b696bd018ddcc76847309cd"
checksum = "52dfc19153a48bde0cbd630453615c8151bce3a5adfac7a0aebfbf0a1e1f57e3"
dependencies = [
"aho-corasick",
"bstr",
@@ -336,15 +344,30 @@ dependencies = [
[[package]]
name = "hashbrown"
version = "0.17.1"
version = "0.15.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
dependencies = [
"foldhash 0.1.5",
]
[[package]]
name = "hashbrown"
version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
dependencies = [
"allocator-api2",
"equivalent",
"foldhash",
"foldhash 0.2.0",
]
[[package]]
name = "hashbrown"
version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51"
[[package]]
name = "heck"
version = "0.5.0"
@@ -457,6 +480,12 @@ dependencies = [
"zerovec",
]
[[package]]
name = "id-arena"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954"
[[package]]
name = "idna"
version = "1.1.0"
@@ -485,7 +514,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9"
dependencies = [
"equivalent",
"hashbrown",
"hashbrown 0.17.0",
"serde",
"serde_core",
]
@@ -504,20 +533,21 @@ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "js-sys"
version = "0.3.103"
version = "0.3.97"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53b44bfcdb3f8d5837a46dae1ca9660a837176eee74a28b229bc626816589102"
checksum = "a1840c94c045fbcf8ba2812c95db44499f7c64910a912551aaaa541decebcacf"
dependencies = [
"cfg-if",
"futures-util",
"once_cell",
"wasm-bindgen",
]
[[package]]
name = "jsonschema"
version = "0.47.0"
version = "0.45.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "281c43ff06dcb331e9356d30e38853d559ce3d0a3f693e0b0e102667dec14fb1"
checksum = "257eb0e588b76827bbddc9e73945a9743693dd2adeaee9da26420f93cfedb798"
dependencies = [
"ahash",
"bytecount",
@@ -528,33 +558,30 @@ dependencies = [
"getrandom 0.3.4",
"idna",
"itoa",
"jsonschema-regex",
"num-cmp",
"num-traits",
"percent-encoding",
"referencing",
"regex",
"regex-syntax",
"serde",
"serde_json",
"unicode-general-category",
"uuid-simd",
]
[[package]]
name = "jsonschema-regex"
version = "0.47.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee0b351864e7ffbc5db9273daf7fa1b4d5177b0946713d667ca571b83c0b4045"
dependencies = [
"regex-syntax",
]
[[package]]
name = "lazy_static"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]]
name = "leb128fmt"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
[[package]]
name = "libc"
version = "0.2.186"
@@ -578,27 +605,21 @@ dependencies = [
[[package]]
name = "log"
version = "0.4.33"
version = "0.4.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad"
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
[[package]]
name = "lru"
version = "0.18.1"
version = "0.16.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b6180140927ee907000b0aa540091f6ea512ead4447c92b8fc35bc72788a5a6"
checksum = "7f66e8d5d03f609abc3a39e6f08e4164ebf1447a732906d39eb9b99b7919ef39"
[[package]]
name = "memchr"
version = "2.8.3"
version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
[[package]]
name = "micromap"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2a86d3146ed3995b5913c414f6664344b9617457320782e64f0bb44afd49d74"
checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
[[package]]
name = "msvc_spectre_libs"
@@ -615,7 +636,7 @@ version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23"
dependencies = [
"num-bigint 0.4.8",
"num-bigint",
"num-complex",
"num-integer",
"num-iter",
@@ -625,19 +646,9 @@ dependencies = [
[[package]]
name = "num-bigint"
version = "0.4.8"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c89e69e7e0f03bea5ef08013795c25018e101932225a656383bd384495ecc367"
dependencies = [
"num-integer",
"num-traits",
]
[[package]]
name = "num-bigint"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93e7820bc0a80a0238e650327316f929ba18d5be054b647490a3a6a339f3e7c0"
checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
dependencies = [
"num-integer",
"num-traits",
@@ -669,10 +680,11 @@ dependencies = [
[[package]]
name = "num-iter"
version = "0.1.46"
version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c92800bd69a1eac91786bcfe9da64a897eb72911b8dc3095decbd07429e8048b"
checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
]
@@ -683,7 +695,7 @@ version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824"
dependencies = [
"num-bigint 0.4.8",
"num-bigint",
"num-integer",
"num-traits",
]
@@ -798,6 +810,16 @@ dependencies = [
"zerovec",
]
[[package]]
name = "prettyplease"
version = "0.2.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
dependencies = [
"proc-macro2",
"syn",
]
[[package]]
name = "proc-macro2"
version = "1.0.106"
@@ -809,9 +831,9 @@ dependencies = [
[[package]]
name = "pyo3"
version = "0.29.0"
version = "0.28.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd274650b21d4bfc26a0a47587962c1edb425f69287324355cd040c3ea66071c"
checksum = "91fd8e38a3b50ed1167fb981cd6fd60147e091784c427b8f7183a7ee32c31c12"
dependencies = [
"anyhow",
"libc",
@@ -824,18 +846,18 @@ dependencies = [
[[package]]
name = "pyo3-build-config"
version = "0.29.0"
version = "0.28.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c5e2a7d2f0d013342f295c048ad19237add5154a55b1c5a254c0ec93d4109078"
checksum = "e368e7ddfdeb98c9bca7f8383be1648fd84ab466bf2bc015e94008db6d35611e"
dependencies = [
"target-lexicon",
]
[[package]]
name = "pyo3-ffi"
version = "0.29.0"
version = "0.28.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca85c467da1bbc8d866eea5deff9cf29ea5f7785054a17da36e65bda9c05845b"
checksum = "7f29e10af80b1f7ccaf7f69eace800a03ecd13e883acfacc1e5d0988605f651e"
dependencies = [
"libc",
"pyo3-build-config",
@@ -843,9 +865,9 @@ dependencies = [
[[package]]
name = "pyo3-macros"
version = "0.29.0"
version = "0.28.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ac53762fd065daa3194dd09337a38bd793a188100fd1a9304c4ab312d901771"
checksum = "df6e520eff47c45997d2fc7dd8214b25dd1310918bbb2642156ef66a67f29813"
dependencies = [
"proc-macro2",
"pyo3-macros-backend",
@@ -855,21 +877,22 @@ dependencies = [
[[package]]
name = "pyo3-macros-backend"
version = "0.29.0"
version = "0.28.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ca3a1557399783172dc5bf39cfca835157732532cba56b71d2292161e53b362"
checksum = "c4cdc218d835738f81c2338f822078af45b4afdf8b2e33cbb5916f108b813acb"
dependencies = [
"heck",
"proc-macro2",
"pyo3-build-config",
"quote",
"syn",
]
[[package]]
name = "quote"
version = "1.0.46"
version = "1.0.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368"
checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
dependencies = [
"proc-macro2",
]
@@ -888,12 +911,12 @@ checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
[[package]]
name = "rand"
version = "0.10.2"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7f5fa3a058cd35567ef9bfa5e75732bee0f9e4c55fa90477bef2dfcdbc4be80"
checksum = "d2e8e8bcc7961af1fdac401278c6a831614941f6164ee3bf4ce61b7edb162207"
dependencies = [
"chacha20",
"getrandom 0.4.3",
"getrandom 0.4.2",
"rand_core",
]
@@ -934,16 +957,14 @@ dependencies = [
[[package]]
name = "referencing"
version = "0.47.0"
version = "0.45.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "348e860aeb0b7bd035778fd11dd9cd5290d32e4aed3b8f2274a00287a9fd362b"
checksum = "e2f38748ceca8d0b0013e60f534d94a6e23dfd89fd2a88318fc5a2d04fda1010"
dependencies = [
"ahash",
"fluent-uri",
"getrandom 0.3.4",
"hashbrown",
"itoa",
"micromap",
"hashbrown 0.16.1",
"parking_lot",
"percent-encoding",
"serde_json",
@@ -951,9 +972,9 @@ dependencies = [
[[package]]
name = "regex"
version = "1.13.1"
version = "1.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d"
checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276"
dependencies = [
"aho-corasick",
"memchr",
@@ -963,9 +984,9 @@ dependencies = [
[[package]]
name = "regex-automata"
version = "0.4.16"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fcfdb36bda0c880c5931cdc7a2bcdc8ba4556847b9d912bca70bc94708711ad"
checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f"
dependencies = [
"aho-corasick",
"memchr",
@@ -974,13 +995,13 @@ dependencies = [
[[package]]
name = "regex-syntax"
version = "0.8.11"
version = "0.8.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4"
checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a"
[[package]]
name = "regorus"
version = "0.11.0"
version = "0.10.0"
dependencies = [
"anyhow",
"chrono",
@@ -993,7 +1014,7 @@ dependencies = [
"lazy_static",
"lru",
"msvc_spectre_libs",
"num-bigint 0.5.1",
"num-bigint",
"num-traits",
"parking_lot",
"postcard",
@@ -1012,7 +1033,7 @@ dependencies = [
[[package]]
name = "regorus-mimalloc"
version = "2.2.7"
version = "2.2.6"
dependencies = [
"regorus-mimalloc-sys",
]
@@ -1026,7 +1047,7 @@ dependencies = [
[[package]]
name = "regoruspy"
version = "0.11.0"
version = "0.10.0"
dependencies = [
"anyhow",
"ordered-float",
@@ -1037,9 +1058,9 @@ dependencies = [
[[package]]
name = "rustversion"
version = "1.0.23"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f"
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
[[package]]
name = "ryu"
@@ -1091,9 +1112,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.150"
version = "1.0.149"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9"
checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86"
dependencies = [
"itoa",
"memchr",
@@ -1117,15 +1138,15 @@ dependencies = [
[[package]]
name = "shlex"
version = "2.0.1"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "siphasher"
version = "1.0.3"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649"
checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e"
[[package]]
name = "slab"
@@ -1135,15 +1156,15 @@ checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
[[package]]
name = "smallvec"
version = "1.15.2"
version = "1.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90"
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
[[package]]
name = "spin"
version = "0.12.2"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8abadc99fd9c7bbb7d0ca2b31d72a067d0c0dcd7aad25ab8cac71ba91417694b"
checksum = "d5fe4ccb98d9c292d56fec89a5e07da7fc4cf0dc11e156b41793132775d3e591"
[[package]]
name = "stable_deref_trait"
@@ -1153,9 +1174,9 @@ checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
[[package]]
name = "syn"
version = "2.0.119"
version = "2.0.117"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297"
checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
dependencies = [
"proc-macro2",
"quote",
@@ -1221,6 +1242,12 @@ version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
[[package]]
name = "unicode-xid"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
[[package]]
name = "unsafe-libyaml"
version = "0.2.11"
@@ -1247,11 +1274,11 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
[[package]]
name = "uuid"
version = "1.24.0"
version = "1.23.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf3923a6f5c4c6382e0b653c4117f48d631ea17f38ed86e2a828e6f7412f5239"
checksum = "ddd74a9687298c6858e9b88ec8935ec45d22e8fd5e6394fa1bd4e99a87789c76"
dependencies = [
"getrandom 0.4.3",
"getrandom 0.4.2",
"rand",
]
@@ -1279,18 +1306,27 @@ checksum = "5c3082ca00d5a5ef149bb8b555a72ae84c9c59f7250f013ac822ac2e49b19c64"
[[package]]
name = "wasip2"
version = "1.0.4+wasi-0.2.12"
version = "1.0.3+wasi-0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487"
checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6"
dependencies = [
"wit-bindgen",
"wit-bindgen 0.57.1",
]
[[package]]
name = "wasip3"
version = "0.4.0+wasi-0.3.0-rc-2026-01-06"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5"
dependencies = [
"wit-bindgen 0.51.0",
]
[[package]]
name = "wasm-bindgen"
version = "0.2.126"
version = "0.2.120"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b067c0c11094aef6b7a801c1e34a26affafdf3d051dba08456b868789aaf9a4"
checksum = "df52b6d9b87e0c74c9edfa1eb2d9bf85e5d63515474513aa50fa181b3c4f5db1"
dependencies = [
"cfg-if",
"once_cell",
@@ -1301,9 +1337,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.126"
version = "0.2.120"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "167ce5e579f6bcf889c4f7175a8a5a585de84e8ff93976ce393efa5f2837aab1"
checksum = "78b1041f495fb322e64aca85f5756b2172e35cd459376e67f2a6c9dffcedb103"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@@ -1311,9 +1347,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.126"
version = "0.2.120"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3997c7839262f4ef12cf90b818d6340c18e80f263f1a94bf157d0ec4420380e"
checksum = "9dcd0ff20416988a18ac686d4d4d0f6aae9ebf08a389ff5d29012b05af2a1b41"
dependencies = [
"bumpalo",
"proc-macro2",
@@ -1324,13 +1360,47 @@ dependencies = [
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.126"
version = "0.2.120"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc1b4cb0cc549fcf58d7dfc081778139b3d283a081644e833e84682ad71cea24"
checksum = "49757b3c82ebf16c57d69365a142940b384176c24df52a087fb748e2085359ea"
dependencies = [
"unicode-ident",
]
[[package]]
name = "wasm-encoder"
version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319"
dependencies = [
"leb128fmt",
"wasmparser",
]
[[package]]
name = "wasm-metadata"
version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909"
dependencies = [
"anyhow",
"indexmap",
"wasm-encoder",
"wasmparser",
]
[[package]]
name = "wasmparser"
version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe"
dependencies = [
"bitflags",
"hashbrown 0.15.5",
"indexmap",
"semver",
]
[[package]]
name = "windows-core"
version = "0.62.2"
@@ -1390,12 +1460,100 @@ dependencies = [
"windows-link",
]
[[package]]
name = "wit-bindgen"
version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5"
dependencies = [
"wit-bindgen-rust-macro",
]
[[package]]
name = "wit-bindgen"
version = "0.57.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e"
[[package]]
name = "wit-bindgen-core"
version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc"
dependencies = [
"anyhow",
"heck",
"wit-parser",
]
[[package]]
name = "wit-bindgen-rust"
version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21"
dependencies = [
"anyhow",
"heck",
"indexmap",
"prettyplease",
"syn",
"wasm-metadata",
"wit-bindgen-core",
"wit-component",
]
[[package]]
name = "wit-bindgen-rust-macro"
version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a"
dependencies = [
"anyhow",
"prettyplease",
"proc-macro2",
"quote",
"syn",
"wit-bindgen-core",
"wit-bindgen-rust",
]
[[package]]
name = "wit-component"
version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2"
dependencies = [
"anyhow",
"bitflags",
"indexmap",
"log",
"serde",
"serde_derive",
"serde_json",
"wasm-encoder",
"wasm-metadata",
"wasmparser",
"wit-parser",
]
[[package]]
name = "wit-parser"
version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736"
dependencies = [
"anyhow",
"id-arena",
"indexmap",
"log",
"semver",
"serde",
"serde_derive",
"serde_json",
"unicode-xid",
"wasmparser",
]
[[package]]
name = "writeable"
version = "0.6.3"
@@ -1404,9 +1562,9 @@ checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4"
[[package]]
name = "yoke"
version = "0.8.3"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "709fe23a0424b6a435d82152b1bd3fdfb0833487d5fa90d05d42762a9891fef5"
checksum = "abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca"
dependencies = [
"stable_deref_trait",
"yoke-derive",
@@ -1427,18 +1585,18 @@ dependencies = [
[[package]]
name = "zerocopy"
version = "0.8.54"
version = "0.8.48"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7cbbc0a705a0fd05cc3676525980d2bf5a9bc4adac6d6475209a7887cf59d19"
checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.8.54"
version = "0.8.48"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2e817b7b52d0c7358d3246da9d69935ebb18116b2b102b4230dac079b4862f5"
checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4"
dependencies = [
"proc-macro2",
"quote",
@@ -1447,9 +1605,9 @@ dependencies = [
[[package]]
name = "zerofrom"
version = "0.1.8"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272"
checksum = "69faa1f2a1ea75661980b013019ed6687ed0e83d069bc1114e2cc74c6c04c4df"
dependencies = [
"zerofrom-derive",
]
@@ -1501,6 +1659,6 @@ dependencies = [
[[package]]
name = "zmij"
version = "1.0.23"
version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b"
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"

View File

@@ -2,7 +2,7 @@
[package]
name = "regoruspy"
version = "0.11.0"
version = "0.10.0"
edition = "2021"
repository = "https://github.com/microsoft/regorus/bindings/python"
description = "Python bindings for Regorus - a fast, lightweight Rego interpreter written in Rust"
@@ -23,7 +23,7 @@ coverage = ["regorus/coverage"]
[dependencies]
anyhow = "1.0"
ordered-float = "5.3.0"
pyo3 = { version = "0.29.0", features = ["abi3-py310", "anyhow", "extension-module"] }
pyo3 = { version = "0.28.3", features = ["abi3-py310", "anyhow", "extension-module"] }
regorus = { path = "../..", default-features = false, features = ["arc", "rvm"] }
serde_json = "1.0.150"
serde_json = "1.0.140"

View File

@@ -1,5 +1,5 @@
[build-system]
requires = ["maturin>=1.14.1,<2.0"]
requires = ["maturin>=1.4,<2.0"]
build-backend = "maturin"
[project]

443
bindings/ruby/Cargo.lock generated
View File

@@ -42,15 +42,15 @@ dependencies = [
[[package]]
name = "anyhow"
version = "1.0.103"
version = "1.0.102"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a4385e2e34eb35d6b3efe798b9eb88096925d87726c0798709bf56d9ed84af3"
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
[[package]]
name = "autocfg"
version = "1.5.1"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
[[package]]
name = "bindgen"
@@ -66,7 +66,7 @@ dependencies = [
"quote",
"regex",
"rustc-hash",
"shlex 1.3.0",
"shlex",
"syn",
]
@@ -87,9 +87,9 @@ checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7"
[[package]]
name = "bitflags"
version = "2.13.1"
version = "2.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da"
checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3"
[[package]]
name = "borrow-or-share"
@@ -99,19 +99,19 @@ checksum = "dc0b364ead1874514c8c2855ab558056ebfeb775653e7ae45ff72f28f8f3166c"
[[package]]
name = "bstr"
version = "1.13.0"
version = "1.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f7dc094d718f2e1c1559ad110e27eeaae14a5465d3d56dd6dbd793079fbd530"
checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab"
dependencies = [
"memchr",
"serde_core",
"serde",
]
[[package]]
name = "bumpalo"
version = "3.20.3"
version = "3.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649"
checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb"
[[package]]
name = "bytecount"
@@ -121,12 +121,12 @@ checksum = "175812e0be2bccb6abe50bb8d566126198344f707e304f45c648fd8f2cc0365e"
[[package]]
name = "cc"
version = "1.2.67"
version = "1.2.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e17dd265a7d0f31ef544e1b20e03add05d3b45b491b633b10d67145d2acc1a38"
checksum = "d16d90359e986641506914ba71350897565610e87ce0ad9e6f28569db3dd5c6d"
dependencies = [
"find-msvc-tools",
"shlex 2.0.1",
"shlex",
]
[[package]]
@@ -146,9 +146,9 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "chacha20"
version = "0.10.1"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d524456ba66e72eb8b115ff89e01e497f8e6d11d78b70b1aa13c0fbd97540a81"
checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601"
dependencies = [
"cfg-if",
"cpufeatures",
@@ -157,9 +157,9 @@ dependencies = [
[[package]]
name = "chrono"
version = "0.4.45"
version = "0.4.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327"
checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0"
dependencies = [
"iana-time-zone",
"js-sys",
@@ -212,9 +212,9 @@ checksum = "a4ae5f15dda3c708c0ade84bfee31ccab44a3da4f88015ed22f63732abe300c8"
[[package]]
name = "displaydoc"
version = "0.2.6"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f"
checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
dependencies = [
"proc-macro2",
"quote",
@@ -223,9 +223,9 @@ dependencies = [
[[package]]
name = "either"
version = "1.16.0"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e"
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
[[package]]
name = "email_address"
@@ -244,9 +244,9 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "fancy-regex"
version = "0.18.0"
version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1e1dacd0d2082dfcf1351c4bdd566bbe89a2b263235a2b50058f1e130a47277"
checksum = "72cf461f865c862bb7dc573f643dd6a2b6842f7c30b07882b56bd148cc2761b8"
dependencies = [
"bit-set",
"regex-automata",
@@ -270,6 +270,12 @@ dependencies = [
"serde",
]
[[package]]
name = "foldhash"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
[[package]]
name = "foldhash"
version = "0.2.0"
@@ -335,14 +341,16 @@ dependencies = [
[[package]]
name = "getrandom"
version = "0.4.3"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099"
checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555"
dependencies = [
"cfg-if",
"libc",
"r-efi 6.0.0",
"rand_core",
"wasip2",
"wasip3",
]
[[package]]
@@ -353,9 +361,9 @@ checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
[[package]]
name = "globset"
version = "0.4.19"
version = "0.4.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e47d37d2ae4464254884b60ab7071be2b876a9c35b696bd018ddcc76847309cd"
checksum = "52dfc19153a48bde0cbd630453615c8151bce3a5adfac7a0aebfbf0a1e1f57e3"
dependencies = [
"aho-corasick",
"bstr",
@@ -365,15 +373,36 @@ dependencies = [
[[package]]
name = "hashbrown"
version = "0.17.1"
version = "0.15.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
dependencies = [
"foldhash 0.1.5",
]
[[package]]
name = "hashbrown"
version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
dependencies = [
"allocator-api2",
"equivalent",
"foldhash",
"foldhash 0.2.0",
]
[[package]]
name = "hashbrown"
version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51"
[[package]]
name = "heck"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "iana-time-zone"
version = "0.1.65"
@@ -480,6 +509,12 @@ dependencies = [
"zerovec",
]
[[package]]
name = "id-arena"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954"
[[package]]
name = "idna"
version = "1.1.0"
@@ -508,7 +543,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9"
dependencies = [
"equivalent",
"hashbrown",
"hashbrown 0.17.0",
"serde",
"serde_core",
]
@@ -536,20 +571,21 @@ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "js-sys"
version = "0.3.103"
version = "0.3.97"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53b44bfcdb3f8d5837a46dae1ca9660a837176eee74a28b229bc626816589102"
checksum = "a1840c94c045fbcf8ba2812c95db44499f7c64910a912551aaaa541decebcacf"
dependencies = [
"cfg-if",
"futures-util",
"once_cell",
"wasm-bindgen",
]
[[package]]
name = "jsonschema"
version = "0.47.0"
version = "0.45.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "281c43ff06dcb331e9356d30e38853d559ce3d0a3f693e0b0e102667dec14fb1"
checksum = "257eb0e588b76827bbddc9e73945a9743693dd2adeaee9da26420f93cfedb798"
dependencies = [
"ahash",
"bytecount",
@@ -560,33 +596,30 @@ dependencies = [
"getrandom 0.3.4",
"idna",
"itoa",
"jsonschema-regex",
"num-cmp",
"num-traits",
"percent-encoding",
"referencing",
"regex",
"regex-syntax",
"serde",
"serde_json",
"unicode-general-category",
"uuid-simd",
]
[[package]]
name = "jsonschema-regex"
version = "0.47.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee0b351864e7ffbc5db9273daf7fa1b4d5177b0946713d667ca571b83c0b4045"
dependencies = [
"regex-syntax",
]
[[package]]
name = "lazy_static"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]]
name = "leb128fmt"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
[[package]]
name = "libc"
version = "0.2.186"
@@ -620,15 +653,15 @@ dependencies = [
[[package]]
name = "log"
version = "0.4.33"
version = "0.4.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad"
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
[[package]]
name = "lru"
version = "0.18.1"
version = "0.16.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b6180140927ee907000b0aa540091f6ea512ead4447c92b8fc35bc72788a5a6"
checksum = "7f66e8d5d03f609abc3a39e6f08e4164ebf1447a732906d39eb9b99b7919ef39"
[[package]]
name = "magnus"
@@ -655,15 +688,9 @@ dependencies = [
[[package]]
name = "memchr"
version = "2.8.3"
version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
[[package]]
name = "micromap"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2a86d3146ed3995b5913c414f6664344b9617457320782e64f0bb44afd49d74"
checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
[[package]]
name = "minimal-lexical"
@@ -696,7 +723,7 @@ version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23"
dependencies = [
"num-bigint 0.4.8",
"num-bigint",
"num-complex",
"num-integer",
"num-iter",
@@ -706,19 +733,9 @@ dependencies = [
[[package]]
name = "num-bigint"
version = "0.4.8"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c89e69e7e0f03bea5ef08013795c25018e101932225a656383bd384495ecc367"
dependencies = [
"num-integer",
"num-traits",
]
[[package]]
name = "num-bigint"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93e7820bc0a80a0238e650327316f929ba18d5be054b647490a3a6a339f3e7c0"
checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
dependencies = [
"num-integer",
"num-traits",
@@ -750,10 +767,11 @@ dependencies = [
[[package]]
name = "num-iter"
version = "0.1.46"
version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c92800bd69a1eac91786bcfe9da64a897eb72911b8dc3095decbd07429e8048b"
checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
]
@@ -764,7 +782,7 @@ version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824"
dependencies = [
"num-bigint 0.4.8",
"num-bigint",
"num-integer",
"num-traits",
]
@@ -852,6 +870,16 @@ dependencies = [
"zerovec",
]
[[package]]
name = "prettyplease"
version = "0.2.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
dependencies = [
"proc-macro2",
"syn",
]
[[package]]
name = "proc-macro2"
version = "1.0.106"
@@ -863,9 +891,9 @@ dependencies = [
[[package]]
name = "quote"
version = "1.0.46"
version = "1.0.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368"
checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
dependencies = [
"proc-macro2",
]
@@ -884,12 +912,12 @@ checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
[[package]]
name = "rand"
version = "0.10.2"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7f5fa3a058cd35567ef9bfa5e75732bee0f9e4c55fa90477bef2dfcdbc4be80"
checksum = "d2e8e8bcc7961af1fdac401278c6a831614941f6164ee3bf4ce61b7edb162207"
dependencies = [
"chacha20",
"getrandom 0.4.3",
"getrandom 0.4.2",
"rand_core",
]
@@ -901,18 +929,18 @@ checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69"
[[package]]
name = "rb-sys"
version = "0.9.128"
version = "0.9.127"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "45ca28513560e56cfb79a62b1fce363c73af170a182024ce880c77ee9429920a"
checksum = "d7d7c9560fe42dcffa576941394075f18a17dce89fcf718a2fa90b7dc2134d12"
dependencies = [
"rb-sys-build",
]
[[package]]
name = "rb-sys-build"
version = "0.9.128"
version = "0.9.127"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce04b2c55eff3a21aaa623fcc655d94373238e72cac6b3e1a3641ff31649f99a"
checksum = "f1688e8f32967ba48c89e4dfa283b57f901075f542fc7ee9c3d7c5f9091ca1d9"
dependencies = [
"bindgen",
"lazy_static",
@@ -960,16 +988,14 @@ dependencies = [
[[package]]
name = "referencing"
version = "0.47.0"
version = "0.45.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "348e860aeb0b7bd035778fd11dd9cd5290d32e4aed3b8f2274a00287a9fd362b"
checksum = "e2f38748ceca8d0b0013e60f534d94a6e23dfd89fd2a88318fc5a2d04fda1010"
dependencies = [
"ahash",
"fluent-uri",
"getrandom 0.3.4",
"hashbrown",
"itoa",
"micromap",
"hashbrown 0.16.1",
"parking_lot",
"percent-encoding",
"serde_json",
@@ -977,9 +1003,9 @@ dependencies = [
[[package]]
name = "regex"
version = "1.13.1"
version = "1.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d"
checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276"
dependencies = [
"aho-corasick",
"memchr",
@@ -989,9 +1015,9 @@ dependencies = [
[[package]]
name = "regex-automata"
version = "0.4.16"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fcfdb36bda0c880c5931cdc7a2bcdc8ba4556847b9d912bca70bc94708711ad"
checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f"
dependencies = [
"aho-corasick",
"memchr",
@@ -1000,13 +1026,13 @@ dependencies = [
[[package]]
name = "regex-syntax"
version = "0.8.11"
version = "0.8.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4"
checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a"
[[package]]
name = "regorus"
version = "0.11.0"
version = "0.10.0"
dependencies = [
"anyhow",
"chrono",
@@ -1019,7 +1045,7 @@ dependencies = [
"lazy_static",
"lru",
"msvc_spectre_libs",
"num-bigint 0.5.1",
"num-bigint",
"num-traits",
"parking_lot",
"rand",
@@ -1037,7 +1063,7 @@ dependencies = [
[[package]]
name = "regorus-mimalloc"
version = "2.2.7"
version = "2.2.6"
dependencies = [
"regorus-mimalloc-sys",
]
@@ -1051,7 +1077,7 @@ dependencies = [
[[package]]
name = "regorusrb"
version = "0.11.0"
version = "0.10.0"
dependencies = [
"magnus",
"regorus",
@@ -1062,15 +1088,15 @@ dependencies = [
[[package]]
name = "rustc-hash"
version = "2.1.3"
version = "2.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d"
checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe"
[[package]]
name = "rustversion"
version = "1.0.23"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f"
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
[[package]]
name = "ryu"
@@ -1128,9 +1154,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.150"
version = "1.0.149"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9"
checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86"
dependencies = [
"itoa",
"memchr",
@@ -1175,17 +1201,11 @@ version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "shlex"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
[[package]]
name = "siphasher"
version = "1.0.3"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649"
checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e"
[[package]]
name = "slab"
@@ -1195,15 +1215,15 @@ checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
[[package]]
name = "smallvec"
version = "1.15.2"
version = "1.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90"
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
[[package]]
name = "spin"
version = "0.12.2"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8abadc99fd9c7bbb7d0ca2b31d72a067d0c0dcd7aad25ab8cac71ba91417694b"
checksum = "d5fe4ccb98d9c292d56fec89a5e07da7fc4cf0dc11e156b41793132775d3e591"
[[package]]
name = "stable_deref_trait"
@@ -1213,9 +1233,9 @@ checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
[[package]]
name = "syn"
version = "2.0.119"
version = "2.0.117"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297"
checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
dependencies = [
"proc-macro2",
"quote",
@@ -1281,6 +1301,12 @@ version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
[[package]]
name = "unicode-xid"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
[[package]]
name = "unsafe-libyaml"
version = "0.2.11"
@@ -1307,11 +1333,11 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
[[package]]
name = "uuid"
version = "1.24.0"
version = "1.23.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf3923a6f5c4c6382e0b653c4117f48d631ea17f38ed86e2a828e6f7412f5239"
checksum = "ddd74a9687298c6858e9b88ec8935ec45d22e8fd5e6394fa1bd4e99a87789c76"
dependencies = [
"getrandom 0.4.3",
"getrandom 0.4.2",
"rand",
]
@@ -1339,18 +1365,27 @@ checksum = "5c3082ca00d5a5ef149bb8b555a72ae84c9c59f7250f013ac822ac2e49b19c64"
[[package]]
name = "wasip2"
version = "1.0.4+wasi-0.2.12"
version = "1.0.3+wasi-0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487"
checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6"
dependencies = [
"wit-bindgen",
"wit-bindgen 0.57.1",
]
[[package]]
name = "wasip3"
version = "0.4.0+wasi-0.3.0-rc-2026-01-06"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5"
dependencies = [
"wit-bindgen 0.51.0",
]
[[package]]
name = "wasm-bindgen"
version = "0.2.126"
version = "0.2.120"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b067c0c11094aef6b7a801c1e34a26affafdf3d051dba08456b868789aaf9a4"
checksum = "df52b6d9b87e0c74c9edfa1eb2d9bf85e5d63515474513aa50fa181b3c4f5db1"
dependencies = [
"cfg-if",
"once_cell",
@@ -1361,9 +1396,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.126"
version = "0.2.120"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "167ce5e579f6bcf889c4f7175a8a5a585de84e8ff93976ce393efa5f2837aab1"
checksum = "78b1041f495fb322e64aca85f5756b2172e35cd459376e67f2a6c9dffcedb103"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@@ -1371,9 +1406,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.126"
version = "0.2.120"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3997c7839262f4ef12cf90b818d6340c18e80f263f1a94bf157d0ec4420380e"
checksum = "9dcd0ff20416988a18ac686d4d4d0f6aae9ebf08a389ff5d29012b05af2a1b41"
dependencies = [
"bumpalo",
"proc-macro2",
@@ -1384,13 +1419,47 @@ dependencies = [
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.126"
version = "0.2.120"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc1b4cb0cc549fcf58d7dfc081778139b3d283a081644e833e84682ad71cea24"
checksum = "49757b3c82ebf16c57d69365a142940b384176c24df52a087fb748e2085359ea"
dependencies = [
"unicode-ident",
]
[[package]]
name = "wasm-encoder"
version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319"
dependencies = [
"leb128fmt",
"wasmparser",
]
[[package]]
name = "wasm-metadata"
version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909"
dependencies = [
"anyhow",
"indexmap",
"wasm-encoder",
"wasmparser",
]
[[package]]
name = "wasmparser"
version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe"
dependencies = [
"bitflags",
"hashbrown 0.15.5",
"indexmap",
"semver",
]
[[package]]
name = "windows-core"
version = "0.62.2"
@@ -1450,12 +1519,100 @@ dependencies = [
"windows-link",
]
[[package]]
name = "wit-bindgen"
version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5"
dependencies = [
"wit-bindgen-rust-macro",
]
[[package]]
name = "wit-bindgen"
version = "0.57.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e"
[[package]]
name = "wit-bindgen-core"
version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc"
dependencies = [
"anyhow",
"heck",
"wit-parser",
]
[[package]]
name = "wit-bindgen-rust"
version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21"
dependencies = [
"anyhow",
"heck",
"indexmap",
"prettyplease",
"syn",
"wasm-metadata",
"wit-bindgen-core",
"wit-component",
]
[[package]]
name = "wit-bindgen-rust-macro"
version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a"
dependencies = [
"anyhow",
"prettyplease",
"proc-macro2",
"quote",
"syn",
"wit-bindgen-core",
"wit-bindgen-rust",
]
[[package]]
name = "wit-component"
version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2"
dependencies = [
"anyhow",
"bitflags",
"indexmap",
"log",
"serde",
"serde_derive",
"serde_json",
"wasm-encoder",
"wasm-metadata",
"wasmparser",
"wit-parser",
]
[[package]]
name = "wit-parser"
version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736"
dependencies = [
"anyhow",
"id-arena",
"indexmap",
"log",
"semver",
"serde",
"serde_derive",
"serde_json",
"unicode-xid",
"wasmparser",
]
[[package]]
name = "writeable"
version = "0.6.3"
@@ -1464,9 +1621,9 @@ checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4"
[[package]]
name = "yoke"
version = "0.8.3"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "709fe23a0424b6a435d82152b1bd3fdfb0833487d5fa90d05d42762a9891fef5"
checksum = "abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca"
dependencies = [
"stable_deref_trait",
"yoke-derive",
@@ -1487,18 +1644,18 @@ dependencies = [
[[package]]
name = "zerocopy"
version = "0.8.54"
version = "0.8.48"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7cbbc0a705a0fd05cc3676525980d2bf5a9bc4adac6d6475209a7887cf59d19"
checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.8.54"
version = "0.8.48"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2e817b7b52d0c7358d3246da9d69935ebb18116b2b102b4230dac079b4862f5"
checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4"
dependencies = [
"proc-macro2",
"quote",
@@ -1507,9 +1664,9 @@ dependencies = [
[[package]]
name = "zerofrom"
version = "0.1.8"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272"
checksum = "69faa1f2a1ea75661980b013019ed6687ed0e83d069bc1114e2cc74c6c04c4df"
dependencies = [
"zerofrom-derive",
]
@@ -1561,6 +1718,6 @@ dependencies = [
[[package]]
name = "zmij"
version = "1.0.23"
version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b"
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"

View File

@@ -11,6 +11,6 @@ gem "minitest", "~> 6.0"
gem "rake", "~> 13.4"
gem "rake-compiler", "~> 1.3"
gem "rake-compiler-dock", "~> 1.12"
gem "rubocop", "~> 1.88", require: false
gem "rubocop", "~> 1.86", require: false
gem "rubocop-minitest", "~> 0.39.1", require: false
gem "rubocop-rake", "~> 0.7.1", require: false

View File

@@ -9,10 +9,10 @@ GEM
specs:
ast (2.4.3)
drb (2.2.3)
json (2.20.0)
json (2.19.4)
language_server-protocol (3.17.0.5)
lint_roller (1.1.0)
minitest (6.0.6)
minitest (6.0.5)
drb (~> 2.0)
prism (~> 1.5)
parallel (2.1.0)
@@ -26,10 +26,10 @@ GEM
rake-compiler (1.3.1)
rake
rake-compiler-dock (1.12.0)
rb_sys (0.9.128)
rb_sys (0.9.127)
rake-compiler-dock (= 1.12.0)
regexp_parser (2.12.0)
rubocop (1.88.0)
rubocop (1.86.1)
json (~> 2.3)
language_server-protocol (~> 3.17.0.2)
lint_roller (~> 1.1.0)
@@ -65,7 +65,7 @@ DEPENDENCIES
rake-compiler (~> 1.3)
rake-compiler-dock (~> 1.12)
regorusrb!
rubocop (~> 1.88)
rubocop (~> 1.86)
rubocop-minitest (~> 0.39.1)
rubocop-rake (~> 0.7.1)

View File

@@ -1,6 +1,6 @@
[package]
name = "regorusrb"
version = "0.11.0"
version = "0.10.0"
edition = "2024"
description = "Ruby bindings for Regorus - a fast, lightweight Rego interpreter written in Rust"
license = "MIT AND Apache-2.0 AND BSD-3-Clause"

View File

@@ -1,5 +1,5 @@
# frozen_string_literal: true
module Regorus
VERSION = "0.11.0"
VERSION = "0.10.0"
end

437
bindings/wasm/Cargo.lock generated
View File

@@ -42,9 +42,9 @@ dependencies = [
[[package]]
name = "anyhow"
version = "1.0.103"
version = "1.0.102"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a4385e2e34eb35d6b3efe798b9eb88096925d87726c0798709bf56d9ed84af3"
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
[[package]]
name = "async-trait"
@@ -59,9 +59,9 @@ dependencies = [
[[package]]
name = "autocfg"
version = "1.5.1"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
[[package]]
name = "bit-set"
@@ -80,9 +80,9 @@ checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7"
[[package]]
name = "bitflags"
version = "2.13.1"
version = "2.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da"
checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3"
[[package]]
name = "borrow-or-share"
@@ -92,19 +92,19 @@ checksum = "dc0b364ead1874514c8c2855ab558056ebfeb775653e7ae45ff72f28f8f3166c"
[[package]]
name = "bstr"
version = "1.13.0"
version = "1.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f7dc094d718f2e1c1559ad110e27eeaae14a5465d3d56dd6dbd793079fbd530"
checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab"
dependencies = [
"memchr",
"serde_core",
"serde",
]
[[package]]
name = "bumpalo"
version = "3.20.3"
version = "3.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649"
checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb"
[[package]]
name = "bytecount"
@@ -120,9 +120,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
[[package]]
name = "cc"
version = "1.2.67"
version = "1.2.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e17dd265a7d0f31ef544e1b20e03add05d3b45b491b633b10d67145d2acc1a38"
checksum = "d16d90359e986641506914ba71350897565610e87ce0ad9e6f28569db3dd5c6d"
dependencies = [
"find-msvc-tools",
"shlex",
@@ -136,9 +136,9 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "chacha20"
version = "0.10.1"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d524456ba66e72eb8b115ff89e01e497f8e6d11d78b70b1aa13c0fbd97540a81"
checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601"
dependencies = [
"cfg-if",
"cpufeatures",
@@ -147,9 +147,9 @@ dependencies = [
[[package]]
name = "chrono"
version = "0.4.45"
version = "0.4.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327"
checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0"
dependencies = [
"iana-time-zone",
"js-sys",
@@ -200,9 +200,9 @@ checksum = "a4ae5f15dda3c708c0ade84bfee31ccab44a3da4f88015ed22f63732abe300c8"
[[package]]
name = "displaydoc"
version = "0.2.6"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f"
checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
dependencies = [
"proc-macro2",
"quote",
@@ -238,9 +238,9 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "fancy-regex"
version = "0.18.0"
version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1e1dacd0d2082dfcf1351c4bdd566bbe89a2b263235a2b50058f1e130a47277"
checksum = "72cf461f865c862bb7dc573f643dd6a2b6842f7c30b07882b56bd148cc2761b8"
dependencies = [
"bit-set",
"regex-automata",
@@ -264,6 +264,12 @@ dependencies = [
"serde",
]
[[package]]
name = "foldhash"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
[[package]]
name = "foldhash"
version = "0.2.0"
@@ -342,23 +348,25 @@ dependencies = [
[[package]]
name = "getrandom"
version = "0.4.3"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099"
checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555"
dependencies = [
"cfg-if",
"js-sys",
"libc",
"r-efi 6.0.0",
"rand_core",
"wasip2",
"wasip3",
"wasm-bindgen",
]
[[package]]
name = "globset"
version = "0.4.19"
version = "0.4.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e47d37d2ae4464254884b60ab7071be2b876a9c35b696bd018ddcc76847309cd"
checksum = "52dfc19153a48bde0cbd630453615c8151bce3a5adfac7a0aebfbf0a1e1f57e3"
dependencies = [
"aho-corasick",
"bstr",
@@ -368,15 +376,36 @@ dependencies = [
[[package]]
name = "hashbrown"
version = "0.17.1"
version = "0.15.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
dependencies = [
"foldhash 0.1.5",
]
[[package]]
name = "hashbrown"
version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
dependencies = [
"allocator-api2",
"equivalent",
"foldhash",
"foldhash 0.2.0",
]
[[package]]
name = "hashbrown"
version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51"
[[package]]
name = "heck"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "iana-time-zone"
version = "0.1.65"
@@ -483,6 +512,12 @@ dependencies = [
"zerovec",
]
[[package]]
name = "id-arena"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954"
[[package]]
name = "idna"
version = "1.1.0"
@@ -511,7 +546,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9"
dependencies = [
"equivalent",
"hashbrown",
"hashbrown 0.17.0",
"serde",
"serde_core",
]
@@ -530,20 +565,21 @@ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "js-sys"
version = "0.3.103"
version = "0.3.97"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53b44bfcdb3f8d5837a46dae1ca9660a837176eee74a28b229bc626816589102"
checksum = "a1840c94c045fbcf8ba2812c95db44499f7c64910a912551aaaa541decebcacf"
dependencies = [
"cfg-if",
"futures-util",
"once_cell",
"wasm-bindgen",
]
[[package]]
name = "jsonschema"
version = "0.47.0"
version = "0.45.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "281c43ff06dcb331e9356d30e38853d559ce3d0a3f693e0b0e102667dec14fb1"
checksum = "257eb0e588b76827bbddc9e73945a9743693dd2adeaee9da26420f93cfedb798"
dependencies = [
"ahash",
"bytecount",
@@ -554,33 +590,30 @@ dependencies = [
"getrandom 0.3.4",
"idna",
"itoa",
"jsonschema-regex",
"num-cmp",
"num-traits",
"percent-encoding",
"referencing",
"regex",
"regex-syntax",
"serde",
"serde_json",
"unicode-general-category",
"uuid-simd",
]
[[package]]
name = "jsonschema-regex"
version = "0.47.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee0b351864e7ffbc5db9273daf7fa1b4d5177b0946713d667ca571b83c0b4045"
dependencies = [
"regex-syntax",
]
[[package]]
name = "lazy_static"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]]
name = "leb128fmt"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
[[package]]
name = "libc"
version = "0.2.186"
@@ -610,27 +643,21 @@ dependencies = [
[[package]]
name = "log"
version = "0.4.33"
version = "0.4.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad"
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
[[package]]
name = "lru"
version = "0.18.1"
version = "0.16.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b6180140927ee907000b0aa540091f6ea512ead4447c92b8fc35bc72788a5a6"
checksum = "7f66e8d5d03f609abc3a39e6f08e4164ebf1447a732906d39eb9b99b7919ef39"
[[package]]
name = "memchr"
version = "2.8.3"
version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
[[package]]
name = "micromap"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2a86d3146ed3995b5913c414f6664344b9617457320782e64f0bb44afd49d74"
checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
[[package]]
name = "minicov"
@@ -666,7 +693,7 @@ version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23"
dependencies = [
"num-bigint 0.4.8",
"num-bigint",
"num-complex",
"num-integer",
"num-iter",
@@ -676,19 +703,9 @@ dependencies = [
[[package]]
name = "num-bigint"
version = "0.4.8"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c89e69e7e0f03bea5ef08013795c25018e101932225a656383bd384495ecc367"
dependencies = [
"num-integer",
"num-traits",
]
[[package]]
name = "num-bigint"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93e7820bc0a80a0238e650327316f929ba18d5be054b647490a3a6a339f3e7c0"
checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
dependencies = [
"num-integer",
"num-traits",
@@ -720,10 +737,11 @@ dependencies = [
[[package]]
name = "num-iter"
version = "0.1.46"
version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c92800bd69a1eac91786bcfe9da64a897eb72911b8dc3095decbd07429e8048b"
checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
]
@@ -734,7 +752,7 @@ version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824"
dependencies = [
"num-bigint 0.4.8",
"num-bigint",
"num-integer",
"num-traits",
]
@@ -841,6 +859,16 @@ dependencies = [
"zerovec",
]
[[package]]
name = "prettyplease"
version = "0.2.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
dependencies = [
"proc-macro2",
"syn",
]
[[package]]
name = "proc-macro2"
version = "1.0.106"
@@ -852,9 +880,9 @@ dependencies = [
[[package]]
name = "quote"
version = "1.0.46"
version = "1.0.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368"
checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
dependencies = [
"proc-macro2",
]
@@ -873,12 +901,12 @@ checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
[[package]]
name = "rand"
version = "0.10.2"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7f5fa3a058cd35567ef9bfa5e75732bee0f9e4c55fa90477bef2dfcdbc4be80"
checksum = "d2e8e8bcc7961af1fdac401278c6a831614941f6164ee3bf4ce61b7edb162207"
dependencies = [
"chacha20",
"getrandom 0.4.3",
"getrandom 0.4.2",
"rand_core",
]
@@ -919,16 +947,14 @@ dependencies = [
[[package]]
name = "referencing"
version = "0.47.0"
version = "0.45.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "348e860aeb0b7bd035778fd11dd9cd5290d32e4aed3b8f2274a00287a9fd362b"
checksum = "e2f38748ceca8d0b0013e60f534d94a6e23dfd89fd2a88318fc5a2d04fda1010"
dependencies = [
"ahash",
"fluent-uri",
"getrandom 0.3.4",
"hashbrown",
"itoa",
"micromap",
"hashbrown 0.16.1",
"parking_lot",
"percent-encoding",
"serde_json",
@@ -936,9 +962,9 @@ dependencies = [
[[package]]
name = "regex"
version = "1.13.1"
version = "1.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d"
checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276"
dependencies = [
"aho-corasick",
"memchr",
@@ -948,9 +974,9 @@ dependencies = [
[[package]]
name = "regex-automata"
version = "0.4.16"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fcfdb36bda0c880c5931cdc7a2bcdc8ba4556847b9d912bca70bc94708711ad"
checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f"
dependencies = [
"aho-corasick",
"memchr",
@@ -959,13 +985,13 @@ dependencies = [
[[package]]
name = "regex-syntax"
version = "0.8.11"
version = "0.8.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4"
checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a"
[[package]]
name = "regorus"
version = "0.11.0"
version = "0.10.0"
dependencies = [
"anyhow",
"chrono",
@@ -978,7 +1004,7 @@ dependencies = [
"lazy_static",
"lru",
"msvc_spectre_libs",
"num-bigint 0.5.1",
"num-bigint",
"num-traits",
"parking_lot",
"postcard",
@@ -996,11 +1022,11 @@ dependencies = [
[[package]]
name = "regorusjs"
version = "0.11.0"
version = "0.10.0"
dependencies = [
"getrandom 0.2.17",
"getrandom 0.3.4",
"getrandom 0.4.3",
"getrandom 0.4.2",
"regorus",
"serde",
"serde-wasm-bindgen",
@@ -1012,9 +1038,9 @@ dependencies = [
[[package]]
name = "rustversion"
version = "1.0.23"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f"
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
[[package]]
name = "ryu"
@@ -1086,9 +1112,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.150"
version = "1.0.149"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9"
checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86"
dependencies = [
"itoa",
"memchr",
@@ -1112,15 +1138,15 @@ dependencies = [
[[package]]
name = "shlex"
version = "2.0.1"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "siphasher"
version = "1.0.3"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649"
checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e"
[[package]]
name = "slab"
@@ -1130,15 +1156,15 @@ checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
[[package]]
name = "smallvec"
version = "1.15.2"
version = "1.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90"
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
[[package]]
name = "spin"
version = "0.12.2"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8abadc99fd9c7bbb7d0ca2b31d72a067d0c0dcd7aad25ab8cac71ba91417694b"
checksum = "d5fe4ccb98d9c292d56fec89a5e07da7fc4cf0dc11e156b41793132775d3e591"
[[package]]
name = "stable_deref_trait"
@@ -1148,9 +1174,9 @@ checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
[[package]]
name = "syn"
version = "2.0.119"
version = "2.0.117"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297"
checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
dependencies = [
"proc-macro2",
"quote",
@@ -1210,6 +1236,12 @@ version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
[[package]]
name = "unicode-xid"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
[[package]]
name = "unsafe-libyaml"
version = "0.2.11"
@@ -1236,11 +1268,11 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
[[package]]
name = "uuid"
version = "1.24.0"
version = "1.23.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf3923a6f5c4c6382e0b653c4117f48d631ea17f38ed86e2a828e6f7412f5239"
checksum = "ddd74a9687298c6858e9b88ec8935ec45d22e8fd5e6394fa1bd4e99a87789c76"
dependencies = [
"getrandom 0.4.3",
"getrandom 0.4.2",
"js-sys",
"rand",
"wasm-bindgen",
@@ -1286,18 +1318,27 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
[[package]]
name = "wasip2"
version = "1.0.4+wasi-0.2.12"
version = "1.0.3+wasi-0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487"
checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6"
dependencies = [
"wit-bindgen",
"wit-bindgen 0.57.1",
]
[[package]]
name = "wasip3"
version = "0.4.0+wasi-0.3.0-rc-2026-01-06"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5"
dependencies = [
"wit-bindgen 0.51.0",
]
[[package]]
name = "wasm-bindgen"
version = "0.2.126"
version = "0.2.120"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b067c0c11094aef6b7a801c1e34a26affafdf3d051dba08456b868789aaf9a4"
checksum = "df52b6d9b87e0c74c9edfa1eb2d9bf85e5d63515474513aa50fa181b3c4f5db1"
dependencies = [
"cfg-if",
"once_cell",
@@ -1308,9 +1349,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-futures"
version = "0.4.76"
version = "0.4.70"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c62df1340f32221cb9c54d6a27b030e3dba64361d4a95bed55f9aacb44da291d"
checksum = "af934872acec734c2d80e6617bbb5ff4f12b052dd8e6332b0817bce889516084"
dependencies = [
"js-sys",
"wasm-bindgen",
@@ -1318,9 +1359,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.126"
version = "0.2.120"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "167ce5e579f6bcf889c4f7175a8a5a585de84e8ff93976ce393efa5f2837aab1"
checksum = "78b1041f495fb322e64aca85f5756b2172e35cd459376e67f2a6c9dffcedb103"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@@ -1328,9 +1369,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.126"
version = "0.2.120"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3997c7839262f4ef12cf90b818d6340c18e80f263f1a94bf157d0ec4420380e"
checksum = "9dcd0ff20416988a18ac686d4d4d0f6aae9ebf08a389ff5d29012b05af2a1b41"
dependencies = [
"bumpalo",
"proc-macro2",
@@ -1341,18 +1382,18 @@ dependencies = [
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.126"
version = "0.2.120"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc1b4cb0cc549fcf58d7dfc081778139b3d283a081644e833e84682ad71cea24"
checksum = "49757b3c82ebf16c57d69365a142940b384176c24df52a087fb748e2085359ea"
dependencies = [
"unicode-ident",
]
[[package]]
name = "wasm-bindgen-test"
version = "0.3.76"
version = "0.3.70"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a0d555ca874445df8d314f94f5c948a4e74e5418f332c89f660a3d8310a96f4"
checksum = "29826f9d9ecaa314c480d376b276d1c790e6cb6a4681fab8532da69cbabf977d"
dependencies = [
"async-trait",
"cast",
@@ -1372,9 +1413,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-test-macro"
version = "0.3.76"
version = "0.3.70"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94eb68555b95bcea5e8cf4abe280b529049479fa995bfc23734af96a6aedc120"
checksum = "c610311887f9e6599a546d278d12d69dfd3a3e92639b2129e4b11ad6cf1961d6"
dependencies = [
"proc-macro2",
"quote",
@@ -1383,9 +1424,43 @@ dependencies = [
[[package]]
name = "wasm-bindgen-test-shared"
version = "0.2.126"
version = "0.2.120"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c31d56021e873866c968588ed85ccdf56db5c426e44afdb4618c39895104b920"
checksum = "60238e5b4b1b295701d6f9a66d2a126fe19990348f5fb9dae3b623a370119d94"
[[package]]
name = "wasm-encoder"
version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319"
dependencies = [
"leb128fmt",
"wasmparser",
]
[[package]]
name = "wasm-metadata"
version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909"
dependencies = [
"anyhow",
"indexmap",
"wasm-encoder",
"wasmparser",
]
[[package]]
name = "wasmparser"
version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe"
dependencies = [
"bitflags",
"hashbrown 0.15.5",
"indexmap",
"semver",
]
[[package]]
name = "winapi-util"
@@ -1464,12 +1539,100 @@ dependencies = [
"windows-link",
]
[[package]]
name = "wit-bindgen"
version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5"
dependencies = [
"wit-bindgen-rust-macro",
]
[[package]]
name = "wit-bindgen"
version = "0.57.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e"
[[package]]
name = "wit-bindgen-core"
version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc"
dependencies = [
"anyhow",
"heck",
"wit-parser",
]
[[package]]
name = "wit-bindgen-rust"
version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21"
dependencies = [
"anyhow",
"heck",
"indexmap",
"prettyplease",
"syn",
"wasm-metadata",
"wit-bindgen-core",
"wit-component",
]
[[package]]
name = "wit-bindgen-rust-macro"
version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a"
dependencies = [
"anyhow",
"prettyplease",
"proc-macro2",
"quote",
"syn",
"wit-bindgen-core",
"wit-bindgen-rust",
]
[[package]]
name = "wit-component"
version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2"
dependencies = [
"anyhow",
"bitflags",
"indexmap",
"log",
"serde",
"serde_derive",
"serde_json",
"wasm-encoder",
"wasm-metadata",
"wasmparser",
"wit-parser",
]
[[package]]
name = "wit-parser"
version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736"
dependencies = [
"anyhow",
"id-arena",
"indexmap",
"log",
"semver",
"serde",
"serde_derive",
"serde_json",
"unicode-xid",
"wasmparser",
]
[[package]]
name = "writeable"
version = "0.6.3"
@@ -1478,9 +1641,9 @@ checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4"
[[package]]
name = "yoke"
version = "0.8.3"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "709fe23a0424b6a435d82152b1bd3fdfb0833487d5fa90d05d42762a9891fef5"
checksum = "abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca"
dependencies = [
"stable_deref_trait",
"yoke-derive",
@@ -1501,18 +1664,18 @@ dependencies = [
[[package]]
name = "zerocopy"
version = "0.8.54"
version = "0.8.48"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7cbbc0a705a0fd05cc3676525980d2bf5a9bc4adac6d6475209a7887cf59d19"
checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.8.54"
version = "0.8.48"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2e817b7b52d0c7358d3246da9d69935ebb18116b2b102b4230dac079b4862f5"
checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4"
dependencies = [
"proc-macro2",
"quote",
@@ -1521,9 +1684,9 @@ dependencies = [
[[package]]
name = "zerofrom"
version = "0.1.8"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272"
checksum = "69faa1f2a1ea75661980b013019ed6687ed0e83d069bc1114e2cc74c6c04c4df"
dependencies = [
"zerofrom-derive",
]
@@ -1575,6 +1738,6 @@ dependencies = [
[[package]]
name = "zmij"
version = "1.0.23"
version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b"
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"

View File

@@ -2,7 +2,7 @@
[package]
name = "regorusjs"
version = "0.11.0"
version = "0.10.0"
edition = "2021"
repository = "https://github.com/microsoft/regorus/bindings/wasm"
description = "WASM bindings for Regorus - a fast, lightweight Rego interpreter written in Rust"
@@ -42,7 +42,7 @@ coverage = ["regorus/coverage"]
[dependencies]
regorus = { path = "../..", default-features = false, features = ["arc", "rvm"] }
serde = { version = "1.0.219", features = ["derive"] }
serde_json = "1.0.150"
serde_json = "1.0.140"
wasm-bindgen = "0.2.100"
serde-wasm-bindgen = "0.6"
# Specify uuid as a mandatory dependency so as to enable `js` feature which is now required
@@ -55,7 +55,7 @@ getrandom03 = { package = "getrandom", version = "0.3.1", features = ["std", "wa
getrandom = { version = "0.4.2", features = ["wasm_js"] }
[dev-dependencies]
wasm-bindgen-test = "0.3.72"
wasm-bindgen-test = "0.3.67"
[lints.rust]
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(wasm_bindgen_unstable_test_coverage)'] }

View File

@@ -254,13 +254,7 @@ include formatted state snapshots where possible.
7. **Host await**: In run-to-completion mode, `HostAwait` consumes a response
from `host_await_responses`. Suspendable mode yields control with a
`SuspendReason::HostAwait { dest, argument, identifier }` that the host must
service. The compiler supports two ways to emit `HostAwait`:
- **Explicit**: `__builtin_host_await(payload, identifier)` — raw 2-argument
form.
- **Registered**: `compile_from_policy_with_host_await` accepts a list of
`(name, arg_count)` pairs. Calls to registered names are compiled as
`HostAwait` with the function name as the identifier literal. Registered
names take precedence over user-defined functions and standard builtins.
service.
8. **Completion**: `Return` wraps the selected register value into
`InstructionOutcome::Return`, unwinding frames until the entry frame is
cleared. `RuleReturn` is a specialised variant used by rule execution

View File

@@ -177,75 +177,6 @@ Parameter tables:
- Suspendable: emits `InstructionOutcome::Suspend` with `SuspendReason::HostAwait`.
The host must resume with a value that will be written into `dest`.
### Registered host-await builtins
The compiler can be configured with a list of function names that map directly
to `HostAwait` instructions. This allows policy authors to write natural
function calls (e.g. `lookup(input.account_id)`) instead of the raw
`__builtin_host_await(payload, identifier)` builtin.
Registration is done at compile time via `Compiler::compile_from_policy_with_host_await`:
```rust
let builtins = [("lookup", 1), ("persist", 1)];
let program = Compiler::compile_from_policy_with_host_await(
&compiled_policy, &entry_points, &builtins,
)?;
```
Each registered name is a `(name, arg_count)` pair. When the compiler
encounters a call to a registered name, it emits a `HostAwait` instruction
with:
- `arg` = the first argument register
- `id` = a register loaded with a string literal containing the function name
Both the explicit `__builtin_host_await(arg, id)` call and a registered
builtin call produce the **same `HostAwait` bytecode instruction**. The only
difference is how the `id` register is populated: explicit calls take it from
the second user-supplied argument, while registered calls auto-generate a
`Load` instruction for the function name string. The VM cannot distinguish
between the two at runtime.
**Resolution order** in `determine_call_target()`:
1. `__builtin_host_await` (magic 2-argument form)
2. Registered host-await builtins (matched by **bare** function name only)
3. User-defined functions (matched by package-qualified path)
4. Standard builtins (matched by bare function name)
Registered names shadow both user-defined functions and standard builtins.
This means `time.parse_duration_ns` can be overridden to route through the
host instead of the built-in Rust implementation.
**Only unqualified calls are intercepted.** Registration matches a call by
the name *as written in the policy*. A bare call — `lookup(x)` — is
intercepted and compiled to a `HostAwait`. A package-qualified call —
`data.pkg.lookup(x)` — is **not** intercepted; it is resolved normally, as
if the name were never registered.
```rego
# "lookup" is registered as a host-await builtin.
package other
import rego.v1
lookup(k) := k # an ordinary rule that happens to share the name
package demo
import rego.v1
a := lookup(input.k) # intercepted -> HostAwait
b := data.other.lookup(input.k) # NOT intercepted -> calls other.lookup
```
The qualified form is resolved exactly as it would be without registration:
if a rule exists at that path it is called, otherwise compilation fails with
`Unknown function`. (A standard builtin like `count` has no qualified form at
all, so `data.pkg.count(x)` is always an `Unknown function` error, registered
or not.)
**Argument handling**: The `HostAwait` instruction carries a single `arg`
register. Registered builtins must use `arg_count: 1`; the compiler rejects
`arg_count > 1` at registration time. To pass multiple values, use object
packing: `lookup({"user": x, "resource": y})`.
---
## Halt instruction

View File

@@ -1,84 +0,0 @@
# Object
Opaque container for `Value::Object`'s key→value storage, enabling
alternative backends without call-site changes.
## Design
`Object` wraps the storage for a key→value collection of `Value`s and
provides a curated set of methods (`get`, `insert`, `remove`, `iter`,
`iter_sorted`, `cursor`, serde). The backing store is private; callers
never see or pattern-match on it, so the representation can change
without rippling through call sites.
Multiple backends can coexist at runtime. Because the backing store is
private, different `Object` instances in the same process can use
different implementations — e.g., a lazy DB-backed object for `input`,
inline small-map objects for SARIF location records, and a regular
sorted map elsewhere — all interoperating through the same opaque
type. This is stronger than the typical Cargo-feature-selected backend
seen in precedent crates.
Iteration is split intentionally. `iter()` makes no ordering promise,
which lets backends that don't keep entries sorted skip any sort work.
`iter_sorted()` returns entries in `Value` order and is what
serialization and `Ord` rely on for deterministic output. Cursor types
add resumable, incremental traversal for the RVM iteration state
without leaking iterator internals.
`Ord` and `PartialOrd` are defined against `iter_sorted()` rather than
derived from the storage. Two `Object`s built on different backends —
or with different insertion histories — compare equal whenever their
sorted entries match, so changing the backend never changes observable
comparison results.
## Precedents
Other crates that hide storage behind a stable API so the implementation
can change without breaking callers:
- **`serde_json::Map`** — opaque newtype allowing cargo-feature based
swap between `BTreeMap` (canonical order) and `IndexMap` (insertion
order).
- **`toml::Table`** — opaque newtype allowing cargo-feature based swap
between `BTreeMap` and `IndexMap`.
- **`simdjson` DOM** — opaque tree that lazily materializes nodes on
access instead of parsing the whole document up front.
## Use cases
- **SARIF small-object pressure** — SARIF reports contain millions of
small objects (location records, rule references, message arguments),
most with 2-5 keys. A small-map-optimized backend (inline storage
for ≤N entries, heap above) eliminates per-object BTreeMap allocation
for the common case.
- **Kubernetes admission policies** — large, deeply-nested resource
objects (Pod specs, CRDs) where policies typically touch a handful
of paths. A lazy-materializing backend (`LazyObjectProvider` over
the incoming JSON) parses only the accessed subtrees.
- **Azure Policy aliases** — ARM exposes the same logical property
under multiple aliases (e.g. paths like
`Microsoft.Compute/virtualMachines/storageProfile.osDisk.managedDisk.id`).
An alias-aware backend resolves lookups across canonical and alias
forms without rewriting every policy.
- **Azure Policy case-insensitive compare** — ARM property names are
case-preserving but case-insensitive on lookup (`tags.Environment`
and `tags.environment` resolve identically). A case-insensitive
backend centralizes this once at the storage layer instead of at
every comparison site.
- **External data sources** — `input` or `data` backed by a database
query, CBOR slice, REST endpoint, or other streaming source via a
`LazyObjectProvider`. Entries materialize on demand; the policy
only pays for what it touches.
- **Eval-time temporaries** — objects constructed during evaluation
(comprehensions, intermediate rule results) on a bumpalo arena.
The whole arena drops at query end with zero per-entry free cost.
- **Host-language interop** — Python dicts or JS objects accessed via
FFI callbacks from the embedding application, without copying into
Rust on every binding boundary.

View File

@@ -1,79 +0,0 @@
# Set
Opaque container for `Value::Set`'s element storage, enabling alternative
backends without call-site changes. Pairs with [`Object`](object.md) under
a shared design philosophy.
## Design
`Set` wraps a `BTreeSet<Value>` today but exposes only a curated method
surface (`contains`, `insert`, `remove`, `iter`, `iter_sorted`, `cursor`,
`is_subset`, `intersection`, `union`, `difference`, serde). The inner set is
private — callers cannot pattern-match it or hand out references to the
backing store, so the backend can change without churn at the ~400 call
sites that name `Set`.
Two iteration methods reflect a real distinction: `iter()` makes no
ordering promise (lets future hash/lazy backends skip sorting work);
`iter_sorted()` guarantees deterministic order (used by serialization and
`Ord`). Cursor types support incremental traversal needed by the RVM
iteration state without exposing iterator internals.
`Ord` is hand-written against `iter_sorted` rather than derived, so two
backends that store elements differently still compare equal when their
sorted contents match.
## Scenarios enabled
- **Hash-backed storage** — `FxHashSet`-backed inner turns O(log n)
membership checks into O(1); swap in for policies where elements aren't
compared ordinally.
- **Lazy/streaming** — wrap a `LazySetProvider` (DB query, CBOR slice,
REST endpoint) and materialize elements on demand.
- **Arena allocation** — bumpalo-backed inner for eval-time temporaries;
drop the whole arena at query end with zero per-element free cost.
- **FFI-backed** — host-language collections (Python set, JS Set) without
copying into Rust.
- **Bloom-filter pre-check** — front a large backing set with a Bloom
filter for fast negative-membership tests on read-mostly allowlists.
## Known use cases
- **Azure Policy allowed-values lists** — large allowlists (allowed
regions, allowed SKUs, allowed image publishers) compared against
single resource values. Hash-backed Set turns O(log n) membership
checks into O(1).
- **SARIF rule deduplication** — collapsing duplicate rule references
across thousands of result records. Set-of-objects with structural
hashing avoids the BTreeSet sort cost on every insert.
- **RBAC role membership** — checking whether a principal belongs to any
of dozens of role groups. Hash-backed Set scales to thousands of
members with constant-time membership.
- **Azure Policy denied-resource-type sets** — exclusion lists used by
deny-effect policies; same hash-backed pattern as allowed-values.
## Precedents
- **`indexmap::IndexSet`** — opaque newtype that pairs hash lookup with
insertion-order iteration; precedent for "Set with alternative
ordering semantics behind a stable surface."
- **`hashbrown::HashSet`** — backs Rust's `std::collections::HashSet`
and demonstrates a fully swappable backend behind a stable API.
- **`roaring::RoaringBitmap`** — bitmap-backed integer set. Not
applicable to `Value` keys directly, but a precedent for the broader
idea of "Set with alternative storage representations chosen by
workload shape."
- **`serde_json`** — note that `serde_json` has no Set equivalent: its
Value enum collapses sets into arrays. Regorus's first-class Set with
storage abstraction is therefore unusually well-positioned among JSON
value libraries.
## Notes
Cursor types are `pub` (referenced by public `IterationState`) but not
re-exported at the crate root. The crate-internal `Set`/`Map`/`MapEntry`
aliases for `BTreeSet`/`BTreeMap` in `lib.rs` were renamed to
`MapSet`/`Map`/`MapEntry` when this type landed, to free the `Set` name
for the new public type. Future Array and String abstractions follow the
same shape — see `docs/value/array.md` and `docs/value/string.md` when
they land.

View File

@@ -23,7 +23,8 @@ use regorus::languages::azure_policy::aliases::AliasRegistry;
use regorus::languages::azure_policy::compiler;
use regorus::languages::azure_policy::parser;
use regorus::rvm::RegoVM;
use regorus::{Rc, Source, Value};
use regorus::Source;
use regorus::Value;
/// Evaluate an Azure Policy definition against a resource.
///
@@ -59,8 +60,11 @@ pub fn azure_policy_eval(
println!("Parsed policy definition from {policy_definition}");
// 3. Compile to RVM bytecode.
let registry = Rc::new(registry);
let program = compiler::compile_policy_definition_with_aliases(&defn, Rc::clone(&registry))?;
let program = compiler::compile_policy_definition_with_aliases(
&defn,
registry.alias_map(),
registry.alias_modifiable_map(),
)?;
println!("Compiled policy to RVM bytecode");
// 4. Build normalized input.
@@ -134,7 +138,7 @@ pub fn azure_policy_aliases(aliases: String, resource_type: Option<String>) -> R
if let Some(ref rt) = resource_type {
let rt_lower = rt.to_lowercase();
let mut found = false;
for alias_name in registry.alias_map().keys() {
for (alias_name, _) in registry.alias_map() {
if alias_name.to_lowercase().starts_with(&rt_lower) {
println!(" {alias_name}");
found = true;
@@ -144,7 +148,7 @@ pub fn azure_policy_aliases(aliases: String, resource_type: Option<String>) -> R
bail!("no aliases found for resource type '{rt}'");
}
} else {
for alias_name in registry.alias_map().keys() {
for (alias_name, _) in registry.alias_map() {
println!(" {alias_name}");
}
}

View File

@@ -2,7 +2,7 @@
name = "regorus-mimalloc"
description = "Vendored mimalloc allocator for regorus"
edition = "2021"
version = "2.2.7"
version = "2.2.6"
license = "MIT"
repository = "https://github.com/microsoft/regorus"

View File

@@ -319,7 +319,7 @@ pub fn resolve_path(root: &Value, path: &str) -> Value {
match &current {
Value::Object(map) => {
let mut next = None;
for (key, value) in map.iter_sorted() {
for (key, value) in map.iter() {
if let Value::String(ref key_str) = *key {
if strings::keys::eq(key_str, &segment) {
next = Some(value.clone());

View File

@@ -8,10 +8,10 @@
use crate::ast::{Expr, Ref};
use crate::builtins;
use crate::lexer::Span;
use crate::value::Object;
use crate::value::Value;
use crate::Rc;
use alloc::collections::BTreeMap;
use alloc::vec::Vec;
use anyhow::Result;
@@ -72,7 +72,7 @@ fn fn_intersection(
// Intersection of objects: keep key-value pairs from the first
// object only when the key exists in every other object AND
// the value is equal across all of them.
let mut result: Object = first.as_ref().clone();
let mut result: BTreeMap<Value, Value> = first.as_ref().clone();
for arg in rest {
let Value::Object(ref other) = *arg else {
return Ok(Value::Undefined);
@@ -114,7 +114,7 @@ fn fn_union(_span: &Span, _params: &[Ref<Expr>], args: &[Value], _strict: bool)
Value::Object(_) => {
// Union of objects: recursive merge. Nested objects are merged
// recursively; all other types (including arrays) use last-writer-wins.
let mut result = Object::new();
let mut result = BTreeMap::<Value, Value>::new();
for arg in args {
let Value::Object(ref obj) = *arg else {
return Ok(Value::Undefined);
@@ -264,7 +264,7 @@ fn fn_create_object(
);
}
let mut map = Object::new();
let mut map = BTreeMap::<Value, Value>::new();
for pair in args.chunks(2) {
#[allow(clippy::pattern_type_mismatch)]
@@ -280,9 +280,9 @@ fn fn_create_object(
/// Recursively merge two objects. Nested objects are merged; everything
/// else (including arrays) uses the value from `incoming`.
fn merge_objects(base: &Object, overlay: &Object) -> Value {
fn merge_objects(base: &BTreeMap<Value, Value>, overlay: &BTreeMap<Value, Value>) -> Value {
let mut result = base.clone();
for (k, v) in overlay.iter() {
for (k, v) in overlay {
#[allow(clippy::needless_borrowed_reference)]
let merged = match (result.get(k), v) {
(Some(&Value::Object(ref prev)), &Value::Object(ref next)) => merge_objects(prev, next),

View File

@@ -8,10 +8,10 @@
use crate::ast::{Expr, Ref};
use crate::builtins;
use crate::lexer::Span;
use crate::value::Object;
use crate::value::Value;
use crate::Rc;
use alloc::collections::BTreeMap;
use alloc::string::{String, ToString as _};
use alloc::vec::Vec;
use anyhow::Result;
@@ -84,8 +84,8 @@ fn fn_items(_span: &Span, _params: &[Ref<Expr>], args: &[Value], _strict: bool)
return Ok(Value::Undefined);
};
let mut result = Vec::with_capacity(obj.len());
for (k, v) in obj.iter_sorted() {
let mut entry = Object::new();
for (k, v) in obj.as_ref() {
let mut entry = BTreeMap::<Value, Value>::new();
entry.insert(Value::from("key"), k.clone());
entry.insert(Value::from("value"), v.clone());
result.push(Value::Object(Rc::new(entry)));

View File

@@ -308,7 +308,7 @@ fn urlquery_encode_object(
{
let mut pairs = url.query_pairs_mut();
for (key, value) in obj.iter_sorted() {
for (key, value) in obj.iter() {
let key = ensure_string(name, &params[0], key)?;
match value {
Value::String(v) => {

View File

@@ -7,11 +7,10 @@ use crate::ast::{Expr, Ref};
use crate::builtins;
use crate::builtins::utils::{enforce_limit, ensure_args_count, ensure_object};
use crate::lexer::Span;
use crate::value::Object;
use crate::value::Value;
use crate::*;
use alloc::collections::BTreeSet;
use alloc::collections::{BTreeMap, BTreeSet};
use anyhow::{bail, Result};
@@ -81,7 +80,7 @@ fn reachable(span: &Span, params: &[Ref<Expr>], args: &[Value], strict: bool) ->
}
fn visit(
graph: &Object,
graph: &BTreeMap<Value, Value>,
visited: &mut BTreeSet<Value>,
node: &Value,
path: &mut Vec<Value>,
@@ -212,7 +211,7 @@ fn walk_visit(path: &mut Vec<Value>, value: &Value, paths: &mut Vec<Value>) -> R
}
}
Value::Object(obj) => {
for (key, value) in obj.iter_sorted() {
for (key, value) in obj.iter() {
path.push(key.clone());
// Guard path stack growth while traversing object entries.
enforce_limit()?;

View File

@@ -205,7 +205,7 @@ fn merge_filters(
let vref = match f {
Value::Object(obj) => {
let obj = Rc::make_mut(obj);
let entry = obj.get_or_insert_with(p.clone(), Value::new_object);
let entry = obj.entry(p.clone()).or_insert_with(Value::new_object);
// Guard filter map growth when creating nested objects.
enforce_limit()?;
entry

View File

@@ -207,7 +207,7 @@ fn to_string(v: &Value, unescape: bool) -> String {
}
Value::Object(o) => {
"{".to_owned()
+ &o.iter_sorted()
+ &o.iter()
.map(|(k, v)| to_string(k, true) + ": " + &to_string(v, true))
.collect::<Vec<String>>()
.join(", ")
@@ -568,7 +568,7 @@ fn replace_n(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -
let mut s = ensure_string(name, &params[1], &args[1])?;
let span = params[0].span();
for item in obj.as_ref().iter_sorted() {
for item in obj.as_ref().iter() {
match item {
(Value::String(k), Value::String(v)) => {
s = s.replace(k.as_ref(), v.as_ref()).into();

View File

@@ -5,12 +5,11 @@
use crate::ast::{Expr, Ref};
use crate::lexer::Span;
use crate::number::Number;
use crate::value::Object;
use crate::Rc;
use crate::Value;
use crate::*;
use alloc::collections::BTreeSet;
use alloc::collections::{BTreeMap, BTreeSet};
use anyhow::{bail, Result};
@@ -169,7 +168,7 @@ pub fn ensure_set(fcn: &str, arg: &Expr, v: Value) -> Result<Rc<BTreeSet<Value>>
})
}
pub fn ensure_object(fcn: &str, arg: &Expr, v: Value) -> Result<Rc<Object>> {
pub fn ensure_object(fcn: &str, arg: &Expr, v: Value) -> Result<Rc<BTreeMap<Value, Value>>> {
Ok(match v {
Value::Object(o) => o,
_ => {

View File

@@ -217,7 +217,7 @@ pub(crate) struct CompiledPolicyData {
pub(crate) default_rules: Map<String, Vec<DefaultRuleInfo>>,
pub(crate) imports: BTreeMap<String, Ref<Expr>>,
pub(crate) functions: FunctionTable,
pub(crate) rule_paths: MapSet<String>,
pub(crate) rule_paths: Set<String>,
#[cfg(feature = "azure_policy")]
pub(crate) target_info: Option<TargetInfo>,
#[cfg(feature = "azure_policy")]

View File

@@ -314,7 +314,7 @@ fn order_element_pairs<T: VariableBindingContext>(
if ready {
let (value_expr, plan, _deps, binds) = remaining.remove(idx);
scheduled.extend(binds);
scheduled.extend(binds.into_iter());
ordered.push((value_expr, plan));
progress = true;
break;

View File

@@ -434,13 +434,7 @@ impl Engine {
/// Add data document.
///
/// The specified data document is deep-merged into the existing data document. Nested
/// objects are merged recursively (matching OPA's data-document merge), so adding
/// `{ "a": { "x": 1 } }` and then `{ "a": { "y": 2 } }` yields `{ "a": { "x": 1, "y": 2 } }`.
/// A conflict — the same path holding two different values — is an error.
///
/// The merge is atomic: if any conflict is detected (including one deep in a nested
/// document), the call fails and the existing data document is left unchanged.
/// The specified data document is merged into existing data document.
///
/// ```
/// # use regorus::*;
@@ -459,13 +453,9 @@ impl Engine {
/// // Merge { "z" : 3 }. Conflict error.
/// assert!(engine.add_data(Value::from_json_str(r#"{ "z" : 3 }"#)?).is_err());
///
/// // Nested objects are deep-merged. Merge { "y" : { "a" : 10 } } then { "y" : { "b" : 20 } }.
/// assert!(engine.add_data(Value::from_json_str(r#"{ "y" : { "a" : 10 } }"#)?).is_ok());
/// assert!(engine.add_data(Value::from_json_str(r#"{ "y" : { "b" : 20 } }"#)?).is_ok());
///
/// assert_eq!(
/// engine.eval_query("data".to_string(), false)?.result[0].expressions[0].value,
/// Value::from_json_str(r#"{ "x": 1, "y": { "a": 10, "b": 20 }, "z": 2}"#)?
/// Value::from_json_str(r#"{ "x": 1, "y": {}, "z": 2}"#)?
/// );
/// # Ok(())
/// # }
@@ -474,29 +464,8 @@ impl Engine {
if data.as_object().is_err() {
bail!("data must be object");
}
// add_data is all-or-nothing; the atomic strategy differs by build because the failure
// modes do: a conflict (same path, differing values) is possible everywhere, an
// allocator-limit failure mid-merge only under `allocator-memory-limits`.
#[cfg(not(feature = "allocator-memory-limits"))]
{
// Conflict is the only failure mode; `check_mergeable` catches it up front without
// allocating, so validate then deep-merge in place (zero-copy fast path).
self.interpreter.get_init_data().check_mergeable(&data)?;
self.prepared = false;
self.interpreter.get_init_data_mut().deep_merge(data)
}
#[cfg(feature = "allocator-memory-limits")]
{
// A limit failure can strike mid-merge and can't be predicted, so merge into a
// candidate and commit only on success. `Value` is copy-on-write, so only touched
// subtrees are cloned.
let mut candidate = self.interpreter.get_init_data().clone();
candidate.deep_merge(data)?;
*self.interpreter.get_init_data_mut() = candidate;
self.prepared = false;
Ok(())
}
self.prepared = false;
self.interpreter.get_init_data_mut().merge(data)
}
/// Get the data document.

View File

@@ -4,9 +4,9 @@
use crate::ast::*;
use crate::builtins::{self, BuiltinFcn};
use crate::compiled_policy::CompiledPolicyData;
#[cfg(feature = "azure_policy")]
use crate::compiled_policy::TargetInfo;
use crate::compiled_policy::{CompiledPolicyData, DefaultRuleInfo};
use crate::compiler::destructuring_planner::{
AssignmentPlan, BindingPlan, DestructuringPlan, WildcardSide,
};
@@ -28,6 +28,7 @@ use crate::{Expression, Extension, Location, QueryResult, QueryResults};
use crate::query::traversal::traverse;
use crate::Rc;
use alloc::collections::btree_map::Entry as BTreeMapEntry;
use alloc::collections::{BTreeMap, BTreeSet};
use anyhow::{anyhow, bail, Result};
use core::ops::Bound::*;
@@ -60,17 +61,6 @@ enum FunctionModifier {
Value(Value),
}
/// How [`Interpreter::update_data`] merges a rule's value into the data document.
#[derive(Debug, Clone, Copy)]
enum RuleValueMerge {
/// Shallow-merge keeping disjoint keys, so rules sharing a path prefix scaffold into one
/// object (`a.foo` + `a.bar` → one `a`) instead of conflicting.
Combine,
/// Complete-rule semantics: existing value must be absent or exactly equal, else conflict.
/// Used for zero-arg function outputs (`f() := …`), which OPA treats like complete rules.
Strict,
}
type RuleValues = BTreeMap<Vec<Value>, (Value, Ref<Expr>)>;
#[derive(Debug)]
@@ -1322,10 +1312,10 @@ impl Interpreter {
*obj = Value::new_object();
}
obj = obj.as_object_mut()?.get_or_insert_with(
Value::String(p.to_string().into()),
Value::new_object,
);
obj = obj
.as_object_mut()?
.entry(Value::String(p.to_string().into()))
.or_insert(Value::new_object());
}
*obj = value;
// Mark modified rules as processed.
@@ -1692,7 +1682,8 @@ impl Interpreter {
let set = obj
.as_object_mut()
.map_err(|_| anyhow!(span.error("previous value is not an object")))?
.get_or_insert_with(p, Value::new_set)
.entry(p)
.or_insert(Value::new_set())
.as_set_mut()
.map_err(|_| anyhow!(span.error("previous value is not a set")))?;
set.append(value.as_set_mut()?);
@@ -1700,13 +1691,20 @@ impl Interpreter {
let obj = obj
.as_object_mut()
.map_err(|_| anyhow!(span.error("previous value is not an object")))?;
if value == Value::Undefined {
// TODO: clean this assumption between Undefined vs Object.
obj.get_or_insert_with(p, Value::new_object);
} else {
let existing = obj.get_or_insert_with(p, || value.clone());
if *existing != value {
bail!(span.error("complete rules should not produce multiple outputs"))
match obj.entry(p) {
BTreeMapEntry::Vacant(v) => {
if value != Value::Undefined {
v.insert(value);
} else {
// TODO: clean this assumption between Undefined vs Object.
v.insert(Value::new_object());
}
}
BTreeMapEntry::Occupied(o) => {
if o.get() != &value && value != Value::Undefined {
bail!(span
.error("complete rules should not produce multiple outputs"))
}
}
}
}
@@ -1715,7 +1713,8 @@ impl Interpreter {
obj = obj
.as_object_mut()
.map_err(|_| anyhow!(span.error("previous value is not an object")))?
.get_or_insert_with(p, Value::new_object);
.entry(p)
.or_insert(Value::new_object());
}
}
Ok(())
@@ -1725,6 +1724,9 @@ impl Interpreter {
// For now, we restrict constant refs to those that contain only simple literals.
fn is_constant_ref(mut expr: &Ref<Expr>) -> Result<bool> {
loop {
if Self::is_simple_literal(expr)? {
return Ok(true);
}
match expr.as_ref() {
Expr::Var { .. } => break,
Expr::RefDot { refr, .. } => expr = refr,
@@ -1748,6 +1750,30 @@ impl Interpreter {
))
}
fn is_constant_key_expr(&self, expr: &Ref<Expr>) -> Result<bool> {
if Self::is_simple_literal(expr)? {
return Ok(true);
}
match expr.as_ref() {
Expr::Var { span, .. } => {
// A variable that is not currently bound in any active local scope behaves like
// a stable global/package reference for this evaluation.
let is_bound = self
.scopes
.iter()
.rev()
.any(|scope| scope.contains_key(&span.source_str()));
Ok(!is_bound)
}
Expr::RefDot { refr, .. } => self.is_constant_key_expr(refr),
Expr::RefBrack { refr, index, .. } => {
Ok(self.is_constant_key_expr(refr)? && self.is_constant_key_expr(index)?)
}
_ => Ok(false),
}
}
// A rule's output expression is constant if it does not contain local variables.
// For now, we restrict output expressions to those that contain only simple literals.
fn is_constant_output(key_expr: &Option<Ref<Expr>>, output_expr: &Ref<Expr>) -> Result<bool> {
@@ -1783,7 +1809,6 @@ impl Interpreter {
let mut comps = self.eval_rule_ref(&rule_ref)?;
if let Some(ke) = &key_expr {
is_const_rule = is_const_rule && Self::is_simple_literal(ke)?;
comps.push(self.eval_expr(ke)?);
}
let output = if let Some(oe) = &output_expr {
@@ -1799,7 +1824,12 @@ impl Interpreter {
comps.pop();
output
} else {
// Rule's constness is determined only by its ref.
// Implicit-true partial object rules can vary with each successful key binding.
if let Some(ke) = &key_expr {
if !is_old_style_set && !self.is_constant_key_expr(ke)? {
is_const_rule = false;
}
}
Value::Bool(true)
};
@@ -1823,7 +1853,8 @@ impl Interpreter {
let set = ctx_mut
.rule_value
.as_object_mut()?
.get_or_insert_with(Value::from_array(comps), Value::new_set);
.entry(Value::from_array(comps))
.or_insert(Value::new_set());
if output != Value::Undefined {
set.as_set_mut()?.insert(output);
return Ok(true);
@@ -1832,13 +1863,20 @@ impl Interpreter {
}
// Non-set rule.
let key = Value::from_array(comps);
let obj_mut = ctx_mut.rule_value.as_object_mut()?;
let existing = obj_mut.get_or_insert_with(key, || output.clone());
if *existing != output {
bail!(rule_ref
match ctx_mut
.rule_value
.as_object_mut()?
.entry(Value::from_array(comps))
{
BTreeMapEntry::Vacant(v) => {
v.insert(output);
}
BTreeMapEntry::Occupied(o) if o.get() != &output => bail!(rule_ref
.span()
.error("rules must not produce multiple outputs"));
.error("rules must not produce multiple outputs")),
_ => {
// Rule produced same value.
}
}
return Ok(true);
@@ -2464,7 +2502,7 @@ impl Interpreter {
}
Value::Object(map) => {
s.push('{');
for (idx, (k, entry_value)) in map.iter_sorted().enumerate() {
for (idx, (k, entry_value)) in map.iter().enumerate() {
if idx > 0 {
s.push_str(", ");
}
@@ -2936,6 +2974,41 @@ impl Interpreter {
Ok(())
}
fn default_rules_for_path(&self, path: &str) -> Option<Vec<DefaultRuleInfo>> {
if let Some(rules) = self.compiled_policy.default_rules.get(path) {
return Some(rules.clone());
}
let (parent_path, index) = path.rsplit_once('.')?;
let rules = self.compiled_policy.default_rules.get(parent_path)?;
let matches = rules
.iter()
.filter(|(_, rule_index)| Self::default_rule_index_matches(rule_index, index))
.cloned()
.collect::<Vec<_>>();
if matches.is_empty() {
None
} else {
Some(matches)
}
}
fn has_default_rules_for_path(&self, path: &str) -> bool {
self.default_rules_for_path(path).is_some()
}
fn default_rule_index_matches(index: &Option<String>, path_component: &str) -> bool {
match index.as_deref() {
Some(index) if index == path_component => true,
Some(index) => index
.strip_prefix('"')
.and_then(|index| index.strip_suffix('"'))
.is_some_and(|index| index == path_component),
None => false,
}
}
fn ensure_rule_evaluated(&mut self, path: String) -> Result<()> {
self.check_execution_time()?;
let mut matched = false;
@@ -2950,9 +3023,9 @@ impl Interpreter {
}
// Evaluate the associated default rules after non-default rules
if let Some(rules) = self.compiled_policy.default_rules.get(&path) {
if let Some(rules) = self.default_rules_for_path(&path) {
matched = true;
for (r, _) in rules.clone() {
for (r, _) in rules {
if !self.processed.contains(&r) {
let module = self.get_rule_module(&r)?;
let prev_module = self.set_current_module(Some(module))?;
@@ -3043,10 +3116,7 @@ impl Interpreter {
let prefix = fields.iter().take(i).copied().collect::<Vec<_>>();
let prefix_path = format!("data.{}", prefix.join("."));
if self.compiled_policy.rules.contains_key(&prefix_path)
|| self
.compiled_policy
.default_rules
.contains_key(&prefix_path)
|| self.has_default_rules_for_path(&prefix_path)
{
self.ensure_rule_evaluated(prefix_path)?;
break;
@@ -3070,7 +3140,7 @@ impl Interpreter {
if !no_error
&& !self.compiled_policy.rules.contains_key(&rule_path)
&& !self.compiled_policy.default_rules.contains_key(&rule_path)
&& !self.has_default_rules_for_path(&rule_path)
&& !self.compiled_policy.imports.contains_key(&rule_path)
{
bail!(span.error(&format!(
@@ -3093,7 +3163,7 @@ impl Interpreter {
};
if self.compiled_policy.rules.contains_key(&path)
|| self.compiled_policy.default_rules.contains_key(&path)
|| self.has_default_rules_for_path(&path)
{
self.ensure_rule_evaluated(path)?;
found = true;
@@ -3419,23 +3489,6 @@ impl Interpreter {
}
}
/// Materialize a complete-rule value: the existing value must be absent or *exactly equal*
/// to `new`, else it is a conflict.
///
/// Unlike the shallow [`Self::merge_rule_value`], differing outputs conflict instead of
/// combining — `f() := {"a": 1}` and `f() := {"b": 2}` conflict — matching OPA's semantics
/// for zero-arg functions.
fn merge_rule_value_strict(span: &Span, value: &mut Value, new: Value) -> Result<()> {
if *value == Value::Undefined {
*value = new;
Ok(())
} else if *value == new {
Ok(())
} else {
Err(span.error("rules should not produce multiple outputs."))
}
}
pub fn get_path_string(refr: &Expr, document: Option<&str>) -> Result<String> {
let mut comps = vec![];
let mut expr_opt = Some(refr);
@@ -3657,7 +3710,7 @@ impl Interpreter {
self.data = Value::Undefined;
self.ensure_loop_var_values_capacity();
let default_rules = self.compiled_policy.default_rules.get(rule_path).cloned();
let default_rules = self.default_rules_for_path(rule_path);
if let Some(rules) = default_rules {
for (rule, _) in rules {
@@ -3691,7 +3744,6 @@ impl Interpreter {
_refr: &Expr,
path: &[&str],
value: Value,
merge: RuleValueMerge,
) -> Result<()> {
if value == Value::Undefined {
return Ok(());
@@ -3699,10 +3751,7 @@ impl Interpreter {
// Ensure that path is created.
let vref = Self::make_or_get_value_mut(&mut self.data, path)?;
if Self::get_value_chained(self.init_data.clone(), path) == Value::Undefined {
match merge {
RuleValueMerge::Strict => Self::merge_rule_value_strict(span, vref, value),
RuleValueMerge::Combine => Self::merge_rule_value(span, vref, value),
}
Self::merge_rule_value(span, vref, value)
} else {
// Retain specified value.
Ok(())
@@ -3810,13 +3859,7 @@ impl Interpreter {
// `a` is created as an empty object.
if let Some((_, prefix)) = path.split_last() {
if !prefix.is_empty() {
self.update_data(
span,
refr,
prefix,
Value::new_object(),
RuleValueMerge::Combine,
)?;
self.update_data(span, refr, prefix, Value::new_object())?;
}
}
@@ -3828,13 +3871,7 @@ impl Interpreter {
};
let value = self.eval_rule_bodies(ctx, span, rule_body)?;
self.update_data(
refr.span(),
refr,
&path[..],
value,
RuleValueMerge::Strict,
)?;
self.update_data(refr.span(), refr, &path[..], value)?;
}
}
}
@@ -4081,7 +4118,6 @@ impl Interpreter {
rule_refr,
&prefix_path,
Value::new_object(),
RuleValueMerge::Combine,
)?;
}
}

View File

@@ -213,10 +213,10 @@ pub fn denormalize_with_aliases(
// Phase 4: Attach properties to result.
if !properties.is_empty() {
if let Some(Value::Object(existing_rc)) = result.get_mut("properties") {
// Merge directly into the Object, avoiding full ObjMap round-trip.
// Merge directly into the BTreeMap, avoiding full ObjMap round-trip.
let existing = Rc::make_mut(existing_rc);
for (k, v) in properties {
existing.get_or_insert_with(Value::String(k), || v);
existing.entry(Value::String(k)).or_insert(v);
}
} else {
obj_insert(&mut result, "properties", make_value(properties));

View File

@@ -7,7 +7,6 @@ use alloc::collections::{BTreeMap, BTreeSet};
use alloc::string::String;
use alloc::vec::Vec;
use crate::value::Object;
use crate::Value;
use super::super::obj_map::{make_value, new_map, obj_insert, val_str, ObjMap};
@@ -142,7 +141,7 @@ fn rewrap_nested_array(
/// BTreeMap-native recursion for nested sub-resource array re-wrapping,
/// avoiding ObjMap round-trips on each array element.
fn rewrap_nested_array_in_btree(
btree: &mut Object,
btree: &mut alloc::collections::BTreeMap<Value, Value>,
parent_parts: &[&str],
array_name: &str,
envelope_fields: &BTreeSet<String>,
@@ -188,7 +187,10 @@ fn rewrap_nested_array_in_btree(
}
/// Find a key in a BTreeMap using case-insensitive comparison.
fn find_key_ci_btree(btree: &Object, key: &str) -> Option<Value> {
fn find_key_ci_btree(
btree: &alloc::collections::BTreeMap<Value, Value>,
key: &str,
) -> Option<Value> {
btree
.keys()
.find(|k| val_str(k).is_some_and(|s| s.eq_ignore_ascii_case(key)))

View File

@@ -172,31 +172,11 @@ impl AliasRegistry {
let prefix = alloc::format!("{}/", fq_type);
for alias in aliases {
// Skip aliases without a default_path — the normalizer's
// resolve_resource_type also skips these, so inserting them into
// compiler maps would cause a divergence where the compiler
// resolves the alias but normalized input never contains the field.
if alias.default_path.is_none() {
continue;
}
// Derive the short name by stripping the resource type prefix.
let raw_short = if alias.name.len() > prefix.len()
&& alias
.name
.get(..prefix.len())
.is_some_and(|s| s.eq_ignore_ascii_case(&prefix))
&& alias.name[..prefix.len()].eq_ignore_ascii_case(&prefix)
{
// Both slice boundaries are valid: prefix is ASCII
// (resource type + '/'), so if `..prefix.len()` succeeded
// above, `prefix.len()..` is guaranteed to be on a char
// boundary too. The `unwrap_or` is a defensive fallback
// that can never trigger for well-formed Azure alias names.
alias
.name
.get(prefix.len()..)
.unwrap_or(&alias.name)
.to_string()
alias.name[prefix.len()..].to_string()
} else if let Some(rest) = alias
.name
.rfind('/')
@@ -280,19 +260,20 @@ impl AliasRegistry {
.map(String::as_str)
}
/// Return a reference to the alias-to-short-name map.
/// Return a clone of the alias-to-short-name map for use by the compiler.
///
/// Keys are lowercase fully-qualified alias names; values are short names.
pub const fn alias_map(&self) -> &BTreeMap<String, String> {
&self.alias_to_short
/// The compiler stores this map internally so it can resolve fully-qualified
/// alias names without holding a reference to the registry.
pub fn alias_map(&self) -> BTreeMap<String, String> {
self.alias_to_short.clone()
}
/// Return a reference to the alias-to-modifiable map.
/// Return a clone of the alias-to-modifiable map for use by the compiler.
///
/// Keys are lowercase fully-qualified alias names; values are `true` when
/// the alias has `defaultMetadata.attributes = "Modifiable"`.
pub const fn alias_modifiable_map(&self) -> &BTreeMap<String, bool> {
&self.alias_modifiable
/// Maps lowercase fully-qualified alias names to `true` when the alias
/// has `defaultMetadata.attributes = "Modifiable"`.
pub fn alias_modifiable_map(&self) -> BTreeMap<String, bool> {
self.alias_modifiable.clone()
}
/// Normalize a raw ARM resource and wrap it in the input envelope.

View File

@@ -4,13 +4,14 @@
//! Per-alias path resolution: reads values from versioned ARM paths and places
//! them at alias short name paths in the normalized output.
use crate::Rc;
use alloc::string::String;
use crate::Value;
use super::super::obj_map::remove_element_field;
use super::super::obj_map::{
collision_safe_key, is_root_field_collision, obj_contains, obj_insert, obj_insert_rc,
obj_remove, set_nested_lowercased, ObjMap,
collision_safe_key, is_root_field_collision, obj_contains, obj_insert, obj_remove,
set_nested_lowercased, ObjMap,
};
use super::super::types::ResolvedAliases;
use super::element_remap::apply_element_remap_precomputed;
@@ -47,14 +48,12 @@ pub fn apply_alias_entries(
if let Some(value) = value {
let value = normalize_value(&value, &entry.short_name, None);
if is_root_field_collision(&entry.short_name, &entry.default_path) {
let target = collision_safe_key(&entry.short_name);
set_nested_lowercased(result, &target, value);
} else if entry.short_name.contains('.') {
set_nested_lowercased(result, &entry.short_name, value);
let target = if is_root_field_collision(&entry.short_name, &entry.default_path) {
collision_safe_key(&entry.short_name)
} else {
obj_insert_rc(result, Rc::clone(&entry.short_name_lc), value);
}
entry.short_name.clone()
};
set_nested_lowercased(result, &target, value);
}
}
@@ -85,13 +84,13 @@ pub fn apply_alias_entries(
}
/// Navigate an ARM path using precomputed segments (avoids per-call split).
fn navigate_arm_path_segments(value: &Value, segments: &[Rc<str>]) -> Option<Value> {
fn navigate_arm_path_segments(value: &Value, segments: &[String]) -> Option<Value> {
let mut current = value;
for segment in segments {
current = current
.as_object()
.ok()?
.get(&Value::String(Rc::clone(segment)))?;
.get(&Value::from(segment.as_str()))?;
}
Some(current.clone())
}

View File

@@ -6,12 +6,11 @@
use alloc::string::String;
use alloc::vec::Vec;
use crate::value::Object;
use crate::Value;
use super::super::obj_map::{
obj_get, obj_get_mut, obj_insert, set_nested, set_nested_lowercased, set_nested_verbatim,
ObjMap,
obj_get, obj_get_mut, obj_insert, set_nested_in_btree, set_nested_lowercased,
set_nested_verbatim, ObjMap,
};
use super::super::types::PrecomputedRemap;
@@ -119,7 +118,7 @@ fn apply_remap_at_depth(
/// BTreeMap-native recursion for element-level remap, avoiding ObjMap
/// round-trips on each array element.
fn remap_at_depth_in_btree(
btree: &mut Object,
btree: &mut alloc::collections::BTreeMap<Value, Value>,
array_chain: &[Vec<String>],
depth: usize,
source_field: &str,
@@ -178,7 +177,12 @@ fn remap_at_depth_in_btree(
}
/// Remap a value between dotted paths directly in a BTreeMap.
fn remap_deep_field_in_btree(btree: &mut Object, source: &str, target: &str, lowercase: bool) {
fn remap_deep_field_in_btree(
btree: &mut alloc::collections::BTreeMap<Value, Value>,
source: &str,
target: &str,
lowercase: bool,
) {
let val = match read_dotted_path_btree(btree, source) {
Some(v) => v,
None => return,
@@ -194,11 +198,14 @@ fn remap_deep_field_in_btree(btree: &mut Object, source: &str, target: &str, low
}
return;
}
set_nested(btree, &segments, val, lowercase);
set_nested_in_btree(btree, &segments, val, lowercase);
}
/// Read a value at a dotted path from a BTreeMap.
fn read_dotted_path_btree(btree: &Object, path: &str) -> Option<Value> {
fn read_dotted_path_btree(
btree: &alloc::collections::BTreeMap<Value, Value>,
path: &str,
) -> Option<Value> {
let segments: Vec<&str> = path.split('.').collect();
let first = segments.first()?;
let mut cur: &Value = btree.get(&Value::from(*first))?;

View File

@@ -13,7 +13,6 @@ mod flatten;
// Re-export items used by the denormalizer.
pub(crate) use element_remap::{apply_element_remap, ElementRemap};
use crate::value::Object;
use crate::Value;
use super::obj_map::{
@@ -110,7 +109,7 @@ pub fn normalize_with_aliases(
/// Merge `properties` fields into the result map, skipping keys that already
/// exist.
fn merge_properties(
obj: &Object,
obj: &alloc::collections::BTreeMap<Value, Value>,
result: &mut ObjMap,
sub_arrays: Option<&alloc::collections::BTreeSet<alloc::string::String>>,
) {

View File

@@ -4,15 +4,14 @@
//! Lightweight string-keyed map used during normalization/denormalization.
//!
//! Internally uses `hashbrown::HashMap<Rc<str>, Value>` for O(1) lookups,
//! then converts to `Value::Object` (an `Object`) only at
//! then converts to `Value::Object` (a `BTreeMap<Value, Value>`) only at
//! the output boundary via [`make_value`].
use alloc::string::String;
use alloc::string::{String, ToString as _};
use alloc::vec::Vec;
use hashbrown::HashMap;
use crate::value::Object;
use crate::Rc;
use crate::Value;
@@ -42,33 +41,6 @@ pub fn obj_insert(map: &mut ObjMap, key: &str, val: Value) {
map.insert(Rc::from(key), val);
}
/// Insert a key-value pair using a pre-allocated `Rc<str>` key.
///
/// Avoids the `Rc::from(key)` heap allocation that [`obj_insert`] performs.
pub fn obj_insert_rc(map: &mut ObjMap, key: Rc<str>, val: Value) {
map.insert(key, val);
}
/// Lowercase a string, returning an `Rc<str>`.
///
/// Both paths allocate an `Rc<str>` (header + string bytes). The fast-path
/// avoids creating an intermediate lowercased `String` when the input is
/// already all-lowercase ASCII.
pub fn rc_lowercase(s: &str) -> Rc<str> {
if s.bytes().all(|b| !b.is_ascii_uppercase()) {
Rc::from(s)
} else {
Rc::from(s.to_ascii_lowercase())
}
}
/// Insert a key-value pair with the key lowercased, using [`rc_lowercase`]
/// for the allocation fast-path.
pub fn obj_insert_lc(map: &mut ObjMap, key: &str, val: Value) {
let lc = rc_lowercase(key);
map.insert(lc, val);
}
/// Check whether a key exists.
pub fn obj_contains(map: &ObjMap, key: &str) -> bool {
map.contains_key(key)
@@ -82,13 +54,14 @@ pub fn obj_remove(map: &mut ObjMap, key: &str) -> Option<Value> {
/// Convert an [`ObjMap`] into a [`Value::Object`].
///
/// Keys are converted from `Rc<str>` to `Value::String` and inserted into
/// an `Object` to match the `Value::Object` representation.
/// a `BTreeMap` to match the `Value::Object` representation.
pub fn make_value(map: ObjMap) -> Value {
let obj: Object = map
.into_iter()
.map(|(k, v)| (Value::String(k), v))
.collect();
Value::Object(Rc::new(obj))
use alloc::collections::BTreeMap;
let mut btree = BTreeMap::new();
for (k, v) in map {
btree.insert(Value::String(k), v);
}
Value::Object(Rc::new(btree))
}
/// Convert a `Vec<Value>` into a `Value::Array`.
@@ -115,14 +88,14 @@ pub fn extract_type_field(resource: &Value) -> Option<&str> {
})
}
/// Convert a `Value::Object` (Object) into an [`ObjMap`].
/// Convert a `Value::Object` (BTreeMap<Value, Value>) into an [`ObjMap`].
///
/// Non-string keys are silently skipped.
#[allow(dead_code)]
pub fn value_to_obj_map(value: &Value) -> Option<ObjMap> {
let obj = value.as_object().ok()?;
let mut map = ObjMap::with_capacity(obj.len());
for (k, v) in obj.iter() {
let btree = value.as_object().ok()?;
let mut map = ObjMap::with_capacity(btree.len());
for (k, v) in btree.iter() {
if let Value::String(s) = k {
map.insert(Rc::clone(s), v.clone());
}
@@ -139,7 +112,7 @@ pub fn set_nested_lowercased(result: &mut ObjMap, path: &str, value: Value) {
}
if segments.len() == 1 {
if let Some(&seg) = segments.first() {
obj_insert_lc(result, seg, value);
obj_insert(result, &seg.to_ascii_lowercase(), value);
}
return;
}
@@ -171,30 +144,30 @@ fn set_nested_inner(obj: &mut ObjMap, segments: &[&str], value: Value, lowercase
};
if segments.len() == 1 {
let key: Rc<str> = if lowercase {
rc_lowercase(first)
let key = if lowercase {
first.to_ascii_lowercase()
} else {
Rc::from(first)
first.to_string()
};
obj_insert_rc(obj, key, value);
obj_insert(obj, &key, value);
return;
}
let seg: Rc<str> = if lowercase {
rc_lowercase(first)
let seg = if lowercase {
first.to_ascii_lowercase()
} else {
Rc::from(first)
first.to_string()
};
// Ensure an intermediate object exists at `seg`.
if !obj.contains_key(&*seg) {
obj_insert_rc(obj, Rc::clone(&seg), make_value(new_map()));
if !obj_contains(obj, &seg) {
obj_insert(obj, &seg, make_value(new_map()));
}
// Descend directly into the BTreeMap, avoiding ObjMap round-trip.
if let Some(Value::Object(inner_rc)) = obj.get_mut(&*seg) {
if let Some(Value::Object(inner_rc)) = obj_get_mut(obj, &seg) {
let inner_btree = Rc::make_mut(inner_rc);
set_nested(
set_nested_in_btree(
inner_btree,
segments.get(1..).unwrap_or_default(),
value,
@@ -203,36 +176,41 @@ fn set_nested_inner(obj: &mut ObjMap, segments: &[&str], value: Value, lowercase
}
}
/// Set a value at a path directly in an `Object`, creating
/// Set a value at a path directly in a `BTreeMap<Value, Value>`, creating
/// intermediate `Value::Object` nodes as needed.
///
/// This avoids the `btree_to_obj_map` / `obj_map_to_btree` round-trip that
/// would clone every sibling entry at each nesting level.
pub fn set_nested(obj: &mut Object, segments: &[&str], value: Value, lowercase: bool) {
pub fn set_nested_in_btree(
btree: &mut alloc::collections::BTreeMap<Value, Value>,
segments: &[&str],
value: Value,
lowercase: bool,
) {
let Some(&first) = segments.first() else {
return;
};
let key_rc: Rc<str> = if lowercase {
rc_lowercase(first)
let key_str: String = if lowercase {
first.to_ascii_lowercase()
} else {
Rc::from(first)
first.to_string()
};
let key_val = Value::String(Rc::clone(&key_rc));
let key_val = Value::String(Rc::from(key_str.as_str()));
if segments.len() == 1 {
obj.insert(key_val, value);
btree.insert(key_val, value);
return;
}
// Ensure an intermediate object exists.
if !obj.contains_key(&key_val) {
obj.insert(key_val.clone(), make_value(new_map()));
if !btree.contains_key(&key_val) {
btree.insert(key_val.clone(), make_value(new_map()));
}
if let Some(Value::Object(inner_rc)) = obj.get_mut(&key_val) {
if let Some(Value::Object(inner_rc)) = btree.get_mut(&key_val) {
let inner = Rc::make_mut(inner_rc);
set_nested(
set_nested_in_btree(
inner,
segments.get(1..).unwrap_or_default(),
value,
@@ -265,24 +243,13 @@ pub const ROOT_FIELDS: &[&str] = &[
"extendedLocation",
];
const PROPERTIES_DOT: &[u8] = b"properties.";
/// Check whether an alias short name collides with a reserved ARM root field
/// and needs a collision-safe key.
pub fn is_root_field_collision(short_name: &str, default_path: &str) -> bool {
ROOT_FIELDS
.iter()
.any(|f| f.eq_ignore_ascii_case(short_name))
&& default_path.len() > PROPERTIES_DOT.len()
&& default_path
.as_bytes()
.get(..PROPERTIES_DOT.len())
.is_some_and(|prefix| {
prefix
.iter()
.zip(PROPERTIES_DOT)
.all(|(a, b)| a.to_ascii_lowercase() == *b)
})
&& default_path.to_ascii_lowercase().starts_with("properties.")
}
/// Return a collision-safe key for an alias whose short name collides with a
@@ -347,15 +314,20 @@ fn remove_field_at_depth(obj: &mut ObjMap, array_chain: &[Vec<String>], depth: u
for elem in inner.iter_mut() {
if let Value::Object(obj_rc) = elem {
let inner_btree = Rc::make_mut(obj_rc);
remove_field_at_depth_obj(inner_btree, array_chain, depth.saturating_add(1), field);
remove_field_at_depth_in_btree(
inner_btree,
array_chain,
depth.saturating_add(1),
field,
);
}
}
}
}
/// Object-native recursion for element-level field removal.
fn remove_field_at_depth_obj(
obj: &mut Object,
/// BTreeMap-native recursion for element-level field removal.
fn remove_field_at_depth_in_btree(
btree: &mut alloc::collections::BTreeMap<Value, Value>,
array_chain: &[Vec<String>],
depth: usize,
field: &str,
@@ -364,10 +336,10 @@ fn remove_field_at_depth_obj(
let segments: Vec<&str> = field.split('.').collect();
if segments.len() == 1 {
if let Some(&seg) = segments.first() {
obj.remove(&Value::from(seg));
btree.remove(&Value::from(seg));
}
} else if segments.len() > 1 {
remove_at_dotted_path_obj(obj, &segments);
remove_at_dotted_path_in_btree(btree, &segments);
}
return;
};
@@ -379,12 +351,12 @@ fn remove_field_at_depth_obj(
let key_val = Value::from(first);
let arr_val = if nav.len() == 1 {
match obj.get_mut(&key_val) {
match btree.get_mut(&key_val) {
Some(v) => v,
None => return,
}
} else {
let mut cur: &mut Value = match obj.get_mut(&key_val) {
let mut cur: &mut Value = match btree.get_mut(&key_val) {
Some(v) => v,
None => return,
};
@@ -405,19 +377,27 @@ fn remove_field_at_depth_obj(
for elem in inner.iter_mut() {
if let Value::Object(obj_rc) = elem {
let inner_btree = Rc::make_mut(obj_rc);
remove_field_at_depth_obj(inner_btree, array_chain, depth.saturating_add(1), field);
remove_field_at_depth_in_btree(
inner_btree,
array_chain,
depth.saturating_add(1),
field,
);
}
}
}
}
/// Remove the leaf segment at a dotted path directly in an Object.
fn remove_at_dotted_path_obj(obj: &mut Object, segments: &[&str]) {
/// Remove the leaf segment at a dotted path directly in a BTreeMap.
fn remove_at_dotted_path_in_btree(
btree: &mut alloc::collections::BTreeMap<Value, Value>,
segments: &[&str],
) {
let Some((&leaf, parent_segs)) = segments.split_last() else {
return;
};
if parent_segs.is_empty() {
obj.remove(&Value::from(leaf));
btree.remove(&Value::from(leaf));
return;
}
@@ -425,7 +405,7 @@ fn remove_at_dotted_path_obj(obj: &mut Object, segments: &[&str]) {
return;
};
let first_key = Value::from(first);
let parent_val = match obj.get_mut(&first_key) {
let parent_val = match btree.get_mut(&first_key) {
Some(v) => v,
None => return,
};

View File

@@ -18,22 +18,6 @@ use alloc::vec::Vec;
use serde::{Deserialize, Deserializer};
use crate::Rc;
// ---------------------------------------------------------------------------
// Deserialization helpers
// ---------------------------------------------------------------------------
/// Deserialize a `Vec<T>` that tolerates JSON `null` by mapping it to an
/// empty vector.
fn deserialize_null_as_empty_vec<'de, T, D>(deserializer: D) -> Result<Vec<T>, D::Error>
where
T: Deserialize<'de>,
D: Deserializer<'de>,
{
Ok(Option::<Vec<T>>::deserialize(deserializer)?.unwrap_or_default())
}
// ─── Top-level response wrappers ────────────────────────────────────────────
/// ARM API response envelope: `{ "value": [...] }`
@@ -114,10 +98,7 @@ pub struct AliasEntry {
/// Versioned path entries. Empty for the vast majority of aliases that
/// have only a `defaultPath`.
///
/// In real Azure catalog data (~97% of aliases), `az provider list` emits
/// `"paths": null` rather than an empty array.
#[serde(default, deserialize_with = "deserialize_null_as_empty_vec")]
#[serde(default)]
pub paths: Vec<AliasPath>,
}
@@ -423,13 +404,11 @@ pub struct ResolvedEntry {
// ── Precomputed fields (derived at registry-load time) ──────────────
/// Whether `short_name` contains `[*]` (i.e., this is a wildcard/array alias).
pub is_wildcard: bool,
/// Pre-lowercased short name as `Rc<str>` for allocation-free common-case inserts.
pub(crate) short_name_lc: Rc<str>,
/// Precomputed `default_path.split('.').collect()` for fast ARM path navigation.
pub(crate) default_path_segments: Vec<Rc<str>>,
pub default_path_segments: Vec<String>,
/// Precomputed path segments for each versioned path, in the same order
/// as `versioned_paths`.
pub(crate) versioned_path_segments: Vec<Vec<Rc<str>>>,
pub versioned_path_segments: Vec<Vec<String>>,
}
impl ResolvedEntry {
@@ -441,15 +420,10 @@ impl ResolvedEntry {
metadata: Option<AliasPathMetadata>,
) -> Self {
let is_wildcard = short_name.contains("[*]");
let short_name_lc = if short_name.bytes().all(|b| !b.is_ascii_uppercase()) {
Rc::from(short_name.as_str())
} else {
Rc::from(short_name.to_ascii_lowercase())
};
let default_path_segments = default_path.split('.').map(Rc::from).collect();
let default_path_segments = default_path.split('.').map(String::from).collect();
let versioned_path_segments = versioned_paths
.iter()
.map(|(_, p)| p.split('.').map(Rc::from).collect())
.map(|(_, p)| p.split('.').map(String::from).collect())
.collect();
Self {
short_name,
@@ -457,7 +431,6 @@ impl ResolvedEntry {
versioned_paths,
metadata,
is_wildcard,
short_name_lc,
default_path_segments,
versioned_path_segments,
}
@@ -483,7 +456,7 @@ impl ResolvedEntry {
/// Returns the versioned segments if `api_version` matches, otherwise
/// the default segments. This avoids per-call `split('.')` for both
/// default and versioned scalar alias navigation.
pub(crate) fn select_path_segments(&self, api_version: Option<&str>) -> &[Rc<str>] {
pub fn select_path_segments(&self, api_version: Option<&str>) -> &[String] {
if let Some(ver) = api_version {
for (i, (v, _)) in self.versioned_paths.iter().enumerate() {
if v.eq_ignore_ascii_case(ver) {

View File

@@ -18,7 +18,6 @@ use crate::rvm::program::{Program, SpanInfo};
use crate::rvm::Instruction;
use crate::{Rc, Value};
use crate::languages::azure_policy::aliases::AliasRegistry;
use crate::languages::azure_policy::ast::PolicyRule;
// ---------------------------------------------------------------------------
@@ -45,9 +44,10 @@ pub(super) struct Compiler {
pub(super) cached_input_reg: Option<u8>,
/// Cached register for `LoadContext` — allocated once on first use.
pub(super) cached_context_reg: Option<u8>,
/// Alias registry for resolving fully-qualified alias names.
/// Shared via `Rc` to avoid cloning the 73K-entry alias maps.
pub(super) alias_registry: Option<Rc<AliasRegistry>>,
/// Map from lowercase fully-qualified alias name → short name.
pub(super) alias_map: BTreeMap<String, String>,
/// Map from lowercase fully-qualified alias name → modifiable flag.
pub(super) alias_modifiable: BTreeMap<String, bool>,
/// Default values for policy parameters.
pub(super) parameter_defaults: Option<Value>,
/// Cached literal-table index for `parameter_defaults` (or an empty object
@@ -338,13 +338,8 @@ impl Compiler {
path: &str,
span: &crate::lexer::Span,
) -> Result<String> {
let alias_map = match &self.alias_registry {
Some(reg) => reg.alias_map(),
None => return Ok(path.to_string()),
};
let lc = path.to_ascii_lowercase();
if let Some(short) = alias_map.get(&lc) {
if let Some(short) = self.alias_map.get(&lc) {
let resolved = short.clone();
let result = Self::strip_fq_prefix(&resolved).to_ascii_lowercase();
return Ok(result);
@@ -353,7 +348,7 @@ impl Compiler {
// Fallback: derive array path from a corresponding `[*]` alias.
if !lc.contains("[*]") {
let wildcard_key = alloc::format!("{}[*]", lc);
if let Some(short) = alias_map.get(&wildcard_key) {
if let Some(short) = self.alias_map.get(&wildcard_key) {
let resolved = Self::strip_fq_prefix(short).to_ascii_lowercase();
if let Some(base) = resolved.strip_suffix("[*]") {
return Ok(base.to_string());
@@ -361,14 +356,14 @@ impl Compiler {
}
}
if !alias_map.is_empty() && !self.alias_fallback_to_raw {
if !self.alias_map.is_empty() && !self.alias_fallback_to_raw {
bail!(span.error(&alloc::format!(
"unknown alias '{}': field references must use fully-qualified alias names when an alias catalog is loaded",
path
)));
}
if alias_map.is_empty() {
if self.alias_map.is_empty() {
Ok(path.to_string())
} else {
let result = Self::strip_fq_prefix(path).to_ascii_lowercase();

View File

@@ -998,13 +998,7 @@ impl Compiler {
return Ok(result);
}
}
Err(e)
if self
.alias_registry
.as_ref()
.is_some_and(|r| !r.alias_map().is_empty())
&& !self.alias_fallback_to_raw =>
{
Err(e) if !self.alias_map.is_empty() && !self.alias_fallback_to_raw => {
return Err(e);
}
_ => {}

View File

@@ -11,7 +11,7 @@
//! to fetch a related resource and an optional `existenceCondition` evaluated
//! inline.
use crate::value::Object;
use alloc::collections::BTreeMap;
use alloc::format;
use alloc::string::ToString as _;
use alloc::vec::Vec;
@@ -692,18 +692,13 @@ impl Compiler {
field_path: &str,
span: &crate::lexer::Span,
) -> Result<()> {
let modifiable_map = match &self.alias_registry {
Some(reg) => reg.alias_modifiable_map(),
None => return Ok(()),
};
if modifiable_map.is_empty() {
if self.alias_modifiable.is_empty() {
return Ok(());
}
let lc = field_path.to_lowercase();
if let Some(&modifiable) = modifiable_map.get(&lc) {
if let Some(&modifiable) = self.alias_modifiable.get(&lc) {
if !modifiable {
bail!(span.error(&format!(
"alias '{}' is not modifiable (defaultMetadata.attributes != 'Modifiable')",
@@ -808,41 +803,26 @@ fn unescape_arm_literal(s: &str) -> alloc::string::String {
/// 1. Build a template `BTreeMap` with `Value::Undefined` placeholders.
/// 2. Sort keys by their literal value (BTreeMap order).
/// 3. Emit `ObjectCreate`.
#[allow(clippy::indexing_slicing)]
pub(super) fn build_object_from_keys(
compiler: &mut Compiler,
mut keys: Vec<(u16, u8)>,
span: &crate::lexer::Span,
) -> Result<u8> {
// Build template: object with all keys set to Undefined.
let mut template = Object::new();
let mut template = BTreeMap::new();
for &(key_idx, _) in &keys {
// key_idx was returned by `add_literal_u16` in the calling code,
// so it is always in bounds. We use `.get()` + `?` instead of
// direct indexing to satisfy the crate-wide `deny(indexing_slicing)`.
let key_val = compiler
.program
.literals
.get(usize::from(key_idx))
.ok_or_else(|| {
anyhow!(
"internal error in build_object_from_keys: \
literal index {} out of bounds (literals len = {})",
key_idx,
compiler.program.literals.len()
)
})?
.clone();
// SAFETY: key_idx was just returned by `add_literal_u16`, so the
// index is guaranteed to be in bounds.
let key_val = compiler.program.literals[usize::from(key_idx)].clone();
template.insert(key_val, Value::Undefined);
}
let template_idx = compiler.add_literal_u16(Value::Object(crate::Rc::new(template)))?;
// Sort keys by literal value (BTreeMap order). All indices were
// validated in the loop above (which returns Err for out-of-bounds),
// so `.get()` always returns `Some` here — `None` is unreachable.
// Sort keys by literal value (BTreeMap order).
keys.sort_by(|a, b| {
let a_val = compiler.program.literals.get(usize::from(a.0));
let b_val = compiler.program.literals.get(usize::from(b.0));
a_val.cmp(&b_val)
compiler.program.literals[usize::from(a.0)]
.cmp(&compiler.program.literals[usize::from(b.0)])
});
let dest = compiler.alloc_register()?;

View File

@@ -272,7 +272,7 @@ impl Compiler {
fn insert_string_set_annotation(
annot: &mut alloc::collections::BTreeMap<String, Value>,
key: &str,
observed: &alloc::collections::BTreeSet<String>,
observed: &BTreeSet<String>,
) {
if !observed.is_empty() {
let set: BTreeSet<Value> = observed

View File

@@ -30,11 +30,11 @@ mod metadata;
mod template_dispatch;
mod utils;
use alloc::string::ToString as _;
use alloc::collections::BTreeMap;
use alloc::string::{String, ToString as _};
use anyhow::Result;
use crate::languages::azure_policy::aliases::AliasRegistry;
use crate::languages::azure_policy::ast::{PolicyDefinition, PolicyRule};
use crate::rvm::program::Program;
use crate::{Rc, Value};
@@ -69,14 +69,17 @@ pub fn compile_policy_rule(rule: &PolicyRule) -> Result<Rc<Program>> {
/// Compile a parsed Azure Policy rule with alias resolution.
///
/// The registry provides alias-to-short-name resolution and modifiability
/// data. Pass it as an `Rc` to avoid cloning the internal alias maps.
/// The `alias_map` maps lowercase fully-qualified alias names to their short
/// names. Obtain it from
/// [`AliasRegistry::alias_map()`](crate::languages::azure_policy::aliases::AliasRegistry::alias_map).
pub fn compile_policy_rule_with_aliases(
rule: &PolicyRule,
registry: Rc<AliasRegistry>,
alias_map: BTreeMap<String, String>,
alias_modifiable: BTreeMap<String, bool>,
) -> Result<Rc<Program>> {
let mut compiler = Compiler::new();
compiler.alias_registry = Some(registry);
compiler.alias_map = alias_map;
compiler.alias_modifiable = alias_modifiable;
init_effect_annotation(&mut compiler, rule);
compiler.compile(rule)
}
@@ -97,10 +100,12 @@ pub fn compile_policy_definition(defn: &PolicyDefinition) -> Result<Rc<Program>>
/// Compile a parsed Azure Policy definition with alias resolution.
pub fn compile_policy_definition_with_aliases(
defn: &PolicyDefinition,
registry: Rc<AliasRegistry>,
alias_map: BTreeMap<String, String>,
alias_modifiable: BTreeMap<String, bool>,
) -> Result<Rc<Program>> {
let mut compiler = Compiler::new();
compiler.alias_registry = Some(registry);
compiler.alias_map = alias_map;
compiler.alias_modifiable = alias_modifiable;
compiler.parameter_defaults = Some(build_parameter_defaults(&defn.parameters)?);
compiler.populate_definition_metadata(defn);
init_effect_annotation(&mut compiler, &defn.policy_rule);
@@ -114,11 +119,13 @@ pub fn compile_policy_definition_with_aliases(
/// a known alias are silently treated as raw property paths.
pub fn compile_policy_definition_with_aliases_opts(
defn: &PolicyDefinition,
registry: Rc<AliasRegistry>,
alias_map: BTreeMap<String, String>,
alias_modifiable: BTreeMap<String, bool>,
alias_fallback_to_raw: bool,
) -> Result<Rc<Program>> {
let mut compiler = Compiler::new();
compiler.alias_registry = Some(registry);
compiler.alias_map = alias_map;
compiler.alias_modifiable = alias_modifiable;
compiler.alias_fallback_to_raw = alias_fallback_to_raw;
compiler.parameter_defaults = Some(build_parameter_defaults(&defn.parameters)?);
compiler.populate_definition_metadata(defn);

View File

@@ -264,17 +264,18 @@ impl<'source> Parser<'source> {
"metadata" => {
*metadata = Some(self.parse_json_value()?);
}
"parameters" if self.token_text() == "{" => {
// Parameters must be a JSON object; if not, push to extra.
*parameters = self.parse_parameter_definitions()?;
}
"parameters" => {
let value = self.parse_json_value()?;
extra.push(ObjectEntry {
key_span,
key: key.into(),
value,
});
// Parameters must be a JSON object; if not, push to extra.
if self.token_text() == "{" {
*parameters = self.parse_parameter_definitions()?;
} else {
let value = self.parse_json_value()?;
extra.push(ObjectEntry {
key_span,
key: key.into(),
value,
});
}
}
"policyrule" => {
// Parse the policyRule directly from the token stream!

View File

@@ -63,14 +63,6 @@ pub enum CompilerError {
#[error("Invalid function expression with package")]
InvalidFunctionExpressionWithPackage,
#[error("partial object rules with constant keys are not yet supported by the RVM compiler")]
PartialObjectConstantKeyUnsupported,
#[error(
"partial object rules with nested bracket keys are not yet supported by the RVM compiler"
)]
PartialObjectNestedKeyUnsupported,
#[error("Compilation error: {message}")]
General { message: String },
}

View File

@@ -11,9 +11,8 @@ use crate::ast::{Expr, ExprRef};
use crate::lexer::Span;
use crate::rvm::instructions::{ArrayCreateParams, ObjectCreateParams, SetCreateParams};
use crate::rvm::Instruction;
use crate::value::Object;
use crate::{Rc, Value};
use alloc::collections::BTreeSet;
use alloc::collections::{BTreeMap, BTreeSet};
use alloc::vec::Vec;
/// Try to evaluate an expression as a compile-time constant.
@@ -44,7 +43,7 @@ pub(in crate::languages::rego::compiler) fn try_eval_const(expr: &Expr) -> Optio
Expr::Object { fields, .. } => fields
.iter()
.map(|(_, k, v)| Some((try_eval_const(k.as_ref())?, try_eval_const(v.as_ref())?)))
.collect::<Option<Object>>()
.collect::<Option<BTreeMap<_, _>>>()
.map(|m| Value::Object(Rc::new(m))),
_ => None,
}
@@ -118,7 +117,7 @@ impl<'a> Compiler<'a> {
fields: &[(crate::lexer::Span, ExprRef, ExprRef)],
span: &Span,
) -> Result<Register> {
let all_const: Option<Object> = fields
let all_const: Option<BTreeMap<_, _>> = fields
.iter()
.map(|(_, k, v)| Some((try_eval_const(k.as_ref())?, try_eval_const(v.as_ref())?)))
.collect();
@@ -167,7 +166,7 @@ impl<'a> Compiler<'a> {
let mut template_keys = literal_keys.clone();
template_keys.sort();
let mut template_obj = Object::new();
let mut template_obj = BTreeMap::new();
for key in &template_keys {
template_obj.insert(key.clone(), Value::Undefined);
}

View File

@@ -17,20 +17,8 @@ use crate::lexer::Span;
use crate::rvm::instructions::{BuiltinCallParams, FunctionCallParams};
use crate::rvm::Instruction;
use crate::utils::get_path_string;
use crate::value::Value;
use alloc::{
format,
string::{String, ToString},
vec::Vec,
};
use alloc::{format, string::ToString, vec::Vec};
/// Resolved destination of a Rego function-call expression. Produced by
/// [`Compiler::determine_call_target`] and consumed by
/// [`Compiler::compile_function_call`] to choose which instruction to emit.
/// Carrying the discrimination in the type (rather than re-matching on a
/// magic name at the emit site) keeps the host-await handling honest under
/// future refactors — the compiler will refuse to build if a new variant is
/// added without updating every match site.
enum CallTarget {
User {
rule_index: u16,
@@ -40,14 +28,9 @@ enum CallTarget {
builtin_index: u16,
expected_args: Option<usize>,
},
/// Explicit `__builtin_host_await(arg, id)` call form (2 user args).
/// The identifier is supplied by the policy author at runtime via the
/// second argument register.
ExplicitHostAwait,
/// A registered host-awaitable builtin invoked by its registered name
/// (1 user arg). The identifier is the registered name itself and is
/// baked into the bytecode as a string literal at compile time.
RegisteredHostAwait { identifier: String },
HostAwait {
expected_args: Option<usize>,
},
}
impl<'a> Compiler<'a> {
@@ -76,11 +59,7 @@ impl<'a> Compiler<'a> {
let expected_args = match &call_target {
CallTarget::User { expected_args, .. } => *expected_args,
CallTarget::Builtin { expected_args, .. } => *expected_args,
// Both host-await variants have a known fixed arity; carrying it
// in the variant lets the rest of the compiler depend on the type
// rather than re-matching on the magic name `__builtin_host_await`.
CallTarget::ExplicitHostAwait => Some(2),
CallTarget::RegisteredHostAwait { .. } => Some(1),
CallTarget::HostAwait { expected_args } => *expected_args,
};
if let Some(expected) = expected_args {
@@ -147,8 +126,7 @@ impl<'a> Compiler<'a> {
});
self.emit_instruction(Instruction::BuiltinCall { params_index }, &span);
}
CallTarget::ExplicitHostAwait => {
// Explicit __builtin_host_await(arg, id) — 2 arguments
CallTarget::HostAwait { .. } => {
if arg_regs.len() != 2 {
return Err(CompilerError::General {
message: format!(
@@ -158,6 +136,7 @@ impl<'a> Compiler<'a> {
}
.at(&span));
}
self.emit_instruction(
Instruction::HostAwait {
dest,
@@ -167,37 +146,6 @@ impl<'a> Compiler<'a> {
&span,
);
}
CallTarget::RegisteredHostAwait { identifier } => {
// Registered host-awaitable builtin — the identifier is the
// registered name and is baked into the bytecode as a literal.
if arg_regs.len() != 1 {
return Err(CompilerError::General {
message: format!(
"host-awaitable builtin '{}' expects exactly 1 argument, got {}",
identifier,
arg_regs.len()
),
}
.at(&span));
}
let id_reg = self.alloc_register();
let literal_idx = self.add_literal(Value::String(identifier.into()));
self.emit_instruction(
Instruction::Load {
dest: id_reg,
literal_idx,
},
&span,
);
self.emit_instruction(
Instruction::HostAwait {
dest,
arg: arg_regs[0],
id: id_reg,
},
&span,
);
}
}
if let Some((plan, plan_span)) = &out_param_plan {
@@ -239,26 +187,8 @@ impl<'a> Compiler<'a> {
span: &Span,
) -> Result<CallTarget> {
if original_fcn_path == "__builtin_host_await" {
return Ok(CallTarget::ExplicitHostAwait);
}
// Check registered host-awaitable builtins. Registered builtins are
// restricted to arg_count == 1 at registration time (see
// `Compiler::register_host_await_builtin`), so the variant doesn't
// need to carry an arity — it's fixed at 1.
//
// We deliberately match against `original_fcn_path` only, not
// `full_fcn_path`. Registration intercepts the *unqualified* call
// form (e.g. `lookup(x)` inside the policy's own package). A
// package-qualified call like `data.other.lookup(x)` is left to
// resolve through the normal user-defined / builtin path, so a
// registered name does not leak into unrelated packages that
// happen to expose a rule with the same identifier. This is
// documented on `register_host_await_builtin`; the
// `registered_host_await.yaml` suite pins the behavior.
if self.host_await_builtins.contains_key(original_fcn_path) {
return Ok(CallTarget::RegisteredHostAwait {
identifier: original_fcn_path.to_string(),
return Ok(CallTarget::HostAwait {
expected_args: Some(2),
});
}

View File

@@ -13,7 +13,7 @@ use crate::ast::{self, ExprRef, LiteralStmt, Query};
use crate::compiler::destructuring_planner::plans::BindingPlan;
use crate::compiler::hoist::{HoistedLoop, LoopType};
use crate::lexer::Span;
use crate::rvm::instructions::{GuardMode, LoopMode, LoopStartParams};
use crate::rvm::instructions::{LoopMode, LoopStartParams};
use crate::rvm::Instruction;
use crate::Value;
use alloc::format;
@@ -197,19 +197,6 @@ impl<'a> Compiler<'a> {
*end = loop_end;
}
// The loop writes its overall pass/fail into `result_reg`
// (`success_count == total_iterations` for `Every`). The enclosing query
// must fail (evaluate to undefined) when the quantifier does not hold, so
// guard on `result_reg` here. Without this the `every` result is computed
// but discarded, leaving the surrounding rule to always succeed.
self.emit_instruction(
Instruction::Guard {
register: result_reg,
mode: GuardMode::Condition,
},
span,
);
Ok(())
}
@@ -325,25 +312,6 @@ impl<'a> Compiler<'a> {
*end = loop_end;
}
// A hoisted index-iteration loop inside an `every` body acts as a
// condition on the current iteration: if the indexed reference matches
// nothing the iteration must fail. The `every` body emits no context
// yield, so the loop result register is otherwise discarded (same
// situation as `some ... in`). Guard on it so a non-matching indexed
// reference fails the enclosing `every` iteration.
if matches!(
self.context_stack.last().map(|c| &c.context_type),
Some(ContextType::Every)
) {
self.emit_instruction(
Instruction::Guard {
register: result_reg,
mode: GuardMode::Condition,
},
collection.span(),
);
}
Ok(())
}

View File

@@ -26,9 +26,7 @@ use crate::rvm::program::{Program, RuleType, SpanInfo};
use crate::CompiledPolicy;
use crate::Value;
use alloc::collections::{BTreeMap, BTreeSet};
use alloc::format;
use alloc::string::String;
use alloc::string::ToString as _;
use alloc::vec;
use alloc::vec::Vec;
use indexmap::IndexMap;
@@ -141,10 +139,6 @@ pub struct Compiler<'a> {
current_call_stack: Vec<u16>,
entry_points: IndexMap<String, usize>,
soft_assert_mode: bool,
/// Registered host-awaitable builtins: name → expected arg count.
/// When the compiler encounters a call to one of these names, it emits a
/// `HostAwait` instruction instead of a regular function or builtin call.
host_await_builtins: BTreeMap<String, usize>,
}
impl<'a> Compiler<'a> {
@@ -179,75 +173,9 @@ impl<'a> Compiler<'a> {
current_call_stack: Vec::new(),
entry_points: IndexMap::new(),
soft_assert_mode: false,
host_await_builtins: BTreeMap::new(),
}
}
/// Register a function name as a host-awaitable builtin.
///
/// When the compiler encounters an **unqualified** call to `name(arg)`
/// (i.e. `name(arg)` from inside the policy's own package, not
/// `data.pkg.name(arg)` or any other package-qualified form), it will
/// emit a `HostAwait` instruction with the argument and `name` as the
/// identifier, instead of treating it as a user-defined or standard
/// builtin function.
///
/// Package-qualified calls (e.g. `data.other.name(arg)`) are **not**
/// intercepted by registration. Those resolve through the normal
/// user-defined / builtin lookup against their fully-qualified path
/// (`data.other.name`).
///
/// `arg_count` must be exactly 1. The `HostAwait` instruction carries a
/// single argument register; use object packing to pass multiple values
/// (e.g. `name({"key1": v1, "key2": v2})`).
///
/// Returns `Err` when:
/// - `name` is the reserved identifier `__builtin_host_await`,
/// - `name` is empty, only whitespace, or has leading/trailing
/// whitespace (whitespace-padded names would never match the
/// trimmed identifier produced by the Rego parser, creating dead
/// registrations),
/// - `name` is already registered (duplicate registration is rejected
/// rather than silently overwritten),
/// - `arg_count` is not exactly 1.
pub fn register_host_await_builtin(&mut self, name: &str, arg_count: usize) -> Result<()> {
if name == "__builtin_host_await" {
return Err(CompilerError::General {
message: "__builtin_host_await is a reserved name and cannot be registered as a host-await builtin"
.to_string(),
}
.into());
}
if name.is_empty() || name != name.trim() {
return Err(CompilerError::General {
message: format!(
"host-await builtin name {name:?} must not be empty or contain leading/trailing whitespace"
),
}
.into());
}
if self.host_await_builtins.contains_key(name) {
return Err(CompilerError::General {
message: format!(
"host-await builtin '{name}' is already registered; \
duplicate registration is not allowed"
),
}
.into());
}
if arg_count != 1 {
return Err(CompilerError::General {
message: format!(
"registered host-await builtin '{name}' must have arg_count == 1, got {arg_count}. \
Use object packing to pass multiple values."
),
}
.into());
}
self.host_await_builtins.insert(name.to_string(), arg_count);
Ok(())
}
pub(super) fn with_soft_assert_mode<F, R>(&mut self, enabled: bool, f: F) -> R
where
F: FnOnce(&mut Self) -> R,

View File

@@ -181,7 +181,7 @@ impl<'a> Compiler<'a> {
}
fn evaluate_default_rule(&mut self, rule_path: &str) -> Option<u16> {
if !self.policy.inner.default_rules.contains_key(rule_path) {
if !self.may_have_default_rule(rule_path) {
return None;
}
@@ -200,6 +200,33 @@ impl<'a> Compiler<'a> {
None
}
fn may_have_default_rule(&self, rule_path: &str) -> bool {
if self.policy.inner.default_rules.contains_key(rule_path) {
return true;
}
let Some((parent_path, index)) = rule_path.rsplit_once('.') else {
return false;
};
self.policy
.inner
.default_rules
.get(parent_path)
.is_some_and(|rules| {
rules
.iter()
.any(|(_, rule_index)| match rule_index.as_deref() {
Some(rule_index) if rule_index == index => true,
Some(rule_index) => rule_index
.strip_prefix('"')
.and_then(|rule_index| rule_index.strip_suffix('"'))
.is_some_and(|rule_index| rule_index == index),
None => false,
})
})
}
fn extract_destructuring_blocks(&self, rule_index: u16) -> Vec<Option<u32>> {
self.rule_definition_destructuring_patterns[rule_index as usize].clone()
}

View File

@@ -70,31 +70,12 @@ impl<'a> Compiler<'a> {
..
} = &stmt.literal
{
let some_result_reg = self.compile_some_in_loop_with_remaining_statements(
self.compile_some_in_loop_with_remaining_statements(
key,
value,
collection,
&stmts[idx..],
)?;
// Inside an `every` body a `some ... in` acts as a condition
// on the current iteration: if it matches nothing the
// iteration must fail. Unlike a top-level rule body (where
// per-iteration context yields produce the results), the
// `every` body has no yield, so the loop result register is
// otherwise discarded. Guard on it so a `some` that matches
// nothing fails the enclosing `every` iteration.
if matches!(
self.context_stack.last().map(|c| &c.context_type),
Some(ContextType::Every)
) {
self.emit_instruction(
Instruction::Guard {
register: some_result_reg,
mode: GuardMode::Condition,
},
&stmt.span,
);
}
return Ok(());
}
}

View File

@@ -11,7 +11,7 @@
)]
use super::{CompilationContext, Compiler, CompilerError, ContextType, Result, WorklistEntry};
use crate::ast::{Expr, ExprRef, Rule, RuleHead};
use crate::ast::{AssignOp, Expr, ExprRef, Rule, RuleHead};
use crate::compiler::destructuring_planner::plans::BindingPlan;
use crate::lexer::Span;
use crate::rvm::program::{Program, RuleType};
@@ -52,14 +52,29 @@ impl<'a> Compiler<'a> {
let rule_types: BTreeSet<RuleType> = definitions
.iter()
.map(|def| {
if let Rule::Spec { head, .. } = def.as_ref() {
if let Rule::Spec { head, bodies, .. } = def.as_ref() {
match head {
RuleHead::Set { .. } => RuleType::PartialSet,
RuleHead::Compr { refr, assign, .. } => match refr.as_ref() {
crate::ast::Expr::RefBrack { .. } if assign.is_some() => {
// Variable-key bracket heads emit one object entry per successful
// binding, so they must compile as partial objects.
crate::ast::Expr::RefBrack { index, .. }
if super::expressions::try_eval_const(index.as_ref()).is_none() =>
{
RuleType::PartialObject
}
crate::ast::Expr::RefBrack { .. } => RuleType::PartialObject,
crate::ast::Expr::RefBrack { .. }
if matches!(
assign.as_ref().map(|assign| &assign.op),
Some(AssignOp::Eq)
) =>
{
RuleType::PartialObject
}
crate::ast::Expr::RefBrack { .. } if bodies.is_empty() => {
RuleType::PartialObject
}
crate::ast::Expr::RefBrack { .. } => RuleType::Complete,
_ => RuleType::Complete,
},
_ => RuleType::Complete,
@@ -88,54 +103,6 @@ impl<'a> Compiler<'a> {
})
}
fn validate_partial_object_shape(&self, refr: &ExprRef) -> Result<()> {
let Expr::RefBrack {
refr: prefix,
index,
..
} = refr.as_ref()
else {
return Ok(());
};
if Self::has_unsupported_bracket_prefix(prefix) {
return Err(CompilerError::PartialObjectNestedKeyUnsupported.at(refr.span()));
}
if Self::is_simple_literal(index) {
return Err(CompilerError::PartialObjectConstantKeyUnsupported.at(index.span()));
}
Ok(())
}
fn has_unsupported_bracket_prefix(expr: &ExprRef) -> bool {
match expr.as_ref() {
Expr::RefBrack { refr, index, .. } => {
!Self::is_string_literal(index) || Self::has_unsupported_bracket_prefix(refr)
}
Expr::RefDot { refr, .. } => Self::has_unsupported_bracket_prefix(refr),
_ => false,
}
}
fn is_string_literal(expr: &ExprRef) -> bool {
matches!(expr.as_ref(), Expr::String { .. } | Expr::RawString { .. })
}
fn is_simple_literal(expr: &ExprRef) -> bool {
match expr.as_ref() {
Expr::String { .. }
| Expr::RawString { .. }
| Expr::Number { .. }
| Expr::Bool { .. }
| Expr::Null { .. } => true,
// Unary expressions like `-1` are constant literals too.
Expr::UnaryExpr { expr, .. } => Self::is_simple_literal(expr),
_ => false,
}
}
pub(super) fn get_or_assign_rule_index(&mut self, rule_path: &str) -> Result<u16> {
if let Some(&index) = self.rule_index_map.get(rule_path) {
return Ok(index);
@@ -234,20 +201,8 @@ impl<'a> Compiler<'a> {
pub fn compile_from_policy(
policy: &CompiledPolicy,
entry_points: &[&str],
) -> Result<Arc<Program>> {
Self::compile_from_policy_with_host_await(policy, entry_points, &[])
}
/// Compile from a CompiledPolicy to RVM Program with registered host-awaitable builtins.
pub fn compile_from_policy_with_host_await(
policy: &CompiledPolicy,
entry_points: &[&str],
host_await_builtins: &[(&str, usize)],
) -> Result<Arc<Program>> {
let mut compiler = Compiler::with_policy(policy);
for &(name, arg_count) in host_await_builtins {
compiler.register_host_await_builtin(name, arg_count)?;
}
compiler.current_rule_path = "".to_string();
let rules = policy.get_rules();
@@ -405,10 +360,6 @@ impl<'a> Compiler<'a> {
let (key_expr, value_expr) = match head {
RuleHead::Compr { refr, assign, .. } => {
if rule_type == RuleType::PartialObject {
self.validate_partial_object_shape(refr)?;
}
self.rule_definition_function_params[rule_index as usize].push(None);
self.rule_definition_destructuring_patterns[rule_index as usize]
.push(None);

View File

@@ -155,7 +155,7 @@ pub mod target;
#[cfg(any(test, all(feature = "yaml", feature = "std")))]
pub mod test_utils;
pub mod utils;
pub mod value;
mod value;
#[cfg(feature = "azure_policy")]
pub use {
@@ -205,10 +205,10 @@ pub use alloc::sync::Arc as Rc;
pub use alloc::rc::Rc;
#[cfg(feature = "std")]
use std::collections::{hash_map::Entry as MapEntry, HashMap as Map, HashSet as MapSet};
use std::collections::{hash_map::Entry as MapEntry, HashMap as Map, HashSet as Set};
#[cfg(not(feature = "std"))]
use alloc::collections::{btree_map::Entry as MapEntry, BTreeMap as Map, BTreeSet as MapSet};
use alloc::collections::{btree_map::Entry as MapEntry, BTreeMap as Map, BTreeSet as Set};
use alloc::{
borrow::ToOwned as _,

View File

@@ -354,33 +354,6 @@ impl<'source> Parser<'source> {
}
}
/// Parse a field name after `.` in a ref expression.
///
/// Unlike [`Self::parse_var`] and [`Self::parse_ident`], this method accepts **any**
/// `TokenKind::Ident` token, including reserved keywords (e.g. `as`, `default`, `else`,
/// `false`, `if`, `import`, `in`, `not`, `null`, `package`, `some`, `true`, `with`).
///
/// The position immediately after `.` is unambiguously a field name, so there is no
/// syntactic ambiguity with statement-level keywords. This matches OPA's
/// `keywords_in_refs` capability, which is enabled by default in standard OPA builds.
///
/// # Example
/// ```rego
/// allow if { input.v0.package.format == "npm" } # `package` is a keyword but valid here
/// ```
fn parse_ref_field(&mut self) -> Result<Span> {
let span = self.tok.1.clone();
match self.tok.0 {
TokenKind::Ident => {
self.next_token()?;
Ok(span)
}
_ => Err(self
.source
.error(self.tok.1.line, self.tok.1.col, "expecting identifier")),
}
}
fn read_number(&mut self, span: Span) -> Result<Expr> {
match Number::from_str(span.text()) {
Ok(v) => Ok(Expr::Number {
@@ -770,10 +743,9 @@ impl<'source> Parser<'source> {
);
}
"." => {
// Read identifier. Keywords are allowed as field names in
// dot-notation refs (e.g. `input.package.name`).
// Read identifier.
self.next_token()?;
let field = self.parse_ref_field()?;
let field = self.parse_var()?;
span.end = self.end;
// Disallow any whitespace between . and identifier.
@@ -1446,10 +1418,9 @@ impl<'source> Parser<'source> {
);
}
"." => {
// Read identifier. Keywords are allowed as field names in
// dot-notation refs (e.g. `import data.my.package`).
// Read identifier.
self.next_token()?;
let field = self.parse_ref_field()?;
let field = self.parse_ident()?;
span.end = self.end;
// Disallow any whitespace between . and identifier.
@@ -1552,8 +1523,7 @@ impl<'source> Parser<'source> {
"." => {
let sep_pos = self.tok.1.start;
self.next_token()?;
// Keywords are allowed as field names in dot-notation refs.
let field = self.parse_ref_field()?;
let field = self.parse_var()?;
span.end = self.end;
// Disallow any whitespace between . and identifier.

View File

@@ -11,9 +11,9 @@
//! values are converted through [`MetadataValue`] — a postcard/bincode-safe
//! enum that avoids `deserialize_any`.
use crate::value::Object;
use crate::Rc;
use alloc::collections::{BTreeMap, BTreeSet};
use alloc::collections::BTreeMap;
use alloc::collections::BTreeSet;
use alloc::string::String;
use alloc::vec::Vec;
use serde::{Deserialize, Serialize};
@@ -52,7 +52,7 @@ impl ProgramMetadata {
pub fn to_value(&self) -> crate::value::Value {
use crate::value::Value;
let mut obj = Object::new();
let mut obj = BTreeMap::new();
obj.insert(
Value::String("compiler_version".into()),
Value::String(self.compiler_version.as_str().into()),
@@ -75,7 +75,7 @@ impl ProgramMetadata {
);
if !self.annotations.is_empty() {
let mut annotations_obj = Object::new();
let mut annotations_obj = BTreeMap::new();
for (k, v) in &self.annotations {
annotations_obj.insert(Value::String(k.as_str().into()), v.clone());
}
@@ -198,7 +198,7 @@ impl MetadataValue {
match *self {
MetadataValue::String(ref s) => Value::String(s.as_str().into()),
MetadataValue::StringSet(ref set) => {
let mut bset = BTreeSet::new();
let mut bset = alloc::collections::BTreeSet::new();
for s in set {
bset.insert(Value::String(s.as_str().into()));
}
@@ -211,7 +211,7 @@ impl MetadataValue {
Value::Array(Rc::new(values))
}
MetadataValue::Map(ref map) => {
let mut obj = Object::new();
let mut obj = BTreeMap::new();
for (k, v) in map {
obj.insert(Value::String(k.as_str().into()), v.to_value());
}
@@ -257,6 +257,7 @@ mod metadata_serde {
mod tests {
use super::*;
use crate::value::Value;
use alloc::collections::BTreeSet;
/// Round-trip: Value → MetadataValue → Value must be equivalent for
/// all lossless variants (strings, bools, integers, arrays, objects).

View File

@@ -1,6 +1,6 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
use alloc::collections::BTreeSet;
use alloc::collections::{BTreeMap, BTreeSet};
use alloc::format;
use alloc::string::String;
use alloc::vec::Vec;
@@ -11,7 +11,6 @@ use serde::ser::{SerializeSeq as _, SerializeTuple as _};
use serde::{Deserialize, Serialize};
use crate::number::Number;
use crate::value::Object;
use crate::value::Value;
const VARIANT_NULL: u32 = 0;
@@ -133,7 +132,7 @@ impl<'a> Serialize for BinarySetRef<'a> {
}
}
struct BinaryObjectRef<'a>(&'a Object);
struct BinaryObjectRef<'a>(&'a BTreeMap<Value, Value>);
impl<'a> Serialize for BinaryObjectRef<'a> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
@@ -141,7 +140,7 @@ impl<'a> Serialize for BinaryObjectRef<'a> {
S: serde::Serializer,
{
let mut seq = serializer.serialize_seq(Some(self.0.len()))?;
for (key, value) in self.0.iter_sorted() {
for (key, value) in self.0.iter() {
seq.serialize_element(&BinaryEntryRef(key, value))?;
}
seq.end()
@@ -262,11 +261,11 @@ impl<'de> Visitor<'de> for BinaryValueVisitor {
}
(BinaryVariant::Object, variant) => {
let entries: Vec<(BinaryValue, BinaryValue)> = variant.newtype_variant()?;
let mut map = Object::new();
let mut map = BTreeMap::new();
for (key, value) in entries {
map.insert(key.into_value(), value.into_value());
}
Ok(BinaryValue(Value::Object(crate::Rc::new(map))))
Ok(BinaryValue(Value::from(map)))
}
(BinaryVariant::Undefined, variant) => {
variant.unit_variant()?;

View File

@@ -6,6 +6,8 @@
// Disable both to keep patterns consistent within this file.
#![allow(clippy::pattern_type_mismatch, clippy::needless_borrowed_reference)]
use alloc::collections::BTreeSet;
use crate::number::Number;
use crate::value::Value;
@@ -30,9 +32,8 @@ impl RegoVM {
match (a, b) {
(&Value::Number(ref x), &Value::Number(ref y)) => Ok(Value::from(x.sub(y)?)),
(&Value::Set(ref left), &Value::Set(ref right)) => {
let diff: alloc::collections::BTreeSet<Value> =
left.difference(right).cloned().collect();
Ok(Value::from(diff))
let diff: BTreeSet<Value> = left.difference(right).cloned().collect();
Ok(Value::from_set(diff))
}
_ => Err(VmError::InvalidSubtraction {
left: a.clone(),

View File

@@ -2,9 +2,9 @@
// Licensed under the MIT License.
use crate::rvm::instructions::{ComprehensionBeginParams, ComprehensionMode};
use crate::value::Object;
use crate::value::Value;
use crate::Rc;
use alloc::collections::BTreeMap;
use alloc::format;
use alloc::sync::Arc;
use alloc::vec::Vec;
@@ -34,12 +34,12 @@ impl RegoVM {
let initial_result = match params.mode {
ComprehensionMode::Set => Value::new_set(),
ComprehensionMode::Array => Value::new_array(),
ComprehensionMode::Object => Value::Object(Rc::new(Object::new())),
ComprehensionMode::Object => Value::Object(Rc::new(BTreeMap::new())),
};
self.set_register(params.result_reg, initial_result.clone())?;
let auto_iterate = params.collection_reg != params.result_reg;
let mut iteration_state = if auto_iterate {
let iteration_state = if auto_iterate {
let source_value = self.get_register(params.collection_reg)?.clone();
match source_value {
Value::Array(items) => {
@@ -53,9 +53,11 @@ impl RegoVM {
if obj.is_empty() {
None
} else {
// O(1) cursor over shared Rc<Object>.
let cursor = obj.cursor();
Some(IterationState::Object { obj, cursor })
Some(IterationState::Object {
obj,
current_key: None,
first_iteration: true,
})
}
}
Value::Set(set) => {
@@ -77,7 +79,7 @@ impl RegoVM {
None
};
let has_iteration = if let Some(state) = iteration_state.as_mut() {
let has_iteration = if let Some(state) = iteration_state.as_ref() {
self.setup_next_iteration(state, params.key_reg, params.value_reg)?
} else {
false
@@ -121,12 +123,12 @@ impl RegoVM {
let initial_result = match params.mode {
ComprehensionMode::Set => Value::new_set(),
ComprehensionMode::Array => Value::new_array(),
ComprehensionMode::Object => Value::Object(Rc::new(Object::new())),
ComprehensionMode::Object => Value::Object(Rc::new(BTreeMap::new())),
};
self.set_register(params.result_reg, initial_result.clone())?;
let auto_iterate = params.collection_reg != params.result_reg;
let mut iteration_state = if auto_iterate {
let iteration_state = if auto_iterate {
let source_value = self.get_register(params.collection_reg)?.clone();
match source_value {
Value::Array(items) => {
@@ -140,8 +142,11 @@ impl RegoVM {
if obj.is_empty() {
None
} else {
let cursor = obj.cursor();
Some(IterationState::Object { obj, cursor })
Some(IterationState::Object {
obj,
current_key: None,
first_iteration: true,
})
}
}
Value::Set(set) => {
@@ -163,7 +168,7 @@ impl RegoVM {
None
};
let has_iteration = if let Some(state) = iteration_state.as_mut() {
let has_iteration = if let Some(state) = iteration_state.as_ref() {
self.setup_next_iteration(state, params.key_reg, params.value_reg)?
} else {
false
@@ -250,21 +255,6 @@ impl RegoVM {
};
let result_reg = comprehension_context.result_reg;
// Snapshot the iteration value register BEFORE taking the result
// register: if the comprehension compiler ever allocates
// `result_reg == context.value_reg`, the writeback at the bottom
// of this function would clobber the value register, and a
// post-writeback read here would feed the wrong value into
// `IterationState::Set::current_item`. Only Set needs the snapshot
// (Object uses a self-advancing cursor; Array advances by index).
let set_resume_snapshot = if matches!(
comprehension_context.iteration_state,
Some(IterationState::Set { .. })
) {
Some(self.get_register(comprehension_context.value_reg)?.clone())
} else {
None
};
// Take ownership of the result register so Rc refcount stays at 1,
// allowing Rc::make_mut to mutate in-place instead of deep-cloning.
let mut current_result = self.take_register(result_reg)?;
@@ -302,16 +292,29 @@ impl RegoVM {
self.set_register(result_reg, current_result)?;
if let Some(iter_state) = comprehension_context.iteration_state.as_mut() {
// Set's `Bound::Excluded(current_item)` resume scheme needs the
// pre-mutation snapshot taken at the top of this function.
// Object uses a self-advancing cursor and needs no snapshot.
if let IterationState::Set {
ref mut current_item,
..
} = *iter_state
{
*current_item = set_resume_snapshot;
match *iter_state {
IterationState::Object {
ref mut current_key,
..
} => {
let tracked_key =
if comprehension_context.key_reg != comprehension_context.value_reg {
self.get_register(comprehension_context.key_reg)?.clone()
} else {
self.get_register(comprehension_context.value_reg)?.clone()
};
*current_key = Some(tracked_key);
}
IterationState::Set {
ref mut current_item,
..
} => {
*current_item =
Some(self.get_register(comprehension_context.value_reg)?.clone());
}
IterationState::Array { .. } | IterationState::Single { .. } => {}
}
iter_state.advance();
let has_next = self.setup_next_iteration(
iter_state,
@@ -356,7 +359,8 @@ impl RegoVM {
result_reg_idx,
key_reg_idx,
value_reg_idx,
iter_is_set,
iteration_key,
iteration_value,
) = {
let frame =
self.execution_stack
@@ -378,8 +382,8 @@ impl RegoVM {
let result_reg_idx = context.result_reg;
let mode = context.mode.clone();
let iter_is_set =
matches!(context.iteration_state, Some(IterationState::Set { .. }));
let iteration_key = self.get_register(context.key_reg)?.clone();
let iteration_value = self.get_register(context.value_reg)?.clone();
(
value_to_add,
@@ -388,7 +392,8 @@ impl RegoVM {
result_reg_idx,
context.key_reg,
context.value_reg,
iter_is_set,
iteration_key,
iteration_value,
)
} else {
return Err(VmError::InvalidIteration {
@@ -398,18 +403,6 @@ impl RegoVM {
}
};
// Snapshot the iteration value register BEFORE the result writeback:
// if the compiler ever allocates `result_reg == value_reg_idx`, a
// post-writeback read would feed the result accumulator into
// `IterationState::Set::current_item`, breaking the next iteration.
// Only Set needs this (Object cursor self-advances; Array advances
// by index).
let set_resume_snapshot = if iter_is_set {
Some(self.get_register(value_reg_idx)?.clone())
} else {
None
};
// Take ownership of the result register so Rc refcount stays at 1,
// allowing Rc::make_mut to mutate in-place instead of deep-cloning.
let mut current_result = self.take_register(result_reg_idx)?;
@@ -457,13 +450,27 @@ impl RegoVM {
} = &mut frame.kind
{
if let Some(iter_state) = context.iteration_state.as_mut() {
if let IterationState::Set {
ref mut current_item,
..
} = *iter_state
{
*current_item = set_resume_snapshot;
match *iter_state {
IterationState::Object {
ref mut current_key,
..
} => {
let tracked_key = if context.key_reg != context.value_reg {
iteration_key.clone()
} else {
iteration_value.clone()
};
*current_key = Some(tracked_key);
}
IterationState::Set {
ref mut current_item,
..
} => {
*current_item = Some(iteration_value.clone());
}
IterationState::Array { .. } | IterationState::Single { .. } => {}
}
iter_state.advance();
}
@@ -480,21 +487,8 @@ impl RegoVM {
}
};
if let Some(mut state) = iteration_state_snapshot {
let has_next = self.setup_next_iteration(&mut state, key_reg_idx, value_reg_idx)?;
// `setup_next_iteration` advances Object's internal cursor; the
// owning frame holds the iteration_state, so we must write the
// updated state back. (The Array/Set variants are also unchanged
// by copy, so the writeback is uniform.)
if let Some(frame) = self.execution_stack.get_mut(comprehension_index) {
if let FrameKind::Comprehension {
ref mut context, ..
} = frame.kind
{
context.iteration_state = Some(state);
}
}
if let Some(state) = iteration_state_snapshot.as_ref() {
let has_next = self.setup_next_iteration(state, key_reg_idx, value_reg_idx)?;
if has_next {
if let Some(frame) = self.execution_stack.get_mut(comprehension_index) {
@@ -534,6 +528,7 @@ impl RegoVM {
Ok(false)
}
}
pub(super) fn handle_comprehension_condition_failure_suspendable(&mut self) -> Result<bool> {
if let Some(mut frame) = self.execution_stack.pop() {
let handled = if let &mut FrameKind::Comprehension {
@@ -559,16 +554,11 @@ impl RegoVM {
context: &mut ComprehensionContext,
) -> Result<()> {
if let Some(iter_state) = context.iteration_state.as_mut() {
// Snapshot the current value into Set's `current_item` so the
// next iteration can resume from `Bound::Excluded(current)`.
// Object uses a self-advancing cursor and needs no snapshot here.
if let IterationState::Set {
ref mut current_item,
..
} = *iter_state
{
*current_item = Some(self.get_register(context.value_reg)?.clone());
}
self.capture_comprehension_iteration_position(
iter_state,
context.key_reg,
context.value_reg,
)?;
iter_state.advance();
let has_next =
self.setup_next_iteration(iter_state, context.key_reg, context.value_reg)?;
@@ -585,10 +575,37 @@ impl RegoVM {
Ok(())
}
fn capture_comprehension_iteration_position(
&mut self,
iter_state: &mut IterationState,
key_reg: u8,
value_reg: u8,
) -> Result<()> {
match *iter_state {
IterationState::Object {
ref mut current_key,
..
} => {
let tracked_key = if key_reg != value_reg {
self.get_register(key_reg)?.clone()
} else {
self.get_register(value_reg)?.clone()
};
*current_key = Some(tracked_key);
}
IterationState::Set {
ref mut current_item,
..
} => {
*current_item = Some(self.get_register(value_reg)?.clone());
}
IterationState::Array { .. } | IterationState::Single { .. } => {}
}
Ok(())
}
fn execute_comprehension_end_run_to_completion(&mut self) -> Result<()> {
// `ComprehensionEnd` is reached from a loaded program; an empty stack
// here means malformed user-supplied bytecode, which must still surface
// as a typed error rather than a panic — including in debug builds.
self.comprehension_stack.pop().map_or_else(
|| {
Err(VmError::InvalidIteration {

View File

@@ -3,9 +3,8 @@
use crate::rvm::instructions::{ComprehensionMode, LoopMode};
use crate::value::Value;
use crate::value::{Object, ObjectCursor};
use crate::Rc;
use alloc::collections::BTreeSet;
use alloc::collections::{BTreeMap, BTreeSet};
use alloc::vec::Vec;
/// Loop execution context for managing iteration state
@@ -25,18 +24,7 @@ pub struct LoopContext {
pub current_iteration_failed: bool, // Track if current iteration had condition failures
}
/// Iterator state for different collection types.
///
/// Snapshot independence for `Object` is provided by the shared
/// `Rc<Object>` — `Rc::make_mut` on an aliased Rc allocates a new
/// collection, leaving the iterator's Rc pointing at the original
/// pre-mutation state. The `ObjectCursor` is opaque and resumes in
/// O(log n) for the BTree backend.
///
/// `Set` continues to use the pre-existing snapshot-by-cloned-key
/// approach (`current_item` + `first_iteration`); migration of `Set`
/// to a cursor-based iterator ships with the `Set` storage abstraction
/// in a follow-up PR.
/// Iterator state for different collection types
#[derive(Debug, Clone)]
pub enum IterationState {
Array {
@@ -44,8 +32,9 @@ pub enum IterationState {
index: usize,
},
Object {
obj: Rc<Object>,
cursor: ObjectCursor,
obj: Rc<BTreeMap<Value, Value>>,
current_key: Option<Value>,
first_iteration: bool,
},
Set {
items: Rc<BTreeSet<Value>>,
@@ -65,21 +54,13 @@ impl IterationState {
pub(super) const fn advance(&mut self) {
match *self {
Self::Array { ref mut index, .. } => {
// Array iteration uses `usize` as the cursor and advances via
// `saturating_add(1)`. A cursor already at `usize::MAX` here
// means a stuck (non-progressing) iteration was emitted by
// malformed bytecode; assert in debug to surface it loudly.
debug_assert!(
*index < usize::MAX,
"IterationState::Array index already at usize::MAX on advance"
);
*index = index.saturating_add(1);
}
// For Object the cursor advances inside `setup_next_iteration`
// when it pulls the next item via `Object::next`, so `advance`
// is a no-op for the cursor-backed Object variant.
Self::Object { .. } => {}
Self::Set {
Self::Object {
ref mut first_iteration,
..
}
| Self::Set {
ref mut first_iteration,
..
} => {
@@ -88,12 +69,6 @@ impl IterationState {
Self::Single {
ref mut consumed, ..
} => {
// `Single` yields exactly once; advancing a consumed Single
// means the compiler emitted a redundant LoopNext.
debug_assert!(
!*consumed,
"IterationState::Single advanced after consumption"
);
*consumed = true;
}
}
@@ -132,71 +107,3 @@ pub(super) struct ComprehensionContext {
/// Resume location for the parent frame once this comprehension completes
pub(super) resume_pc: usize,
}
#[cfg(test)]
#[allow(
clippy::expect_used,
clippy::unwrap_used,
clippy::unreachable,
clippy::pattern_type_mismatch,
clippy::shadow_unrelated,
clippy::panic
)]
mod tests {
use super::*;
use crate::value::Object;
/// IterationState::Object holds an `Rc<Object>` plus an opaque cursor.
/// Mutating an aliased Rc via `Rc::make_mut` allocates a new collection
/// (CoW) so the in-flight iterator's source is unaffected.
#[test]
fn iteration_state_object_is_snapshot_independent_of_source() {
let mut obj = Object::new();
obj.insert(Value::from("a"), Value::from(1));
obj.insert(Value::from("b"), Value::from(2));
obj.insert(Value::from("c"), Value::from(3));
let source = Value::Object(Rc::new(obj));
let snapshot_obj = match &source {
Value::Object(o) => Rc::clone(o),
_ => unreachable!(),
};
let state = IterationState::Object {
obj: Rc::clone(&snapshot_obj),
cursor: snapshot_obj.cursor(),
};
// Mutate a clone of the source mid-iteration.
let mut alias = source.clone();
let inner = alias.as_object_mut().expect("object");
inner.insert(Value::from("a"), Value::from(999));
inner.insert(Value::from("d"), Value::from(4));
inner.remove(&Value::from("b"));
// Drain the snapshot via the cursor — must still report the original
// 3 entries with original values.
let mut collected: Vec<(Value, Value)> = Vec::new();
if let IterationState::Object {
ref obj,
mut cursor,
} = state
{
while let Some((k, v)) = obj.next(&mut cursor) {
collected.push((k.clone(), v.clone()));
}
} else {
unreachable!();
}
assert_eq!(collected.len(), 3);
assert!(collected.contains(&(Value::from("a"), Value::from(1))));
assert!(collected.contains(&(Value::from("b"), Value::from(2))));
assert!(collected.contains(&(Value::from("c"), Value::from(3))));
assert!(!collected.iter().any(|kv| kv.0 == Value::from("d")));
// The original source Value (untouched) is also unchanged.
let src_obj = source.as_object().expect("object");
assert_eq!(src_obj.len(), 3);
assert_eq!(src_obj.get(&Value::from("a")), Some(&Value::from(1)));
}
}

View File

@@ -4,6 +4,7 @@
use crate::rvm::instructions::{GuardMode, Instruction, LiteralOrRegister};
use crate::rvm::program::Program;
use crate::value::Value;
use alloc::collections::BTreeSet;
use alloc::vec::Vec;
use core::mem;
@@ -453,7 +454,19 @@ impl RegoVM {
let mut obj_value = self.take_register(obj)?;
if let Ok(obj_mut) = obj_value.as_object_mut() {
obj_mut.insert(key_value, value_value);
match obj_mut.get(&key_value) {
Some(existing_value) if existing_value != &value_value => {
self.set_register(obj, obj_value)?;
return Err(VmError::RuleMultipleOutputs { pc: self.pc });
}
Some(_) => {
self.set_register(obj, obj_value)?;
return Ok(InstructionOutcome::Continue);
}
None => {
obj_mut.insert(key_value, value_value);
}
}
self.set_register(obj, obj_value)?;
} else {
let offending = obj_value.clone();
@@ -669,7 +682,7 @@ impl RegoVM {
}
}
SetNew { dest } => {
let empty_set = Value::new_set();
let empty_set = Value::Set(crate::Rc::new(BTreeSet::new()));
self.set_register(dest, empty_set)?;
Ok(InstructionOutcome::Continue)
}
@@ -706,7 +719,7 @@ impl RegoVM {
if any_undefined {
self.set_register(params.dest, Value::Undefined)?;
} else {
let mut set = alloc::collections::BTreeSet::new();
let mut set = BTreeSet::new();
for &reg in params.element_registers() {
set.insert(self.get_register(reg)?.clone());
}

View File

@@ -209,6 +209,9 @@ pub enum VmError {
#[error("Rule-data conflict: {message} (pc={pc})")]
RuleDataConflict { message: String, pc: usize },
#[error("rules must not produce multiple outputs (pc={pc})")]
RuleMultipleOutputs { pc: usize },
#[error("Arithmetic error: {message} (pc={pc})")]
ArithmeticError { message: String, pc: usize },
@@ -295,13 +298,6 @@ pub enum VmError {
#[error("Call rule stack underflow during rule finalization (pc={pc})")]
CallRuleStackUnderflow { pc: usize },
#[error("Call rule stack mismatch during rule finalization: expected rule_index {expected}, popped {actual} (pc={pc})")]
CallRuleStackMismatch {
expected: u16,
actual: u16,
pc: usize,
},
#[error("Internal VM error: {message} (pc={pc})")]
Internal { message: String, pc: usize },
}

View File

@@ -117,10 +117,6 @@ impl RegoVM {
let target = self.convert_pc(target, "jump target")?;
self.pc = target;
while self.pc < program.instructions.len() {
// Per-instruction sanity check: every iteration of the dispatch
// loop must re-enter with the VM in a Running/Ready state and the
// working data structures coherent.
self.assert_vm_invariants();
self.memory_check()?;
if self.executed_instructions >= self.max_instructions {
return Err(VmError::InstructionLimitExceeded {
@@ -193,9 +189,6 @@ impl RegoVM {
}
fn execute_suspendable_entry(&mut self, entry_point_pc: usize) -> Result<Value> {
// Precondition: callers (execute_entry_point_by_{index,name}) reset the
// VM before invoking this method, so the VM must be in a clean state.
self.debug_assert_state_is_clean();
self.execution_state = ExecutionState::Running;
self.reset_execution_timer_state();
match self.run_stackless_from(entry_point_pc) {
@@ -208,10 +201,6 @@ impl RegoVM {
}
pub fn resume(&mut self, resume_value: Option<Value>) -> Result<Value> {
// Precondition is enforced below by returning `VmError::InvalidResumeState`
// for any non-`Suspended` state. A `debug_assert!` here would diverge
// debug vs release behavior and, when invoked via FFI, would trip the
// unwind guard and poison the engine on a recoverable misuse.
let (reason, mut last_result) = match self.execution_state.clone() {
ExecutionState::Suspended {
reason,
@@ -300,9 +289,6 @@ impl RegoVM {
fn run_stackless_loop(&mut self, program: &Program, last_result: &mut Value) -> Result<()> {
while !self.execution_stack.is_empty() {
// Per-instruction sanity check: see `assert_vm_invariants` for the
// exact contract. Compiled out in release.
self.assert_vm_invariants();
self.memory_check()?;
self.frame_pc_overridden = false;
let should_finalize_rule = self.execution_stack.last().is_some_and(|frame| {

View File

@@ -3,7 +3,6 @@
use crate::rvm::instructions::LoopMode;
use crate::value::Value;
use crate::Rc;
use super::context::{IterationState, LoopContext};
use super::errors::{Result, VmError};
@@ -90,13 +89,13 @@ impl RegoVM {
) -> Result<()> {
self.set_register(params.result_reg, Value::Bool(false))?;
let mut iteration_state = match self.resolve_iteration_state(mode, &params)? {
let iteration_state = match self.resolve_iteration_state(mode, &params)? {
Some(state) => state,
None => return Ok(()),
};
let has_next =
self.setup_next_iteration(&mut iteration_state, params.key_reg, params.value_reg)?;
self.setup_next_iteration(&iteration_state, params.key_reg, params.value_reg)?;
if !has_next {
self.pc = usize::from(params.loop_end);
return Ok(());
@@ -156,10 +155,15 @@ impl RegoVM {
LoopAction::Continue => {}
}
// Snapshot the current value for Set so its next iteration can resume
// from `Bound::Excluded(current)`. Object uses a cursor and advances
// inside `setup_next_iteration` itself.
if let &mut IterationState::Set {
if let &mut IterationState::Object {
ref mut current_key,
..
} = &mut loop_ctx.iteration_state
{
if loop_ctx.key_reg != loop_ctx.value_reg {
*current_key = Some(self.get_register(loop_ctx.key_reg)?.clone());
}
} else if let &mut IterationState::Set {
ref mut current_item,
..
} = &mut loop_ctx.iteration_state
@@ -169,7 +173,7 @@ impl RegoVM {
loop_ctx.iteration_state.advance();
let has_next = self.setup_next_iteration(
&mut loop_ctx.iteration_state,
&loop_ctx.iteration_state,
loop_ctx.key_reg,
loop_ctx.value_reg,
)?;
@@ -207,13 +211,13 @@ impl RegoVM {
) -> Result<()> {
self.set_register(params.result_reg, Value::Bool(false))?;
let mut iteration_state = match self.resolve_iteration_state(mode, &params)? {
let iteration_state = match self.resolve_iteration_state(mode, &params)? {
Some(state) => state,
None => return Ok(()),
};
let has_next =
self.setup_next_iteration(&mut iteration_state, params.key_reg, params.value_reg)?;
self.setup_next_iteration(&iteration_state, params.key_reg, params.value_reg)?;
if !has_next {
self.pc = usize::from(params.loop_end);
return Ok(());
@@ -312,14 +316,7 @@ impl RegoVM {
Ok(())
}
LoopAction::Continue => {
let (
mode,
success_count,
total_iterations,
key_reg,
value_reg,
mut iteration_state,
) = {
let (mode, success_count, total_iterations, key_reg, value_reg, iteration_state) = {
let (mode, success_count, total_iterations, key_reg, value_reg) = {
let frame = self
.execution_stack
@@ -337,6 +334,11 @@ impl RegoVM {
}
};
let key_value = if key_reg != value_reg {
Some(self.get_register(key_reg)?.clone())
} else {
None
};
let value_value = self.get_register(value_reg)?.clone();
let frame = self
@@ -347,16 +349,20 @@ impl RegoVM {
&mut FrameKind::Loop {
ref mut context, ..
} => {
// Snapshot the current value for Set so its next
// iteration can resume from `Bound::Excluded(current)`.
// Object uses a cursor and advances inside
// `setup_next_iteration` itself.
if let &mut IterationState::Set {
if let &mut IterationState::Object {
ref mut current_key,
..
} = &mut context.iteration_state
{
if context.key_reg != context.value_reg {
*current_key = key_value;
}
} else if let &mut IterationState::Set {
ref mut current_item,
..
} = &mut context.iteration_state
{
*current_item = Some(value_value);
*current_item = Some(value_value.clone());
}
context.iteration_state.advance();
@@ -375,21 +381,7 @@ impl RegoVM {
}
};
let has_next =
self.setup_next_iteration(&mut iteration_state, key_reg, value_reg)?;
// `setup_next_iteration` advances Object's internal cursor;
// the owning frame holds the iteration_state, so we must
// write the updated state back. (Array/Set are unchanged by
// the call, so the writeback is uniform.)
if let Some(frame) = self.execution_stack.last_mut() {
if let FrameKind::Loop {
ref mut context, ..
} = frame.kind
{
context.iteration_state = iteration_state;
}
}
let has_next = self.setup_next_iteration(&iteration_state, key_reg, value_reg)?;
if has_next {
if let Some(frame) = self.execution_stack.last_mut() {
@@ -467,14 +459,10 @@ impl RegoVM {
self.handle_empty_collection(mode, params.result_reg, params.loop_end)?;
return Ok(None);
}
// O(1) resumable cursor over the shared Rc<Object>.
// No eager pair snapshot: avoids O(N) setup, O(N) memory
// floor, and O(N) memory-limit checks. Snapshot
// independence is via the shared Rc (CoW).
let cursor = obj.cursor();
Ok(Some(IterationState::Object {
obj: Rc::clone(obj),
cursor,
obj: obj.clone(),
current_key: None,
first_iteration: true,
}))
}
}
@@ -495,15 +483,8 @@ impl RegoVM {
// over a virtual null element.
Ok(Some(IterationState::Single { consumed: false }))
} else {
// Standard Rego: iterating a non-collection scalar (number,
// string, bool, null, undefined) yields no iterations. For
// `every` this makes the quantifier undefined (it fails) — it
// is NOT vacuously true, which only applies to a genuinely
// empty collection. `any`/`forEach` remain false.
let result = match *mode {
LoopMode::Every => Value::Undefined,
LoopMode::Any | LoopMode::ForEach => Value::Bool(false),
};
// Standard Rego or count/forEach: non-collection → immediate result.
let result = non_collection_result(mode);
self.set_register(params.result_reg, result)?;
self.pc = usize::from(params.loop_end).saturating_sub(1);
Ok(None)
@@ -531,7 +512,7 @@ impl RegoVM {
pub(super) fn setup_next_iteration(
&mut self,
state: &mut IterationState,
state: &IterationState,
key_reg: u8,
value_reg: u8,
) -> Result<bool> {
@@ -557,19 +538,33 @@ impl RegoVM {
}
IterationState::Object {
ref obj,
ref mut cursor,
ref current_key,
ref first_iteration,
} => {
// Object iterates via a resumable cursor on the shared
// `Rc<Object>`; `next` both yields the current entry and
// advances the cursor. No explicit `current_key` snapshot is
// needed — see the doc on `IterationState`.
if let Some((key, value)) = obj.next(cursor) {
let value = value.clone();
if key_reg != value_reg {
self.set_register(key_reg, key.clone())?;
if *first_iteration {
if let Some((key, value)) = obj.iter().next() {
if key_reg != value_reg {
self.set_register(key_reg, key.clone())?;
}
self.set_register(value_reg, value.clone())?;
Ok(true)
} else {
Ok(false)
}
} else if let Some(ref current) = *current_key {
let mut range_iter = obj.range((
core::ops::Bound::Excluded(current),
core::ops::Bound::Unbounded,
));
if let Some((key, value)) = range_iter.next() {
if key_reg != value_reg {
self.set_register(key_reg, key.clone())?;
}
self.set_register(value_reg, value.clone())?;
Ok(true)
} else {
Ok(false)
}
self.set_register(value_reg, value)?;
Ok(true)
} else {
Ok(false)
}

View File

@@ -17,8 +17,9 @@ use super::execution_model::{
use super::machine::RegoVM;
impl RegoVM {
/// Returns true if the error represents a resource-limit violation that
/// must never be silently absorbed by rule evaluation.
/// Returns true if the error must never be silently absorbed by rule
/// evaluation backtracking, including resource-limit failures and semantic
/// rule consistency errors.
pub(super) const fn is_fatal_vm_error(err: &VmError) -> bool {
matches!(
err,
@@ -26,6 +27,7 @@ impl RegoVM {
| VmError::MemoryLimitExceeded { .. }
| VmError::RegexSizeLimitExceeded { .. }
| VmError::InstructionLimitExceeded { .. }
| VmError::RuleMultipleOutputs { .. }
)
}
@@ -277,19 +279,6 @@ impl RegoVM {
.call_rule_stack
.pop()
.ok_or(VmError::CallRuleStackUnderflow { pc: self.pc })?;
// Stack discipline: the context we just popped must belong to the
// rule we are finalizing. A mismatch indicates a missing push or an
// extra pop somewhere in this rule's execution and would otherwise
// silently restore the wrong return_pc / rule_type. Surface as a
// typed VmError so the contract holds the same in debug and release
// builds (avoiding FFI poisoning via a debug-only panic).
if rule_index != call_context.rule_index {
return Err(VmError::CallRuleStackMismatch {
expected: rule_index,
actual: call_context.rule_index,
pc: self.pc,
});
}
self.pc = call_context.return_pc;
let result_from_rule = if !rule_failed_due_to_inconsistency {
@@ -844,9 +833,6 @@ impl RegoVM {
self.registers = parent_registers;
// Underflow here means malformed/poisoned program state; surface as a
// typed error rather than a debug-only panic so the public load_program
// contract holds the same in debug and release.
if self.call_rule_stack.pop().is_none() {
return Err(VmError::CallRuleStackUnderflow { pc: self.pc });
}

View File

@@ -34,139 +34,6 @@ impl RegoVM {
// Builtin cache entries only live for a single execution
self.builtins_cache.clear();
// Postcondition: every stack/cache that `reset_execution_state` touches
// must be in its documented "clean" shape. This catches accidental
// omissions in future edits to this function.
self.debug_assert_state_is_clean();
}
/// Debug-only postcondition for `reset_execution_state`.
///
/// Asserts the invariants every caller of `reset_execution_state` relies on
/// before starting a fresh execution. The body is fully gated by
/// `#[cfg(debug_assertions)]` so this is a zero-cost no-op in release.
#[inline]
pub(super) fn debug_assert_state_is_clean(&self) {
#[cfg(debug_assertions)]
{
// --- Stacks: every per-execution stack must be drained. ---
debug_assert!(
self.execution_stack.is_empty(),
"reset_execution_state postcondition: execution_stack must be empty"
);
debug_assert!(
self.loop_stack.is_empty(),
"reset_execution_state postcondition: loop_stack must be empty"
);
debug_assert!(
self.comprehension_stack.is_empty(),
"reset_execution_state postcondition: comprehension_stack must be empty"
);
debug_assert!(
self.call_rule_stack.is_empty(),
"reset_execution_state postcondition: call_rule_stack must be empty"
);
debug_assert!(
self.register_stack.is_empty(),
"reset_execution_state postcondition: register_stack must be empty"
);
// --- Caches: cleared so a new program/input cannot read stale entries. ---
debug_assert!(
self.builtins_cache.is_empty(),
"reset_execution_state postcondition: builtins_cache must be empty"
);
// --- Registers: window resized to the program's base count and zeroed. ---
debug_assert_eq!(
self.registers.len(),
self.base_register_count,
"reset_execution_state postcondition: registers must be sized to base_register_count"
);
debug_assert!(
self.registers.iter().all(|v| matches!(v, Value::Undefined)),
"reset_execution_state postcondition: all registers must be Undefined"
);
// --- Rule cache: sized to the current program and marked uncomputed. ---
debug_assert_eq!(
self.rule_cache.len(),
self.program.rule_infos.len(),
"reset_execution_state postcondition: rule_cache size must match program rule_infos"
);
debug_assert!(
self.rule_cache.iter().all(|entry| !entry.0),
"reset_execution_state postcondition: rule_cache entries must be uncomputed"
);
// --- Counters and execution-state machine: zeroed and back to Ready. ---
debug_assert_eq!(
self.pc, 0,
"reset_execution_state postcondition: pc must be 0"
);
debug_assert_eq!(
self.executed_instructions, 0,
"reset_execution_state postcondition: executed_instructions must be 0"
);
debug_assert!(
matches!(self.execution_state, ExecutionState::Ready),
"reset_execution_state postcondition: execution_state must be Ready"
);
}
}
/// Per-opcode VM invariants checked from the inner dispatch loop.
///
/// These hold every time control re-enters the dispatch loop with another
/// instruction to execute. Only conditions that are *purely VM-internal*
/// (i.e. cannot be made false by any host-supplied program or out-of-order
/// API call) are asserted here — anything reachable from `load_program`
/// input must surface as a typed `VmError` instead, to avoid panicking in
/// debug builds and poisoning the engine across FFI.
///
/// Fully `#[cfg(debug_assertions)]`-gated so the method body compiles out
/// in release.
#[inline]
pub(super) fn assert_vm_invariants(&self) {
#[cfg(debug_assertions)]
{
// The dispatch loop only runs while execution is live. Once the VM
// has transitioned to a terminal state (Suspended/Completed/Error)
// the loop must have exited. Note `Ready` is also valid here because
// some entry points (e.g. `execute_entry_point_by_index` in
// RunToCompletion mode) drive `jump_to` without flipping the state.
// `execution_state` is mutated only inside the VM and is not
// host-controllable.
debug_assert!(
matches!(
self.execution_state,
ExecutionState::Ready | ExecutionState::Running
),
"vm invariant: execution_state must be Ready or Running inside the dispatch loop, was {:?}",
self.execution_state
);
// Rule cache is sized once at reset (against the currently loaded
// program) and the VM does not resize it mid-execution. Any
// mismatch here would indicate an internal accounting bug rather
// than malformed input.
debug_assert_eq!(
self.rule_cache.len(),
self.program.rule_infos.len(),
"vm invariant: rule_cache size must equal program.rule_infos size"
);
// NOTE: `!registers.is_empty()` and an `execution_stack` depth
// ceiling were intentionally *not* asserted here: both can be
// triggered by a host-loaded program (registers via
// `RuleInfo::num_registers == 0`; stack depth via deeply nested
// rules/loops/comprehensions) and would therefore panic in debug
// and poison the engine across FFI. Register access is already
// guarded by `VmError::RegisterIndexOutOfBounds`; runaway recursion
// is bounded in production by `set_max_instructions` and
// `memory_check`.
}
}
/// Return all active objects to their respective pools for reuse

View File

@@ -569,7 +569,7 @@ impl Analyzer {
}
Ok(false)
}
Expr::Array { .. } | Expr::Object { .. } => Ok(true),
Array { .. } | Object { .. } => Ok(true),
_ => Ok(false),
})?;
Ok(true)
@@ -666,7 +666,7 @@ impl Analyzer {
Ok(false)
}
// TODO: key vs value for object binding
Expr::Array { .. } | Expr::Object { .. } => Ok(true),
Array { .. } | Object { .. } => Ok(true),
_ => Ok(false),
})?;
Ok(vars)
@@ -853,7 +853,7 @@ impl Analyzer {
Ok(false)
}
// TODO: Object key/value
Expr::Array { .. } | Expr::Object { .. } => Ok(true),
Array { .. } | Object { .. } => Ok(true),
_ => {
non_vars.push(e.clone());
Ok(false)

View File

@@ -1498,7 +1498,7 @@ fn test_deserialize_object_default_empty_object() {
let s = Schema::from_serde_json_value(schema).unwrap();
match s.as_type() {
Type::Object { default, .. } => {
assert_eq!(default, &Some(Value::new_object()));
assert_eq!(default, &Some(Value::Object(Rc::new(BTreeMap::new()))));
}
_ => panic!("Expected Type::Object"),
}
@@ -1778,11 +1778,8 @@ fn test_deserialize_enum_values_with_object_non_string_keys() {
match s.as_type() {
Type::Enum { values, .. } => match &values[0] {
Value::Object(obj) => {
assert_eq!(*obj.get(&Value::from("1")).expect("1"), Value::from("one"));
assert_eq!(
*obj.get(&Value::from("true")).expect("true"),
Value::from("bool")
);
assert_eq!(obj[&Value::from("1")], Value::from("one"));
assert_eq!(obj[&Value::from("true")], Value::from("bool"));
}
_ => panic!("Expected object in enum values"),
},
@@ -1805,21 +1802,18 @@ fn test_deserialize_enum_values_with_deeply_nested_structures() {
match s.as_type() {
Type::Enum { values, .. } => match &values[0] {
Value::Object(obj) => {
let a = obj.get(&Value::from("a")).expect("a");
let a = &obj[&Value::from("a")];
match a {
Value::Array(arr) => match &arr[0] {
Value::Object(inner) => {
let b = inner.get(&Value::from("b")).expect("b");
let b = &inner[&Value::from("b")];
match b {
Value::Array(barr) => {
assert_eq!(barr[0], Value::from(1));
assert_eq!(barr[1], Value::from(2));
match &barr[2] {
Value::Object(cobj) => {
assert_eq!(
*cobj.get(&Value::from("c")).expect("c"),
Value::Null
);
assert_eq!(cobj[&Value::from("c")], Value::Null);
}
_ => panic!("Expected object for 'c'"),
}
@@ -1892,11 +1886,8 @@ fn test_deserialize_const_value_object() {
match s.as_type() {
Type::Const { value, .. } => match value {
Value::Object(ref obj) => {
assert_eq!(
*obj.get(&Value::from("foo")).expect("foo"),
Value::from("bar")
);
assert_eq!(*obj.get(&Value::from("baz")).expect("baz"), Value::from(1));
assert_eq!(obj[&Value::from("foo")], Value::from("bar"));
assert_eq!(obj[&Value::from("baz")], Value::from(1));
}
_ => panic!("Expected object for const value"),
},
@@ -1949,13 +1940,13 @@ fn test_deserialize_const_value_deeply_nested() {
match s.as_type() {
Type::Const { value, .. } => match value {
Value::Object(ref obj) => {
let a = obj.get(&Value::from("a")).expect("a");
let a = &obj[&Value::from("a")];
match a {
Value::Array(arr) => {
assert_eq!(arr[0], Value::from(1));
match &arr[1] {
Value::Object(inner) => {
let b = inner.get(&Value::from("b")).expect("b");
let b = &inner[&Value::from("b")];
match b {
Value::Array(barr) => {
assert_eq!(barr[0], Value::Null);

View File

@@ -7,7 +7,6 @@
use crate::{
schema::{error::ValidationError, Schema, Type},
value::Object,
*,
};
use alloc::collections::BTreeMap;
@@ -538,7 +537,7 @@ impl SchemaValidator {
}
fn validate_discriminated_subobject_with_base(
object_value: &Object,
object_value: &BTreeMap<Value, Value>,
discriminated_subobject: &crate::schema::DiscriminatedSubobject,
base_properties: &BTreeMap<String, Schema>,
base_additional_properties: Option<&Schema>,
@@ -654,7 +653,7 @@ impl SchemaValidator {
}
fn validate_subobject(
object_value: &Object,
object_value: &BTreeMap<Value, Value>,
subobject: &crate::schema::Subobject,
path: &str,
) -> Result<(), ValidationError> {

View File

@@ -24,7 +24,6 @@ use anyhow::{bail, Result};
use core::num::NonZeroU32;
use core::time::Duration;
use serde::{Deserialize, Serialize};
use std::collections::{BTreeMap, BTreeSet};
use test_generator::test_resources;
use timer_test_support::{
apply_engine_timer, configure_time_source, reset_time_source, GlobalTimerGuard,
@@ -819,446 +818,3 @@ fn test_get_data() -> Result<()> {
Ok(())
}
#[test]
fn test_add_data_deep_merge() -> Result<()> {
let mut engine = Engine::new();
// Nested objects under a shared top-level key are deep-merged, not replaced.
engine.add_data(Value::from_json_str(r#"{ "a" : { "x" : 1 } }"#)?)?;
engine.add_data(Value::from_json_str(r#"{ "a" : { "y" : 2 } }"#)?)?;
assert_eq!(
engine.get_data(),
Value::from_json_str(r#"{ "a" : { "x" : 1, "y" : 2 } }"#)?
);
Ok(())
}
#[test]
fn test_add_data_deep_merge_multi_level() -> Result<()> {
let mut engine = Engine::new();
// Merging recurses through multiple levels of nesting.
engine.add_data(Value::from_json_str(
r#"{ "a" : { "b" : { "x" : 1 } }, "top" : 0 }"#,
)?)?;
engine.add_data(Value::from_json_str(
r#"{ "a" : { "b" : { "y" : 2 }, "c" : 3 } }"#,
)?)?;
assert_eq!(
engine.get_data(),
Value::from_json_str(r#"{ "a" : { "b" : { "x" : 1, "y" : 2 }, "c" : 3 }, "top" : 0 }"#)?
);
Ok(())
}
#[test]
fn test_add_data_leaf_conflict_errors() -> Result<()> {
let mut engine = Engine::new();
// A genuine leaf conflict (same nested path, different value) is an error.
engine.add_data(Value::from_json_str(r#"{ "a" : { "x" : 1 } }"#)?)?;
assert!(engine
.add_data(Value::from_json_str(r#"{ "a" : { "x" : 2 } }"#)?)
.is_err());
Ok(())
}
#[test]
fn test_add_data_object_vs_scalar_conflict_errors() -> Result<()> {
let mut engine = Engine::new();
// An object cannot be merged with a scalar at the same path.
engine.add_data(Value::from_json_str(r#"{ "a" : { "x" : 1 } }"#)?)?;
assert!(engine
.add_data(Value::from_json_str(r#"{ "a" : 5 }"#)?)
.is_err());
Ok(())
}
#[test]
fn test_add_data_equal_leaf_is_noop() -> Result<()> {
let mut engine = Engine::new();
// Re-adding identical data (including equal nested leaves) is tolerated as a no-op.
engine.add_data(Value::from_json_str(r#"{ "a" : { "x" : 1 } }"#)?)?;
engine.add_data(Value::from_json_str(r#"{ "a" : { "x" : 1 }, "b" : 2 }"#)?)?;
assert_eq!(
engine.get_data(),
Value::from_json_str(r#"{ "a" : { "x" : 1 }, "b" : 2 }"#)?
);
Ok(())
}
#[test]
fn test_add_data_set_union() -> Result<()> {
let mut engine = Engine::new();
// Sets under a shared key are unioned rather than conflicting (consistent with the
// rule-evaluation merge, where partial set rules accumulate elements). JSON cannot express
// sets, so the data documents are built via the `Value` API.
engine.add_data(Value::from(BTreeMap::from([(
Value::from("s"),
Value::from(BTreeSet::from([Value::from(1_u64), Value::from(2_u64)])),
)])))?;
engine.add_data(Value::from(BTreeMap::from([(
Value::from("s"),
Value::from(BTreeSet::from([Value::from(2_u64), Value::from(3_u64)])),
)])))?;
let expected = Value::from(BTreeMap::from([(
Value::from("s"),
Value::from(BTreeSet::from([
Value::from(1_u64),
Value::from(2_u64),
Value::from(3_u64),
])),
)]));
assert_eq!(engine.get_data(), expected);
Ok(())
}
#[test]
fn test_add_data_nested_set_union() -> Result<()> {
let mut engine = Engine::new();
// A set nested under an object key exercises the recursive merge: the outer objects are
// deep-merged and the inner sets are then unioned.
engine.add_data(Value::from(BTreeMap::from([(
Value::from("a"),
Value::from(BTreeMap::from([(
Value::from("s"),
Value::from(BTreeSet::from([Value::from(1_u64)])),
)])),
)])))?;
engine.add_data(Value::from(BTreeMap::from([(
Value::from("a"),
Value::from(BTreeMap::from([(
Value::from("s"),
Value::from(BTreeSet::from([Value::from(2_u64)])),
)])),
)])))?;
let expected = Value::from(BTreeMap::from([(
Value::from("a"),
Value::from(BTreeMap::from([(
Value::from("s"),
Value::from(BTreeSet::from([Value::from(1_u64), Value::from(2_u64)])),
)])),
)]));
assert_eq!(engine.get_data(), expected);
Ok(())
}
#[test]
fn test_add_data_equal_set_is_noop() -> Result<()> {
let mut engine = Engine::new();
// Re-adding an identical set is tolerated as a no-op (not a conflict).
engine.add_data(Value::from(BTreeMap::from([(
Value::from("s"),
Value::from(BTreeSet::from([Value::from(1_u64), Value::from(2_u64)])),
)])))?;
engine.add_data(Value::from(BTreeMap::from([(
Value::from("s"),
Value::from(BTreeSet::from([Value::from(1_u64), Value::from(2_u64)])),
)])))?;
let expected = Value::from(BTreeMap::from([(
Value::from("s"),
Value::from(BTreeSet::from([Value::from(1_u64), Value::from(2_u64)])),
)]));
assert_eq!(engine.get_data(), expected);
Ok(())
}
#[test]
fn test_add_data_failed_merge_is_atomic() -> Result<()> {
let mut engine = Engine::new();
engine.add_data(Value::from_json_str(r#"{ "a" : { "z" : 1 } }"#)?)?;
// Mixes a new key `m` with a conflicting leaf `z` (1 vs 3). Because `m` sorts
// before `z`, a naive in-place merge would insert `m` and only then hit the `z`
// conflict. add_data must be all-or-nothing: the whole call fails AND leaves the
// existing data untouched — `m` must not leak in.
assert!(engine
.add_data(Value::from_json_str(r#"{ "a" : { "m" : 2, "z" : 3 } }"#)?)
.is_err());
assert_eq!(
engine.get_data(),
Value::from_json_str(r#"{ "a" : { "z" : 1 } }"#)?
);
Ok(())
}
#[test]
fn test_add_data_failed_set_merge_is_atomic() -> Result<()> {
let mut engine = Engine::new();
// Existing data: a set `s` alongside a scalar `z` under `a`.
engine.add_data(Value::from(BTreeMap::from([(
Value::from("a"),
Value::from(BTreeMap::from([
(
Value::from("s"),
Value::from(BTreeSet::from([Value::from(1_u64), Value::from(2_u64)])),
),
(Value::from("z"), Value::from(1_u64)),
])),
)])))?;
// This add would union `s` with {3} but conflicts on `z` (1 vs 2). Since `s`
// sorts before `z`, a naive in-place merge would union the set *before* failing
// on `z`, leaking {3} into `s`. The atomic add must reject the whole call and
// leave `s` as {1, 2}.
assert!(engine
.add_data(Value::from(BTreeMap::from([(
Value::from("a"),
Value::from(BTreeMap::from([
(
Value::from("s"),
Value::from(BTreeSet::from([Value::from(3_u64)])),
),
(Value::from("z"), Value::from(2_u64)),
])),
)])))
.is_err());
// `s` must be unchanged ({1, 2}, not {1, 2, 3}) and `z` must still be 1.
let expected = Value::from(BTreeMap::from([(
Value::from("a"),
Value::from(BTreeMap::from([
(
Value::from("s"),
Value::from(BTreeSet::from([Value::from(1_u64), Value::from(2_u64)])),
),
(Value::from("z"), Value::from(1_u64)),
])),
)]));
assert_eq!(engine.get_data(), expected);
Ok(())
}
#[test]
fn test_add_data_failed_array_merge_is_atomic() -> Result<()> {
let mut engine = Engine::new();
engine.add_data(Value::from_json_str(r#"{ "a" : { "arr" : [1, 2] } }"#)?)?;
// Arrays are atomic leaves (never element-merged), so a differing array at the
// same path is a conflict. The new key `aa` sorts before `arr`, so a naive
// in-place merge would insert `aa` and only then hit the `arr` conflict. add_data
// must reject the whole call and leave the data untouched — `aa` must not leak in.
assert!(engine
.add_data(Value::from_json_str(
r#"{ "a" : { "aa" : 5, "arr" : [3] } }"#
)?)
.is_err());
assert_eq!(
engine.get_data(),
Value::from_json_str(r#"{ "a" : { "arr" : [1, 2] } }"#)?
);
Ok(())
}
// The `Value::merge` used by `add_data` is shared with the rule-evaluation path
// (`Interpreter::merge_rule_value`, reached via `with data.* as ...` and rule-value
// materialization). The tests below pin down that making `merge` recursive changed only the
// data-document semantics and left rule evaluation — in particular the `with data.* as ...`
// modifier — behaving exactly as before (an override, never a deep merge).
#[test]
fn test_with_data_modifier_replaces_nested_object() -> Result<()> {
let mut engine = Engine::new();
// Base data provides a nested object with two keys.
engine.add_data(Value::from_json_str(
r#"{ "base" : { "foo" : { "a" : 1, "b" : 2 } } }"#,
)?)?;
engine.add_policy(
"policy.rego".to_string(),
r#"
package test
result := x if {
x := data.base.foo with data.base.foo as {"a": 99}
}
"#
.to_string(),
)?;
// `with data.base.foo as {"a": 99}` REPLACES the whole subtree for the duration of the
// rule; it must NOT deep-merge with the base `{ "a": 1, "b": 2 }`. So `b` is gone.
assert_eq!(
engine
.eval_query("data.test.result".to_string(), false)?
.result[0]
.expressions[0]
.value
.clone(),
Value::from_json_str(r#"{ "a" : 99 }"#)?
);
Ok(())
}
#[test]
fn test_with_data_modifier_replaces_whole_subtree() -> Result<()> {
let mut engine = Engine::new();
engine.add_data(Value::from_json_str(
r#"{ "base" : { "foo" : 1, "bar" : 2 } }"#,
)?)?;
engine.add_policy(
"policy.rego".to_string(),
r#"
package test
result := x if {
x := data.base with data.base as {"only": 3}
}
"#
.to_string(),
)?;
// `with data.base as {...}` replaces the entire `data.base` object; the original
// `foo`/`bar` keys are not merged in.
assert_eq!(
engine
.eval_query("data.test.result".to_string(), false)?
.result[0]
.expressions[0]
.value
.clone(),
Value::from_json_str(r#"{ "only" : 3 }"#)?
);
Ok(())
}
#[test]
fn test_with_data_modifier_nested_replace_preserves_siblings() -> Result<()> {
let mut engine = Engine::new();
// `data.base` has a nested `foo` object AND a sibling `bar`.
engine.add_data(Value::from_json_str(
r#"{ "base" : { "foo" : { "a" : 1, "b" : 2 }, "bar" : 7 } }"#,
)?)?;
engine.add_policy(
"policy.rego".to_string(),
r#"
package test
# `with` targets the nested `data.base.foo`, but the rule observes the PARENT `data.base`.
result := x if {
x := data.base with data.base.foo as {"a": 99}
}
"#
.to_string(),
)?;
// The nested `foo` is deep-replaced (its `b` is gone — `with` never merges), while the
// sibling `bar` under the same parent is preserved.
assert_eq!(
engine
.eval_query("data.test.result".to_string(), false)?
.result[0]
.expressions[0]
.value
.clone(),
Value::from_json_str(r#"{ "foo" : { "a" : 99 }, "bar" : 7 }"#)?
);
Ok(())
}
#[test]
fn test_rule_reads_deep_merged_base_data() -> Result<()> {
let mut engine = Engine::new();
// Two add_data calls deep-merge into a single nested object...
engine.add_data(Value::from_json_str(
r#"{ "base" : { "foo" : { "a" : 1 } } }"#,
)?)?;
engine.add_data(Value::from_json_str(
r#"{ "base" : { "foo" : { "b" : 2 } } }"#,
)?)?;
engine.add_policy(
"policy.rego".to_string(),
r#"
package test
a := data.base.foo.a
b := data.base.foo.b
"#
.to_string(),
)?;
// ...and both merged leaves are visible to rule evaluation.
assert_eq!(
engine.eval_query("data.test".to_string(), false)?.result[0].expressions[0]
.value
.clone(),
Value::from_json_str(r#"{ "a" : 1, "b" : 2 }"#)?
);
Ok(())
}
#[test]
fn test_rule_values_coexist_with_merged_base_data() -> Result<()> {
let mut engine = Engine::new();
// Deep-merged base data under `base`...
engine.add_data(Value::from_json_str(
r#"{ "base" : { "foo" : { "a" : 1 } } }"#,
)?)?;
engine.add_data(Value::from_json_str(
r#"{ "base" : { "foo" : { "b" : 2 } } }"#,
)?)?;
engine.add_policy(
"policy.rego".to_string(),
r#"
package test
computed := data.base.foo.a + data.base.foo.b
"#
.to_string(),
)?;
let data = engine.eval_query("data".to_string(), false)?.result[0].expressions[0]
.value
.clone();
// Base data is preserved and deep-merged...
assert_eq!(
data["base"],
Value::from_json_str(r#"{ "foo" : { "a" : 1, "b" : 2 } }"#)?
);
// ...and the rule-computed value materializes alongside it without disturbing the merge.
assert_eq!(data["test"]["computed"], Value::from(3_u64));
Ok(())
}

View File

@@ -88,7 +88,7 @@ fn analyze_file(regos: &[String], expected_scopes: &[Scope]) -> Result<()> {
}
}
scopes.sort_by_key(|a| a.0.span.line);
scopes.sort_by(|a, b| a.0.span.line.cmp(&b.0.span.line));
for (idx, (_, scope)) in scopes.iter().enumerate() {
if idx > expected_scopes.len() {
bail!("extra scope generated.")

Some files were not shown because too many files have changed in this diff Show More