47: Add explicit format versions to configuration files
This commit is contained in:
@@ -3,17 +3,19 @@ package com.fanitas.evelyn.core.impl.ref;
|
||||
import com.fanitas.evelyn.core.State;
|
||||
import com.fanitas.evelyn.raydium.RaydiumLiquidityPoolPositionAccounting;
|
||||
import com.fanitas.evelyn.raydium.RaydiumLiquidityPoolPositionConcentrated;
|
||||
import com.r35157.libs.configuration.ConfigurationFormatVersionValidator;
|
||||
import com.r35157.libs.raydium.*;
|
||||
import com.r35157.libs.valuetypes.basic.WellKnownCurrencyTypes;
|
||||
import com.r35157.libs.valuetypes.basic.*;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
@@ -72,14 +74,26 @@ public class StateImpl implements State {
|
||||
}
|
||||
|
||||
private void updateStateFromPersistence() throws IOException {
|
||||
try (BufferedReader reader = Files.newBufferedReader(PATH_EVELYN_STATE, StandardCharsets.UTF_8)) {
|
||||
iterationInterval = readIterationInterval(reader);
|
||||
syrupOwnedByEvelyn = readSyrupOwnedByEvelyn(reader);
|
||||
solanaAddressForEvelynIOU = readSolanaAddress(reader);
|
||||
solanaAddressForEvelynIOUBurner = readSolanaAddress(reader);
|
||||
raydiumPoolId = readRaydiumPoolId(reader);
|
||||
curveWidth = readCurveWidth(reader);
|
||||
}
|
||||
List<String> lines = Files.readAllLines(
|
||||
PATH_EVELYN_STATE,
|
||||
StandardCharsets.UTF_8
|
||||
);
|
||||
int formatVersionLineIndex = ConfigurationFormatVersionValidator.validate(
|
||||
PATH_EVELYN_STATE,
|
||||
lines,
|
||||
SUPPORTED_EVELYN_FORMAT_VERSION
|
||||
);
|
||||
Iterator<String> lineIterator = lines.subList(
|
||||
formatVersionLineIndex + 1,
|
||||
lines.size()
|
||||
).iterator();
|
||||
|
||||
iterationInterval = readIterationInterval(lineIterator);
|
||||
syrupOwnedByEvelyn = readSyrupOwnedByEvelyn(lineIterator);
|
||||
solanaAddressForEvelynIOU = readSolanaAddress(lineIterator);
|
||||
solanaAddressForEvelynIOUBurner = readSolanaAddress(lineIterator);
|
||||
raydiumPoolId = readRaydiumPoolId(lineIterator);
|
||||
curveWidth = readCurveWidth(lineIterator);
|
||||
}
|
||||
|
||||
private void updateStateFromRaydium() throws IOException, InterruptedException {
|
||||
@@ -176,33 +190,37 @@ public class StateImpl implements State {
|
||||
private Map<ΩRaydiumLiquidityPoolPositionNftIdΩ, RaydiumLiquidityPoolPositionAccounting> readPositionAccounting()
|
||||
throws IOException {
|
||||
Map<ΩRaydiumLiquidityPoolPositionNftIdΩ, RaydiumLiquidityPoolPositionAccounting> result = new HashMap<>();
|
||||
List<String> lines = Files.readAllLines(PATH_POSITIONS, StandardCharsets.UTF_8);
|
||||
int formatVersionLineIndex = ConfigurationFormatVersionValidator.validate(
|
||||
PATH_POSITIONS,
|
||||
lines,
|
||||
SUPPORTED_POSITIONS_FORMAT_VERSION
|
||||
);
|
||||
|
||||
try (BufferedReader reader = Files.newBufferedReader(PATH_POSITIONS, StandardCharsets.UTF_8)) {
|
||||
String line;
|
||||
for (int lineIndex = formatVersionLineIndex + 1;
|
||||
lineIndex < lines.size();
|
||||
lineIndex++) {
|
||||
String line = lines.get(lineIndex).trim();
|
||||
|
||||
while ((line = reader.readLine()) != null) {
|
||||
line = line.trim();
|
||||
if (line.isEmpty() || line.startsWith("#")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (line.isEmpty() || line.startsWith("#")) {
|
||||
continue;
|
||||
}
|
||||
// Remove comments at end of line
|
||||
int index = line.indexOf('#');
|
||||
if(index > -1) {
|
||||
line = line.substring(0, index).trim();
|
||||
}
|
||||
|
||||
// Remove comments at end of line
|
||||
int index = line.indexOf('#');
|
||||
if(index > -1) {
|
||||
line = line.substring(0, index).trim();
|
||||
}
|
||||
RaydiumLiquidityPoolPositionAccounting accounting = mapPositionAccounting(line);
|
||||
|
||||
RaydiumLiquidityPoolPositionAccounting accounting = mapPositionAccounting(line);
|
||||
RaydiumLiquidityPoolPositionAccounting previous = result.put(
|
||||
accounting.nftId(),
|
||||
accounting
|
||||
);
|
||||
|
||||
RaydiumLiquidityPoolPositionAccounting previous = result.put(
|
||||
accounting.nftId(),
|
||||
accounting
|
||||
);
|
||||
|
||||
if (previous != null) {
|
||||
throw new IOException("Duplicate position accounting for NFT id: " + accounting.nftId());
|
||||
}
|
||||
if (previous != null) {
|
||||
throw new IOException("Duplicate position accounting for NFT id: " + accounting.nftId());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -234,47 +252,46 @@ public class StateImpl implements State {
|
||||
);
|
||||
}
|
||||
|
||||
private ΩSolanaAddressΩ readSolanaAddress(BufferedReader reader) throws IOException {
|
||||
ΩSolanaAddressΩ sa = readLineFromFile(reader);
|
||||
private ΩSolanaAddressΩ readSolanaAddress(Iterator<String> lineIterator) throws IOException {
|
||||
ΩSolanaAddressΩ sa = readLineFromFile(lineIterator);
|
||||
return sa;
|
||||
}
|
||||
|
||||
private ΩRaydiumLiquidityPoolIdΩ readRaydiumPoolId(BufferedReader reader) throws IOException {
|
||||
ΩRaydiumLiquidityPoolIdΩ rlpid = readLineFromFile(reader);
|
||||
private ΩRaydiumLiquidityPoolIdΩ readRaydiumPoolId(Iterator<String> lineIterator) throws IOException {
|
||||
ΩRaydiumLiquidityPoolIdΩ rlpid = readLineFromFile(lineIterator);
|
||||
return rlpid;
|
||||
}
|
||||
|
||||
private ΩCurveWidthΩ readCurveWidth(BufferedReader reader) throws IOException {
|
||||
String cwStr = readLineFromFile(reader);
|
||||
private ΩCurveWidthΩ readCurveWidth(Iterator<String> lineIterator) throws IOException {
|
||||
String cwStr = readLineFromFile(lineIterator);
|
||||
ΩCurveWidthΩ cw = new ΩCurveWidthΩ(cwStr);
|
||||
return cw;
|
||||
}
|
||||
|
||||
private ΩmilliSecondsΩ readIterationInterval(BufferedReader reader) throws IOException {
|
||||
String iterationIntervalStr = readLineFromFile(reader);
|
||||
private ΩmilliSecondsΩ readIterationInterval(Iterator<String> lineIterator) throws IOException {
|
||||
String iterationIntervalStr = readLineFromFile(lineIterator);
|
||||
ΩmilliSecondsΩ millis = Long.parseLong(iterationIntervalStr) * 1000;
|
||||
return millis;
|
||||
}
|
||||
|
||||
private ΩSyrupAmountΩ readSyrupOwnedByEvelyn(BufferedReader reader) throws IOException {
|
||||
String syrupOwnedByEvelynStr = readLineFromFile(reader);
|
||||
private ΩSyrupAmountΩ readSyrupOwnedByEvelyn(Iterator<String> lineIterator) throws IOException {
|
||||
String syrupOwnedByEvelynStr = readLineFromFile(lineIterator);
|
||||
CurrencyType ct = WellKnownCurrencyTypes.SYRUPUSDC.getCurrencyType();
|
||||
ΩSyrupAmountΩ ma = stringToMoneyAmount(syrupOwnedByEvelynStr, ct);
|
||||
return ma;
|
||||
}
|
||||
|
||||
private static String readLineFromFile(BufferedReader reader) throws IOException {
|
||||
String line;
|
||||
|
||||
while ((line = reader.readLine()) != null) {
|
||||
private static String readLineFromFile(Iterator<String> lineIterator) {
|
||||
while (lineIterator.hasNext()) {
|
||||
String line = lineIterator.next();
|
||||
line = line.trim();
|
||||
|
||||
if (line.isEmpty() || line.startsWith("#")) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
return line;
|
||||
}
|
||||
return line;
|
||||
return null;
|
||||
}
|
||||
|
||||
private MoneyAmount bigDecimalToMoneyAmount(BigDecimal bd, CurrencyType ct) {
|
||||
@@ -290,6 +307,8 @@ public class StateImpl implements State {
|
||||
|
||||
private static final Path PATH_EVELYN_STATE = Path.of("conf/evelyn.conf");
|
||||
private static final Path PATH_POSITIONS = Path.of("conf/positions.conf");
|
||||
private static final int SUPPORTED_EVELYN_FORMAT_VERSION = 1;
|
||||
private static final int SUPPORTED_POSITIONS_FORMAT_VERSION = 1;
|
||||
|
||||
private final Raydium raydium;
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.r35157.jupiterperpsalarm.impl.ref;
|
||||
|
||||
import com.r35157.libs.configuration.ConfigurationFormatVersionValidator;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
@@ -11,10 +13,19 @@ public final class AlarmConfigurationParser {
|
||||
|
||||
public static AlarmConfiguration parse(Path path) throws IOException {
|
||||
List<String> lines = Files.readAllLines(path);
|
||||
int formatVersionLineIndex = ConfigurationFormatVersionValidator.validate(
|
||||
path,
|
||||
lines,
|
||||
SUPPORTED_FORMAT_VERSION
|
||||
);
|
||||
List<PriceAlarmDefinition> alarms = new ArrayList<>();
|
||||
Map<String, String> constants = new LinkedHashMap<>();
|
||||
|
||||
for (int lineNumber = 1; lineNumber <= lines.size(); lineNumber++) {
|
||||
if (lineNumber - 1 == formatVersionLineIndex) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String line = lines.get(lineNumber - 1);
|
||||
String trimmed = line.trim();
|
||||
|
||||
@@ -313,6 +324,7 @@ public final class AlarmConfigurationParser {
|
||||
|
||||
private static final Pattern PERSISTENT_GRACE_PERIOD_PATTERN =
|
||||
Pattern.compile("(\\d+)(ms|s|m|h|d|w|M|y)");
|
||||
private static final int SUPPORTED_FORMAT_VERSION = 1;
|
||||
|
||||
private AlarmConfigurationParser() {
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.r35157.jupiterperpsalarm.impl.ref;
|
||||
|
||||
import com.r35157.libs.configuration.ConfigurationFormatVersionValidator;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
@@ -49,9 +51,18 @@ public final class ConsoleAlarmAction implements ConfiguredAlarmAction {
|
||||
|
||||
private static Set<Integer> parseAlarmIds(Path path) throws IOException {
|
||||
List<String> lines = Files.readAllLines(path);
|
||||
int formatVersionLineIndex = ConfigurationFormatVersionValidator.validate(
|
||||
path,
|
||||
lines,
|
||||
SUPPORTED_FORMAT_VERSION
|
||||
);
|
||||
Set<Integer> result = new LinkedHashSet<>();
|
||||
|
||||
for (int lineNumber = 1; lineNumber <= lines.size(); lineNumber++) {
|
||||
if (lineNumber - 1 == formatVersionLineIndex) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String line = removeComment(lines.get(lineNumber - 1)).trim();
|
||||
if (line.isEmpty()) {
|
||||
continue;
|
||||
@@ -86,5 +97,7 @@ public final class ConsoleAlarmAction implements ConfiguredAlarmAction {
|
||||
return commentStart < 0 ? line : line.substring(0, commentStart);
|
||||
}
|
||||
|
||||
private static final int SUPPORTED_FORMAT_VERSION = 1;
|
||||
|
||||
private final Set<Integer> alarmIds;
|
||||
}
|
||||
|
||||
+14
-1
@@ -1,5 +1,7 @@
|
||||
package com.r35157.jupiterperpsalarm.impl.ref;
|
||||
|
||||
import com.r35157.libs.configuration.ConfigurationFormatVersionValidator;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.nio.file.Files;
|
||||
@@ -17,6 +19,11 @@ public final class JupiterPerpsPositionDecreaseAlarmActionConfigurationParser {
|
||||
) throws IOException {
|
||||
Objects.requireNonNull(variableResolver, "variableResolver");
|
||||
List<String> lines = Files.readAllLines(path);
|
||||
int formatVersionLineIndex = ConfigurationFormatVersionValidator.validate(
|
||||
path,
|
||||
lines,
|
||||
SUPPORTED_FORMAT_VERSION
|
||||
);
|
||||
Map<Integer, JupiterPerpsPositionDecreaseAlarmActionConfiguration.PositionDecrease>
|
||||
positionDecreases = new LinkedHashMap<>();
|
||||
ΩSolanaWalletIdΩ walletId = null;
|
||||
@@ -24,6 +31,10 @@ public final class JupiterPerpsPositionDecreaseAlarmActionConfigurationParser {
|
||||
ΩUSDCAmountΩ reservePositionSizeUsdLimit = null;
|
||||
|
||||
for (int lineNumber = 1; lineNumber <= lines.size(); lineNumber++) {
|
||||
if (lineNumber - 1 == formatVersionLineIndex) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String line = removeComment(lines.get(lineNumber - 1)).trim();
|
||||
if (line.isEmpty()) {
|
||||
continue;
|
||||
@@ -162,6 +173,8 @@ public final class JupiterPerpsPositionDecreaseAlarmActionConfigurationParser {
|
||||
: line.substring(0, commentStart);
|
||||
}
|
||||
|
||||
private static final int SUPPORTED_FORMAT_VERSION = 1;
|
||||
|
||||
private JupiterPerpsPositionDecreaseAlarmActionConfigurationParser() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+13
@@ -1,5 +1,7 @@
|
||||
package com.r35157.jupiterperpsalarm.impl.ref;
|
||||
|
||||
import com.r35157.libs.configuration.ConfigurationFormatVersionValidator;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.nio.file.Files;
|
||||
@@ -17,6 +19,11 @@ public final class JupiterPerpsPositionIncreaseAlarmActionConfigurationParser {
|
||||
) throws IOException {
|
||||
Objects.requireNonNull(variableResolver, "variableResolver");
|
||||
List<String> lines = Files.readAllLines(path);
|
||||
int formatVersionLineIndex = ConfigurationFormatVersionValidator.validate(
|
||||
path,
|
||||
lines,
|
||||
SUPPORTED_FORMAT_VERSION
|
||||
);
|
||||
Map<Integer, JupiterPerpsPositionIncreaseAlarmActionConfiguration.PositionIncrease>
|
||||
positionIncreases = new LinkedHashMap<>();
|
||||
ΩSolanaWalletIdΩ walletId = null;
|
||||
@@ -24,6 +31,10 @@ public final class JupiterPerpsPositionIncreaseAlarmActionConfigurationParser {
|
||||
ΩUSDCAmountΩ reserveUsdcLimit = null;
|
||||
|
||||
for (int lineNumber = 1; lineNumber <= lines.size(); lineNumber++) {
|
||||
if (lineNumber - 1 == formatVersionLineIndex) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String line = removeComment(lines.get(lineNumber - 1)).trim();
|
||||
if (line.isEmpty()) {
|
||||
continue;
|
||||
@@ -139,6 +150,8 @@ public final class JupiterPerpsPositionIncreaseAlarmActionConfigurationParser {
|
||||
return commentStart < 0 ? line : line.substring(0, commentStart);
|
||||
}
|
||||
|
||||
private static final int SUPPORTED_FORMAT_VERSION = 1;
|
||||
|
||||
private JupiterPerpsPositionIncreaseAlarmActionConfigurationParser() {
|
||||
}
|
||||
}
|
||||
|
||||
+12
@@ -1,6 +1,7 @@
|
||||
package com.r35157.jupiterperpsalarm.impl.ref;
|
||||
|
||||
import com.r35157.jupiterperpsalarm.AlarmSeverity;
|
||||
import com.r35157.libs.configuration.ConfigurationFormatVersionValidator;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
@@ -18,12 +19,21 @@ public final class PushoverAlarmActionConfigurationParser {
|
||||
) throws IOException {
|
||||
Objects.requireNonNull(variableResolver, "variableResolver");
|
||||
List<String> lines = Files.readAllLines(path);
|
||||
int formatVersionLineIndex = ConfigurationFormatVersionValidator.validate(
|
||||
path,
|
||||
lines,
|
||||
SUPPORTED_FORMAT_VERSION
|
||||
);
|
||||
Map<Integer, PushoverAlarmActionConfiguration.Notification> notifications =
|
||||
new LinkedHashMap<>();
|
||||
String applicationToken = null;
|
||||
String userKey = null;
|
||||
|
||||
for (int lineNumber = 1; lineNumber <= lines.size(); lineNumber++) {
|
||||
if (lineNumber - 1 == formatVersionLineIndex) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String line = lines.get(lineNumber - 1);
|
||||
String trimmed = line.trim();
|
||||
|
||||
@@ -205,6 +215,8 @@ public final class PushoverAlarmActionConfigurationParser {
|
||||
private int position;
|
||||
}
|
||||
|
||||
private static final int SUPPORTED_FORMAT_VERSION = 1;
|
||||
|
||||
private PushoverAlarmActionConfigurationParser() {
|
||||
}
|
||||
}
|
||||
|
||||
+152
@@ -0,0 +1,152 @@
|
||||
package com.r35157.libs.configuration;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public final class ConfigurationFormatVersionValidator {
|
||||
|
||||
public static int validate(
|
||||
Path path,
|
||||
List<String> lines,
|
||||
int supportedVersion
|
||||
) {
|
||||
Objects.requireNonNull(path, "path");
|
||||
Objects.requireNonNull(lines, "lines");
|
||||
|
||||
if (supportedVersion <= 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"Supported configuration format version must be a positive integer"
|
||||
);
|
||||
}
|
||||
|
||||
int firstConfigurationEntryIndex = -1;
|
||||
List<FormatVersionDeclaration> declarations = new ArrayList<>();
|
||||
|
||||
for (int lineIndex = 0; lineIndex < lines.size(); lineIndex++) {
|
||||
String trimmedLine = lines.get(lineIndex).trim();
|
||||
|
||||
if (trimmedLine.isEmpty() || trimmedLine.startsWith("#")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (firstConfigurationEntryIndex < 0) {
|
||||
firstConfigurationEntryIndex = lineIndex;
|
||||
}
|
||||
|
||||
if (trimmedLine.startsWith(FORMAT_VERSION_KEY)) {
|
||||
declarations.add(parseDeclaration(path, trimmedLine, lineIndex));
|
||||
}
|
||||
}
|
||||
|
||||
if (declarations.isEmpty()) {
|
||||
throw new IllegalArgumentException(
|
||||
path + ": Missing " + FORMAT_VERSION_KEY + " declaration. "
|
||||
+ "It must be the first actual configuration entry."
|
||||
);
|
||||
}
|
||||
|
||||
if (declarations.size() > 1) {
|
||||
String lineNumbers = declarations.stream()
|
||||
.map(declaration -> Integer.toString(declaration.lineIndex() + 1))
|
||||
.reduce((left, right) -> left + ", " + right)
|
||||
.orElseThrow();
|
||||
|
||||
throw new IllegalArgumentException(
|
||||
path + ": Duplicate " + FORMAT_VERSION_KEY
|
||||
+ " declarations on lines " + lineNumbers
|
||||
);
|
||||
}
|
||||
|
||||
FormatVersionDeclaration declaration = declarations.get(0);
|
||||
|
||||
if (declaration.lineIndex() != firstConfigurationEntryIndex) {
|
||||
throw new IllegalArgumentException(
|
||||
path + ":" + (declaration.lineIndex() + 1) + ": "
|
||||
+ FORMAT_VERSION_KEY
|
||||
+ " must be the first actual configuration entry"
|
||||
);
|
||||
}
|
||||
|
||||
if (declaration.version() != supportedVersion) {
|
||||
throw versionMismatch(
|
||||
path,
|
||||
declaration.version(),
|
||||
supportedVersion
|
||||
);
|
||||
}
|
||||
|
||||
return declaration.lineIndex();
|
||||
}
|
||||
|
||||
private static FormatVersionDeclaration parseDeclaration(
|
||||
Path path,
|
||||
String trimmedLine,
|
||||
int lineIndex
|
||||
) {
|
||||
Matcher matcher = FORMAT_VERSION_PATTERN.matcher(trimmedLine);
|
||||
|
||||
if (!matcher.matches()) {
|
||||
throw malformedVersion(path, lineIndex, trimmedLine, null);
|
||||
}
|
||||
|
||||
try {
|
||||
int version = Integer.parseInt(matcher.group(1));
|
||||
|
||||
if (version <= 0) {
|
||||
throw malformedVersion(path, lineIndex, trimmedLine, null);
|
||||
}
|
||||
|
||||
return new FormatVersionDeclaration(lineIndex, version);
|
||||
} catch (NumberFormatException exception) {
|
||||
throw malformedVersion(path, lineIndex, trimmedLine, exception);
|
||||
}
|
||||
}
|
||||
|
||||
private static IllegalArgumentException malformedVersion(
|
||||
Path path,
|
||||
int lineIndex,
|
||||
String line,
|
||||
Exception cause
|
||||
) {
|
||||
return new IllegalArgumentException(
|
||||
path + ":" + (lineIndex + 1) + ": Invalid "
|
||||
+ FORMAT_VERSION_KEY + " declaration '" + line
|
||||
+ "'. Expected " + FORMAT_VERSION_KEY
|
||||
+ "=<positive integer>",
|
||||
cause
|
||||
);
|
||||
}
|
||||
|
||||
private static IllegalArgumentException versionMismatch(
|
||||
Path path,
|
||||
int fileVersion,
|
||||
int supportedVersion
|
||||
) {
|
||||
return new IllegalArgumentException(
|
||||
"Configuration format version mismatch in " + path + ":\n"
|
||||
+ "File version: " + fileVersion + "\n"
|
||||
+ "Supported version: " + supportedVersion + "\n\n"
|
||||
+ "The configuration file and the running application "
|
||||
+ "are not compatible.\n"
|
||||
+ "Update the configuration file or compile and deploy "
|
||||
+ "the correct application version."
|
||||
);
|
||||
}
|
||||
|
||||
private record FormatVersionDeclaration(
|
||||
int lineIndex,
|
||||
int version
|
||||
) {
|
||||
}
|
||||
|
||||
private static final String FORMAT_VERSION_KEY = "FORMAT_VERSION";
|
||||
private static final Pattern FORMAT_VERSION_PATTERN =
|
||||
Pattern.compile(FORMAT_VERSION_KEY + "=(\\d+)");
|
||||
|
||||
private ConfigurationFormatVersionValidator() {
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user