mirror of
https://github.com/microsoft/regorus.git
synced 2026-08-05 02:16:11 +00:00
Compare commits
8 Commits
copilot/co
...
copilot/fi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eb6e156e8a | ||
|
|
5ae1d8abf2 | ||
|
|
03c4275855 | ||
|
|
78f226f957 | ||
|
|
bec159a580 | ||
|
|
117671d959 | ||
|
|
8617c79da5 | ||
|
|
47124623ab |
75
CHANGELOG.md
75
CHANGELOG.md
@@ -6,10 +6,85 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
## [0.10.0] - 2026-05-05
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
|
- *(copilot)* add multi-agent code review skills (#707)
|
||||||
|
- *(azure_policy)* test runner, compiler fixes, and example program (#700)
|
||||||
|
- *(azure-policy)* implement effect compilation and metadata population (#691)
|
||||||
|
- *(azure-policy)* implement count/count.where compilation (#688)
|
||||||
|
- *(azure-policy)* implement condition, expression, field, and template dispatch compilation (#686)
|
||||||
|
- *(azure-policy)* add compiler skeleton with core types and stubs (#674)
|
||||||
|
- *(rvm)* implement Azure Policy condition evaluation (#661)
|
||||||
|
- *(rvm)* new instructions and loop semantics for Azure Policy support (#659)
|
||||||
|
- *(azure-policy)* add policy rule and policy definition parsers (#660)
|
||||||
|
- add Azure Policy constraint parser (#658)
|
||||||
|
- *(rvm)* extend program metadata and bump serialization to v6 (#654)
|
||||||
|
- add Azure Policy core JSON parser and expression parser (#655)
|
||||||
|
- add Azure Policy AST types (#653)
|
||||||
|
- *(azure-policy)* add alias normalization and denormalization (#635)
|
||||||
|
- add Azure Policy builtins with YAML test suite (#630)
|
||||||
|
- make policy length limits configurable per engine (#624)
|
||||||
|
- implement add_extension in Python binding (#596)
|
||||||
|
- *(rbac)* [**breaking**] add Azure RBAC engine, FFI API, and cross-language tests (#577)
|
||||||
- Azure RBAC condition interpreter with builtin evaluation coverage and YAML test suite, including quantifier (ForAnyOfAnyValues/ForAllOfAllValues), datetime (DateTimeEquals), IP (IpInRange), GUID (GuidEquals), list (ListContains), and string (StringEquals) semantics.
|
- Azure RBAC condition interpreter with builtin evaluation coverage and YAML test suite, including quantifier (ForAnyOfAnyValues/ForAllOfAllValues), datetime (DateTimeEquals), IP (IpInRange), GUID (GuidEquals), list (ListContains), and string (StringEquals) semantics.
|
||||||
- FFI surface for Azure RBAC condition evaluation (see bindings changelog for language-specific wrappers).
|
- FFI surface for Azure RBAC condition evaluation (see bindings changelog for language-specific wrappers).
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- harden regex builtins with compiled-size limit (#705)
|
||||||
|
- *(ci)* skip mimalloc FFI and disable isolation for Miri (#621)
|
||||||
|
|
||||||
|
### Other
|
||||||
|
|
||||||
|
- bump version to 0.10.0 across all bindings
|
||||||
|
- *(deps)* update all Rust dependencies and fix lockfile refresh workflow (#704)
|
||||||
|
- *(deps)* bump com.google.code.gson:gson (#702)
|
||||||
|
- *(deps)* bump the github-actions group across 1 directory with 5 updates (#690)
|
||||||
|
- *(deps)* bump the per-dependency group across 1 directory with 5 updates (#703)
|
||||||
|
- Make `git rev-parse` in `build.rs` optional with graceful fallback (#701)
|
||||||
|
- *(azure_policy)* add foundation test cases (#698)
|
||||||
|
- *(azure_policy)* add end-to-end policy test cases (#699)
|
||||||
|
- fix rand advisory and harden python CI caching (#675)
|
||||||
|
- azure-policy parser: allow overriding the column-width limit (#673)
|
||||||
|
- *(deps)* bump the rust-dependencies group across 5 directories with 6 updates (#671)
|
||||||
|
- *(deps)* bump ruby/setup-ruby in the github-actions group (#670)
|
||||||
|
- *(csharp)* prepare NuGet package for nuget.org publishing (#668)
|
||||||
|
- Fix RVM evaluation of default-only rules (#664)
|
||||||
|
- *(deps)* bump minitest in /bindings/ruby in the per-dependency group (#656)
|
||||||
|
- *(deps)* bump the rust-dependencies group across 2 directories with 3 updates (#657)
|
||||||
|
- consolidate RVM instruction variants and clean up VM internals (#651)
|
||||||
|
- *(deps)* bump wasm-bindgen-test (#650)
|
||||||
|
- *(deps)* bump rb_sys in /bindings/ruby in the per-dependency group (#649)
|
||||||
|
- *(deps)* bump the rust-dependencies group across 3 directories with 4 updates (#647)
|
||||||
|
- *(deps)* bump the github-actions group across 1 directory with 3 updates (#646)
|
||||||
|
- *(dependabot)* restore cargo dependency grouping (#645)
|
||||||
|
- Fix build break (#634)
|
||||||
|
- *(deps)* bump the rust-dependencies group across 5 directories with 16 updates (#633)
|
||||||
|
- *(dependabot)* fix cargo config quoting (#632)
|
||||||
|
- *(dependabot)* fix cargo workspace updates and refresh lockfiles (#629)
|
||||||
|
- *(deps)* bump rubocop in /bindings/ruby in the per-dependency group (#622)
|
||||||
|
- *(deps)* bump the github-actions group with 11 updates (#628)
|
||||||
|
- Consolidate Dependabot, fix #595 (mimalloc + indexmap), add feature-matrix CI (#627)
|
||||||
|
- RVM compiler & runtime optimizations: caching, instruction fusion, constant hoisting, and correctness fixes (#626)
|
||||||
|
- Rvm optimizations (#620)
|
||||||
|
- *(deps)* bump rubocop in /bindings/ruby in the per-dependency group (#618)
|
||||||
|
- *(ci)* add miri workflow (#581)
|
||||||
|
- *(ci)* add cargo audit and deny (#580)
|
||||||
|
- switch binary serialization to postcard (#582)
|
||||||
|
- *(deps-dev)* bump org.apache.maven.plugins:maven-surefire-plugin (#605)
|
||||||
|
- *(deps)* bump bytes (#569)
|
||||||
|
- *(deps)* bump the per-dependency group with 2 updates (#603)
|
||||||
|
- *(deps)* bump the per-dependency group across 1 directory with 3 updates (#607)
|
||||||
|
- boolean mapping (#612)
|
||||||
|
- Bump the per-dependency group with 1 update (#587)
|
||||||
|
- *(deps)* bump the per-dependency group (#585)
|
||||||
|
- *(deps)* bump the per-dependency group (#586)
|
||||||
|
- *(deps-dev)* bump the per-dependency group (#583)
|
||||||
|
- *(deps)* bump the per-dependency group with 12 updates (#593)
|
||||||
|
- *(dependabot)* expand coverage and pin workflows (#579)
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- [**breaking**] Switch RVM binary serialization to postcard, bump the format to v4, and mark v1-3 loads as partial (recompile required).
|
- [**breaking**] Switch RVM binary serialization to postcard, bump the format to v4, and mark v1-3 loads as partial (recompile required).
|
||||||
|
|
||||||
|
|||||||
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -1388,7 +1388,7 @@ checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regorus"
|
name = "regorus"
|
||||||
version = "0.9.1"
|
version = "0.10.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ members = [
|
|||||||
[package]
|
[package]
|
||||||
name = "regorus"
|
name = "regorus"
|
||||||
description = "A fast, lightweight Rego (OPA policy language) interpreter"
|
description = "A fast, lightweight Rego (OPA policy language) interpreter"
|
||||||
version = "0.9.1"
|
version = "0.10.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT AND Apache-2.0 AND BSD-3-Clause"
|
license = "MIT AND Apache-2.0 AND BSD-3-Clause"
|
||||||
repository = "https://github.com/microsoft/regorus"
|
repository = "https://github.com/microsoft/regorus"
|
||||||
|
|||||||
@@ -6,8 +6,6 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<!-- If the environment variable is set (such as in a Github Action run), append the suffix to the version number -->
|
|
||||||
<RegorusPackageVersionSuffix Condition="'$(VersionSuffix)' != ''">-$(VersionSuffix)</RegorusPackageVersionSuffix>
|
|
||||||
<UsePackageReference Condition="'$(UsePackageReference)' == ''">false</UsePackageReference>
|
<UsePackageReference Condition="'$(UsePackageReference)' == ''">false</UsePackageReference>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<Project>
|
<Project>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
||||||
<RegorusPackageVersion>0.9.1</RegorusPackageVersion>
|
<RegorusPackageVersion>0.10.0</RegorusPackageVersion>
|
||||||
<RegorusPackageVersionSuffix Condition="'$(VersionSuffix)' != ''">-$(VersionSuffix)</RegorusPackageVersionSuffix>
|
<RegorusPackageVersionSuffix Condition="'$(VersionSuffix)' != ''">-$(VersionSuffix)</RegorusPackageVersionSuffix>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
|||||||
@@ -10,8 +10,6 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<!-- If the environment variable is set (such as in a Github Action run), append the suffix to the version number -->
|
|
||||||
<RegorusPackageVersionSuffix Condition="'$(VersionSuffix)' != ''">-$(VersionSuffix)</RegorusPackageVersionSuffix>
|
|
||||||
<UsePackageReference Condition="'$(UsePackageReference)' == ''">false</UsePackageReference>
|
<UsePackageReference Condition="'$(UsePackageReference)' == ''">false</UsePackageReference>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
<LangVersion>10.0</LangVersion>
|
<LangVersion>10.0</LangVersion>
|
||||||
|
|
||||||
<!-- See https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-pack -->
|
<!-- See https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-pack -->
|
||||||
<VersionPrefix>0.9.1</VersionPrefix>
|
<VersionPrefix>$(RegorusPackageVersion)</VersionPrefix>
|
||||||
<VersionSuffix>$(VersionSuffix)</VersionSuffix>
|
<VersionSuffix>$(VersionSuffix)</VersionSuffix>
|
||||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||||
<PackageLicenseExpression>MIT AND Apache-2.0 AND BSD-3-Clause</PackageLicenseExpression>
|
<PackageLicenseExpression>MIT AND Apache-2.0 AND BSD-3-Clause</PackageLicenseExpression>
|
||||||
|
|||||||
@@ -9,8 +9,6 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<!-- If the environment variable is set (such as in a Github Action run), append the suffix to the version number -->
|
|
||||||
<RegorusPackageVersionSuffix Condition="'$(VersionSuffix)' != ''">-$(VersionSuffix)</RegorusPackageVersionSuffix>
|
|
||||||
<UsePackageReference Condition="'$(UsePackageReference)' == ''">false</UsePackageReference>
|
<UsePackageReference Condition="'$(UsePackageReference)' == ''">false</UsePackageReference>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
|||||||
@@ -11,8 +11,6 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<!-- Allow CI to append the version suffix for locally built packages -->
|
|
||||||
<RegorusPackageVersionSuffix Condition="'$(VersionSuffix)' != ''">-$(VersionSuffix)</RegorusPackageVersionSuffix>
|
|
||||||
<UsePackageReference Condition="'$(UsePackageReference)' == ''">false</UsePackageReference>
|
<UsePackageReference Condition="'$(UsePackageReference)' == ''">false</UsePackageReference>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
|||||||
4
bindings/ffi/Cargo.lock
generated
4
bindings/ffi/Cargo.lock
generated
@@ -1117,7 +1117,7 @@ checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regorus"
|
name = "regorus"
|
||||||
version = "0.9.1"
|
version = "0.10.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"chrono",
|
"chrono",
|
||||||
@@ -1152,7 +1152,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regorus-ffi"
|
name = "regorus-ffi"
|
||||||
version = "0.9.1"
|
version = "0.10.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"cbindgen",
|
"cbindgen",
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "regorus-ffi"
|
name = "regorus-ffi"
|
||||||
version = "0.9.1"
|
version = "0.10.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT AND Apache-2.0 AND BSD-3-Clause"
|
license = "MIT AND Apache-2.0 AND BSD-3-Clause"
|
||||||
|
|
||||||
|
|||||||
4
bindings/java/Cargo.lock
generated
4
bindings/java/Cargo.lock
generated
@@ -992,7 +992,7 @@ checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regorus"
|
name = "regorus"
|
||||||
version = "0.9.1"
|
version = "0.10.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"chrono",
|
"chrono",
|
||||||
@@ -1024,7 +1024,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regorus-java"
|
name = "regorus-java"
|
||||||
version = "0.9.1"
|
version = "0.10.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"jni",
|
"jni",
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "regorus-java"
|
name = "regorus-java"
|
||||||
version = "0.9.1"
|
version = "0.10.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
repository = "https://github.com/microsoft/regorus/bindings/java"
|
repository = "https://github.com/microsoft/regorus/bindings/java"
|
||||||
description = "Java bindings for Regorus - a fast, lightweight Rego interpreter written in Rust"
|
description = "Java bindings for Regorus - a fast, lightweight Rego interpreter written in Rust"
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
<groupId>com.microsoft.regorus</groupId>
|
<groupId>com.microsoft.regorus</groupId>
|
||||||
<artifactId>regorus-java</artifactId>
|
<artifactId>regorus-java</artifactId>
|
||||||
<version>0.9.1</version>
|
<version>0.10.0</version>
|
||||||
|
|
||||||
<name>Regorus Java</name>
|
<name>Regorus Java</name>
|
||||||
<description>Java bindings for Regorus - a fast, lightweight Rego interpreter written in Rust</description>
|
<description>Java bindings for Regorus - a fast, lightweight Rego interpreter written in Rust</description>
|
||||||
|
|||||||
4
bindings/python/Cargo.lock
generated
4
bindings/python/Cargo.lock
generated
@@ -1001,7 +1001,7 @@ checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regorus"
|
name = "regorus"
|
||||||
version = "0.9.1"
|
version = "0.10.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"chrono",
|
"chrono",
|
||||||
@@ -1047,7 +1047,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regoruspy"
|
name = "regoruspy"
|
||||||
version = "0.9.1"
|
version = "0.10.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"ordered-float",
|
"ordered-float",
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "regoruspy"
|
name = "regoruspy"
|
||||||
version = "0.9.1"
|
version = "0.10.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
repository = "https://github.com/microsoft/regorus/bindings/python"
|
repository = "https://github.com/microsoft/regorus/bindings/python"
|
||||||
description = "Python bindings for Regorus - a fast, lightweight Rego interpreter written in Rust"
|
description = "Python bindings for Regorus - a fast, lightweight Rego interpreter written in Rust"
|
||||||
|
|||||||
4
bindings/ruby/Cargo.lock
generated
4
bindings/ruby/Cargo.lock
generated
@@ -1032,7 +1032,7 @@ checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regorus"
|
name = "regorus"
|
||||||
version = "0.9.1"
|
version = "0.10.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"chrono",
|
"chrono",
|
||||||
@@ -1077,7 +1077,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regorusrb"
|
name = "regorusrb"
|
||||||
version = "0.9.1"
|
version = "0.10.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"magnus",
|
"magnus",
|
||||||
"regorus",
|
"regorus",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "regorusrb"
|
name = "regorusrb"
|
||||||
version = "0.9.1"
|
version = "0.10.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
description = "Ruby bindings for Regorus - a fast, lightweight Rego interpreter written in Rust"
|
description = "Ruby bindings for Regorus - a fast, lightweight Rego interpreter written in Rust"
|
||||||
license = "MIT AND Apache-2.0 AND BSD-3-Clause"
|
license = "MIT AND Apache-2.0 AND BSD-3-Clause"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
module Regorus
|
module Regorus
|
||||||
VERSION = "0.9.1"
|
VERSION = "0.10.0"
|
||||||
end
|
end
|
||||||
|
|||||||
4
bindings/wasm/Cargo.lock
generated
4
bindings/wasm/Cargo.lock
generated
@@ -991,7 +991,7 @@ checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regorus"
|
name = "regorus"
|
||||||
version = "0.9.1"
|
version = "0.10.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"chrono",
|
"chrono",
|
||||||
@@ -1022,7 +1022,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regorusjs"
|
name = "regorusjs"
|
||||||
version = "0.9.1"
|
version = "0.10.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom 0.2.17",
|
"getrandom 0.2.17",
|
||||||
"getrandom 0.3.4",
|
"getrandom 0.3.4",
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "regorusjs"
|
name = "regorusjs"
|
||||||
version = "0.9.1"
|
version = "0.10.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
repository = "https://github.com/microsoft/regorus/bindings/wasm"
|
repository = "https://github.com/microsoft/regorus/bindings/wasm"
|
||||||
description = "WASM bindings for Regorus - a fast, lightweight Rego interpreter written in Rust"
|
description = "WASM bindings for Regorus - a fast, lightweight Rego interpreter written in Rust"
|
||||||
|
|||||||
@@ -4,9 +4,9 @@
|
|||||||
|
|
||||||
use crate::ast::*;
|
use crate::ast::*;
|
||||||
use crate::builtins::{self, BuiltinFcn};
|
use crate::builtins::{self, BuiltinFcn};
|
||||||
use crate::compiled_policy::CompiledPolicyData;
|
|
||||||
#[cfg(feature = "azure_policy")]
|
#[cfg(feature = "azure_policy")]
|
||||||
use crate::compiled_policy::TargetInfo;
|
use crate::compiled_policy::TargetInfo;
|
||||||
|
use crate::compiled_policy::{CompiledPolicyData, DefaultRuleInfo};
|
||||||
use crate::compiler::destructuring_planner::{
|
use crate::compiler::destructuring_planner::{
|
||||||
AssignmentPlan, BindingPlan, DestructuringPlan, WildcardSide,
|
AssignmentPlan, BindingPlan, DestructuringPlan, WildcardSide,
|
||||||
};
|
};
|
||||||
@@ -1724,6 +1724,9 @@ impl Interpreter {
|
|||||||
// For now, we restrict constant refs to those that contain only simple literals.
|
// For now, we restrict constant refs to those that contain only simple literals.
|
||||||
fn is_constant_ref(mut expr: &Ref<Expr>) -> Result<bool> {
|
fn is_constant_ref(mut expr: &Ref<Expr>) -> Result<bool> {
|
||||||
loop {
|
loop {
|
||||||
|
if Self::is_simple_literal(expr)? {
|
||||||
|
return Ok(true);
|
||||||
|
}
|
||||||
match expr.as_ref() {
|
match expr.as_ref() {
|
||||||
Expr::Var { .. } => break,
|
Expr::Var { .. } => break,
|
||||||
Expr::RefDot { refr, .. } => expr = refr,
|
Expr::RefDot { refr, .. } => expr = refr,
|
||||||
@@ -1747,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.
|
// 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.
|
// 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> {
|
fn is_constant_output(key_expr: &Option<Ref<Expr>>, output_expr: &Ref<Expr>) -> Result<bool> {
|
||||||
@@ -1797,7 +1824,12 @@ impl Interpreter {
|
|||||||
comps.pop();
|
comps.pop();
|
||||||
output
|
output
|
||||||
} else {
|
} 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)
|
Value::Bool(true)
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -2942,6 +2974,41 @@ impl Interpreter {
|
|||||||
Ok(())
|
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<()> {
|
fn ensure_rule_evaluated(&mut self, path: String) -> Result<()> {
|
||||||
self.check_execution_time()?;
|
self.check_execution_time()?;
|
||||||
let mut matched = false;
|
let mut matched = false;
|
||||||
@@ -2956,9 +3023,9 @@ impl Interpreter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Evaluate the associated default rules after non-default rules
|
// 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;
|
matched = true;
|
||||||
for (r, _) in rules.clone() {
|
for (r, _) in rules {
|
||||||
if !self.processed.contains(&r) {
|
if !self.processed.contains(&r) {
|
||||||
let module = self.get_rule_module(&r)?;
|
let module = self.get_rule_module(&r)?;
|
||||||
let prev_module = self.set_current_module(Some(module))?;
|
let prev_module = self.set_current_module(Some(module))?;
|
||||||
@@ -3049,10 +3116,7 @@ impl Interpreter {
|
|||||||
let prefix = fields.iter().take(i).copied().collect::<Vec<_>>();
|
let prefix = fields.iter().take(i).copied().collect::<Vec<_>>();
|
||||||
let prefix_path = format!("data.{}", prefix.join("."));
|
let prefix_path = format!("data.{}", prefix.join("."));
|
||||||
if self.compiled_policy.rules.contains_key(&prefix_path)
|
if self.compiled_policy.rules.contains_key(&prefix_path)
|
||||||
|| self
|
|| self.has_default_rules_for_path(&prefix_path)
|
||||||
.compiled_policy
|
|
||||||
.default_rules
|
|
||||||
.contains_key(&prefix_path)
|
|
||||||
{
|
{
|
||||||
self.ensure_rule_evaluated(prefix_path)?;
|
self.ensure_rule_evaluated(prefix_path)?;
|
||||||
break;
|
break;
|
||||||
@@ -3076,7 +3140,7 @@ impl Interpreter {
|
|||||||
|
|
||||||
if !no_error
|
if !no_error
|
||||||
&& !self.compiled_policy.rules.contains_key(&rule_path)
|
&& !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)
|
&& !self.compiled_policy.imports.contains_key(&rule_path)
|
||||||
{
|
{
|
||||||
bail!(span.error(&format!(
|
bail!(span.error(&format!(
|
||||||
@@ -3099,7 +3163,7 @@ impl Interpreter {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if self.compiled_policy.rules.contains_key(&path)
|
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)?;
|
self.ensure_rule_evaluated(path)?;
|
||||||
found = true;
|
found = true;
|
||||||
@@ -3646,7 +3710,7 @@ impl Interpreter {
|
|||||||
self.data = Value::Undefined;
|
self.data = Value::Undefined;
|
||||||
self.ensure_loop_var_values_capacity();
|
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 {
|
if let Some(rules) = default_rules {
|
||||||
for (rule, _) in rules {
|
for (rule, _) in rules {
|
||||||
|
|||||||
@@ -181,7 +181,7 @@ impl<'a> Compiler<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn evaluate_default_rule(&mut self, rule_path: &str) -> Option<u16> {
|
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;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -200,6 +200,33 @@ impl<'a> Compiler<'a> {
|
|||||||
None
|
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>> {
|
fn extract_destructuring_blocks(&self, rule_index: u16) -> Vec<Option<u32>> {
|
||||||
self.rule_definition_destructuring_patterns[rule_index as usize].clone()
|
self.rule_definition_destructuring_patterns[rule_index as usize].clone()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
)]
|
)]
|
||||||
|
|
||||||
use super::{CompilationContext, Compiler, CompilerError, ContextType, Result, WorklistEntry};
|
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::compiler::destructuring_planner::plans::BindingPlan;
|
||||||
use crate::lexer::Span;
|
use crate::lexer::Span;
|
||||||
use crate::rvm::program::{Program, RuleType};
|
use crate::rvm::program::{Program, RuleType};
|
||||||
@@ -52,14 +52,29 @@ impl<'a> Compiler<'a> {
|
|||||||
let rule_types: BTreeSet<RuleType> = definitions
|
let rule_types: BTreeSet<RuleType> = definitions
|
||||||
.iter()
|
.iter()
|
||||||
.map(|def| {
|
.map(|def| {
|
||||||
if let Rule::Spec { head, .. } = def.as_ref() {
|
if let Rule::Spec { head, bodies, .. } = def.as_ref() {
|
||||||
match head {
|
match head {
|
||||||
RuleHead::Set { .. } => RuleType::PartialSet,
|
RuleHead::Set { .. } => RuleType::PartialSet,
|
||||||
RuleHead::Compr { refr, assign, .. } => match refr.as_ref() {
|
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
|
RuleType::PartialObject
|
||||||
}
|
}
|
||||||
crate::ast::Expr::RefBrack { .. } => RuleType::PartialSet,
|
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,
|
||||||
},
|
},
|
||||||
_ => RuleType::Complete,
|
_ => RuleType::Complete,
|
||||||
|
|||||||
@@ -454,7 +454,19 @@ impl RegoVM {
|
|||||||
let mut obj_value = self.take_register(obj)?;
|
let mut obj_value = self.take_register(obj)?;
|
||||||
|
|
||||||
if let Ok(obj_mut) = obj_value.as_object_mut() {
|
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)?;
|
self.set_register(obj, obj_value)?;
|
||||||
} else {
|
} else {
|
||||||
let offending = obj_value.clone();
|
let offending = obj_value.clone();
|
||||||
|
|||||||
@@ -209,6 +209,9 @@ pub enum VmError {
|
|||||||
#[error("Rule-data conflict: {message} (pc={pc})")]
|
#[error("Rule-data conflict: {message} (pc={pc})")]
|
||||||
RuleDataConflict { message: String, pc: usize },
|
RuleDataConflict { message: String, pc: usize },
|
||||||
|
|
||||||
|
#[error("rules must not produce multiple outputs (pc={pc})")]
|
||||||
|
RuleMultipleOutputs { pc: usize },
|
||||||
|
|
||||||
#[error("Arithmetic error: {message} (pc={pc})")]
|
#[error("Arithmetic error: {message} (pc={pc})")]
|
||||||
ArithmeticError { message: String, pc: usize },
|
ArithmeticError { message: String, pc: usize },
|
||||||
|
|
||||||
|
|||||||
@@ -17,8 +17,9 @@ use super::execution_model::{
|
|||||||
use super::machine::RegoVM;
|
use super::machine::RegoVM;
|
||||||
|
|
||||||
impl RegoVM {
|
impl RegoVM {
|
||||||
/// Returns true if the error represents a resource-limit violation that
|
/// Returns true if the error must never be silently absorbed by rule
|
||||||
/// must never be silently absorbed by rule evaluation.
|
/// evaluation backtracking, including resource-limit failures and semantic
|
||||||
|
/// rule consistency errors.
|
||||||
pub(super) const fn is_fatal_vm_error(err: &VmError) -> bool {
|
pub(super) const fn is_fatal_vm_error(err: &VmError) -> bool {
|
||||||
matches!(
|
matches!(
|
||||||
err,
|
err,
|
||||||
@@ -26,6 +27,7 @@ impl RegoVM {
|
|||||||
| VmError::MemoryLimitExceeded { .. }
|
| VmError::MemoryLimitExceeded { .. }
|
||||||
| VmError::RegexSizeLimitExceeded { .. }
|
| VmError::RegexSizeLimitExceeded { .. }
|
||||||
| VmError::InstructionLimitExceeded { .. }
|
| VmError::InstructionLimitExceeded { .. }
|
||||||
|
| VmError::RuleMultipleOutputs { .. }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -341,6 +341,35 @@ cases:
|
|||||||
result: false
|
result: false
|
||||||
reasons: []
|
reasons: []
|
||||||
|
|
||||||
|
- note: default_rule_with_object_key
|
||||||
|
data: {}
|
||||||
|
input: {}
|
||||||
|
modules:
|
||||||
|
- |
|
||||||
|
package test
|
||||||
|
import rego.v1
|
||||||
|
default config["timeout"] := 30
|
||||||
|
config["timeout"] := val if {
|
||||||
|
val := input.val
|
||||||
|
}
|
||||||
|
query: data.test.config.timeout
|
||||||
|
want_result: 30
|
||||||
|
|
||||||
|
- note: default_rule_with_object_key_override
|
||||||
|
data: {}
|
||||||
|
input:
|
||||||
|
val: 60
|
||||||
|
modules:
|
||||||
|
- |
|
||||||
|
package test
|
||||||
|
import rego.v1
|
||||||
|
default config["timeout"] := 30
|
||||||
|
config["timeout"] := val if {
|
||||||
|
val := input.val
|
||||||
|
}
|
||||||
|
query: data.test.config.timeout
|
||||||
|
want_result: 60
|
||||||
|
|
||||||
- note: default_only_rule_with_package_query
|
- note: default_only_rule_with_package_query
|
||||||
data: {}
|
data: {}
|
||||||
modules:
|
modules:
|
||||||
|
|||||||
242
tests/interpreter/cases/rule/partial_object_iteration.yaml
Normal file
242
tests/interpreter/cases/rule/partial_object_iteration.yaml
Normal file
@@ -0,0 +1,242 @@
|
|||||||
|
# Copyright (c) Microsoft Corporation.
|
||||||
|
# Licensed under the MIT License.
|
||||||
|
|
||||||
|
cases:
|
||||||
|
- note: partial_object_iteration_some_in_object_v1
|
||||||
|
data: {}
|
||||||
|
input:
|
||||||
|
x:
|
||||||
|
FOO: 1
|
||||||
|
BAR: 2
|
||||||
|
BAZ: 3
|
||||||
|
modules:
|
||||||
|
- |
|
||||||
|
package test
|
||||||
|
import rego.v1
|
||||||
|
|
||||||
|
violations[k] if {
|
||||||
|
some k, _ in input.x
|
||||||
|
}
|
||||||
|
query: data.test
|
||||||
|
want_result:
|
||||||
|
violations:
|
||||||
|
BAR: true
|
||||||
|
BAZ: true
|
||||||
|
FOO: true
|
||||||
|
|
||||||
|
- note: partial_object_iteration_some_in_array_v1
|
||||||
|
data: {}
|
||||||
|
input:
|
||||||
|
arr: ["FOO", "BAR", "BAZ"]
|
||||||
|
modules:
|
||||||
|
- |
|
||||||
|
package test
|
||||||
|
import rego.v1
|
||||||
|
|
||||||
|
violations[v] if {
|
||||||
|
some _, v in input.arr
|
||||||
|
}
|
||||||
|
query: data.test
|
||||||
|
want_result:
|
||||||
|
violations:
|
||||||
|
BAR: true
|
||||||
|
BAZ: true
|
||||||
|
FOO: true
|
||||||
|
|
||||||
|
- note: partial_object_iteration_with_filter_v1
|
||||||
|
data: {}
|
||||||
|
input:
|
||||||
|
x:
|
||||||
|
FOO: 1
|
||||||
|
BAR: 2
|
||||||
|
BAZ: 3
|
||||||
|
modules:
|
||||||
|
- |
|
||||||
|
package test
|
||||||
|
import rego.v1
|
||||||
|
|
||||||
|
violations[k] if {
|
||||||
|
some k, _ in input.x
|
||||||
|
k != "BAR"
|
||||||
|
}
|
||||||
|
query: data.test
|
||||||
|
want_result:
|
||||||
|
violations:
|
||||||
|
BAZ: true
|
||||||
|
FOO: true
|
||||||
|
|
||||||
|
- note: partial_object_iteration_input_lookup_future_keywords
|
||||||
|
data: {}
|
||||||
|
input:
|
||||||
|
x:
|
||||||
|
FOO: 1
|
||||||
|
BAR: 2
|
||||||
|
BAZ: 3
|
||||||
|
modules:
|
||||||
|
- |
|
||||||
|
package test
|
||||||
|
import future.keywords.if
|
||||||
|
|
||||||
|
violations[k] if {
|
||||||
|
input.x[k]
|
||||||
|
}
|
||||||
|
query: data.test
|
||||||
|
want_result:
|
||||||
|
violations:
|
||||||
|
BAR: true
|
||||||
|
BAZ: true
|
||||||
|
FOO: true
|
||||||
|
|
||||||
|
- note: partial_object_multiple_bodies_collect_all_keys_v1
|
||||||
|
data: {}
|
||||||
|
input:
|
||||||
|
primary:
|
||||||
|
FOO: 1
|
||||||
|
BAR: 2
|
||||||
|
secondary:
|
||||||
|
BAZ: 3
|
||||||
|
modules:
|
||||||
|
- |
|
||||||
|
package test
|
||||||
|
import rego.v1
|
||||||
|
|
||||||
|
violations[k] if {
|
||||||
|
some k, _ in input.primary
|
||||||
|
}
|
||||||
|
|
||||||
|
violations[k] if {
|
||||||
|
some k, _ in input.secondary
|
||||||
|
}
|
||||||
|
query: data.test
|
||||||
|
want_result:
|
||||||
|
violations:
|
||||||
|
BAR: true
|
||||||
|
BAZ: true
|
||||||
|
FOO: true
|
||||||
|
|
||||||
|
- note: constant_key_implicit_true_rule_is_complete_v1
|
||||||
|
data: {}
|
||||||
|
input:
|
||||||
|
enabled: true
|
||||||
|
other: false
|
||||||
|
modules:
|
||||||
|
- |
|
||||||
|
package test
|
||||||
|
import rego.v1
|
||||||
|
|
||||||
|
p["x"] if {
|
||||||
|
input.enabled
|
||||||
|
}
|
||||||
|
|
||||||
|
p["x"] if {
|
||||||
|
input.other
|
||||||
|
}
|
||||||
|
query: data.test.p.x
|
||||||
|
want_result: true
|
||||||
|
|
||||||
|
- note: partial_object_duplicate_keys_same_value_are_ok_v1
|
||||||
|
data: {}
|
||||||
|
input:
|
||||||
|
arr: ["FOO", "FOO", "BAR"]
|
||||||
|
modules:
|
||||||
|
- |
|
||||||
|
package test
|
||||||
|
import rego.v1
|
||||||
|
|
||||||
|
violations[v] if {
|
||||||
|
some _, v in input.arr
|
||||||
|
}
|
||||||
|
query: data.test
|
||||||
|
want_result:
|
||||||
|
violations:
|
||||||
|
BAR: true
|
||||||
|
FOO: true
|
||||||
|
|
||||||
|
- note: partial_object_duplicate_keys_different_values_error_v1
|
||||||
|
data: {}
|
||||||
|
input:
|
||||||
|
entries:
|
||||||
|
- k: "FOO"
|
||||||
|
v: 1
|
||||||
|
- k: "FOO"
|
||||||
|
v: 2
|
||||||
|
modules:
|
||||||
|
- |
|
||||||
|
package test
|
||||||
|
import rego.v1
|
||||||
|
|
||||||
|
violations[k] := v if {
|
||||||
|
some entry in input.entries
|
||||||
|
k := entry.k
|
||||||
|
v := entry.v
|
||||||
|
}
|
||||||
|
query: data.test.violations
|
||||||
|
error: "rules must not produce multiple outputs"
|
||||||
|
|
||||||
|
- note: partial_object_and_partial_set_iteration_coexist_v1
|
||||||
|
data: {}
|
||||||
|
input:
|
||||||
|
x:
|
||||||
|
FOO: 1
|
||||||
|
BAR: 2
|
||||||
|
BAZ: 3
|
||||||
|
modules:
|
||||||
|
- |
|
||||||
|
package test
|
||||||
|
import rego.v1
|
||||||
|
|
||||||
|
violations[k] if {
|
||||||
|
some k, _ in input.x
|
||||||
|
}
|
||||||
|
|
||||||
|
seen contains k if {
|
||||||
|
some k, _ in input.x
|
||||||
|
}
|
||||||
|
query: data.test
|
||||||
|
want_result:
|
||||||
|
seen:
|
||||||
|
set!: ["BAR", "BAZ", "FOO"]
|
||||||
|
violations:
|
||||||
|
BAR: true
|
||||||
|
BAZ: true
|
||||||
|
FOO: true
|
||||||
|
|
||||||
|
- note: partial_object_key_bound_in_outer_scope_v1
|
||||||
|
data: {}
|
||||||
|
input:
|
||||||
|
outer:
|
||||||
|
FOO: [1, 2]
|
||||||
|
BAR: [3]
|
||||||
|
BAZ: []
|
||||||
|
modules:
|
||||||
|
- |
|
||||||
|
package test
|
||||||
|
import rego.v1
|
||||||
|
|
||||||
|
violations[k] if {
|
||||||
|
some k, arr in input.outer
|
||||||
|
some _ in arr
|
||||||
|
}
|
||||||
|
query: data.test.violations
|
||||||
|
want_result:
|
||||||
|
BAR: true
|
||||||
|
FOO: true
|
||||||
|
|
||||||
|
- note: complete_rule_same_value_definitions_still_work_v1
|
||||||
|
data: {}
|
||||||
|
input:
|
||||||
|
role: "superuser"
|
||||||
|
modules:
|
||||||
|
- |
|
||||||
|
package test
|
||||||
|
import rego.v1
|
||||||
|
|
||||||
|
allowed if {
|
||||||
|
input.role == "admin"
|
||||||
|
}
|
||||||
|
|
||||||
|
allowed if {
|
||||||
|
input.role == "superuser"
|
||||||
|
}
|
||||||
|
query: data.test.allowed
|
||||||
|
want_result: true
|
||||||
@@ -48,18 +48,34 @@ cases:
|
|||||||
want_result: true
|
want_result: true
|
||||||
|
|
||||||
- note: default_rule_with_object_key
|
- note: default_rule_with_object_key
|
||||||
skip: true # TODO: Fix rule type classification for config["timeout"] - should be Complete, not PartialObject
|
|
||||||
data: {}
|
data: {}
|
||||||
|
input: {}
|
||||||
modules:
|
modules:
|
||||||
- |
|
- |
|
||||||
package test
|
package test
|
||||||
|
import rego.v1
|
||||||
default config["timeout"] := 30
|
default config["timeout"] := 30
|
||||||
config["timeout"] := 60 if {
|
config["timeout"] := val if {
|
||||||
false # This will fail
|
val := input.val
|
||||||
}
|
}
|
||||||
query: data.test.config.timeout
|
query: data.test.config.timeout
|
||||||
want_result: 30
|
want_result: 30
|
||||||
|
|
||||||
|
- note: default_rule_with_object_key_override
|
||||||
|
data: {}
|
||||||
|
input:
|
||||||
|
val: 60
|
||||||
|
modules:
|
||||||
|
- |
|
||||||
|
package test
|
||||||
|
import rego.v1
|
||||||
|
default config["timeout"] := 30
|
||||||
|
config["timeout"] := val if {
|
||||||
|
val := input.val
|
||||||
|
}
|
||||||
|
query: data.test.config.timeout
|
||||||
|
want_result: 60
|
||||||
|
|
||||||
- note: default_rule_complex_value
|
- note: default_rule_complex_value
|
||||||
data: {}
|
data: {}
|
||||||
modules:
|
modules:
|
||||||
|
|||||||
241
tests/rvm/rego/cases/partial_object_rules.yaml
Normal file
241
tests/rvm/rego/cases/partial_object_rules.yaml
Normal file
@@ -0,0 +1,241 @@
|
|||||||
|
# Copyright (c) Microsoft Corporation.
|
||||||
|
# Licensed under the MIT License.
|
||||||
|
|
||||||
|
cases:
|
||||||
|
- note: partial_object_iteration_some_in_object_v1
|
||||||
|
data: {}
|
||||||
|
input:
|
||||||
|
x:
|
||||||
|
FOO: 1
|
||||||
|
BAR: 2
|
||||||
|
BAZ: 3
|
||||||
|
modules:
|
||||||
|
- |
|
||||||
|
package test
|
||||||
|
import rego.v1
|
||||||
|
|
||||||
|
violations[k] if {
|
||||||
|
some k, _ in input.x
|
||||||
|
}
|
||||||
|
query: data.test.violations
|
||||||
|
want_result:
|
||||||
|
BAR: true
|
||||||
|
BAZ: true
|
||||||
|
FOO: true
|
||||||
|
|
||||||
|
- note: partial_object_iteration_some_in_array_v1
|
||||||
|
data: {}
|
||||||
|
input:
|
||||||
|
arr: ["FOO", "BAR", "BAZ"]
|
||||||
|
modules:
|
||||||
|
- |
|
||||||
|
package test
|
||||||
|
import rego.v1
|
||||||
|
|
||||||
|
violations[v] if {
|
||||||
|
some _, v in input.arr
|
||||||
|
}
|
||||||
|
query: data.test.violations
|
||||||
|
want_result:
|
||||||
|
BAR: true
|
||||||
|
BAZ: true
|
||||||
|
FOO: true
|
||||||
|
|
||||||
|
- note: partial_object_iteration_with_filter_v1
|
||||||
|
data: {}
|
||||||
|
input:
|
||||||
|
x:
|
||||||
|
FOO: 1
|
||||||
|
BAR: 2
|
||||||
|
BAZ: 3
|
||||||
|
modules:
|
||||||
|
- |
|
||||||
|
package test
|
||||||
|
import rego.v1
|
||||||
|
|
||||||
|
violations[k] if {
|
||||||
|
some k, _ in input.x
|
||||||
|
k != "BAR"
|
||||||
|
}
|
||||||
|
query: data.test.violations
|
||||||
|
want_result:
|
||||||
|
BAZ: true
|
||||||
|
FOO: true
|
||||||
|
|
||||||
|
- note: partial_object_iteration_input_lookup_future_keywords
|
||||||
|
data: {}
|
||||||
|
input:
|
||||||
|
x:
|
||||||
|
FOO: 1
|
||||||
|
BAR: 2
|
||||||
|
BAZ: 3
|
||||||
|
modules:
|
||||||
|
- |
|
||||||
|
package test
|
||||||
|
import future.keywords.if
|
||||||
|
|
||||||
|
violations[k] if {
|
||||||
|
input.x[k]
|
||||||
|
}
|
||||||
|
query: data.test.violations
|
||||||
|
want_result:
|
||||||
|
BAR: true
|
||||||
|
BAZ: true
|
||||||
|
FOO: true
|
||||||
|
|
||||||
|
- note: partial_object_multiple_bodies_collect_all_keys_v1
|
||||||
|
data: {}
|
||||||
|
input:
|
||||||
|
primary:
|
||||||
|
FOO: 1
|
||||||
|
BAR: 2
|
||||||
|
secondary:
|
||||||
|
BAZ: 3
|
||||||
|
modules:
|
||||||
|
- |
|
||||||
|
package test
|
||||||
|
import rego.v1
|
||||||
|
|
||||||
|
violations[k] if {
|
||||||
|
some k, _ in input.primary
|
||||||
|
}
|
||||||
|
|
||||||
|
violations[k] if {
|
||||||
|
some k, _ in input.secondary
|
||||||
|
}
|
||||||
|
query: data.test.violations
|
||||||
|
want_result:
|
||||||
|
BAR: true
|
||||||
|
BAZ: true
|
||||||
|
FOO: true
|
||||||
|
|
||||||
|
- note: constant_key_implicit_true_rule_is_complete_v1
|
||||||
|
data: {}
|
||||||
|
input:
|
||||||
|
enabled: true
|
||||||
|
other: false
|
||||||
|
modules:
|
||||||
|
- |
|
||||||
|
package test
|
||||||
|
import rego.v1
|
||||||
|
|
||||||
|
p["x"] if {
|
||||||
|
input.enabled
|
||||||
|
}
|
||||||
|
|
||||||
|
p["x"] if {
|
||||||
|
input.other
|
||||||
|
}
|
||||||
|
query: data.test.p.x
|
||||||
|
want_result: true
|
||||||
|
|
||||||
|
- note: partial_object_duplicate_keys_same_value_are_ok_v1
|
||||||
|
data: {}
|
||||||
|
input:
|
||||||
|
arr: ["FOO", "FOO", "BAR"]
|
||||||
|
modules:
|
||||||
|
- |
|
||||||
|
package test
|
||||||
|
import rego.v1
|
||||||
|
|
||||||
|
violations[v] if {
|
||||||
|
some _, v in input.arr
|
||||||
|
}
|
||||||
|
query: data.test.violations
|
||||||
|
want_result:
|
||||||
|
BAR: true
|
||||||
|
FOO: true
|
||||||
|
|
||||||
|
- note: partial_object_duplicate_keys_different_values_error_v1
|
||||||
|
data: {}
|
||||||
|
input:
|
||||||
|
entries:
|
||||||
|
- k: "FOO"
|
||||||
|
v: 1
|
||||||
|
- k: "FOO"
|
||||||
|
v: 2
|
||||||
|
modules:
|
||||||
|
- |
|
||||||
|
package test
|
||||||
|
import rego.v1
|
||||||
|
|
||||||
|
violations[k] := v if {
|
||||||
|
some entry in input.entries
|
||||||
|
k := entry.k
|
||||||
|
v := entry.v
|
||||||
|
}
|
||||||
|
query: data.test.violations
|
||||||
|
want_error: "multiple outputs"
|
||||||
|
|
||||||
|
- note: partial_object_and_partial_set_iteration_coexist_v1
|
||||||
|
data: {}
|
||||||
|
input:
|
||||||
|
x:
|
||||||
|
FOO: 1
|
||||||
|
BAR: 2
|
||||||
|
BAZ: 3
|
||||||
|
modules:
|
||||||
|
- |
|
||||||
|
package test
|
||||||
|
import rego.v1
|
||||||
|
|
||||||
|
violations[k] if {
|
||||||
|
some k, _ in input.x
|
||||||
|
}
|
||||||
|
|
||||||
|
seen contains k if {
|
||||||
|
some k, _ in input.x
|
||||||
|
}
|
||||||
|
|
||||||
|
main := {
|
||||||
|
"seen": seen,
|
||||||
|
"violations": violations,
|
||||||
|
}
|
||||||
|
query: data.test.main
|
||||||
|
want_result:
|
||||||
|
seen:
|
||||||
|
set!: ["BAR", "BAZ", "FOO"]
|
||||||
|
violations:
|
||||||
|
BAR: true
|
||||||
|
BAZ: true
|
||||||
|
FOO: true
|
||||||
|
|
||||||
|
- note: partial_object_key_bound_in_outer_scope_v1
|
||||||
|
data: {}
|
||||||
|
input:
|
||||||
|
outer:
|
||||||
|
FOO: [1, 2]
|
||||||
|
BAR: [3]
|
||||||
|
BAZ: []
|
||||||
|
modules:
|
||||||
|
- |
|
||||||
|
package test
|
||||||
|
import rego.v1
|
||||||
|
|
||||||
|
violations[k] if {
|
||||||
|
some k, arr in input.outer
|
||||||
|
some _ in arr
|
||||||
|
}
|
||||||
|
query: data.test.violations
|
||||||
|
want_result:
|
||||||
|
BAR: true
|
||||||
|
FOO: true
|
||||||
|
|
||||||
|
- note: complete_rule_same_value_definitions_still_work_v1
|
||||||
|
data: {}
|
||||||
|
input:
|
||||||
|
role: "superuser"
|
||||||
|
modules:
|
||||||
|
- |
|
||||||
|
package test
|
||||||
|
import rego.v1
|
||||||
|
|
||||||
|
allowed if {
|
||||||
|
input.role == "admin"
|
||||||
|
}
|
||||||
|
|
||||||
|
allowed if {
|
||||||
|
input.role == "superuser"
|
||||||
|
}
|
||||||
|
query: data.test.allowed
|
||||||
|
want_result: true
|
||||||
@@ -6,9 +6,9 @@
|
|||||||
# Covers dynamic keys, collisions, non-string keys, and template validation
|
# Covers dynamic keys, collisions, non-string keys, and template validation
|
||||||
|
|
||||||
cases:
|
cases:
|
||||||
- note: object_key_collision_overwrite
|
- note: object_key_collision_conflict
|
||||||
description: Setting same key twice should overwrite the value
|
description: Setting same key twice with different values should raise a rule output conflict
|
||||||
example_rego: "{\"key\": 1, \"key\": 2}"
|
example_rego: "p[\"key\"] = value { value := [1, 2][_] }"
|
||||||
literals:
|
literals:
|
||||||
- {}
|
- {}
|
||||||
- "key"
|
- "key"
|
||||||
@@ -26,9 +26,31 @@ cases:
|
|||||||
- "Load { dest: 2, literal_idx: 2 }" # value 1
|
- "Load { dest: 2, literal_idx: 2 }" # value 1
|
||||||
- "ObjectSet { obj: 0, key: 1, value: 2 }"
|
- "ObjectSet { obj: 0, key: 1, value: 2 }"
|
||||||
- "Load { dest: 3, literal_idx: 3 }" # value 2
|
- "Load { dest: 3, literal_idx: 3 }" # value 2
|
||||||
- "ObjectSet { obj: 0, key: 1, value: 3 }" # Overwrite
|
- "ObjectSet { obj: 0, key: 1, value: 3 }" # Conflict
|
||||||
- "Return { value: 0 }"
|
- "Return { value: 0 }"
|
||||||
want_result: {"key": 2}
|
want_error: "multiple outputs"
|
||||||
|
|
||||||
|
- note: object_key_duplicate_same_value
|
||||||
|
description: Setting same key twice with the same value should succeed
|
||||||
|
example_rego: "p[\"key\"] := 1 if { some _ in [0, 1] }"
|
||||||
|
literals:
|
||||||
|
- {}
|
||||||
|
- "key"
|
||||||
|
- 1
|
||||||
|
instruction_params:
|
||||||
|
object_create_params:
|
||||||
|
- dest: 0
|
||||||
|
template_literal_idx: 0
|
||||||
|
literal_key_fields: []
|
||||||
|
fields: []
|
||||||
|
instructions:
|
||||||
|
- "ObjectCreate { params_index: 0 }"
|
||||||
|
- "Load { dest: 1, literal_idx: 1 }" # key
|
||||||
|
- "Load { dest: 2, literal_idx: 2 }" # value
|
||||||
|
- "ObjectSet { obj: 0, key: 1, value: 2 }"
|
||||||
|
- "ObjectSet { obj: 0, key: 1, value: 2 }"
|
||||||
|
- "Return { value: 0 }"
|
||||||
|
want_result: {"key": 1}
|
||||||
|
|
||||||
- note: object_dynamic_key_generation
|
- note: object_dynamic_key_generation
|
||||||
description: Generate object keys dynamically from loop iteration
|
description: Generate object keys dynamically from loop iteration
|
||||||
|
|||||||
@@ -124,7 +124,7 @@ struct Binding {
|
|||||||
manifest: Option<&'static str>,
|
manifest: Option<&'static str>,
|
||||||
ruby_version: Option<&'static str>,
|
ruby_version: Option<&'static str>,
|
||||||
pom_xml: Option<&'static str>,
|
pom_xml: Option<&'static str>,
|
||||||
csharp_project: Option<&'static str>,
|
csharp_version_file: Option<&'static str>,
|
||||||
csharp_dependents: &'static [&'static str],
|
csharp_dependents: &'static [&'static str],
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,7 +139,7 @@ const BINDINGS: &[Binding] = &[
|
|||||||
manifest: Some("bindings/ffi/Cargo.toml"),
|
manifest: Some("bindings/ffi/Cargo.toml"),
|
||||||
ruby_version: None,
|
ruby_version: None,
|
||||||
pom_xml: None,
|
pom_xml: None,
|
||||||
csharp_project: None,
|
csharp_version_file: None,
|
||||||
csharp_dependents: EMPTY,
|
csharp_dependents: EMPTY,
|
||||||
},
|
},
|
||||||
Binding {
|
Binding {
|
||||||
@@ -148,7 +148,7 @@ const BINDINGS: &[Binding] = &[
|
|||||||
manifest: Some("bindings/java/Cargo.toml"),
|
manifest: Some("bindings/java/Cargo.toml"),
|
||||||
ruby_version: None,
|
ruby_version: None,
|
||||||
pom_xml: Some("bindings/java/pom.xml"),
|
pom_xml: Some("bindings/java/pom.xml"),
|
||||||
csharp_project: None,
|
csharp_version_file: None,
|
||||||
csharp_dependents: EMPTY,
|
csharp_dependents: EMPTY,
|
||||||
},
|
},
|
||||||
Binding {
|
Binding {
|
||||||
@@ -157,7 +157,7 @@ const BINDINGS: &[Binding] = &[
|
|||||||
manifest: Some("bindings/python/Cargo.toml"),
|
manifest: Some("bindings/python/Cargo.toml"),
|
||||||
ruby_version: None,
|
ruby_version: None,
|
||||||
pom_xml: None,
|
pom_xml: None,
|
||||||
csharp_project: None,
|
csharp_version_file: None,
|
||||||
csharp_dependents: EMPTY,
|
csharp_dependents: EMPTY,
|
||||||
},
|
},
|
||||||
Binding {
|
Binding {
|
||||||
@@ -166,7 +166,7 @@ const BINDINGS: &[Binding] = &[
|
|||||||
manifest: Some("bindings/wasm/Cargo.toml"),
|
manifest: Some("bindings/wasm/Cargo.toml"),
|
||||||
ruby_version: None,
|
ruby_version: None,
|
||||||
pom_xml: None,
|
pom_xml: None,
|
||||||
csharp_project: None,
|
csharp_version_file: None,
|
||||||
csharp_dependents: EMPTY,
|
csharp_dependents: EMPTY,
|
||||||
},
|
},
|
||||||
Binding {
|
Binding {
|
||||||
@@ -175,7 +175,7 @@ const BINDINGS: &[Binding] = &[
|
|||||||
manifest: Some("bindings/ruby/ext/regorusrb/Cargo.toml"),
|
manifest: Some("bindings/ruby/ext/regorusrb/Cargo.toml"),
|
||||||
ruby_version: Some("bindings/ruby/lib/regorus/version.rb"),
|
ruby_version: Some("bindings/ruby/lib/regorus/version.rb"),
|
||||||
pom_xml: None,
|
pom_xml: None,
|
||||||
csharp_project: None,
|
csharp_version_file: None,
|
||||||
csharp_dependents: EMPTY,
|
csharp_dependents: EMPTY,
|
||||||
},
|
},
|
||||||
Binding {
|
Binding {
|
||||||
@@ -184,14 +184,8 @@ const BINDINGS: &[Binding] = &[
|
|||||||
manifest: None,
|
manifest: None,
|
||||||
ruby_version: None,
|
ruby_version: None,
|
||||||
pom_xml: None,
|
pom_xml: None,
|
||||||
csharp_project: Some("bindings/csharp/Regorus/Regorus.csproj"),
|
csharp_version_file: Some("bindings/csharp/Directory.Packages.props"),
|
||||||
csharp_dependents: &[
|
csharp_dependents: EMPTY,
|
||||||
"bindings/csharp/Directory.Packages.props",
|
|
||||||
"bindings/csharp/Regorus.Tests/Regorus.Tests.csproj",
|
|
||||||
"bindings/csharp/Benchmarks/Benchmarks.csproj",
|
|
||||||
"bindings/csharp/TargetExampleApp/TargetExampleApp.csproj",
|
|
||||||
"bindings/csharp/TestApp/TestApp.csproj",
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -335,7 +329,7 @@ fn read_binding_version(root: &Path, binding: &Binding) -> Result<Version> {
|
|||||||
if let Some(manifest) = binding.manifest {
|
if let Some(manifest) = binding.manifest {
|
||||||
return read_manifest_version(&root.join(manifest));
|
return read_manifest_version(&root.join(manifest));
|
||||||
}
|
}
|
||||||
if let Some(project) = binding.csharp_project {
|
if let Some(project) = binding.csharp_version_file {
|
||||||
return read_csharp_version(root.join(project));
|
return read_csharp_version(root.join(project));
|
||||||
}
|
}
|
||||||
bail!("binding '{}' missing version source", binding.name)
|
bail!("binding '{}' missing version source", binding.name)
|
||||||
@@ -357,13 +351,18 @@ fn read_manifest_version(path: &Path) -> Result<Version> {
|
|||||||
fn read_csharp_version(path: PathBuf) -> Result<Version> {
|
fn read_csharp_version(path: PathBuf) -> Result<Version> {
|
||||||
let contents =
|
let contents =
|
||||||
fs::read_to_string(&path).with_context(|| format!("failed to read {}", path.display()))?;
|
fs::read_to_string(&path).with_context(|| format!("failed to read {}", path.display()))?;
|
||||||
let re = Regex::new(r#"(?s)<VersionPrefix>(?P<value>[^<]+)</VersionPrefix>"#)?;
|
let re = Regex::new(r#"(?s)<RegorusPackageVersion>(?P<value>[^<]+)</RegorusPackageVersion>"#)?;
|
||||||
let caps = re
|
let caps = re
|
||||||
.captures(&contents)
|
.captures(&contents)
|
||||||
.ok_or_else(|| anyhow!("{} missing <VersionPrefix> entry", path.display()))?;
|
.ok_or_else(|| anyhow!("{} missing <RegorusPackageVersion> entry", path.display()))?;
|
||||||
let version = caps.name("value").unwrap().as_str();
|
let version = caps.name("value").unwrap().as_str().trim();
|
||||||
Version::parse(version)
|
Version::parse(version).with_context(|| {
|
||||||
.with_context(|| format!("invalid VersionPrefix '{}' in {}", version, path.display()))
|
format!(
|
||||||
|
"invalid RegorusPackageVersion '{}' in {}",
|
||||||
|
version,
|
||||||
|
path.display()
|
||||||
|
)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calculates the version to write, bumping the minor release when the binding
|
/// Calculates the version to write, bumping the minor release when the binding
|
||||||
@@ -420,8 +419,8 @@ fn apply_binding_version(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(project) = binding.csharp_project {
|
if let Some(project) = binding.csharp_version_file {
|
||||||
touched.extend(update_csharp_projects(
|
touched.extend(update_csharp_version_files(
|
||||||
root,
|
root,
|
||||||
project,
|
project,
|
||||||
binding.csharp_dependents,
|
binding.csharp_dependents,
|
||||||
@@ -502,60 +501,55 @@ fn update_java_pom(root: &Path, pom_path: &str, version: &str, check: bool) -> R
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Updates the NuGet packaging project and any sample/test consumers.
|
/// Updates the NuGet packaging project and any sample/test consumers.
|
||||||
fn update_csharp_projects(
|
fn update_csharp_version_files(
|
||||||
root: &Path,
|
root: &Path,
|
||||||
package_project: &str,
|
version_file: &str,
|
||||||
dependent_projects: &[&str],
|
dependent_projects: &[&str],
|
||||||
version: &str,
|
version: &str,
|
||||||
check: bool,
|
check: bool,
|
||||||
) -> Result<Vec<String>> {
|
) -> Result<Vec<String>> {
|
||||||
let mut touched = Vec::new();
|
let mut touched = Vec::new();
|
||||||
let version_prefix = Regex::new(
|
let version_prop = Regex::new(
|
||||||
r#"(?s)(?P<prefix><VersionPrefix>)(?P<value>[^<]+)(?P<suffix></VersionPrefix>)"#,
|
r#"(?s)(?P<prefix><RegorusPackageVersion>)(?P<value>[^<]+)(?P<suffix></RegorusPackageVersion>)"#,
|
||||||
)?;
|
|
||||||
let pkg_ref = Regex::new(
|
|
||||||
r#"(?i)(?P<prefix><Package(?:Reference|Version)[^>]*Include="microsoft\.regorus"[^>]*Version=")(?P<value>\d+\.\d+\.\d+)(?P<suffix>[^\"]*")"#,
|
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let package_path = root.join(package_project);
|
let package_path = root.join(version_file);
|
||||||
|
if !version_prop.is_match(
|
||||||
|
&fs::read_to_string(&package_path)
|
||||||
|
.with_context(|| format!("failed to read {}", package_path.display()))?,
|
||||||
|
) {
|
||||||
|
anyhow::bail!(
|
||||||
|
"{} missing <RegorusPackageVersion> entry",
|
||||||
|
package_path.display()
|
||||||
|
);
|
||||||
|
}
|
||||||
if edit_file(&package_path, check, |contents| {
|
if edit_file(&package_path, check, |contents| {
|
||||||
let mut changed = false;
|
let mut changed = false;
|
||||||
let mut new_contents = contents.to_owned();
|
let new_contents = version_prop
|
||||||
|
.replace(contents, |caps: ®ex::Captures| {
|
||||||
|
let current = caps.name("value").unwrap().as_str().trim();
|
||||||
|
if current == version {
|
||||||
|
caps[0].to_string()
|
||||||
|
} else {
|
||||||
|
changed = true;
|
||||||
|
format!("{}{}{}", &caps["prefix"], version, &caps["suffix"])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.into_owned();
|
||||||
|
|
||||||
if version_prefix.is_match(&new_contents) {
|
if changed && new_contents != *contents {
|
||||||
new_contents = version_prefix
|
Ok(Some(new_contents))
|
||||||
.replace(&new_contents, |caps: ®ex::Captures| {
|
|
||||||
let current = caps.name("value").unwrap().as_str();
|
|
||||||
if current == version {
|
|
||||||
caps[0].to_string()
|
|
||||||
} else {
|
|
||||||
changed = true;
|
|
||||||
format!("{}{}{}", &caps["prefix"], version, &caps["suffix"])
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.into_owned();
|
|
||||||
}
|
|
||||||
|
|
||||||
let replaced = pkg_ref.replace_all(&new_contents, |caps: ®ex::Captures| {
|
|
||||||
let current = caps.name("value").unwrap().as_str();
|
|
||||||
if current == version {
|
|
||||||
caps[0].to_string()
|
|
||||||
} else {
|
|
||||||
changed = true;
|
|
||||||
format!("{}{}{}", &caps["prefix"], version, &caps["suffix"])
|
|
||||||
}
|
|
||||||
});
|
|
||||||
let final_contents = replaced.into_owned();
|
|
||||||
|
|
||||||
if changed && final_contents != *contents {
|
|
||||||
Ok(Some(final_contents))
|
|
||||||
} else {
|
} else {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
})? {
|
})? {
|
||||||
touched.push(package_project.to_string());
|
touched.push(version_file.to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let pkg_ref = Regex::new(
|
||||||
|
r#"(?i)(?P<prefix><Package(?:Reference|Version)[^>]*Include="microsoft\.regorus"[^>]*Version=")(?P<value>\d+\.\d+\.\d+)(?P<suffix>[^\"]*")"#,
|
||||||
|
)?;
|
||||||
|
|
||||||
for rel_path in dependent_projects {
|
for rel_path in dependent_projects {
|
||||||
let path = root.join(rel_path);
|
let path = root.join(rel_path);
|
||||||
if edit_file(&path, check, |contents| {
|
if edit_file(&path, check, |contents| {
|
||||||
|
|||||||
Reference in New Issue
Block a user