Skip to content
Merged
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": "Add support for negative GetAttr indexing",
"pull_requests": [
"[#2937](https://github.com/smithy-lang/smithy/pull/2937)"
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,10 @@ parsed value of the ``foo`` parameter:
]
}

.. note::

Negative array indexing is available since 1.1.


.. _rules-engine-standard-library-getAttr-path-strings:

Expand All @@ -126,7 +130,9 @@ Parsing ``path`` strings
Path strings for the `getAttr function`_ are composed of two components:

#. Keys, e.g. ``scheme`` in ``uri#scheme``.
#. Indexes, e.g. ``[2]`` in ``list[2]``.
#. Indexes, e.g. ``[2]`` in ``list[2]``. Negative indexes are supported,
where ``[-1]`` returns the last element, ``[-2]`` returns the second-to-last,
and so on.

An index MUST only occur at the end of a path, as indexes always return
``option`` values.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,13 +77,13 @@ private static boolean isAzIdSubstringBinding(Condition cond) {
return target instanceof Reference && ID_BUCKET.equals(((Reference) target).getName());
}

// Creates: s3expressAvailabilityZoneId = split(Bucket, "--", 0)[1]
// Creates: s3expressAvailabilityZoneId = split(Bucket, "--", 0)[-2]
private static Condition createCanonicalAzCondition(Condition original) {
Split split = Split.ofExpressions(
Expression.getReference(ID_BUCKET),
Expression.of("--"),
Expression.of(0));
GetAttr azExpr = GetAttr.ofExpressions(split, "[1]");
GetAttr azExpr = GetAttr.ofExpressions(split, "[-2]");
return original.toBuilder().fn(azExpr).build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@
import software.amazon.smithy.model.node.Node;
import software.amazon.smithy.model.node.ObjectNode;
import software.amazon.smithy.model.node.StringNode;
import software.amazon.smithy.rulesengine.language.RulesVersion;
import software.amazon.smithy.rulesengine.language.error.InnerParseError;
import software.amazon.smithy.rulesengine.language.error.InvalidRulesException;
import software.amazon.smithy.rulesengine.language.evaluation.Scope;
import software.amazon.smithy.rulesengine.language.evaluation.type.ArrayType;
import software.amazon.smithy.rulesengine.language.evaluation.type.RecordType;
import software.amazon.smithy.rulesengine.language.evaluation.type.Type;
import software.amazon.smithy.rulesengine.language.evaluation.value.ArrayValue;
import software.amazon.smithy.rulesengine.language.evaluation.value.Value;
import software.amazon.smithy.rulesengine.language.syntax.Identifier;
import software.amazon.smithy.rulesengine.language.syntax.ToExpression;
Expand Down Expand Up @@ -79,6 +81,21 @@ public static GetAttr ofExpressions(ToExpression arg1, String arg2) {
return ofExpressions(arg1, Expression.of(arg2));
}

@Override
public RulesVersion availableSince() {
// Negative index only available since 1.1.
for (Part part : path) {
if (part instanceof Part.Index) {
Part.Index index = (Part.Index) part;
if (index.index < 0) {
return RulesVersion.V1_1;
}
}
}

return RulesVersion.V1_0;
}

/**
* Parses the path argument to getAttr.
*
Expand All @@ -100,10 +117,6 @@ private static List<Part> parse(String path, FromSourceLocation sourceLocation)
try {
String number = slicePart.substring(1, slicePart.length() - 1);
int slice = Integer.parseInt(number);
if (slice < 0) {
throw new InvalidRulesException("Invalid path component: slice index must be >= 0",
sourceLocation);
}
if (slicePartIndex > 0) {
result.add(Part.Key.of(component.substring(0, slicePartIndex)));
}
Expand Down Expand Up @@ -316,7 +329,8 @@ public Type typeCheck(Type container) throws InnerParseError {

@Override
public Value eval(Value container) {
return container.expectArrayValue().get(index);
ArrayValue values = container.expectArrayValue();
return index >= 0 ? values.get(index) : values.get(values.getValues().size() + index);
}

public int index() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[WARNING] example#TestService: This shape applies a trait that is unstable: smithy.rules#endpointRuleSet | UnstableTrait.smithy.rules#endpointRuleSet
[WARNING] example#TestService: This shape applies a trait that is unstable: smithy.rules#endpointTests | UnstableTrait.smithy.rules#endpointTests
[WARNING] example#TestService: This shape applies a trait that is unstable: smithy.rules#clientContextParams | UnstableTrait.smithy.rules#clientContextParams
[ERROR] example#TestService: GetAttr requires rules engine version >= 1.1, but ruleset declares version 1.0 | RulesEngineVersion
[ERROR] example#TestService: Split requires rules engine version >= 1.1, but ruleset declares version 1.0 | RulesEngineVersion
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
$version: "2.0"

namespace example

use smithy.rules#clientContextParams
use smithy.rules#endpointRuleSet
use smithy.rules#endpointTests

@endpointRuleSet({
"version": "1.0", // this will fail because negative getAttr requires 1.1
"parameters": {
"Input": {
"type": "string",
"required": true,
"documentation": "The input string to split"
}
},
"rules": [
{
"documentation": "Test negative index access",
"conditions": [
{
"fn": "split",
"argv": ["{Input}", "--", 0],
"assign": "parts"
},
{
"fn": "getAttr",
"argv": [{"ref": "parts"}, "[-1]"],
"assign": "last"
},
{
"fn": "getAttr",
"argv": [{"ref": "parts"}, "[-2]"],
"assign": "secondLast"
}
],
"endpoint": {
"url": "https://example.com/{secondLast}/{last}"
},
"type": "endpoint"
},
{
"documentation": "Fallback",
"conditions": [],
"error": "No input provided",
"type": "error"
}
]
})
@endpointTests({
"version": "1.0",
"testCases": [
{
"documentation": "S3Express bucket with multiple delimiters extracts AZ correctly",
"params": {"Input": "my--s3--bucket--abcd-ab1--x-s3"},
"expect": {
"endpoint": {"url": "https://example.com/abcd-ab1/x-s3"}
}
}
]
})
@clientContextParams(
Input: {type: "string", documentation: "The input string"}
)
service TestService {}
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ use smithy.rules#endpointRuleSet
"type": "endpoint"
}
],
"version": "1.3"
"version": "1.1"
})
@clientContextParams(
Region: {type: "string", documentation: "docs"}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use smithy.rules#endpointRuleSet
use smithy.rules#staticContextParams

@endpointRuleSet({
"version": "1.3",
"version": "1.1",
"parameters": {
"ParameterBar": {
"type": "String",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[WARNING] example#TestService: This shape applies a trait that is unstable: smithy.rules#endpointRuleSet | UnstableTrait
[WARNING] example#TestService: This shape applies a trait that is unstable: smithy.rules#endpointTests | UnstableTrait
[WARNING] example#TestService: This shape applies a trait that is unstable: smithy.rules#clientContextParams | UnstableTrait
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
$version: "2.0"

namespace example

use smithy.rules#clientContextParams
use smithy.rules#endpointRuleSet
use smithy.rules#endpointTests

@endpointRuleSet({
"version": "1.1",
"parameters": {
"Input": {
"type": "string",
"required": true,
"documentation": "The input string to split"
}
},
"rules": [
{
"documentation": "Test negative index access",
"conditions": [
{
"fn": "split",
"argv": ["{Input}", "--", 0],
"assign": "parts"
},
{
"fn": "getAttr",
"argv": [{"ref": "parts"}, "[-1]"],
"assign": "last"
},
{
"fn": "getAttr",
"argv": [{"ref": "parts"}, "[-2]"],
"assign": "secondLast"
}
],
"endpoint": {
"url": "https://example.com/{secondLast}/{last}"
},
"type": "endpoint"
},
{
"documentation": "Fallback",
"conditions": [],
"error": "No input provided",
"type": "error"
}
]
})
@endpointTests({
"version": "1.0",
"testCases": [
{
"documentation": "S3Express bucket with multiple delimiters extracts AZ correctly",
"params": {"Input": "my--s3--bucket--abcd-ab1--x-s3"},
"expect": {
"endpoint": {"url": "https://example.com/abcd-ab1/x-s3"}
}
},
{
"documentation": "Simple two-part split",
"params": {"Input": "first--second"},
"expect": {
"endpoint": {"url": "https://example.com/first/second"}
}
}
]
})
@clientContextParams(
Input: {type: "string", documentation: "The input string"}
)
service TestService {}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use smithy.rules#clientContextParams
use smithy.rules#endpointRuleSet

@endpointRuleSet({
"version": "1.3",
"version": "1.1",
"parameters": {
"Bucket": {
"type": "string",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ use smithy.rules#endpointTests
"type": "error"
}
],
"version": "1.3"
"version": "1.1"
})
@endpointTests(
"version": "1.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use smithy.rules#clientContextParams
use smithy.rules#endpointRuleSet

@endpointRuleSet({
"version": "1.3",
"version": "1.1",
"parameters": {
"Region": {
"required": true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use smithy.rules#endpointRuleSet
use smithy.rules#endpointTests

@endpointRuleSet({
"version": "1.3",
"version": "1.1",
"parameters": {
"Endpoint": {
"type": "string",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use smithy.rules#endpointRuleSet
use smithy.rules#endpointTests

@endpointRuleSet({
"version": "1.3",
"version": "1.1",
"parameters": {
"Input": {
"type": "string",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ use smithy.rules#endpointTests
"type": "error"
}
],
"version": "1.3"
"version": "1.1"
})
@endpointTests(
version: "1.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use smithy.rules#endpointRuleSet
use smithy.rules#endpointTests

@endpointRuleSet({
"version": "1.3",
"version": "1.1",
"parameters": {
"TestCaseId": {
"type": "string",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ use smithy.rules#endpointTests
"type": "error"
}
],
"version": "1.3"
"version": "1.1"
})
@endpointTests(
version: "1.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use smithy.rules#endpointRuleSet
use smithy.rules#staticContextParams

@endpointRuleSet({
"version": "1.3",
"version": "1.1",
"parameters": {
"Region": {
"type": "string",
Expand Down
Loading