Skip to content

Commit 894a3c9

Browse files
committed
Update config
1 parent 45106fd commit 894a3c9

6 files changed

Lines changed: 156 additions & 79 deletions

File tree

‎config/src/main/java/dev/faststats/config/SimpleConfig.java‎

Lines changed: 71 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -4,41 +4,48 @@
44
import dev.faststats.internal.LoggerFactory;
55
import org.jetbrains.annotations.ApiStatus;
66
import org.jetbrains.annotations.Contract;
7+
import org.jspecify.annotations.Nullable;
78

89
import java.io.IOException;
910
import java.io.OutputStreamWriter;
1011
import java.nio.file.Files;
1112
import java.nio.file.Path;
12-
import java.util.Optional;
1313
import java.util.Properties;
1414
import java.util.UUID;
1515
import java.util.concurrent.atomic.AtomicBoolean;
16-
import java.util.function.BiPredicate;
16+
import java.util.function.Function;
17+
import java.util.function.Supplier;
1718
import java.util.logging.Level;
1819

1920
import static java.nio.charset.StandardCharsets.UTF_8;
2021

2122
@ApiStatus.Internal
2223
public record SimpleConfig(
2324
UUID serverId,
25+
boolean enabled,
2426
boolean additionalMetrics,
2527
boolean debug,
26-
boolean enabled,
28+
boolean submitMetrics,
2729
boolean errorTracking,
2830
boolean firstRun
2931
) implements Config {
32+
private static final int CONFIG_VERSION = 1;
3033

31-
public static final String COMMENT = """
34+
private static final String COMMENT = """
3235
FastStats (https://faststats.dev) collects anonymous usage statistics.
3336
# This helps developers understand how their projects are used in the real world.
3437
#
3538
# No IP addresses, player data, or personal information is collected.
3639
# The server ID below is randomly generated and can be regenerated at any time.
3740
#
3841
# Enabling metrics has no noticeable performance impact.
39-
# Keeping metrics enabled is recommended, but you can opt out by setting 'enabled=false'.
42+
# Keeping FastStats enabled is recommended.
43+
# To disable all FastStats features, set 'enabled=false'.
44+
# To disable only metrics submission, set 'submitMetrics=false'.
45+
# To disable only additional metrics, set 'submitAdditionalMetrics=false'.
46+
# To disable only error tracking, set 'submitErrors=false'.
4047
#
41-
# If you suspect a developer is collecting personal data or bypassing the "enabled" option,
48+
# If you suspect a developer is collecting personal data or bypassing any opt-out option,
4249
# please report it at: https://faststats.dev/abuse
4350
#
4451
# For more information, visit: https://faststats.dev/info
@@ -55,63 +62,87 @@ public record SimpleConfig(
5562
@Contract(mutates = "io")
5663
public static SimpleConfig read(final Path file) throws RuntimeException {
5764
final var properties = readOrEmpty(file);
58-
final var firstRun = properties.isEmpty();
65+
final var firstRun = properties != null;
5966
final var saveConfig = new AtomicBoolean(firstRun);
6067

61-
final var serverId = properties.map(object -> object.getProperty("serverId")).map(string -> {
62-
try {
63-
final var trimmed = string.trim();
64-
final var corrected = trimmed.length() > 36 ? trimmed.substring(0, 36) : trimmed;
65-
if (!corrected.equals(string)) saveConfig.set(true);
66-
return UUID.fromString(corrected);
67-
} catch (final IllegalArgumentException e) {
68-
saveConfig.set(true);
69-
return UUID.randomUUID();
70-
}
71-
}).orElseGet(() -> {
72-
saveConfig.set(true);
73-
return UUID.randomUUID();
68+
final var serverId = parse(properties, saveConfig, "serverId", UUID::randomUUID, value -> {
69+
final var corrected = value.length() > 36 ? value.substring(0, 36) : value;
70+
final var uuid = UUID.fromString(corrected);
71+
if (!value.equals(uuid.toString())) saveConfig.set(true);
72+
return uuid;
7473
});
75-
76-
final BiPredicate<String, Boolean> predicate = (key, defaultValue) -> {
77-
return properties.map(object -> object.getProperty(key)).map(Boolean::parseBoolean).orElseGet(() -> {
78-
saveConfig.set(true);
79-
return defaultValue;
80-
});
81-
};
82-
83-
final var enabled = predicate.test("enabled", true);
84-
final var errorTracking = predicate.test("submitErrors", true);
85-
final var additionalMetrics = predicate.test("submitAdditionalMetrics", true);
86-
final var debug = predicate.test("debug", false);
87-
88-
if (saveConfig.get()) try {
74+
final var configVersion = parse(properties, saveConfig, "configVersion", null, Integer::parseInt);
75+
final boolean enabled = parse(properties, saveConfig, "enabled", () -> true, Boolean::parseBoolean);
76+
final boolean submitMetrics = parse(properties, saveConfig, "submitMetrics", () -> true, Boolean::parseBoolean);
77+
final boolean errorTracking = parse(properties, saveConfig, "submitErrors", () -> true, Boolean::parseBoolean);
78+
final boolean additionalMetrics = parse(properties, saveConfig, "submitAdditionalMetrics", () -> true, Boolean::parseBoolean);
79+
final boolean debug = parse(properties, saveConfig, "debug", () -> true, Boolean::parseBoolean);
80+
81+
if (saveConfig.get() && (configVersion == null || configVersion <= CONFIG_VERSION)) try {
8982
Files.createDirectories(file.getParent());
9083
try (final var out = Files.newOutputStream(file);
9184
final var writer = new OutputStreamWriter(out, UTF_8)) {
9285
final var store = new Properties();
9386

94-
store.setProperty("serverId", serverId.toString());
9587
store.setProperty("enabled", Boolean.toString(enabled));
96-
store.setProperty("submitErrors", Boolean.toString(errorTracking));
88+
store.setProperty("submitMetrics", Boolean.toString(submitMetrics));
9789
store.setProperty("submitAdditionalMetrics", Boolean.toString(additionalMetrics));
90+
store.setProperty("submitErrors", Boolean.toString(errorTracking));
91+
92+
store.setProperty("serverId", serverId.toString());
93+
9894
store.setProperty("debug", Boolean.toString(debug));
95+
store.setProperty("configVersion", Integer.toString(CONFIG_VERSION));
9996

10097
store.store(writer, COMMENT);
10198
}
10299
} catch (final IOException e) {
103100
throw new RuntimeException("Failed to save metrics config", e);
104101
}
105102

106-
return new SimpleConfig(serverId, additionalMetrics, debug, enabled, errorTracking, firstRun);
103+
return new SimpleConfig(
104+
serverId,
105+
enabled,
106+
enabled && additionalMetrics,
107+
debug,
108+
enabled && submitMetrics,
109+
enabled && errorTracking,
110+
firstRun
111+
);
112+
}
113+
114+
// fixme: this code sucks ass
115+
@Contract(value = "_, _, _, !null, _ -> !null")
116+
private static <T> @Nullable T parse(
117+
@Nullable final Properties properties,
118+
final AtomicBoolean saveConfig,
119+
final String key,
120+
@Nullable final Supplier<T> defaultValue,
121+
final Function<String, T> parser
122+
) {
123+
if (properties == null) {
124+
saveConfig.set(true);
125+
return defaultValue != null ? defaultValue.get() : null;
126+
}
127+
final var property = properties.getProperty(key);
128+
if (property == null) {
129+
saveConfig.set(true);
130+
return defaultValue != null ? defaultValue.get() : null;
131+
}
132+
try {
133+
return parser.apply(property.trim());
134+
} catch (final Exception e) {
135+
saveConfig.set(true);
136+
return defaultValue != null ? defaultValue.get() : null;
137+
}
107138
}
108139

109-
private static Optional<Properties> readOrEmpty(final Path file) throws RuntimeException {
110-
if (!Files.isRegularFile(file)) return Optional.empty();
140+
private static @Nullable Properties readOrEmpty(final Path file) throws RuntimeException {
141+
if (!Files.isRegularFile(file)) return null;
111142
try (final var reader = Files.newBufferedReader(file, UTF_8)) {
112143
final var properties = new Properties();
113144
properties.load(reader);
114-
return Optional.of(properties);
145+
return properties;
115146
} catch (final IOException e) {
116147
throw new RuntimeException("Failed to read metrics config", e);
117148
}

‎core/src/main/java/dev/faststats/Config.java‎

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,15 @@ public interface Config {
1919
@Contract(pure = true)
2020
UUID serverId();
2121

22+
/**
23+
* Whether all FastStats features are enabled.
24+
*
25+
* @return {@code true} if FastStats features are enabled, {@code false} otherwise
26+
* @since 0.24.0
27+
*/
28+
@Contract(pure = true)
29+
boolean enabled();
30+
2231
/**
2332
* Whether metrics submission is enabled.
2433
* <p>
@@ -29,7 +38,7 @@ public interface Config {
2938
* @since 0.24.0
3039
*/
3140
@Contract(pure = true)
32-
boolean enabled();
41+
boolean submitMetrics();
3342

3443
/**
3544
* Whether error tracking is enabled across all metrics instances.

‎core/src/main/java/dev/faststats/SimpleMetrics.java‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ private void startSubmitting(final long initialDelay, final long period, final T
102102

103103
final var enabled = Boolean.parseBoolean(System.getProperty("faststats.enabled", "true"));
104104

105-
if (!context.getConfig().enabled() || !enabled) {
105+
if (!context.getConfig().submitMetrics() || !enabled) {
106106
logger.warn("Metrics disabled, not starting submission");
107107
return;
108108
}

‎core/src/test/java/dev/faststats/FeatureFlagTest.java‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,11 @@ public boolean enabled() {
201201
return true;
202202
}
203203

204+
@Override
205+
public boolean submitMetrics() {
206+
return true;
207+
}
208+
204209
@Override
205210
public boolean errorTracking() {
206211
return true;

‎core/src/test/java/dev/faststats/MockContext.java‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@ public boolean enabled() {
2828
return true;
2929
}
3030

31+
@Override
32+
public boolean submitMetrics() {
33+
return true;
34+
}
35+
3136
@Override
3237
public boolean errorTracking() {
3338
return true;

0 commit comments

Comments
 (0)