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
2 changes: 2 additions & 0 deletions app.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
"eu.maveniverse.maven.mima.runtime:standalone-static": "2.4.15",
"eu.maveniverse.maven.mima:context": "2.4.15",
"info.picocli:picocli": "4.7.6",
"org.jline:jline-console-ui": "3.26.2",
"org.jline:jline-terminal-jni": "3.26.2",
"org.slf4j:slf4j-api": "2.0.13",
"org.slf4j:slf4j-log4j12": "2.0.13",
"org.slf4j:slf4j-simple": "2.0.13"
Expand Down
11 changes: 11 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
<version.mima>2.4.15</version.mima>
<version.picocli>4.7.6</version.picocli>
<version.gson>2.11.0</version.gson>
<version.jline>3.26.2</version.jline>
<version.slf4j>2.0.13</version.slf4j>
<version.spotless>2.43.0</version.spotless>
<version.google-java-format>1.22.0</version.google-java-format>
Expand All @@ -61,6 +62,16 @@
<artifactId>gson</artifactId>
<version>${version.gson}</version>
</dependency>
<dependency>
<groupId>org.jline</groupId>
<artifactId>jline-console-ui</artifactId>
<version>${version.jline}</version>
</dependency>
<dependency>
<groupId>org.jline</groupId>
<artifactId>jline-terminal-jni</artifactId>
<version>${version.jline}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
Expand Down
145 changes: 136 additions & 9 deletions src/main/java/org/codejive/jpm/Main.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
//DEPS eu.maveniverse.maven.mima:context:2.4.15 eu.maveniverse.maven.mima.runtime:standalone-static:2.4.15
//DEPS info.picocli:picocli:4.7.6
//DEPS com.google.code.gson:gson:2.11.0
//DEPS org.jline:jline-console-ui:3.26.2 org.jline:jline-terminal-jni:3.26.2
//DEPS org.slf4j:slf4j-api:2.0.13 org.slf4j:slf4j-simple:2.0.13
//SOURCES Jpm.java json/AppInfo.java util/FileUtils.java util/ResolverUtils.java util/SearchUtils.java
//SOURCES util/SearchResult.java util/SyncStats.java util/Version.java
Expand All @@ -16,6 +17,13 @@
import java.util.stream.Collectors;
import org.codejive.jpm.util.SyncStats;
import org.codejive.jpm.util.Version;
import org.jline.consoleui.prompt.ConsolePrompt;
import org.jline.consoleui.prompt.ListResult;
import org.jline.consoleui.prompt.PromptResultItemIF;
import org.jline.consoleui.prompt.builder.ListPromptBuilder;
import org.jline.consoleui.prompt.builder.PromptBuilder;
import org.jline.terminal.Terminal;
import org.jline.terminal.TerminalBuilder;
import picocli.CommandLine;
import picocli.CommandLine.Command;
import picocli.CommandLine.Mixin;
Expand Down Expand Up @@ -74,6 +82,12 @@ static class Sync implements Callable<Integer> {
@Mixin QuietMixin quietMixin;
@Mixin CopyMixin copyMixin;

@Option(
names = {"-i", "--interactive"},
description = "Interactively search and select artifacts to install",
defaultValue = "false")
private boolean interactive;

@Option(
names = {"-m", "--max"},
description = "Maximum number of results to return",
Expand All @@ -82,22 +96,135 @@ static class Sync implements Callable<Integer> {

@Parameters(
paramLabel = "artifactPattern",
description = "Partial or full artifact name to search for.")
description = "Partial or full artifact name to search for.",
defaultValue = "")
private String artifactPattern;

@Override
public Integer call() throws Exception {
String[] artifactNames =
Jpm.builder()
.directory(copyMixin.directory)
.noLinks(copyMixin.noLinks)
.build()
.search(artifactPattern, Math.min(max, 200));
if (artifactNames.length > 0) {
Arrays.stream(artifactNames).forEach(System.out::println);
if (interactive || artifactPattern == null || artifactPattern.isEmpty()) {
try (Terminal terminal = TerminalBuilder.builder().build()) {
while (true) {
ConsolePrompt prompt = new ConsolePrompt(terminal);
if (artifactPattern == null || artifactPattern.isEmpty()) {
artifactPattern = askString(prompt, "Search for:");
}
String[] artifactNames = search(artifactPattern);
PromptBuilder promptBuilder = prompt.getPromptBuilder();
addSelectItem(promptBuilder, "Select artifact:", artifactNames);
addSelectArtifactAction(promptBuilder);
Map<String, PromptResultItemIF> result =
prompt.prompt(promptBuilder.build());
String selectedArtifact = getSelectedId(result, "item");
String artifactAction = getSelectedId(result, "action");
if ("install".equals(artifactAction)) {
SyncStats stats =
Jpm.builder()
.directory(copyMixin.directory)
.noLinks(copyMixin.noLinks)
.build()
.install(new String[] {selectedArtifact});
if (!quietMixin.quiet) {
printStats(stats);
}
} else if ("copy".equals(artifactAction)) {
SyncStats stats =
Jpm.builder()
.directory(copyMixin.directory)
.noLinks(copyMixin.noLinks)
.build()
.copy(new String[] {selectedArtifact}, false);
if (!quietMixin.quiet) {
printStats(stats);
}
} else if ("version".equals(artifactAction)) {
artifactPattern = selectedArtifact;
continue;
} else { // quit
break;
}
String finalAction = selectFinalAction(prompt);
if ("quit".equals(finalAction)) {
break;
}
artifactPattern = null;
}
}
} else {
String[] artifactNames = search(artifactPattern);
if (artifactNames.length > 0) {
Arrays.stream(artifactNames).forEach(System.out::println);
}
}
return 0;
}

String[] search(String artifactPattern) throws IOException {
return Jpm.builder()
.directory(copyMixin.directory)
.noLinks(copyMixin.noLinks)
.build()
.search(artifactPattern, Math.min(max, 200));
}

String askString(ConsolePrompt prompt, String message) throws IOException {
PromptBuilder promptBuilder = prompt.getPromptBuilder();
promptBuilder.createInputPrompt().name("input").message(message).addPrompt();
Map<String, PromptResultItemIF> result = prompt.prompt(promptBuilder.build());
return result.get("input").getResult();
}

void addSelectItem(PromptBuilder promptBuilder, String message, String[] items)
throws IOException {
ListPromptBuilder artifactsList =
promptBuilder.createListPrompt().name("item").message(message).pageSize(10);
for (String artifactName : items) {
artifactsList.newItem(artifactName).text(artifactName).add();
}
artifactsList.addPrompt();
}

void addSelectArtifactAction(PromptBuilder promptBuilder) throws IOException {
promptBuilder
.createListPrompt()
.name("action")
.message("What to do:")
.newItem("install")
.text("Install artifact")
.add()
.newItem("copy")
.text("Copy artifact")
.add()
.newItem("version")
.text("Select different version")
.add()
.newItem("quit")
.text("Quit")
.add()
.addPrompt();
}

String selectFinalAction(ConsolePrompt prompt) throws IOException {
PromptBuilder promptBuilder = prompt.getPromptBuilder();
promptBuilder
.createListPrompt()
.name("action")
.message("What to do:")
.newItem("again")
.text("Search again")
.add()
.newItem("quit")
.text("Quit")
.add()
.addPrompt();
Map<String, PromptResultItemIF> result = prompt.prompt(promptBuilder.build());
return getSelectedId(result, "action");
}

private static String getSelectedId(
Map<String, PromptResultItemIF> result, String itemName) {
return ((ListResult) result.get(itemName)).getSelectedId();
}
}

@Command(
Expand Down
28 changes: 15 additions & 13 deletions src/main/java/org/codejive/jpm/util/SearchUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ private static SearchResult select(String query, int start, int count) throws IO
}
String searchUrl =
String.format(
"https://search.maven.org/solrsearch/select?start=%d&rows=%d&q=%s",
"https://search.maven.org/solrsearch/select?start=%d&rows=%d&q=p:jar+AND+%s",
start, count, URLEncoder.encode(finalQuery, "UTF-8"));
if (parts.length >= 3) {
searchUrl += "&core=gav";
Expand All @@ -90,23 +90,23 @@ private static SearchResult select(String query, int start, int count) throws IO
}
List<DefaultArtifact> artifacts =
result.response.docs.stream()
.filter(
d ->
parts.length != 2
|| d.g.contains(parts[0])
&& d.a.contains(parts[1]))
.map(
d ->
new DefaultArtifact(
d.g,
d.a,
"",
d.v != null ? d.v : d.latestVersion))
.filter(d -> acceptDoc(d, parts))
.map(SearchUtils::toArtifact)
.collect(Collectors.toList());
return new SearchResult(artifacts, query, start, count, result.response.numFound);
}
}
}

private static boolean acceptDoc(MsrDoc d, String[] parts) {
return d.ec != null
&& d.ec.contains(".jar")
&& (parts.length != 2 || d.g.contains(parts[0]) && d.a.contains(parts[1]));
}

private static DefaultArtifact toArtifact(MsrDoc d) {
return new DefaultArtifact(d.g, d.a, "", d.v != null ? d.v : d.latestVersion);
}
}

class MvnSearchResult {
Expand All @@ -129,4 +129,6 @@ class MsrDoc {
public String a;
public String v;
public String latestVersion;
public String p;
public List<String> ec;
}