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

Add --show to select command #1953

Merged
merged 1 commit into from
Aug 25, 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
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public void doesNotShowWarnings() {

@Test
public void selectsVariables() {
List<String> args = Arrays.asList("select", "--show-vars", "--selector", "list $list(*) > member > string");
List<String> args = Arrays.asList("select", "--show", "vars", "--selector", "list $list(*) > member > string");
IntegUtils.run("simple-config-sources", args, result -> {
assertThat(result.getExitCode(), equalTo(0));
String content = result.getOutput().trim();
Expand Down Expand Up @@ -79,4 +79,80 @@ public void showTraitsCannotHaveEmptyValues() {
assertThat(result.getExitCode(), not(equalTo(0)));
});
}

@Test
public void showCannotBeEmpty() {
List<String> args = Arrays.asList("select", "--show", "", "--selector", "string");
IntegUtils.run("simple-config-sources", args, result -> {
assertThat(result.getExitCode(), equalTo(1));
});
}

@Test
public void showCannotContainInvalidValues() {
List<String> args = Arrays.asList("select", "--show", "foo", "--selector", "string");
IntegUtils.run("simple-config-sources", args, result -> {
assertThat(result.getExitCode(), equalTo(1));
});
}

@Test
public void showCannotContainInvalidValuesInCsv() {
List<String> args = Arrays.asList("select", "--show", "vars,foo", "--selector", "string");
IntegUtils.run("simple-config-sources", args, result -> {
assertThat(result.getExitCode(), equalTo(1));
});
}

@Test
public void includesType() {
List<String> args = Arrays.asList("select", "--show", "type", "--selector", "string");
IntegUtils.run("simple-config-sources", args, result -> {
assertThat(result.getExitCode(), equalTo(0));
String content = result.getOutput().trim();
// Ensure it's valid JSON
Node.parse(content);
assertThat(content, containsString("\"type\": \"string\""));
});
}

@Test
public void includesFile() {
List<String> args = Arrays.asList("select", "--show", "file", "--selector", "string");
IntegUtils.run("simple-config-sources", args, result -> {
assertThat(result.getExitCode(), equalTo(0));
String content = result.getOutput().trim();
// Ensure it's valid JSON
Node.parse(content);
assertThat(content, containsString("\"file\": "));
});
}

@Test
public void includesFileAndType() {
List<String> args = Arrays.asList("select", "--show", "file, type", "--selector", "string");
IntegUtils.run("simple-config-sources", args, result -> {
assertThat(result.getExitCode(), equalTo(0));
String content = result.getOutput().trim();
// Ensure it's valid JSON
Node.parse(content);
assertThat(content, containsString("\"type\": \"string\""));
assertThat(content, containsString("\"file\": "));
});
}

@Test
public void includesFileAndTypeAndVars() {
List<String> args = Arrays.asList("select", "--show", "file, type,vars", "--selector", "string $hi(*)");
IntegUtils.run("simple-config-sources", args, result -> {
assertThat(result.getExitCode(), equalTo(0));
String content = result.getOutput().trim();
// Ensure it's valid JSON
Node.parse(content);
assertThat(content, containsString("\"type\": \"string\""));
assertThat(content, containsString("\"file\": "));
assertThat(content, containsString("\"vars\": "));
assertThat(content, containsString("\"hi\": "));
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.function.Consumer;
import java.util.logging.Level;
import java.util.logging.Logger;
Expand Down Expand Up @@ -86,30 +87,88 @@ public int execute(Arguments arguments, Env env) {
}

private String getDocumentation(ColorFormatter colors) {
return "By default, each matching shape ID is printed to stdout on a new line. Pass --show-vars to print out "
+ "a JSON array that contains a 'shape' and 'vars' property, where the 'vars' property is a map of "
+ "each variable that was captured when the shape was matched.";
return "By default, each matching shape ID is printed to stdout on a new line. Pass --show or --show-traits "
+ "to get JSON array output.";
}

private static final class Options implements ArgumentReceiver {
private boolean showVars;
private Selector selector;
private final List<ShapeId> showTraits = new ArrayList<>();
private final Set<Show> show = new TreeSet<>();

private enum Show {
TYPE("type") {
@Override
protected void inject(Selector.ShapeMatch match, ObjectNode.Builder builder) {
builder.withMember("type", match.getShape().getType().toString());
}
},

FILE("file") {
@Override
protected void inject(Selector.ShapeMatch match, ObjectNode.Builder builder) {
SourceLocation source = match.getShape().getSourceLocation();
// Only shapes with a real source location add a file.
if (!source.getFilename().equals(SourceLocation.NONE.getFilename())) {
builder.withMember("file", source.getFilename()
+ ':' + source.getLine()
+ ':' + source.getColumn());
}
}
},

VARS("vars") {
@Override
protected void inject(Selector.ShapeMatch match, ObjectNode.Builder builder) {
if (!match.isEmpty()) {
ObjectNode.Builder varBuilder = Node.objectNodeBuilder();
for (Map.Entry<String, Set<Shape>> varEntry : match.entrySet()) {
varBuilder.withMember(
varEntry.getKey(),
sortShapeIds(varEntry.getValue()).map(Node::from).collect(ArrayNode.collect())
);
}
ObjectNode collectedVars = varBuilder.build();
builder.withMember("vars", collectedVars);
}
}
};

private final String value;

Show(String value) {
this.value = value;
}

protected abstract void inject(Selector.ShapeMatch match, ObjectNode.Builder builder);

private static Show from(String value) {
for (Show variant : values()) {
if (variant.value.equals(value)) {
return variant;
}
}
throw new CliError("Invalid value given to --show: `" + value + "`");
}
}

@Override
public boolean testOption(String name) {
switch (name) {
case "--vars":
LOGGER.warning("--vars is deprecated. Use --show-vars instead.");
// fall-through
case "--show-vars":
showVars = true;
return true;
return deprecatedVars(name);
default:
return false;
}
}

private boolean deprecatedVars(String name) {
LOGGER.warning(name + " is deprecated. Use `--show vars` instead.");
show.add(Show.VARS);
return true;
}

@Override
public Consumer<String> testParameter(String name) {
switch (name) {
Expand All @@ -128,6 +187,13 @@ public Consumer<String> testParameter(String name) {
throw new CliError("--show-traits must contain traits");
}
};
case "--show":
return value -> {
String[] parts = value.split("\\s*,\\s*");
for (String part : parts) {
show.add(Show.from(part));
}
};
default:
return null;
}
Expand All @@ -137,16 +203,17 @@ public Consumer<String> testParameter(String name) {
public void registerHelp(HelpPrinter printer) {
printer.param("--selector", null, "SELECTOR",
"The Smithy selector to execute. Reads from STDIN when not provided.");
printer.param("--show", null, "DATA",
"Displays additional top-level members in each match and forces JSON output. This parameter "
+ "accepts a comma-separated list of values, including 'type', 'file', and 'vars'. 'type' "
+ "adds a string member containing the shape type of each match. 'file' adds a string "
+ "member containing the absolute path to where the shape is defined followed by the line "
+ "number then column (e.g., '/path/example.smithy:10:1'). 'vars' adds an object containing "
+ "the variables that were captured when a shape was matched.");
printer.param("--show-traits", null, "TRAITS",
"Returns JSON output that includes the values of specific traits applied to matched shapes, "
+ "stored in a 'traits' property. Provide a comma-separated list of trait shape IDs. "
+ "Prelude traits may omit a namespace (e.g., 'required' or 'smithy.api#required').");
printer.option("--show-vars", null, "Returns JSON output that includes the variables that were captured "
+ "when a shape was matched, stored in a 'vars' property.");
}

public boolean showVars() {
return showVars;
}

public Selector selector() {
Expand Down Expand Up @@ -199,8 +266,8 @@ void dumpResults(Selector selector, Model model, Options options, CliPrinter std
ObjectNode.Builder builder = Node.objectNodeBuilder()
.withMember("shape", Node.from(match.getShape().getId().toString()));

if (!match.isEmpty()) {
builder.withMember("vars", collectVars(match));
for (Options.Show showData : options.show) {
showData.inject(match, builder);
}

if (!options.showTraits.isEmpty()) {
Expand All @@ -223,21 +290,11 @@ void dumpResults(Selector selector, Model model, Options options, CliPrinter std
abstract void dumpResults(Selector selector, Model model, Options options, CliPrinter stdout);

static OutputFormat determineFormat(Options options) {
// If --show-vars isn't provided and --show-traits is empty, then use the SHAPE_ID_LINES output.
return !options.showVars() && options.showTraits.isEmpty() ? SHAPE_ID_LINES : JSON;
}

private static Stream<String> sortShapeIds(Collection<Shape> shapes) {
return shapes.stream().map(Shape::getId).map(ShapeId::toString).sorted();
return options.showTraits.isEmpty() && options.show.isEmpty() ? SHAPE_ID_LINES : JSON;
}
}

private static ObjectNode collectVars(Map<String, Set<Shape>> vars) {
ObjectNode.Builder varBuilder = Node.objectNodeBuilder();
for (Map.Entry<String, Set<Shape>> varEntry : vars.entrySet()) {
ArrayNode value = sortShapeIds(varEntry.getValue()).map(Node::from).collect(ArrayNode.collect());
varBuilder.withMember(varEntry.getKey(), value);
}
return varBuilder.build();
}
private static Stream<String> sortShapeIds(Collection<Shape> shapes) {
return shapes.stream().map(Shape::getId).map(ShapeId::toString).sorted();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public void printsSuccessfulMatchesToStdout() throws Exception {
public void printsJsonVarsToStdout() throws Exception {
String model = Paths.get(getClass().getResource("valid-model.smithy").toURI()).toString();
CliUtils.Result result = CliUtils.runSmithy("select", "--selector", "string $referenceMe(<)",
"--show-vars", model);
"--show", "vars", model);

assertThat(result.code(), equalTo(0));
validateSelectorOutput(result.stdout());
Expand All @@ -81,7 +81,7 @@ public void readsSelectorFromStdinToo() throws Exception {
try {
// Send the selector through input stream.
System.setIn(new ByteArrayInputStream("string $referenceMe(<)".getBytes()));
CliUtils.Result result = CliUtils.runSmithy("select", "--show-vars", model);
CliUtils.Result result = CliUtils.runSmithy("select", "--show", "vars", model);

assertThat(result.code(), equalTo(0));
validateSelectorOutput(result.stdout());
Expand Down