Complete persistence metadata foundation
This commit is contained in:
@@ -0,0 +1,44 @@
|
|||||||
|
package com.livingworld.data.saved;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Immutable metadata for saved game data.
|
||||||
|
*
|
||||||
|
* @param schemaVersion the schema version number (must be > 0)
|
||||||
|
* @param modVersion the mod version string (must not be blank)
|
||||||
|
* @param createdAt the timestamp when the save was created
|
||||||
|
* @param updatedAt the timestamp when the save was last updated
|
||||||
|
*/
|
||||||
|
public record SaveMetadata(
|
||||||
|
int schemaVersion,
|
||||||
|
String modVersion,
|
||||||
|
long createdAt,
|
||||||
|
long updatedAt
|
||||||
|
) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a {@link SaveMetadata} and validates all fields.
|
||||||
|
*
|
||||||
|
* @param schemaVersion the schema version number (must be > 0)
|
||||||
|
* @param modVersion the mod version string (must not be blank)
|
||||||
|
* @param createdAt the timestamp when the save was created
|
||||||
|
* @param updatedAt the timestamp when the save was last updated
|
||||||
|
* @throws IllegalArgumentException if {@code schemaVersion} is not greater than 0
|
||||||
|
* @throws IllegalArgumentException if {@code modVersion} is blank
|
||||||
|
* @throws IllegalArgumentException if {@code updatedAt} is less than {@code createdAt}
|
||||||
|
*/
|
||||||
|
public SaveMetadata {
|
||||||
|
if (schemaVersion <= 0) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"schemaVersion must be greater than 0, got: " + schemaVersion);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (modVersion == null || modVersion.isBlank()) {
|
||||||
|
throw new IllegalArgumentException("modVersion must not be blank");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (updatedAt < createdAt) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"updatedAt (" + updatedAt + ") must be >= createdAt (" + createdAt + ")");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,56 +1,47 @@
|
|||||||
package com.livingworld.data.serialization;
|
package com.livingworld.data.serialization;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstraction for writing save data to a persistence target.
|
* Interface for writing data values with associated keys.
|
||||||
*
|
|
||||||
* <p>This interface hides the underlying storage format (JSON, NBT, etc.) from
|
|
||||||
* modules. Modules write their data through this abstraction without knowing
|
|
||||||
* how or where it is persisted.</p>
|
|
||||||
*/
|
*/
|
||||||
public interface PersistenceWriter {
|
public interface PersistenceWriter {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes a string value with the given key.
|
* Writes a string value.
|
||||||
*
|
*
|
||||||
* @param key the identifier for this value (must not be null or blank)
|
* @param key the key identifier
|
||||||
* @param value the string to write
|
* @param value the string value to write
|
||||||
* @throws IllegalArgumentException if key is null or blank
|
|
||||||
*/
|
*/
|
||||||
void writeString(String key, String value);
|
void writeString(String key, String value);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes an integer value with the given key.
|
* Writes an integer value.
|
||||||
*
|
*
|
||||||
* @param key the identifier for this value (must not be null or blank)
|
* @param key the key identifier
|
||||||
* @param value the integer to write
|
* @param value the integer value to write
|
||||||
* @throws IllegalArgumentException if key is null or blank
|
|
||||||
*/
|
*/
|
||||||
void writeInt(String key, int value);
|
void writeInt(String key, int value);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes a long value with the given key.
|
* Writes a long value.
|
||||||
*
|
*
|
||||||
* @param key the identifier for this value (must not be null or blank)
|
* @param key the key identifier
|
||||||
* @param value the long to write
|
* @param value the long value to write
|
||||||
* @throws IllegalArgumentException if key is null or blank
|
|
||||||
*/
|
*/
|
||||||
void writeLong(String key, long value);
|
void writeLong(String key, long value);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes a double value with the given key.
|
* Writes a double value.
|
||||||
*
|
*
|
||||||
* @param key the identifier for this value (must not be null or blank)
|
* @param key the key identifier
|
||||||
* @param value the double to write
|
* @param value the double value to write
|
||||||
* @throws IllegalArgumentException if key is null or blank
|
|
||||||
*/
|
*/
|
||||||
void writeDouble(String key, double value);
|
void writeDouble(String key, double value);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes a boolean value with the given key.
|
* Writes a boolean value.
|
||||||
*
|
*
|
||||||
* @param key the identifier for this value (must not be null or blank)
|
* @param key the key identifier
|
||||||
* @param value the boolean to write
|
* @param value the boolean value to write
|
||||||
* @throws IllegalArgumentException if key is null or blank
|
|
||||||
*/
|
*/
|
||||||
void writeBoolean(String key, boolean value);
|
void writeBoolean(String key, boolean value);
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package com.livingworld.data.saved;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
class SaveMetadataTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void acceptsVersionedChronologicalMetadata() {
|
||||||
|
SaveMetadata metadata = new SaveMetadata(1, "0.1.0", 100L, 200L);
|
||||||
|
|
||||||
|
assertEquals(1, metadata.schemaVersion());
|
||||||
|
assertEquals("0.1.0", metadata.modVersion());
|
||||||
|
assertEquals(100L, metadata.createdAt());
|
||||||
|
assertEquals(200L, metadata.updatedAt());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void rejectsInvalidSchemaVersion() {
|
||||||
|
assertThrows(
|
||||||
|
IllegalArgumentException.class,
|
||||||
|
() -> new SaveMetadata(0, "0.1.0", 100L, 100L));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void rejectsBlankModVersion() {
|
||||||
|
assertThrows(
|
||||||
|
IllegalArgumentException.class,
|
||||||
|
() -> new SaveMetadata(1, " ", 100L, 100L));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void rejectsUpdateBeforeCreation() {
|
||||||
|
assertThrows(
|
||||||
|
IllegalArgumentException.class,
|
||||||
|
() -> new SaveMetadata(1, "0.1.0", 200L, 100L));
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user