diff --git a/nb-engine/nb-engine-cli/src/main/java/io/nosqlbench/engine/cli/atfiles/NBAtFile.java b/nb-engine/nb-engine-cli/src/main/java/io/nosqlbench/engine/cli/atfiles/NBAtFile.java
index f47ce4dfa..584481543 100644
--- a/nb-engine/nb-engine-cli/src/main/java/io/nosqlbench/engine/cli/atfiles/NBAtFile.java
+++ b/nb-engine/nb-engine-cli/src/main/java/io/nosqlbench/engine/cli/atfiles/NBAtFile.java
@@ -21,6 +21,7 @@ import io.nosqlbench.nb.api.nbio.NBIO;
import io.nosqlbench.nb.api.nbio.NBPathsAPI;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
+import org.jetbrains.annotations.NotNull;
import org.snakeyaml.engine.v2.api.Load;
import org.snakeyaml.engine.v2.api.LoadSettings;
@@ -49,6 +50,12 @@ public class NBAtFile {
*
{@code >-- } asserts each value starts with global option syntax (--)
*
*
+ * Files can be included recursively using a format like
{@code
+ * - include:${DIR}/somefile.yaml
+ * }
+ *
+ * Standard formatting specifiers above should work in this mode as well.
+ *
* @param processInPlace The linked list which is statefully modified. If you need
* an unmodified copy, then this is the responsibility of the caller.
* @return An updated list with all values expanded and injected
@@ -59,10 +66,10 @@ public class NBAtFile {
ListIterator iter = processInPlace.listIterator();
while (iter.hasNext()) {
String spec = iter.next();
- if (spec.startsWith("@")) {
+ if (spec.startsWith("@") || spec.startsWith("include=")|| spec.startsWith("include:")) {
iter.previous();
iter.remove();
- LinkedList spliceIn = includeAt(spec);
+ LinkedList spliceIn = includeAt(spec.replaceFirst("include=","@").replaceFirst("include:","@"));
for (String s : spliceIn) {
iter.add(s);
}
@@ -89,6 +96,22 @@ public class NBAtFile {
* @return The linked list of arguments which is to be spliced into the caller's command list
*/
public static LinkedList includeAt(String spec) {
+ LinkedList toInclude = doInclude(spec);
+ boolean recurse = false;
+ for (String s : toInclude) {
+ if (s.startsWith("include=")||s.startsWith("include:")) {
+ recurse=true;
+ break;
+ }
+ }
+ if (recurse) {
+ toInclude=includeAt(toInclude);
+ }
+ return toInclude;
+
+ }
+
+ private static @NotNull LinkedList doInclude(String spec) {
Matcher matcher = includePattern.matcher(spec);
if (matcher.matches()) {
String filepathSpec = matcher.group("filepath");
@@ -135,7 +158,6 @@ public class NBAtFile {
} else {
throw new RuntimeException("Unable to match at-file specifier: " + spec + " to pattern '" + includePattern.pattern() + "'");
}
-
}
private static LinkedList interposePath(LinkedList formatted, Path atPath) {
diff --git a/nb-engine/nb-engine-cli/src/test/java/io/nosqlbench/engine/cli/atfiles/NBAtFileTest.java b/nb-engine/nb-engine-cli/src/test/java/io/nosqlbench/engine/cli/atfiles/NBAtFileTest.java
index c42744096..fc53effa8 100644
--- a/nb-engine/nb-engine-cli/src/test/java/io/nosqlbench/engine/cli/atfiles/NBAtFileTest.java
+++ b/nb-engine/nb-engine-cli/src/test/java/io/nosqlbench/engine/cli/atfiles/NBAtFileTest.java
@@ -79,4 +79,9 @@ class NBAtFileTest {
assertThat(strings).containsExactly("--option1", "--option2=value2", "--option3=value3", "--option4=value4");
}
+ @Test
+ public void testAtfileRecursion() {
+ LinkedList strings = NBAtFile.includeAt("@src/test/resources/atfiles/simple_recursion.yaml");
+ assertThat(strings).containsExactly("arg1","arg1","arg2","arg3","arg3");
+ }
}
diff --git a/nb-engine/nb-engine-cli/src/test/resources/atfiles/simple_recursion.yaml b/nb-engine/nb-engine-cli/src/test/resources/atfiles/simple_recursion.yaml
new file mode 100644
index 000000000..08adde1c2
--- /dev/null
+++ b/nb-engine/nb-engine-cli/src/test/resources/atfiles/simple_recursion.yaml
@@ -0,0 +1,3 @@
+- arg1
+- include:${DIR}/simple_list.yaml
+- arg3