Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add functions (min, max, to_array, to_string, to_number, not_null, reverse) to JMESPath visitor #952

Merged
merged 6 commits into from
Sep 25, 2023

Conversation

0marperez
Copy link
Contributor

Issue #

Description of changes

-Added min function
-Added max function
-Added to_array function
-Added to_string function
-Added to_number function
-Added not_null function
-Added reverse funtion

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

@0marperez 0marperez added the no-changelog Indicates that a changelog entry isn't required for a pull request. Use sparingly. label Sep 14, 2023
@0marperez 0marperez marked this pull request as ready for review September 14, 2023 16:53
@0marperez 0marperez requested a review from a team as a code owner September 14, 2023 16:53
Comment on lines 289 to 304
"max" -> {
writer.addImport(RuntimeTypes.Core.Utils.max)
val list = expression.singleArg()
list.dotFunction(expression, "max()")
}

"min" -> {
writer.addImport(RuntimeTypes.Core.Utils.min)
val list = expression.singleArg()
list.dotFunction(expression, "min()")
}

"reverse" -> {
val listOrString = expression.singleArg()
listOrString.dotFunction(expression, "reversed()")
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: Do these not need null guards?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The dotFunction function takes care of that:

private fun VisitedExpression.dotFunction(expression: FunctionExpression, expr: String, elvisExpr: String? = null, isObject: Boolean = false): VisitedExpression {
    val dotFunctionExpr = ensureNullGuard(shape, expr, elvisExpr)
     ...
}

Comment on lines 529 to 544
private fun List<VisitedExpression>.getNotNull(): String {
val answer = bestTempVarName("answer")
writer.write("var $answer: Any? = null")

val found = bestTempVarName("found")
writer.write("var $found = false")

forEach {
writer.withBlock("if (${it.identifier} != null && !$found) {", "}") {
write("$answer = ${it.identifier}")
write("$found = true")
}
}

return answer
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: This should be possible without mutability and intermediate variables like found:

private fun List<VisitedExpression>.getNotNull(): String {
    val notNull = bestTempVarName("notNull")
    writer.withBlock("val $notNull = listOfNotNull(", ").firstOrNull()") {
        write("${it.identifier},")
    }

    return notNull
}

Which produces code like:

val notNull1 = listOfNotNull(
    foo,
    bar,
    baz,
).firstOrNull()

Comment on lines 39 to 42
@InternalApi
public fun List<Number>.max(): Double? =
@Suppress("UNCHECKED_CAST")
(this as List<Double>).maxOrNull()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: Why is casting to Double correct here? Shouldn't the max of [1L, 2L, 3L] be 3L instead of 3.0?

Comment on lines 55 to 59
@InternalApi
public fun Number.toNumber(): Double = this.toDouble()

@InternalApi
public fun String.toNumber(): Double? = this.toDoubleOrNull()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: Same as above, why is Double always right here?

Copy link
Contributor Author

@0marperez 0marperez Sep 14, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So other parts of the code require a specific type of number and I chose double because I think all the number types we deal with can be represented as a Double (as long as they are small enough)

Copy link
Contributor Author

@0marperez 0marperez Sep 14, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's also the problem of getting a number as a string, I'm not 100% sure but I think I have to choose a type of number for it when converting it to a number

@@ -35,3 +35,24 @@ public fun truthiness(value: Any?): Boolean = when (value) {
null -> false
else -> true
}

@InternalApi
public fun Short.toNumber(): Short = this
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we move/group all the JMESPath related utilities into their own file JMESPath.kt (including ones introduced previously), it will help with understanding why these things exist and where they are used (should be used).

public fun String.toNumber(): Double? = this.toDoubleOrNull()

@InternalApi
public fun Any.toNumber(): Nothing? = null
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why Nothing? as return type? Seems like Number? would be closer to correct.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to the JMESPath docs for to_number if it's used on anything other than a string or number it should return null. And from looking at the Kotlin docs for Nothing :

The nullable variant of this type, Nothing?, has exactly one possible value, which is null

@sonarqubecloud
Copy link

Kudos, SonarCloud Quality Gate passed!    Quality Gate passed

Bug A 0 Bugs
Vulnerability A 0 Vulnerabilities
Security Hotspot A 0 Security Hotspots
Code Smell A 0 Code Smells

No Coverage information No Coverage information
0.0% 0.0% Duplication

@0marperez 0marperez merged commit 2d4403f into main Sep 25, 2023
9 checks passed
@0marperez 0marperez deleted the jmes-path-more-functions branch September 25, 2023 21:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
no-changelog Indicates that a changelog entry isn't required for a pull request. Use sparingly.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants