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

Std functions #115

Merged
merged 3 commits into from
Jan 16, 2023
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
5 changes: 4 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright 2019-2022 the original author or authors.
Copyright 2019-2023 the original author or authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -85,6 +85,8 @@
<fastparse.version>2.3.0</fastparse.version>
<pprint.version>0.5.9</pprint.version>
<scalatags.version>0.9.1</scalatags.version>

<jakarta-xml-bind-api-version>4.0.0</jakarta-xml-bind-api-version>
</properties>

<dependencies>
Expand Down Expand Up @@ -475,6 +477,7 @@
</dependency>
</dependencies>
</profile>

<profile>
<id>release</id>
<build>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.datasonnet.plugins;

/*-
* Copyright 2019-2021 the original author or authors.
* Copyright 2019-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -15,7 +15,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import com.datasonnet.RecentsMap;
import com.datasonnet.document.DefaultDocument;
import com.datasonnet.document.Document;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.datasonnet.plugins.jackson;

/*-
* Copyright 2019-2020 the original author or authors.
* Copyright 2019-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -15,7 +15,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.datasonnet.plugins.jackson;

/*-
* Copyright 2019-2020 the original author or authors.
* Copyright 2019-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -15,7 +15,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
Expand Down
92 changes: 81 additions & 11 deletions src/main/scala/com/datasonnet/jsonnet/Std.scala
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,14 @@ package com.datasonnet.jsonnet
import java.io.StringWriter
import java.nio.charset.StandardCharsets.UTF_8
import java.util.Base64
import java.util.zip.GZIPOutputStream

import com.datasonnet.jsonnet.Expr.Member.Visibility
import com.datasonnet.jsonnet.Expr.{BinaryOp, False, Params}

import scala.collection.mutable
import scala.collection.compat._
import com.datasonnet.jsonnet.Std.builtinWithDefaults
import scala.util.matching.Regex

import ujson.Value
import sourcecode.Text.generate
import ujson.Bool

import util.control.Breaks._
import scala.util.matching.Regex

/**
* The Jsonnet standard library, `std`, with each builtin function implemented
Expand Down Expand Up @@ -80,8 +75,8 @@ object Std {
val default = args("default")

val fieldValue = args("inc_hidden") match {
case Val.False => if (o.getVisibleKeys().get(f.value) == Some(false)) o.value(f.value, 0)(new FileScope(null, Map.empty), ev) else default
case Val.True => if (o.getVisibleKeys().get(f.value).isDefined) o.value(f.value, 0)(new FileScope(null, Map.empty), ev) else default
case Val.False => if (o.getVisibleKeys().get(f.value) == Some(false)) o.value(f.value, -1)(new FileScope(null, Map.empty), ev) else default
case Val.True => if (o.getVisibleKeys().get(f.value).isDefined) o.value(f.value, -1)(new FileScope(null, Map.empty), ev) else default
case _ => throw Error.Delegate("inc_hidden has to be a boolean, got" + args("inc_hidden").getClass)
}

Expand Down Expand Up @@ -115,6 +110,28 @@ object Std {
}
Val.Arr(maybeSorted.map(k => Val.Lazy(Val.Str(k))))
},
builtin("objectValues", "o"){ (ev, fs, v1: Val.Obj) =>
val keys = v1.getVisibleKeys()
.collect{case (k, false) => k}
.toSeq
val maybeSorted = if(ev.preserveOrder) {
keys
} else {
keys.sorted
}
Val.Arr(maybeSorted.map(k => Val.Lazy(v1.value(k, -1)(fs, ev))))
},
builtin("objectValuesAll", "o"){ (ev, fs, v1: Val.Obj) =>
val keys = v1.getVisibleKeys()
.collect{case (k, _) => k}
.toSeq
val maybeSorted = if(ev.preserveOrder) {
keys
} else {
keys.sorted
}
Val.Arr(maybeSorted.map(k => Val.Lazy(v1.value(k, -1)(fs, ev))))
},
builtin("type", "x"){ (ev, fs, v1: Val) =>
v1 match{
case Val.True | Val.False => "boolean"
Expand Down Expand Up @@ -487,6 +504,10 @@ object Std {
Val.Arr(out.toSeq)
},

builtin("reverse", "arr"){ (ev, fs, arr: Val.Arr) =>
Val.Arr(arr.value.reverse)
},

builtin("manifestIni", "v"){ (ev, fs, v: Val) =>
val materialized = Materializer(v)(ev)
def render(x: ujson.Value) = x match{
Expand Down Expand Up @@ -601,6 +622,16 @@ object Std {
rec(Materializer(value)(ev)).render

},

builtin("manifestTomlEx", "toml", "indent") { (ev, fs, toml: Val, indent: Int) =>
throw new Error.Delegate("Function manifestTomlEx is not yet implemented")
false
},
builtin("parseYaml", "str") { (ev, fs, str: Val) =>
throw new Error.Delegate("Function parseYaml is not yet implemented")
false
},

builtin("base64", "v"){ (ev, fs, v: Val) =>
v match{
case Val.Str(value) => Base64.getEncoder().encodeToString(value.getBytes)
Expand Down Expand Up @@ -781,6 +812,14 @@ object Std {
builtin("splitLimit", "str", "c", "maxSplits"){ (ev, fs, str: String, c: String, maxSplits: Int) =>
Val.Arr(str.split(java.util.regex.Pattern.quote(c), maxSplits + 1).map(s => Val.Lazy(Val.Str(s))))
},
builtin("splitLimitR", "str", "c", "maxSplits"){ (ev, fs, str: String, c: String, maxSplits: Int) =>
if (maxSplits == -1) {
Val.Arr(str.split(java.util.regex.Pattern.quote(c), maxSplits + 1).map(s => Val.Lazy(Val.Str(s))))
} else {
val split = str.reverse.split(java.util.regex.Pattern.quote(c.reverse), maxSplits + 1)
Val.Arr(split.map(s => Val.Lazy(Val.Str(s.reverse))).reverse)
}
},
builtin("stringChars", "str"){ (ev, fs, str: String) =>
stringChars(str)
},
Expand Down Expand Up @@ -852,7 +891,31 @@ object Std {
scope.bindings(1).get.force
}
),

builtin("slice", "indexable", "index", "end", "step") { (ev, fs, indexable: Val, index: Int, end: Int, step: Int) =>
val slice: Val = indexable match {
case Val.Arr(arr) =>
val a = arr.value
Val.Arr(List.tabulate(a.size) {i =>
if ((i+1) % step == 0 && i >= index && i < end) Some(a(i))
else None }.flatten)
case Val.Str(str) =>
val a = str.value
Val.Str(List.tabulate(a.size) {i =>
if ((i+1) % step == 0 && i >= index && i < end) Some(a(i))
else None }.flatten.mkString)
case i => throw Error.Delegate("Expected Array or String, got: " + i.prettyName)
}
slice
},
builtin("any", "arr") { (ev, fs, arr: Val.Arr) =>
val a = arr.value
//First see if all values are boolean
val allBool = a.find(b => !(Materializer.apply(b.force)(ev).isInstanceOf[Bool])) == None
if (!allBool) {
throw Error.Delegate("Array must contain only boolean values")
}
a.find(b => Materializer.apply(b.force)(ev).value == true) != None
},
"extVar" -> Val.Func(
None,
Params(Array(("x", None, 0))),
Expand Down Expand Up @@ -928,6 +991,13 @@ object Std {
val Seq(v1: T1, v2: T2, v3: T3) = validate(vs, ev, fs, Array(implicitly[ReadWriter[T1]], implicitly[ReadWriter[T2]], implicitly[ReadWriter[T3]]))
eval(ev, fs, v1, v2, v3)
}

def builtin[R: ReadWriter, T1: ReadWriter, T2: ReadWriter, T3: ReadWriter, T4: ReadWriter](name: String, p1: String, p2: String, p3: String, p4: String)
(eval: (EvalScope, FileScope, T1, T2, T3, T4) => R): (String, Val.Func) = builtin0(name, p1, p2, p3, p4){ (vs, ev, fs) =>
val Seq(v1: T1, v2: T2, v3: T3, v4: T4) = validate(vs, ev, fs, Array(implicitly[ReadWriter[T1]], implicitly[ReadWriter[T2]], implicitly[ReadWriter[T3]], implicitly[ReadWriter[T4]]))
eval(ev, fs, v1, v2, v3, v4)
}

def builtin0[R: ReadWriter](name: String, params: String*)(eval: (Array[Val], EvalScope, FileScope) => R) = {
val paramData = params.zipWithIndex.map{case (k, i) => (k, None, i)}.toArray
val paramIndices = params.indices.toArray
Expand Down
2 changes: 1 addition & 1 deletion src/test/java/com/datasonnet/JavaWriterTest.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.datasonnet;

/*-
* Copyright 2019-2021 the original author or authors.
* Copyright 2019-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down
69 changes: 66 additions & 3 deletions src/test/java/com/datasonnet/StdTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,78 @@
import java.io.IOException;
import java.net.URISyntaxException;

import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
import static org.junit.jupiter.api.Assertions.*;

public class StdTest {

@Test
void testStdGet() throws IOException, URISyntaxException, JSONException {
Mapper mapper = new Mapper(TestResourceReader.readFileAsString("stdGet.ds"));
Document<String> response = mapper.transform(new DefaultDocument<>("{}", MediaTypes.APPLICATION_JSON));
Document<String> response = mapper.transform(new DefaultDocument<>("{}", MediaTypes.APPLICATION_JSON));
JSONAssert.assertEquals("{\"hidden\":\"HiddenMessage\",\"noHidden\":\"NONE\",\"obj\":{\"Hello\":\"World\"},\"nonExistent\":null,\"nonExistentD\":\"DefaultNonExistent\",\"a\":[1,2,3],\"b\":[9,8,7]}", response.getContent(), true);
}

@Test
void testStdObjectValues() throws IOException, URISyntaxException, JSONException {
Mapper mapper = new Mapper("std.objectValues(" + TestResourceReader.readFileAsString("stdObjectValues.ds") + ")");
Document<String> response = mapper.transform(new DefaultDocument<>("{}", MediaTypes.APPLICATION_JSON));
JSONAssert.assertEquals("[\"Hello\",{\"Hello\":\"World\"},[1,2,3]]", response.getContent(), true);
}

@Test
void testStdObjectValuesAll() throws IOException, URISyntaxException, JSONException {
Mapper mapper = new Mapper("std.objectValuesAll(" + TestResourceReader.readFileAsString("stdObjectValues.ds") + ")");
Document<String> response = mapper.transform(new DefaultDocument<>("{}", MediaTypes.APPLICATION_JSON));
JSONAssert.assertEquals("[\"Hello\",{\"Hello\":\"World\"},[1,2,3],\"HiddenMessage\"]", response.getContent(), true);
}

@Test
void testStdReverse() throws IOException, URISyntaxException, JSONException {
Mapper mapper = new Mapper("std.reverse([1,2,3])");
Document<String> response = mapper.transform(new DefaultDocument<>("{}", MediaTypes.APPLICATION_JSON));
JSONAssert.assertEquals("[3,2,1]", response.getContent(), true);
}

@Test
void testStdSplitLimitR() throws IOException, URISyntaxException, JSONException {
Mapper mapper = new Mapper("std.splitLimitR(\"testX1YsplitX1YrightX1Ytest2\", \"X1Y\", 2)");
Document<String> response = mapper.transform(new DefaultDocument<>("{}", MediaTypes.APPLICATION_JSON));
JSONAssert.assertEquals("[\"testX1Ysplit\",\"right\",\"test2\"]", response.getContent(), true);
}

@Test
void testStdSlice() throws IOException, URISyntaxException, JSONException {
Mapper mapper = new Mapper("std.slice([1, 2, 3, 4, 5, 6], 0, 4, 1)");
Document<String> response = mapper.transform(new DefaultDocument<>("{}", MediaTypes.APPLICATION_JSON));
JSONAssert.assertEquals("[ 1, 2, 3, 4 ]", response.getContent(), true);

mapper = new Mapper("std.slice([1, 2, 3, 4, 5, 6], 1, 6, 2)");
response = mapper.transform(new DefaultDocument<>("{}", MediaTypes.APPLICATION_JSON));
JSONAssert.assertEquals("[ 2, 4, 6 ]", response.getContent(), true);

mapper = new Mapper("std.slice(\"jsonnet\", 0, 4, 1)");
response = mapper.transform(new DefaultDocument<>("{}", MediaTypes.APPLICATION_JSON));
JSONAssert.assertEquals("\"json\"", response.getContent(), true);
}

@Test
void testStdAny() throws IOException, URISyntaxException, JSONException {
Mapper mapper = new Mapper("std.any([false, true, false])");
Document<String> response = mapper.transform(new DefaultDocument<>("{}", MediaTypes.APPLICATION_JSON));
assertEquals("true", response.getContent());

mapper = new Mapper("std.any([false, false, false])");
response = mapper.transform(new DefaultDocument<>("{}", MediaTypes.APPLICATION_JSON));
assertEquals("false", response.getContent());

try {
mapper = new Mapper("std.any([false, \"HELLO\", false])");
response = mapper.transform(new DefaultDocument<>("{}", MediaTypes.APPLICATION_JSON));
fail("This should fail with java.lang.IllegalArgumentException:");
} catch (Exception e) {
String msg = e.getMessage();
assertTrue(msg != null && msg.contains("Array must contain only boolean values"));
}

}
}
3 changes: 1 addition & 2 deletions src/test/java/com/datasonnet/javatest/WsdlGeneratedObj.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.datasonnet.javatest;

/*-
* Copyright 2019-2020 the original author or authors.
* Copyright 2019-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -15,7 +15,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
Expand Down
8 changes: 8 additions & 0 deletions src/test/resources/stdObjectValues.ds
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
s: "Hello",
o: {
Hello: "World"
},
a: [1,2,3],
hidden:: "HiddenMessage"
}