Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "feature",
"description": "Implement rules engine ITE fn and S3 tree transform",
"pull_requests": [
"[#2903](https://github.com/smithy-lang/smithy/pull/2903)"
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "feature",
"description": "Improve BDD sifting (2x speed, more reduction)",
"pull_requests": [
"[#2890](https://github.com/smithy-lang/smithy/pull/2890)"
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,103 @@ The following example uses ``isValidHostLabel`` to check if the value of the
}


.. _rules-engine-standard-library-ite:

``ite`` function
================

Summary
An if-then-else function that returns one of two values based on a boolean condition.
Argument types
* condition: ``bool``
* trueValue: ``T`` or ``option<T>``
* falseValue: ``T`` or ``option<T>``
Return type
* ``ite(bool, T, T)`` → ``T`` (both non-optional, result is non-optional)
* ``ite(bool, T, option<T>)`` → ``option<T>`` (any optional makes result optional)
* ``ite(bool, option<T>, T)`` → ``option<T>`` (any optional makes result optional)
* ``ite(bool, option<T>, option<T>)`` → ``option<T>`` (both optional, result is optional)
Since
1.1

The ``ite`` (if-then-else) function evaluates a boolean condition and returns one of two values based on
the result. If the condition is ``true``, it returns ``trueValue``; if ``false``, it returns ``falseValue``.
This function is particularly useful for computing conditional values without branching in the rule tree, resulting
in fewer result nodes, and enabling better BDD optimizations as a result of reduced fragmentation.

.. important::
Both ``trueValue`` and ``falseValue`` must have the same base type ``T``. The result type follows
the "least upper bound" rule: if either branch is optional, the result is optional.

The following example uses ``ite`` to compute a URL suffix based on whether FIPS is enabled:

.. code-block:: json

{
"fn": "ite",
"argv": [
{"ref": "UseFIPS"},
"-fips",
""
],
"assign": "fipsSuffix"
}

The following example uses ``ite`` with ``coalesce`` to handle an optional boolean parameter:

.. code-block:: json

{
"fn": "ite",
"argv": [
{
"fn": "coalesce",
"argv": [
{"ref": "DisableFeature"},
false
]
},
"disabled",
"enabled"
],
"assign": "featureState"
}


.. _rules-engine-standard-library-ite-examples:

--------
Examples
--------

The following table shows various inputs and their corresponding outputs for the ``ite`` function:

.. list-table::
:header-rows: 1
:widths: 20 25 25 30

* - Condition
- True Value
- False Value
- Output
* - ``true``
- ``"-fips"``
- ``""``
- ``"-fips"``
* - ``false``
- ``"-fips"``
- ``""``
- ``""``
* - ``true``
- ``"sigv4"``
- ``"sigv4-s3express"``
- ``"sigv4"``
* - ``false``
- ``"sigv4"``
- ``"sigv4-s3express"``
- ``"sigv4-s3express"``


.. _rules-engine-standard-library-not:

``not`` function
Expand Down
2 changes: 2 additions & 0 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ pluginManagement {
}
}



rootProject.name = "smithy"

include(":smithy-aws-iam-traits")
Expand Down
54 changes: 54 additions & 0 deletions smithy-aws-endpoints/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,64 @@ description = "AWS specific components for managing endpoints in Smithy"
extra["displayName"] = "Smithy :: AWS Endpoints Components"
extra["moduleName"] = "software.amazon.smithy.aws.endpoints"

// Custom configuration for S3 model - kept separate from test classpath to avoid
// polluting other tests with S3 model discovery
val s3Model: Configuration by configurations.creating

dependencies {
api(project(":smithy-aws-traits"))
api(project(":smithy-diff"))
api(project(":smithy-rules-engine"))
api(project(":smithy-model"))
api(project(":smithy-utils"))

s3Model("software.amazon.api.models:s3:1.0.11")
}

// Integration test source set for tests that require the S3 model
// These tests require JDK 21+ due to the S3 model dependency
sourceSets {
create("it") {
compileClasspath += sourceSets["main"].output + sourceSets["test"].output
runtimeClasspath += sourceSets["main"].output + sourceSets["test"].output
}
}

