Skip to content

Commit 4233b39

Browse files
authored
Add ini-path subcommand (#688)
1 parent d1d7fe4 commit 4233b39

File tree

7 files changed

+460
-111
lines changed

7 files changed

+460
-111
lines changed

README.md

Lines changed: 285 additions & 110 deletions
Large diffs are not rendered by default.

build.gradle

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,8 @@ dependencies {
9292
implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-xml'
9393
implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-toml'
9494
implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310'
95+
// http://www.ini4j.org/
96+
implementation 'org.ini4j:ini4j:0.5.4'
9597
// https://github.com/mbknor/mbknor-jackson-jsonSchema
9698
implementation 'com.kjetland:mbknor-jackson-jsonschema_2.13:1.0.39'
9799
implementation 'com.jayway.jsonpath:json-path:2.10.0'

src/main/java/me/itzg/helpers/McImageHelper.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import me.itzg.helpers.errors.ExceptionHandler;
2121
import me.itzg.helpers.errors.ExitCodeMapper;
2222
import me.itzg.helpers.fabric.InstallFabricLoaderCommand;
23+
import me.itzg.helpers.files.IniPathCommand;
2324
import me.itzg.helpers.files.TomlPathCommand;
2425
import me.itzg.helpers.files.YamlPathCommand;
2526
import me.itzg.helpers.find.FindCommand;
@@ -71,6 +72,7 @@
7172
GetCommand.class,
7273
GithubCommands.class,
7374
HashCommand.class,
75+
IniPathCommand.class,
7476
InstallCurseForgeCommand.class,
7577
InstallFabricLoaderCommand.class,
7678
InstallForgeCommand.class,
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
package me.itzg.helpers.files;
2+
3+
import com.jayway.jsonpath.Configuration;
4+
import com.jayway.jsonpath.DocumentContext;
5+
import com.jayway.jsonpath.JsonPath;
6+
import com.jayway.jsonpath.ParseContext;
7+
import com.jayway.jsonpath.spi.json.JacksonJsonProvider;
8+
import java.io.File;
9+
import java.util.concurrent.Callable;
10+
import java.util.regex.Matcher;
11+
import java.util.regex.Pattern;
12+
import me.itzg.helpers.errors.InvalidParameterException;
13+
import org.ini4j.Config;
14+
import org.ini4j.Ini;
15+
import org.ini4j.Profile.Section;
16+
import picocli.CommandLine.Command;
17+
import picocli.CommandLine.ExitCode;
18+
import picocli.CommandLine.Option;
19+
import picocli.CommandLine.Parameters;
20+
21+
@Command(name = "ini-path", description = "Extracts a field from an INI file")
22+
public class IniPathCommand implements Callable<Integer> {
23+
24+
private static final String EXPRESSION_SYNTAX_DESC = "section/option, section/option[index], /option, /option[index]";
25+
private final static Pattern expressions = Pattern.compile(
26+
"(?<section>.+?)?/(?<key>[^\\[]+?)(\\[(?<index>\\d+)])?"
27+
);
28+
29+
@Option(names = "--file", paramLabel = "FILE", description = "An INI file to query. If not set, reads stdin")
30+
File iniFile;
31+
32+
@Parameters(arity = "1",
33+
paramLabel = "ref",
34+
description = EXPRESSION_SYNTAX_DESC)
35+
String query;
36+
37+
38+
@Override
39+
public Integer call() throws Exception {
40+
final Matcher matcher = expressions.matcher(query);
41+
if (!matcher.matches()) {
42+
throw new InvalidParameterException("Query expression is invalid. Should be " + EXPRESSION_SYNTAX_DESC);
43+
}
44+
45+
final Ini ini = new Ini();
46+
final Config iniCfg = Config.getGlobal().clone();
47+
iniCfg.setGlobalSection(true);
48+
ini.setConfig(iniCfg);
49+
ini.load(iniFile);
50+
51+
final String sectionKey = matcher.group("section");
52+
final String fieldKey = matcher.group("key");
53+
final String indexStr = matcher.group("index");
54+
55+
final Section section = ini.get(sectionKey != null ? sectionKey : Config.DEFAULT_GLOBAL_SECTION_NAME);
56+
if (section == null) {
57+
throw new InvalidParameterException("Section not found: " + sectionKey);
58+
}
59+
60+
if (indexStr == null) {
61+
final String value = section.fetch(fieldKey);
62+
if (value == null) {
63+
throw new InvalidParameterException("Field not found: " + fieldKey + " in section: " + sectionKey);
64+
}
65+
66+
System.out.println(value);
67+
}
68+
else {
69+
final int index = Integer.parseInt(indexStr);
70+
if (index <= 0) {
71+
throw new InvalidParameterException("Index must be greater than 0.");
72+
}
73+
final String value = section.fetch(fieldKey, index);
74+
if (value == null) {
75+
throw new InvalidParameterException("Field not found: " + fieldKey + " with index: " + index + " in section: " + sectionKey);
76+
}
77+
78+
System.out.println(value);
79+
}
80+
81+
return ExitCode.OK;
82+
}
83+
84+
}

src/main/java/me/itzg/helpers/paper/PaperDownloadsClient.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ private Mono<ProjectResponse> getProjectVersions(String project) {
149149
);
150150
}
151151

152-
@RequiredArgsConstructor
152+
@Data
153153
private static class VersionAndBuildResponse {
154154
final VersionResponse versionResponse;
155155
final BuildResponse buildResponse;
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
package me.itzg.helpers.files;
2+
3+
import org.junit.jupiter.params.ParameterizedTest;
4+
import org.junit.jupiter.params.provider.Arguments;
5+
import org.junit.jupiter.params.provider.MethodSource;
6+
import org.junit.jupiter.params.provider.ValueSource;
7+
import picocli.CommandLine;
8+
import picocli.CommandLine.ExitCode;
9+
10+
import java.nio.file.Paths;
11+
import java.util.stream.Stream;
12+
13+
import static com.github.stefanbirkner.systemlambda.SystemLambda.*;
14+
import static org.assertj.core.api.Assertions.assertThat;
15+
import static org.junit.jupiter.params.provider.Arguments.arguments;
16+
17+
class IniPathCommandTest {
18+
19+
public static Stream<Arguments> extractsFromUnsupConfig_args() {
20+
return Stream.of(
21+
arguments("/version", "1"),
22+
arguments("/preset", "minecraft"),
23+
arguments("flavors/flavor", "standard")
24+
);
25+
}
26+
27+
@ParameterizedTest
28+
@MethodSource("extractsFromUnsupConfig_args")
29+
void extractsFromUnsupConfig(String query, String expectedValue) throws Exception {
30+
final String out = tapSystemOutNormalized(() -> {
31+
final int exitCode = new CommandLine(new IniPathCommand())
32+
.execute(
33+
"--file", Paths.get("src/test/resources/unsup.ini").toString(),
34+
query
35+
);
36+
37+
assertThat(exitCode).isEqualTo(ExitCode.OK);
38+
});
39+
40+
assertThat(out).isEqualTo(expectedValue + "\n");
41+
}
42+
43+
@ParameterizedTest
44+
@ValueSource(strings = {"/no1", "flavors/no3"})
45+
void errorOnMissingFields(String query) throws Exception {
46+
final String err = tapSystemErrNormalized(() -> {
47+
final int exitCode = new CommandLine(new IniPathCommand())
48+
.execute(
49+
"--file", Paths.get("src/test/resources/unsup.ini").toString(),
50+
query
51+
);
52+
53+
assertThat(exitCode).isNotEqualTo(ExitCode.OK);
54+
});
55+
56+
assertThat(err).contains("Field not found");
57+
}
58+
59+
@ParameterizedTest
60+
@ValueSource(strings = {"noglobal", "dangling/", "section/field["})
61+
void invalidQuerySyntax(String query) throws Exception {
62+
final String err = tapSystemErrNormalized(() -> {
63+
final int exitCode = new CommandLine(new IniPathCommand())
64+
.execute(
65+
"--file", Paths.get("src/test/resources/unsup.ini").toString(),
66+
query
67+
);
68+
69+
assertThat(exitCode).isNotEqualTo(ExitCode.OK);
70+
});
71+
72+
assertThat(err).contains("Query expression is invalid");
73+
}
74+
}

src/test/resources/unsup.ini

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
version=1
2+
preset=minecraft
3+
4+
source_format=packwiz
5+
source=https://rewindmc.com/packwiz/upsilon/pack.toml
6+
7+
force_env=server
8+
9+
[flavors]
10+
flavor=standard
11+
gregtech=gregtech_on
12+
dartcraft=dartcraft_off

0 commit comments

Comments
 (0)