46: Support human-readable time units for PERSISTENT alarm grace periods

This commit is contained in:
2026-08-05 10:12:39 +02:00
parent 5afac819d8
commit f5c449fe23
6 changed files with 101 additions and 53 deletions
@@ -4,6 +4,8 @@ import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public final class AlarmConfigurationParser {
@@ -91,41 +93,24 @@ public final class AlarmConfigurationParser {
String normalized = triggerText.toUpperCase(Locale.ROOT);
if (normalized.equals("ONETIME")) {
return new TriggerConfiguration(AlarmTrigger.ONETIME, 0);
return new TriggerConfiguration(AlarmTrigger.ONETIME, 0L);
}
if (normalized.equals("CROSSING")) {
return new TriggerConfiguration(AlarmTrigger.CROSSING, 0);
return new TriggerConfiguration(AlarmTrigger.CROSSING, 0L);
}
if (normalized.equals("PERSISTENT")) {
return new TriggerConfiguration(AlarmTrigger.PERSISTENT, 0);
return new TriggerConfiguration(AlarmTrigger.PERSISTENT, 0L);
}
if (normalized.startsWith("PERSISTENT:")) {
String graceText = normalized.substring("PERSISTENT:".length());
String graceText = triggerText.substring("PERSISTENT:".length());
if (graceText.isEmpty()) {
throw new IllegalArgumentException("Missing persistent grace period: " + triggerText);
}
ΩsecondsΩ gracePeriodSeconds;
try {
gracePeriodSeconds = Integer.parseInt(graceText);
} catch (NumberFormatException exception) {
throw new IllegalArgumentException(
"Invalid persistent grace period: " + triggerText,
exception
);
}
if (gracePeriodSeconds < 0) {
throw new IllegalArgumentException(
"Persistent grace period cannot be negative: " + triggerText
);
}
return new TriggerConfiguration(AlarmTrigger.PERSISTENT, gracePeriodSeconds);
return new TriggerConfiguration(
AlarmTrigger.PERSISTENT,
parsePersistentGracePeriod(graceText)
);
}
if (normalized.startsWith("ONETIME:")) {
@@ -139,6 +124,51 @@ public final class AlarmConfigurationParser {
throw new IllegalArgumentException("Unknown trigger: " + triggerText);
}
private static ΩmilliSecondsΩ parsePersistentGracePeriod(String graceText) {
Matcher matcher = PERSISTENT_GRACE_PERIOD_PATTERN.matcher(graceText);
if (!matcher.matches()) {
throw invalidPersistentGracePeriod(graceText);
}
try {
long value = Long.parseLong(matcher.group(1));
long millisecondsPerUnit = switch (matcher.group(2)) {
case "ms" -> 1L;
case "s" -> 1_000L;
case "m" -> 60_000L;
case "h" -> 3_600_000L;
case "d" -> 86_400_000L;
case "w" -> 604_800_000L;
case "M" -> 2_592_000_000L;
case "y" -> 31_536_000_000L;
default -> throw new AssertionError("Unsupported grace period suffix");
};
return Math.multiplyExact(value, millisecondsPerUnit);
} catch (NumberFormatException | ArithmeticException exception) {
throw invalidPersistentGracePeriod(graceText, exception);
}
}
private static IllegalArgumentException invalidPersistentGracePeriod(
String graceText
) {
return invalidPersistentGracePeriod(graceText, null);
}
private static IllegalArgumentException invalidPersistentGracePeriod(
String graceText,
Exception cause
) {
return new IllegalArgumentException(
"Invalid persistent grace period '" + graceText
+ "'. Expected a non-negative whole number followed by one of: "
+ "ms, s, m, h, d, w, M, y",
cause
);
}
private static final class Cursor {
private Cursor(String line) {
this.line = line;
@@ -245,10 +275,13 @@ public final class AlarmConfigurationParser {
private record TriggerConfiguration(
AlarmTrigger trigger,
ΩsecondsΩ gracePeriod
ΩmilliSecondsΩ gracePeriod
) {
}
private static final Pattern PERSISTENT_GRACE_PERIOD_PATTERN =
Pattern.compile("(\\d+)(ms|s|m|h|d|w|M|y)");
private AlarmConfigurationParser() {
}
}
@@ -91,14 +91,14 @@ public final class PriceAlarm {
return true;
}
ΩsecondsΩ gracePeriod = definition.triggerGracePeriod();
ΩmilliSecondsΩ gracePeriod = definition.triggerGracePeriod();
if (gracePeriod == 0) {
return true;
}
return !Instant.now().isBefore(
lastTriggeredAt.plusSeconds(gracePeriod)
lastTriggeredAt.plusMillis(gracePeriod)
);
}
@@ -7,7 +7,7 @@ public record PriceAlarmDefinition(
JupiterPerpsAsset asset,
String conditionExpression,
AlarmTrigger trigger,
ΩsecondsΩ triggerGracePeriod
ΩmilliSecondsΩ triggerGracePeriod
) {
public PriceAlarmDefinition {
Objects.requireNonNull(asset, "asset");
@@ -5,6 +5,6 @@ public record ResolvedPriceAlarm(
JupiterPerpsAsset asset,
PriceCondition condition,
AlarmTrigger trigger,
ΩsecondsΩ triggerGracePeriod
ΩmilliSecondsΩ triggerGracePeriod
) {
}