configurations["itImplementation"].extendsFrom(configurations["testImplementation"])
configurations["itRuntimeOnly"].extendsFrom(configurations["testRuntimeOnly"])
configurations["itImplementation"].extendsFrom(s3Model)

// Configure IT source set to compile with JDK 21
tasks.named<JavaCompile>("compileItJava") {
javaCompiler.set(
javaToolchains.compilerFor {
languageVersion.set(JavaLanguageVersion.of(21))
},
)
sourceCompatibility = "21"
targetCompatibility = "21"
}

val integrationTest by tasks.registering(Test::class) {
description = "Runs integration tests that require external models like S3"
group = "verification"
testClassesDirs = sourceSets["it"].output.classesDirs
classpath = sourceSets["it"].runtimeClasspath
dependsOn(tasks.jar)
shouldRunAfter(tasks.test)

// Run with JDK 21
javaLauncher.set(
javaToolchains.launcherFor {
languageVersion.set(JavaLanguageVersion.of(21))
},
)
}

tasks.test {
finalizedBy(integrationTest)
}

tasks.named("check") {
dependsOn(integrationTest)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package software.amazon.smithy.rulesengine.aws.language.functions;

import static org.junit.jupiter.api.Assertions.assertFalse;

import java.util.List;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import software.amazon.smithy.model.Model;
import software.amazon.smithy.model.shapes.ServiceShape;
import software.amazon.smithy.model.shapes.ShapeId;
import software.amazon.smithy.rulesengine.language.EndpointRuleSet;
import software.amazon.smithy.rulesengine.language.evaluation.TestEvaluator;
import software.amazon.smithy.rulesengine.traits.EndpointRuleSetTrait;
import software.amazon.smithy.rulesengine.traits.EndpointTestCase;
import software.amazon.smithy.rulesengine.traits.EndpointTestsTrait;

/**
* Runs the endpoint test cases against the transformed S3 model. We're fixed to a specific version for this test,
* but could periodically bump the version if needed.
*/
class S3TreeRewriterTest {
private static final ShapeId S3_SERVICE_ID = ShapeId.from("com.amazonaws.s3#AmazonS3");

private static EndpointRuleSet originalRules;
private static List<EndpointTestCase> testCases;

@BeforeAll
static void loadS3Model() {
Model model = Model.assembler()
.discoverModels()
.assemble()
.unwrap();

ServiceShape s3Service = model.expectShape(S3_SERVICE_ID, ServiceShape.class);
originalRules = s3Service.expectTrait(EndpointRuleSetTrait.class).getEndpointRuleSet();
testCases = s3Service.expectTrait(EndpointTestsTrait.class).getTestCases();
}

@Test
void transformPreservesEndpointTestSemantics() {
assertFalse(testCases.isEmpty(), "S3 model should have endpoint test cases");

EndpointRuleSet transformed = S3TreeRewriter.transform(originalRules);
for (EndpointTestCase testCase : testCases) {
TestEvaluator.evaluate(transformed, testCase);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,21 @@ public double applyAsDouble(Condition condition) {

// Region is almost always provided
if (s.contains("isSet(Region)")) {
return 0.95;
return 0.96;
}

// Endpoint override is rare
if (s.contains("isSet(Endpoint)")) {
return 0.1;
return 0.2;
}

// S3 Express is rare (includes ITE variables from S3TreeRewriter)
if (s.contains("S3Express") || s.contains("--x-s3")
|| s.contains("--xa-s3")
|| s.contains("s3e_fips")
|| s.contains("s3e_ds")
|| s.contains("s3e_auth")) {
return 0.001;
}

// Most isSet checks on optional params succeed moderately
Expand All @@ -48,11 +57,6 @@ public double applyAsDouble(Condition condition) {
return 0.05;
}

// S3 Express is relatively rare
if (s.contains("S3Express") || s.contains("--x-s3") || s.contains("--xa-s3")) {
return 0.1;
}

// ARN-based buckets are uncommon
if (s.contains("parseArn") || s.contains("arn:")) {
return 0.15;
Expand Down
Loading
Loading