aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthias Andreas Benkard <code@mail.matthias.benkard.de>2021-12-19 22:56:09 +0100
committerMatthias Andreas Benkard <code@mail.matthias.benkard.de>2021-12-28 00:45:53 +0100
commit4e8423db22a77af394bb519e2a828714ab48898d (patch)
tree91cc55474c67c5be7507080d264cd0f9cff495c2
parent796b19da1b9ef6c1721faa2ddf35100eb01a8a28 (diff)
Add jgvariant-ostree module.
Change-Id: Idf7bacad28d7cf65eb1ddd0994dcc2c2c2a7e18e
-rw-r--r--README.md5
-rw-r--r--jgvariant-core/src/main/java/eu/mulk/jgvariant/core/Decoder.java68
-rw-r--r--jgvariant-core/src/main/java/eu/mulk/jgvariant/core/Signature.java2
-rw-r--r--jgvariant-core/src/main/java/module-info.java5
-rw-r--r--jgvariant-core/src/test/java/eu/mulk/jgvariant/core/DecoderTest.java27
-rw-r--r--jgvariant-ostree/pom.xml68
-rw-r--r--jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/ByteString.java41
-rw-r--r--jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/Checksum.java24
-rw-r--r--jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/Commit.java51
-rw-r--r--jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/DeltaFallback.java25
-rw-r--r--jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/DeltaMetaEntry.java38
-rw-r--r--jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/DeltaPartPayload.java41
-rw-r--r--jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/DeltaSuperblock.java43
-rw-r--r--jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/DirMeta.java25
-rw-r--r--jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/DirTree.java49
-rw-r--r--jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/FileMeta.java25
-rw-r--r--jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/Metadata.java33
-rw-r--r--jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/SignedDelta.java38
-rw-r--r--jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/Summary.java47
-rw-r--r--jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/SummarySignature.java35
-rw-r--r--jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/Xattr.java18
-rw-r--r--jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/package-info.java10
-rw-r--r--jgvariant-ostree/src/main/java/module-info.java85
-rw-r--r--jgvariant-ostree/src/test/java/eu/mulk/jgvariant/ostree/OstreeDecoderTest.java104
-rw-r--r--jgvariant-ostree/src/test/resources/ostree/.lock0
-rw-r--r--jgvariant-ostree/src/test/resources/ostree/config4
-rw-r--r--jgvariant-ostree/src/test/resources/ostree/delta-indexes/Mc/iDXVydLGaHpQCRyFFC0bLYU_9Bap+4G07jB1RRDVI.indexbin0 -> 141 bytes
-rw-r--r--jgvariant-ostree/src/test/resources/ostree/delta-indexes/PT/szKdyjiHHymu2hv1hU12xwf6JpdZqJnQmFyRgV_m8.indexbin0 -> 205 bytes
-rw-r--r--jgvariant-ostree/src/test/resources/ostree/delta-indexes/Zv/8Wf_Nc6H2qyBdEepSQomLudfCV8Bdxam6xqdnrM1A.indexbin0 -> 205 bytes
-rw-r--r--jgvariant-ostree/src/test/resources/ostree/deltas/Mc/iDXVydLGaHpQCRyFFC0bLYU_9Bap+4G07jB1RRDVI-PTszKdyjiHHymu2hv1hU12xwf6JpdZqJnQmFyRgV_m8/0bin0 -> 489 bytes
-rw-r--r--jgvariant-ostree/src/test/resources/ostree/deltas/Mc/iDXVydLGaHpQCRyFFC0bLYU_9Bap+4G07jB1RRDVI-PTszKdyjiHHymu2hv1hU12xwf6JpdZqJnQmFyRgV_m8/superblockbin0 -> 524 bytes
-rw-r--r--jgvariant-ostree/src/test/resources/ostree/deltas/Mc/iDXVydLGaHpQCRyFFC0bLYU_9Bap+4G07jB1RRDVI/0bin0 -> 509 bytes
-rw-r--r--jgvariant-ostree/src/test/resources/ostree/deltas/Mc/iDXVydLGaHpQCRyFFC0bLYU_9Bap+4G07jB1RRDVI/superblockbin0 -> 628 bytes
-rw-r--r--jgvariant-ostree/src/test/resources/ostree/deltas/PT/szKdyjiHHymu2hv1hU12xwf6JpdZqJnQmFyRgV_m8-Zv8Wf_Nc6H2qyBdEepSQomLudfCV8Bdxam6xqdnrM1A/0bin0 -> 433 bytes
-rw-r--r--jgvariant-ostree/src/test/resources/ostree/deltas/PT/szKdyjiHHymu2hv1hU12xwf6JpdZqJnQmFyRgV_m8-Zv8Wf_Nc6H2qyBdEepSQomLudfCV8Bdxam6xqdnrM1A/superblockbin0 -> 460 bytes
-rw-r--r--jgvariant-ostree/src/test/resources/ostree/objects/14/c9b958ac59df4979095a3485b4da5a045fe8737ffdba8cfbfff24988b238f7.dirtreebin0 -> 45 bytes
-rw-r--r--jgvariant-ostree/src/test/resources/ostree/objects/15/87cb76cddd6ecdd5830a0f218d6ba60a95e79643ed8764ad62cf18e62a0b59.file1
-rw-r--r--jgvariant-ostree/src/test/resources/ostree/objects/31/c8835d5c9d2c6687a50091c85142d1b2d853ff416a9fb81b4ee30754510d52.commitbin0 -> 182 bytes
-rw-r--r--jgvariant-ostree/src/test/resources/ostree/objects/3d/3b3329dca38871f29aeda1bf5854d76c707fa269759a899d0985c91815fe6f.commitbin0 -> 214 bytes
-rw-r--r--jgvariant-ostree/src/test/resources/ostree/objects/46/22b8c6cdcb4cbe29b8f79641f0304b30066596194b6b32981e46422d12c282.dirtreebin0 -> 43 bytes
-rw-r--r--jgvariant-ostree/src/test/resources/ostree/objects/48/cc6a2ecdab284b9d1e5b0e875c905866ff32f65ee1e857df0e691285d6f14c.dirmetabin0 -> 41 bytes
-rw-r--r--jgvariant-ostree/src/test/resources/ostree/objects/66/ff167ff35ce87daac817447a9490a262ee75f095f017716a6eb1a9d9eb3350.commitbin0 -> 214 bytes
-rw-r--r--jgvariant-ostree/src/test/resources/ostree/objects/73/4ed4332dd46f0ec95395ea6b404f9a19eacfc74de9100e19842cbe9b960d0a.dirtreebin0 -> 44 bytes
-rw-r--r--jgvariant-ostree/src/test/resources/ostree/objects/76/766e52e4a737646788570c8c44a3cf70b17ece81ce4c9b44f4f5869f138e8d.dirtreebin0 -> 43 bytes
-rw-r--r--jgvariant-ostree/src/test/resources/ostree/objects/84/2d6670d6c0d116a9723bd4329cacec722079177e886bd833f182500b879bfe.dirtreebin0 -> 264 bytes
-rw-r--r--jgvariant-ostree/src/test/resources/ostree/objects/88/534f940aa700c0f5d470c86f699179bf11fe486f3a8514f56a9703355d761b.dirtreebin0 -> 339 bytes
-rw-r--r--jgvariant-ostree/src/test/resources/ostree/objects/8f/576d91bd42c0d7682000271d40aa27866537d477220b2b97f536ada9da0d7c.file1
-rw-r--r--jgvariant-ostree/src/test/resources/ostree/objects/92/6fd84a0da031392cd97a07948238946894979695a25a3dd9da5fe3f719c98a.file1
-rw-r--r--jgvariant-ostree/src/test/resources/ostree/objects/c9/af809d0778808c3b5f38bbac35d88da150cbb4cf3929b2e5ed2f9828080e08.file1
-rw-r--r--jgvariant-ostree/src/test/resources/ostree/objects/cf/ff1525790c356cf268894ce6cabda5c5aa6fabc8b2becb39faf2d195f8ebaa.dirtreebin0 -> 339 bytes
-rw-r--r--jgvariant-ostree/src/test/resources/ostree/objects/d3/365463d0acd8eda475a874a01dae6b7f20b5ce8864e36fa27d485a7b821d6d.file1
-rw-r--r--jgvariant-ostree/src/test/resources/ostree/objects/df/f640414799416e7a874eaec41471905adaf18b694ec537fb99e662626864f3.file1
-rw-r--r--jgvariant-ostree/src/test/resources/ostree/objects/e0/83c4299293b067841a51af2c9156dc3b35f95af27773147b0458934ca37d4f.file1
-rw-r--r--jgvariant-ostree/src/test/resources/ostree/refs/heads/mulkos/1.x/amd641
-rw-r--r--jgvariant-ostree/src/test/resources/ostree/summarybin0 -> 784 bytes
-rw-r--r--jgvariant-parent/pom.xml7
-rw-r--r--pom.xml1
57 files changed, 925 insertions, 2 deletions
diff --git a/README.md b/README.md
index 56afd9e..39d39e3 100644
--- a/README.md
+++ b/README.md
@@ -61,6 +61,10 @@ pairs of [String][] and `int`, you can use the following code:
<groupId>eu.mulk.jgvariant</groupId>
<artifactId>jgvariant-core</artifactId>
</dependency>
+ <dependency>
+ <groupId>eu.mulk.jgvariant</groupId>
+ <artifactId>jgvariant-ostree</artifactId>
+ </dependency>
...
</dependencies>
@@ -76,6 +80,7 @@ pairs of [String][] and `int`, you can use the following code:
implementation(platform("eu.mulk.jgvariant:jgvariant-bom:0.1.4")
implementation("eu.mulk.jgvariant:jgvariant-core")
+ implementation("eu.mulk.jgvariant:jgvariant-ostree")
...
}
diff --git a/jgvariant-core/src/main/java/eu/mulk/jgvariant/core/Decoder.java b/jgvariant-core/src/main/java/eu/mulk/jgvariant/core/Decoder.java
index d2f2403..fc11eab 100644
--- a/jgvariant-core/src/main/java/eu/mulk/jgvariant/core/Decoder.java
+++ b/jgvariant-core/src/main/java/eu/mulk/jgvariant/core/Decoder.java
@@ -12,6 +12,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
+import java.util.function.Function;
import org.apiguardian.api.API;
import org.apiguardian.api.API.Status;
import org.jetbrains.annotations.Nullable;
@@ -80,7 +81,7 @@ public abstract class Decoder<T> {
* @param byteOrder the byte order to use.
* @return a new, decorated {@link Decoder}.
*/
- public Decoder<T> withByteOrder(ByteOrder byteOrder) {
+ public final Decoder<T> withByteOrder(ByteOrder byteOrder) {
var delegate = this;
return new Decoder<>() {
@@ -103,6 +104,34 @@ public abstract class Decoder<T> {
}
/**
+ * Creates a new {@link Decoder} from an existing one by applying a function to the result.
+ *
+ * @param function the function to apply.
+ * @return a new, decorated {@link Decoder}.
+ * @see java.util.stream.Stream#map
+ */
+ public final <U> Decoder<U> map(Function<T, U> function) {
+ var delegate = this;
+
+ return new Decoder<>() {
+ @Override
+ public byte alignment() {
+ return delegate.alignment();
+ }
+
+ @Override
+ public @Nullable Integer fixedSize() {
+ return delegate.fixedSize();
+ }
+
+ @Override
+ public U decode(ByteBuffer byteSlice) {
+ return function.apply(delegate.decode(byteSlice));
+ }
+ };
+ }
+
+ /**
* Creates a {@link Decoder} for an {@code Array} type.
*
* @param elementDecoder a {@link Decoder} for the elements of the array.
@@ -114,6 +143,16 @@ public abstract class Decoder<T> {
}
/**
+ * Creates a {@link Decoder} for an {@code Array} type of element type {@code byte} into a
+ * primitive {@code byte[]} array.
+ *
+ * @return a new {@link Decoder}.
+ */
+ public static Decoder<byte[]> ofByteArray() {
+ return new ByteArrayDecoder();
+ }
+
+ /**
* Creates a {@link Decoder} for a {@code Maybe} type.
*
* @param elementDecoder a {@link Decoder} for the contained element.
@@ -299,6 +338,9 @@ public abstract class Decoder<T> {
var element = elementDecoder.decode(byteSlice.slice(i, elementSize));
elements.add(element);
}
+ } else if (byteSlice.limit() == 0) {
+ // A degenerate zero-length array of variable width.
+ elements = List.of();
} else {
// An array with aligned elements and a vector of framing offsets in the end.
int framingOffsetSize = byteCount(byteSlice.limit());
@@ -321,6 +363,30 @@ public abstract class Decoder<T> {
}
}
+ private static class ByteArrayDecoder extends Decoder<byte[]> {
+
+ private static final int ELEMENT_SIZE = 1;
+
+ @Override
+ public byte alignment() {
+ return ELEMENT_SIZE;
+ }
+
+ @Override
+ @Nullable
+ Integer fixedSize() {
+ return null;
+ }
+
+ @Override
+ public byte[] decode(ByteBuffer byteSlice) {
+ // A simple C-style array.
+ byte[] elements = new byte[byteSlice.limit() / ELEMENT_SIZE];
+ byteSlice.get(elements);
+ return elements;
+ }
+ }
+
private static class MaybeDecoder<U> extends Decoder<Optional<U>> {
private final Decoder<U> elementDecoder;
diff --git a/jgvariant-core/src/main/java/eu/mulk/jgvariant/core/Signature.java b/jgvariant-core/src/main/java/eu/mulk/jgvariant/core/Signature.java
index d9de5f1..78e4bdd 100644
--- a/jgvariant-core/src/main/java/eu/mulk/jgvariant/core/Signature.java
+++ b/jgvariant-core/src/main/java/eu/mulk/jgvariant/core/Signature.java
@@ -43,7 +43,7 @@ public final class Signature {
return new Signature(signatureBytes);
}
- static Signature parse(String signatureString) throws ParseException {
+ public static Signature parse(String signatureString) throws ParseException {
var signatureBytes = ByteBuffer.wrap(signatureString.getBytes(StandardCharsets.US_ASCII));
return parse(signatureBytes);
}
diff --git a/jgvariant-core/src/main/java/module-info.java b/jgvariant-core/src/main/java/module-info.java
index a1830f6..0ef35cc 100644
--- a/jgvariant-core/src/main/java/module-info.java
+++ b/jgvariant-core/src/main/java/module-info.java
@@ -49,6 +49,10 @@
* <groupId>eu.mulk.jgvariant</groupId>
* <artifactId>jgvariant-core</artifactId>
* </dependency>
+ * <dependency>
+ * <groupId>eu.mulk.jgvariant</groupId>
+ * <artifactId>jgvariant-ostree</artifactId>
+ * </dependency>
*
* ...
* </dependencies>
@@ -65,6 +69,7 @@
*
* implementation(platform("eu.mulk.jgvariant:jgvariant-bom:0.1.4")
* implementation("eu.mulk.jgvariant:jgvariant-core")
+ * implementation("eu.mulk.jgvariant:jgvariant-ostree")
*
* ...
* }
diff --git a/jgvariant-core/src/test/java/eu/mulk/jgvariant/core/DecoderTest.java b/jgvariant-core/src/test/java/eu/mulk/jgvariant/core/DecoderTest.java
index 5cf1a1c..d72a1b6 100644
--- a/jgvariant-core/src/test/java/eu/mulk/jgvariant/core/DecoderTest.java
+++ b/jgvariant-core/src/test/java/eu/mulk/jgvariant/core/DecoderTest.java
@@ -242,6 +242,26 @@ class DecoderTest {
}
@Test
+ void testPrimitiveByteArray() {
+ var data = new byte[] {0x04, 0x05, 0x06, 0x07};
+
+ var decoder = Decoder.ofByteArray();
+
+ assertArrayEquals(data, decoder.decode(ByteBuffer.wrap(data)));
+ }
+
+ @Test
+ void testPrimitiveByteArrayRecord() {
+ var data = new byte[] {0x04, 0x05, 0x06, 0x07};
+
+ record TestRecord(byte[] bytes) {}
+
+ var decoder = Decoder.ofStructure(TestRecord.class, Decoder.ofByteArray());
+
+ assertArrayEquals(data, decoder.decode(ByteBuffer.wrap(data)).bytes());
+ }
+
+ @Test
void testIntegerArray() {
var data = new byte[] {0x04, 0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00};
@@ -434,4 +454,11 @@ class DecoderTest {
var signature = Signature.parse(ByteBuffer.wrap(data));
assertEquals("(bynqiuxtdsogvmiai)", signature.toString());
}
+
+ @Test
+ void testMap() {
+ var data = new byte[] {0x0A, 0x0B, 0x0C};
+ var decoder = Decoder.ofByteArray().map(bytes -> bytes.length);
+ assertEquals(3, decoder.decode(ByteBuffer.wrap(data)));
+ }
}
diff --git a/jgvariant-ostree/pom.xml b/jgvariant-ostree/pom.xml
new file mode 100644
index 0000000..f7da288
--- /dev/null
+++ b/jgvariant-ostree/pom.xml
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"
+ xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <version>0.1.4-SNAPSHOT</version>
+
+ <artifactId>jgvariant-ostree</artifactId>
+ <packaging>jar</packaging>
+
+ <name>JGVariant OSTree Parser</name>
+ <url>https://gerrit.benkard.de/plugins/gitiles/jgvariant</url>
+
+ <description>
+ GVariant serialization and deserialization.
+ </description>
+
+ <parent>
+ <groupId>eu.mulk.jgvariant</groupId>
+ <artifactId>jgvariant-parent</artifactId>
+ <version>0.1.4-SNAPSHOT</version>
+ <relativePath>../jgvariant-parent/pom.xml</relativePath>
+ </parent>
+
+ <dependencies>
+ <!-- JGVariant -->
+ <dependency>
+ <groupId>eu.mulk.jgvariant</groupId>
+ <artifactId>jgvariant-core</artifactId>
+ <version>0.1.4-SNAPSHOT</version>
+ </dependency>
+
+ <!-- Annotations -->
+ <dependency>
+ <groupId>com.google.errorprone</groupId>
+ <artifactId>error_prone_annotations</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.jetbrains</groupId>
+ <artifactId>annotations</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apiguardian</groupId>
+ <artifactId>apiguardian-api</artifactId>
+ </dependency>
+
+ <!-- Testing -->
+ <dependency>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter-engine</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter-api</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>io.hosuaby</groupId>
+ <artifactId>inject-resources-junit-jupiter</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+</project>
diff --git a/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/ByteString.java b/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/ByteString.java
new file mode 100644
index 0000000..1a85547
--- /dev/null
+++ b/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/ByteString.java
@@ -0,0 +1,41 @@
+package eu.mulk.jgvariant.ostree;
+
+import eu.mulk.jgvariant.core.Decoder;
+import java.util.Arrays;
+import java.util.HexFormat;
+
+/**
+ * A wrapper for a {@code byte[]} that implements {@link #equals(Object)}, {@link #hashCode()}, and
+ * {@link #toString()} according to value semantics.
+ */
+public record ByteString(byte[] bytes) {
+
+ private static final Decoder<ByteString> DECODER = Decoder.ofByteArray().map(ByteString::new);
+
+ public static Decoder<ByteString> decoder() {
+ return DECODER;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return (o instanceof ByteString byteString) && Arrays.equals(bytes, byteString.bytes);
+ }
+
+ @Override
+ public int hashCode() {
+ return Arrays.hashCode(bytes);
+ }
+
+ @Override
+ public String toString() {
+ return "ByteString{hex=\"%s\"}".formatted(hex());
+ }
+
+ public String hex() {
+ return HexFormat.of().formatHex(bytes);
+ }
+
+ public static ByteString ofHex(String hex) {
+ return new ByteString(HexFormat.of().parseHex(hex));
+ }
+}
diff --git a/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/Checksum.java b/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/Checksum.java
new file mode 100644
index 0000000..f77eb57
--- /dev/null
+++ b/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/Checksum.java
@@ -0,0 +1,24 @@
+package eu.mulk.jgvariant.ostree;
+
+import eu.mulk.jgvariant.core.Decoder;
+
+/**
+ * A wrapper for {@link ByteString} that refers to a content-addressed object in an OSTree
+ * repository.
+ */
+public record Checksum(ByteString bytes) {
+
+ private static final Decoder<Checksum> DECODER = ByteString.decoder().map(Checksum::new);
+
+ public static Decoder<Checksum> decoder() {
+ return DECODER;
+ }
+
+ public String hex() {
+ return bytes.hex();
+ }
+
+ public static Checksum ofHex(String hex) {
+ return new Checksum(ByteString.ofHex(hex));
+ }
+}
diff --git a/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/Commit.java b/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/Commit.java
new file mode 100644
index 0000000..43909ba
--- /dev/null
+++ b/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/Commit.java
@@ -0,0 +1,51 @@
+package eu.mulk.jgvariant.ostree;
+
+import eu.mulk.jgvariant.core.Decoder;
+import java.nio.ByteOrder;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+
+/**
+ * A commit in an OSTree repository.
+ *
+ * <p>Has an optional parent, a root directory, and various metadata.
+ *
+ * <p>Reference: {@code ostree-core.h#OSTREE_COMMIT_GVARIANT_STRING}
+ */
+public record Commit(
+ Metadata metadata,
+ Checksum parentChecksum,
+ List<RelatedObject> relatedObjects,
+ String subject,
+ String body,
+ long timestamp,
+ Checksum rootDirTreeChecksum,
+ Checksum rootDirMetaChecksum) {
+
+ public record RelatedObject(String ref, Checksum commitChecksum) {
+
+ private static final Decoder<RelatedObject> DECODER =
+ Decoder.ofStructure(
+ RelatedObject.class, Decoder.ofString(StandardCharsets.UTF_8), Checksum.decoder());
+
+ public static Decoder<RelatedObject> decoder() {
+ return DECODER;
+ }
+ }
+
+ private static final Decoder<Commit> DECODER =
+ Decoder.ofStructure(
+ Commit.class,
+ Metadata.decoder(),
+ Checksum.decoder(),
+ Decoder.ofArray(RelatedObject.decoder()),
+ Decoder.ofString(StandardCharsets.UTF_8),
+ Decoder.ofString(StandardCharsets.UTF_8),
+ Decoder.ofLong().withByteOrder(ByteOrder.BIG_ENDIAN),
+ Checksum.decoder(),
+ Checksum.decoder());
+
+ public static Decoder<Commit> decoder() {
+ return DECODER;
+ }
+}
diff --git a/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/DeltaFallback.java b/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/DeltaFallback.java
new file mode 100644
index 0000000..1b3cc0a
--- /dev/null
+++ b/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/DeltaFallback.java
@@ -0,0 +1,25 @@
+package eu.mulk.jgvariant.ostree;
+
+import eu.mulk.jgvariant.core.Decoder;
+import java.nio.ByteOrder;
+
+/**
+ * A fallback entry in a {@link DeltaSuperblock}.
+ *
+ * <p>Reference: {@code ostree-repo-static-delta-private.h#OSTREE_STATIC_DELTA_FALLBACK_FORMAT}
+ */
+public record DeltaFallback(
+ byte objectType, Checksum checksum, long compressedSize, long uncompressedSize) {
+
+ private static final Decoder<DeltaFallback> DECODER =
+ Decoder.ofStructure(
+ DeltaFallback.class,
+ Decoder.ofByte(),
+ Checksum.decoder(),
+ Decoder.ofLong().withByteOrder(ByteOrder.LITTLE_ENDIAN), // FIXME: non-canonical
+ Decoder.ofLong().withByteOrder(ByteOrder.LITTLE_ENDIAN)); // FIXME: non-canonical
+
+ public static Decoder<DeltaFallback> decoder() {
+ return DECODER;
+ }
+}
diff --git a/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/DeltaMetaEntry.java b/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/DeltaMetaEntry.java
new file mode 100644
index 0000000..39ada86
--- /dev/null
+++ b/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/DeltaMetaEntry.java
@@ -0,0 +1,38 @@
+package eu.mulk.jgvariant.ostree;
+
+import eu.mulk.jgvariant.core.Decoder;
+import java.nio.ByteOrder;
+import java.util.List;
+
+/**
+ * An entry in a {@link DeltaSuperblock}.
+ *
+ * <p>Reference: {@code ostree-repo-static-delta-private.h#OSTREE_STATIC_DELTA_META_ENTRY_FORMAT}
+ */
+public record DeltaMetaEntry(
+ int version, Checksum checksum, long size, long usize, List<DeltaObject> objects) {
+
+ record DeltaObject(byte objectType, Checksum checksum) {
+
+ private static final Decoder<DeltaObject> DECODER =
+ Decoder.ofStructure(DeltaObject.class, Decoder.ofByte(), Checksum.decoder());
+
+ public static Decoder<DeltaObject> decoder() {
+ return DECODER;
+ }
+ }
+
+ private static final Decoder<DeltaMetaEntry> DECODER =
+ Decoder.ofStructure(
+ DeltaMetaEntry.class,
+ Decoder.ofInt().withByteOrder(ByteOrder.LITTLE_ENDIAN), // FIXME: non-canonical
+ Checksum.decoder(),
+ Decoder.ofLong().withByteOrder(ByteOrder.LITTLE_ENDIAN), // FIXME: non-canonical
+ Decoder.ofLong().withByteOrder(ByteOrder.LITTLE_ENDIAN), // FIXME: non-canonical
+ Decoder.ofByteArray().map(x -> List.of()) // FIXME
+ );
+
+ public static Decoder<DeltaMetaEntry> decoder() {
+ return DECODER;
+ }
+}
diff --git a/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/DeltaPartPayload.java b/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/DeltaPartPayload.java
new file mode 100644
index 0000000..c8c5fe7
--- /dev/null
+++ b/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/DeltaPartPayload.java
@@ -0,0 +1,41 @@
+package eu.mulk.jgvariant.ostree;
+
+import eu.mulk.jgvariant.core.Decoder;
+import java.nio.ByteOrder;
+import java.util.List;
+
+/**
+ * Reference: {@code ostree-repo-static-delta-private.h#OSTREE_STATIC_DELTA_PART_PAYLOAD_FORMAT_V0}
+ */
+public record DeltaPartPayload(
+ List<FileMode> fileModes,
+ List<List<Xattr>> xattrs,
+ ByteString rawDataSource,
+ ByteString operations) {
+
+ private static final Decoder<DeltaPartPayload> DECODER =
+ Decoder.ofStructure(
+ DeltaPartPayload.class,
+ Decoder.ofArray(FileMode.decoder()),
+ Decoder.ofArray(Decoder.ofArray(Xattr.decoder())),
+ ByteString.decoder(),
+ ByteString.decoder());
+
+ public record FileMode(int uid, int gid, int mode) {
+
+ private static final Decoder<FileMode> DECODER =
+ Decoder.ofStructure(
+ FileMode.class,
+ Decoder.ofInt().withByteOrder(ByteOrder.LITTLE_ENDIAN),
+ Decoder.ofInt().withByteOrder(ByteOrder.LITTLE_ENDIAN),
+ Decoder.ofInt().withByteOrder(ByteOrder.LITTLE_ENDIAN));
+
+ public static Decoder<FileMode> decoder() {
+ return DECODER;
+ }
+ }
+
+ public static Decoder<DeltaPartPayload> decoder() {
+ return DECODER;
+ }
+}
diff --git a/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/DeltaSuperblock.java b/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/DeltaSuperblock.java
new file mode 100644
index 0000000..2b09645
--- /dev/null
+++ b/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/DeltaSuperblock.java
@@ -0,0 +1,43 @@
+package eu.mulk.jgvariant.ostree;
+
+import eu.mulk.jgvariant.core.Decoder;
+import java.nio.ByteOrder;
+import java.util.List;
+
+/** Reference: {@code ostree-repo-static-delta-private.h#OSTREE_STATIC_DELTA_SUPERBLOCK_FORMAT} */
+public record DeltaSuperblock(
+ Metadata metadata,
+ long timestamp,
+ Checksum fromChecksum,
+ Checksum toChecksum,
+ Commit commit,
+ List<DeltaName> dependencies,
+ List<DeltaMetaEntry> entries,
+ List<DeltaFallback> fallbacks) {
+
+ public record DeltaName(Checksum fromChecksum, Checksum toChecksum) {
+
+ private static final Decoder<DeltaName> DECODER =
+ Decoder.ofStructure(DeltaName.class, Checksum.decoder(), Checksum.decoder());
+
+ public static Decoder<DeltaName> decoder() {
+ return DECODER;
+ }
+ }
+
+ private static final Decoder<DeltaSuperblock> DECODER =
+ Decoder.ofStructure(
+ DeltaSuperblock.class,
+ Metadata.decoder(),
+ Decoder.ofLong().withByteOrder(ByteOrder.BIG_ENDIAN),
+ Checksum.decoder(),
+ Checksum.decoder(),
+ Commit.decoder(),
+ Decoder.ofByteArray().map(x -> List.of()), // FIXME
+ Decoder.ofArray(DeltaMetaEntry.decoder()),
+ Decoder.ofArray(DeltaFallback.decoder()));
+
+ public static Decoder<DeltaSuperblock> decoder() {
+ return DECODER;
+ }
+}
diff --git a/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/DirMeta.java b/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/DirMeta.java
new file mode 100644
index 0000000..3a9672d
--- /dev/null
+++ b/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/DirMeta.java
@@ -0,0 +1,25 @@
+package eu.mulk.jgvariant.ostree;
+
+import eu.mulk.jgvariant.core.Decoder;
+import java.nio.ByteOrder;
+import java.util.List;
+
+/**
+ * Permission bits and extended attributes for a directory.
+ *
+ * <p>Reference: {@code ostree-core.h#OSTREE_DIRMETA_GVARIANT_STRING}
+ */
+public record DirMeta(int uid, int gid, int mode, List<Xattr> xattrs) {
+
+ private static final Decoder<DirMeta> DECODER =
+ Decoder.ofStructure(
+ DirMeta.class,
+ Decoder.ofInt().withByteOrder(ByteOrder.BIG_ENDIAN),
+ Decoder.ofInt().withByteOrder(ByteOrder.BIG_ENDIAN),
+ Decoder.ofInt().withByteOrder(ByteOrder.BIG_ENDIAN),
+ Decoder.ofArray(Xattr.decoder()));
+
+ public static Decoder<DirMeta> decoder() {
+ return DECODER;
+ }
+}
diff --git a/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/DirTree.java b/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/DirTree.java
new file mode 100644
index 0000000..3a14abb
--- /dev/null
+++ b/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/DirTree.java
@@ -0,0 +1,49 @@
+package eu.mulk.jgvariant.ostree;
+
+import eu.mulk.jgvariant.core.Decoder;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+
+/**
+ * Metadata describing files and directories of a file tree.
+ *
+ * <p>Referenced by {@link Commit#rootDirTreeChecksum()} and recursively by {@link
+ * Directory#treeChecksum()}.
+ *
+ * <p>Reference: {@code ostree-core.h#OSTREE_TREE_GVARIANT_STRING}
+ */
+public record DirTree(List<File> files, List<Directory> directories) {
+
+ public record File(String name, Checksum checksum) {
+
+ private static final Decoder<File> DECODER =
+ Decoder.ofStructure(
+ File.class, Decoder.ofString(StandardCharsets.UTF_8), Checksum.decoder());
+
+ public static Decoder<File> decoder() {
+ return DECODER;
+ }
+ }
+
+ public record Directory(String name, Checksum treeChecksum, Checksum dirChecksum) {
+
+ private static final Decoder<Directory> DECODER =
+ Decoder.ofStructure(
+ Directory.class,
+ Decoder.ofString(StandardCharsets.UTF_8),
+ Checksum.decoder(),
+ Checksum.decoder());
+
+ public static Decoder<Directory> decoder() {
+ return DECODER;
+ }
+ }
+
+ private static final Decoder<DirTree> DECODER =
+ Decoder.ofStructure(
+ DirTree.class, Decoder.ofArray(File.decoder()), Decoder.ofArray(Directory.decoder()));
+
+ public static Decoder<DirTree> decoder() {
+ return DECODER;
+ }
+}
diff --git a/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/FileMeta.java b/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/FileMeta.java
new file mode 100644
index 0000000..19e0677
--- /dev/null
+++ b/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/FileMeta.java
@@ -0,0 +1,25 @@
+package eu.mulk.jgvariant.ostree;
+
+import eu.mulk.jgvariant.core.Decoder;
+import java.nio.ByteOrder;
+import java.util.List;
+
+/**
+ * Permission bits and extended attributes for a file.
+ *
+ * <p>Reference: {@code ostree-core.h#OSTREE_FILEMETA_GVARIANT_STRING}
+ */
+public record FileMeta(int uid, int gid, int mode, List<Xattr> xattrs) {
+
+ private static final Decoder<FileMeta> DECODER =
+ Decoder.ofStructure(
+ FileMeta.class,
+ Decoder.ofInt().withByteOrder(ByteOrder.BIG_ENDIAN),
+ Decoder.ofInt().withByteOrder(ByteOrder.BIG_ENDIAN),
+ Decoder.ofInt().withByteOrder(ByteOrder.BIG_ENDIAN),
+ Decoder.ofArray(Xattr.decoder()));
+
+ public static Decoder<FileMeta> decoder() {
+ return DECODER;
+ }
+}
diff --git a/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/Metadata.java b/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/Metadata.java
new file mode 100644
index 0000000..cf838d3
--- /dev/null
+++ b/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/Metadata.java
@@ -0,0 +1,33 @@
+package eu.mulk.jgvariant.ostree;
+
+import eu.mulk.jgvariant.core.Decoder;
+import eu.mulk.jgvariant.core.Variant;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+
+/**
+ * A wrapper for a list of metadata fields.
+ *
+ * <p>Reference: (embedded in other data types)
+ */
+public record Metadata(List<Field> fields) {
+
+ /** A metadata field with a key and a value. */
+ public record Field(String key, Variant value) {
+
+ private static final Decoder<Field> DECODER =
+ Decoder.ofStructure(
+ Field.class, Decoder.ofString(StandardCharsets.UTF_8), Decoder.ofVariant());
+
+ public static Decoder<Field> decoder() {
+ return DECODER;
+ }
+ }
+
+ private static final Decoder<Metadata> DECODER =
+ Decoder.ofArray(Field.decoder()).map(Metadata::new);
+
+ public static Decoder<Metadata> decoder() {
+ return DECODER;
+ }
+}
diff --git a/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/SignedDelta.java b/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/SignedDelta.java
new file mode 100644
index 0000000..2fc5c25
--- /dev/null
+++ b/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/SignedDelta.java
@@ -0,0 +1,38 @@
+package eu.mulk.jgvariant.ostree;
+
+import eu.mulk.jgvariant.core.Decoder;
+import eu.mulk.jgvariant.core.Variant;
+import java.nio.ByteOrder;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+
+/**
+ * A {@link DeltaSuperblock} signed with some sort of key.
+ *
+ * <p>Reference: {@code ostree-repo-static-delta-private.h#OSTREE_STATIC_DELTA_SIGNED_FORMAT}
+ */
+public record SignedDelta(
+ long magicNumber, ByteString superblock, List<SignedDelta.Signature> signatures) {
+
+ /** A cryptographic signature. */
+ public record Signature(String key, Variant data) {
+ private static final Decoder<Signature> DECODER =
+ Decoder.ofStructure(
+ Signature.class, Decoder.ofString(StandardCharsets.US_ASCII), Decoder.ofVariant());
+
+ public static Decoder<Signature> decoder() {
+ return DECODER;
+ }
+ }
+
+ private static final Decoder<SignedDelta> DECODER =
+ Decoder.ofStructure(
+ SignedDelta.class,
+ Decoder.ofLong().withByteOrder(ByteOrder.BIG_ENDIAN),
+ ByteString.decoder(),
+ Decoder.ofArray(Signature.decoder()));
+
+ public static Decoder<SignedDelta> decoder() {
+ return DECODER;
+ }
+}
diff --git a/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/Summary.java b/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/Summary.java
new file mode 100644
index 0000000..8f4ddf6
--- /dev/null
+++ b/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/Summary.java
@@ -0,0 +1,47 @@
+package eu.mulk.jgvariant.ostree;
+
+import eu.mulk.jgvariant.core.Decoder;
+import java.nio.ByteOrder;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+
+/**
+ * The summary file of an OSTree repository.
+ *
+ * <p>Stored as a file named {@code summary} in the OSTree repository root.
+ *
+ * <p>Reference: {@code ostree-core.h#OSTREE_SUMMARY_GVARIANT_STRING}
+ */
+public record Summary(List<Entry> entries, Metadata metadata) {
+
+ public record Entry(String ref, Value value) {
+
+ public record Value(long size, Checksum checksum, Metadata metadata) {
+
+ private static final Decoder<Value> DECODER =
+ Decoder.ofStructure(
+ Value.class,
+ Decoder.ofLong().withByteOrder(ByteOrder.LITTLE_ENDIAN),
+ Checksum.decoder(),
+ Metadata.decoder());
+
+ public static Decoder<Value> decoder() {
+ return DECODER;
+ }
+ }
+
+ private static final Decoder<Entry> DECODER =
+ Decoder.ofStructure(Entry.class, Decoder.ofString(StandardCharsets.UTF_8), Value.decoder());
+
+ public static Decoder<Entry> decoder() {
+ return DECODER;
+ }
+ }
+
+ private static final Decoder<Summary> DECODER =
+ Decoder.ofStructure(Summary.class, Decoder.ofArray(Entry.decoder()), Metadata.decoder());
+
+ public static Decoder<Summary> decoder() {
+ return DECODER;
+ }
+}
diff --git a/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/SummarySignature.java b/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/SummarySignature.java
new file mode 100644
index 0000000..a05b96d
--- /dev/null
+++ b/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/SummarySignature.java
@@ -0,0 +1,35 @@
+package eu.mulk.jgvariant.ostree;
+
+import eu.mulk.jgvariant.core.Decoder;
+import eu.mulk.jgvariant.core.Variant;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+
+/**
+ * A collection of cryptographic signatures for a {@link Summary}.
+ *
+ * <p>Stored as a file named {@code summary.sig} in the OSTree repository root.
+ *
+ * <p>Reference: {@code ostree-repo-static-delta-private.h#OSTREE_SUMMARY_SIG_GVARIANT_STRING}
+ */
+public record SummarySignature(List<Signature> signatures) {
+
+ /** A cryptographic signature. */
+ public record Signature(String key, Variant data) {
+
+ private static final Decoder<Signature> DECODER =
+ Decoder.ofStructure(
+ Signature.class, Decoder.ofString(StandardCharsets.UTF_8), Decoder.ofVariant());
+
+ public static Decoder<Signature> decoder() {
+ return DECODER;
+ }
+ }
+
+ private static final Decoder<SummarySignature> DECODER =
+ Decoder.ofArray(Signature.decoder()).map(SummarySignature::new);
+
+ public static Decoder<SummarySignature> decoder() {
+ return DECODER;
+ }
+}
diff --git a/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/Xattr.java b/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/Xattr.java
new file mode 100644
index 0000000..68628c4
--- /dev/null
+++ b/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/Xattr.java
@@ -0,0 +1,18 @@
+package eu.mulk.jgvariant.ostree;
+
+import eu.mulk.jgvariant.core.Decoder;
+
+/**
+ * Reference: (embedded in other data types, e.g. {@code
+ * ostree-core.h#OSTREE_DIRMETA_GVARIANT_STRING}, {@code
+ * ostree-core.h#OSTREE_FILEMETA_GVARIANT_STRING})
+ */
+public record Xattr(ByteString name, ByteString value) {
+
+ private static final Decoder<Xattr> DECODER =
+ Decoder.ofStructure(Xattr.class, ByteString.decoder(), ByteString.decoder());
+
+ public static Decoder<Xattr> decoder() {
+ return DECODER;
+ }
+}
diff --git a/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/package-info.java b/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/package-info.java
new file mode 100644
index 0000000..c6a61f1
--- /dev/null
+++ b/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/package-info.java
@@ -0,0 +1,10 @@
+/**
+ * Provides record classes describing the elements of <a
+ * href="https://ostreedev.github.io/ostree/">OSTree</a> repositories and factory methods to create
+ * {@link eu.mulk.jgvariant.core.Decoder} instances for them.
+ */
+@API(status = Status.EXPERIMENTAL)
+package eu.mulk.jgvariant.ostree;
+
+import org.apiguardian.api.API;
+import org.apiguardian.api.API.Status;
diff --git a/jgvariant-ostree/src/main/java/module-info.java b/jgvariant-ostree/src/main/java/module-info.java
new file mode 100644
index 0000000..e51c586
--- /dev/null
+++ b/jgvariant-ostree/src/main/java/module-info.java
@@ -0,0 +1,85 @@
+/**
+ * Provides a parser for the <a href="https://docs.gtk.org/glib/struct.Variant.html">GVariant</a>
+ * serialization format.
+ *
+ * <ul>
+ * <li><a href="#sect-overview">Overview</a>
+ * <li><a href="#sect-installation">Installation</a>
+ * </ul>
+ *
+ * <h2 id="sect-overview">Overview</h2>
+ *
+ * <p>The {@link eu.mulk.jgvariant.ostree} package contains record classes describing the elements
+ * of <a href="https://ostreedev.github.io/ostree/">OSTree</a> repositories and factory methods to
+ * create {@link eu.mulk.jgvariant.core.Decoder} instances for them.
+ *
+ * <h2 id="sect-installation">Installation</h2>
+ *
+ * <ul>
+ * <li><a href="#sect-installation-maven">Usage with Maven</a>
+ * <li><a href="#sect-installation-gradle">Usage with Gradle</a>
+ * </ul>
+ *
+ * <h3 id="sect-installation-maven">Usage with Maven</h3>
+ *
+ * <pre>{@code
+ * <project>
+ * ...
+ *
+ * <dependencyManagement>
+ * ...
+ *
+ * <dependencies>
+ * <dependency>
+ * <groupId>eu.mulk.jgvariant</groupId>
+ * <artifactId>jgvariant-bom</artifactId>
+ * <version>0.1.4</version>
+ * <type>pom</type>
+ * <scope>import</scope>
+ * </dependency>
+ * </dependencies>
+ *
+ * ...
+ * </dependencyManagement>
+ *
+ * <dependencies>
+ * ...
+ *
+ * <dependency>
+ * <groupId>eu.mulk.jgvariant</groupId>
+ * <artifactId>jgvariant-core</artifactId>
+ * </dependency>
+ * <dependency>
+ * <groupId>eu.mulk.jgvariant</groupId>
+ * <artifactId>jgvariant-ostree</artifactId>
+ * </dependency>
+ *
+ * ...
+ * </dependencies>
+ *
+ * ...
+ * </project>
+ * }</pre>
+ *
+ * <h3 id="sect-installation-gradle">Usage with Gradle</h3>
+ *
+ * <pre>{@code
+ * dependencies {
+ * ...
+ *
+ * implementation(platform("eu.mulk.jgvariant:jgvariant-bom:0.1.4")
+ * implementation("eu.mulk.jgvariant:jgvariant-core")
+ * implementation("eu.mulk.jgvariant:jgvariant-ostree")
+ *
+ * ...
+ * }
+ * }</pre>
+ */
+module eu.mulk.jgvariant.gvariant {
+ requires transitive eu.mulk.jgvariant.core;
+ requires com.google.errorprone.annotations;
+ requires org.jetbrains.annotations;
+ requires org.apiguardian.api;
+
+ exports eu.mulk.jgvariant.ostree;
+}
diff --git a/jgvariant-ostree/src/test/java/eu/mulk/jgvariant/ostree/OstreeDecoderTest.java b/jgvariant-ostree/src/test/java/eu/mulk/jgvariant/ostree/OstreeDecoderTest.java
new file mode 100644
index 0000000..5e4d37c
--- /dev/null
+++ b/jgvariant-ostree/src/test/java/eu/mulk/jgvariant/ostree/OstreeDecoderTest.java
@@ -0,0 +1,104 @@
+package eu.mulk.jgvariant.ostree;
+
+import static org.junit.jupiter.api.Assertions.assertAll;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import com.adelean.inject.resources.junit.jupiter.GivenBinaryResource;
+import com.adelean.inject.resources.junit.jupiter.TestWithResources;
+import eu.mulk.jgvariant.core.Signature;
+import eu.mulk.jgvariant.core.Variant;
+import java.nio.ByteBuffer;
+import java.util.List;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+
+@TestWithResources
+class OstreeDecoderTest {
+
+ @GivenBinaryResource("/ostree/summary")
+ byte[] summaryBytes;
+
+ @GivenBinaryResource(
+ "/ostree/objects/3d/3b3329dca38871f29aeda1bf5854d76c707fa269759a899d0985c91815fe6f.commit")
+ byte[] commitBytes;
+
+ @GivenBinaryResource(
+ "/ostree/objects/14/c9b958ac59df4979095a3485b4da5a045fe8737ffdba8cfbfff24988b238f7.dirtree")
+ byte[] dirTreeBytes;
+
+ @GivenBinaryResource(
+ "/ostree/objects/48/cc6a2ecdab284b9d1e5b0e875c905866ff32f65ee1e857df0e691285d6f14c.dirmeta")
+ byte[] dirMetaBytes;
+
+ @GivenBinaryResource("/ostree/deltas/Mc/iDXVydLGaHpQCRyFFC0bLYU_9Bap+4G07jB1RRDVI/superblock")
+ byte[] deltaSuperblockBytes;
+
+ @GivenBinaryResource("/ostree/deltas/Mc/iDXVydLGaHpQCRyFFC0bLYU_9Bap+4G07jB1RRDVI/0")
+ byte[] deltaPartPayloadBytes;
+
+ @Test
+ void testTrivial() {
+ assertTrue(true);
+ }
+
+ @Test
+ void testSummaryDecoder() {
+ var decoder = Summary.decoder();
+ var summary = decoder.decode(ByteBuffer.wrap(summaryBytes));
+ assertAll(
+ () ->
+ assertEquals(
+ List.of(
+ new Summary.Entry(
+ "mulkos/1.x/amd64",
+ new Summary.Entry.Value(
+ 214,
+ Checksum.ofHex(
+ "66ff167ff35ce87daac817447a9490a262ee75f095f017716a6eb1a9d9eb3350"),
+ new Metadata(
+ List.of(
+ new Metadata.Field(
+ "ostree.commit.timestamp",
+ new Variant(Signature.parse("t"), 1640537170L))))))),
+ summary.entries()));
+ // FIXME: check metadata field
+ System.out.println(summary);
+ }
+
+ @Test
+ void testCommitDecoder() {
+ var decoder = Commit.decoder();
+ var commit = decoder.decode(ByteBuffer.wrap(commitBytes));
+ System.out.println(commit);
+ }
+
+ @Test
+ void testDirTreeDecoder() {
+ var decoder = DirTree.decoder();
+ var dirTree = decoder.decode(ByteBuffer.wrap(dirTreeBytes));
+ System.out.println(dirTree);
+ }
+
+ @Test
+ void testDirMetaDecoder() {
+ var decoder = DirMeta.decoder();
+ var dirMeta = decoder.decode(ByteBuffer.wrap(dirMetaBytes));
+ System.out.println(dirMeta);
+ }
+
+ @Test
+ void testSuperblockDecoder() {
+ var decoder = DeltaSuperblock.decoder();
+ var deltaSuperblock = decoder.decode(ByteBuffer.wrap(deltaSuperblockBytes));
+ System.out.println(deltaSuperblock);
+ }
+
+ @Disabled("invalid: compression byte not taken into account")
+ @Test
+ void testPartPayloadDecoder() {
+ var decoder = DeltaPartPayload.decoder();
+ var deltaPartPayload = decoder.decode(ByteBuffer.wrap(deltaPartPayloadBytes));
+ System.out.println(deltaPartPayload);
+ }
+}
diff --git a/jgvariant-ostree/src/test/resources/ostree/.lock b/jgvariant-ostree/src/test/resources/ostree/.lock
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/jgvariant-ostree/src/test/resources/ostree/.lock
diff --git a/jgvariant-ostree/src/test/resources/ostree/config b/jgvariant-ostree/src/test/resources/ostree/config
new file mode 100644
index 0000000..15694aa
--- /dev/null
+++ b/jgvariant-ostree/src/test/resources/ostree/config
@@ -0,0 +1,4 @@
+[core]
+repo_version=1
+mode=bare
+indexed-deltas=true
diff --git a/jgvariant-ostree/src/test/resources/ostree/delta-indexes/Mc/iDXVydLGaHpQCRyFFC0bLYU_9Bap+4G07jB1RRDVI.index b/jgvariant-ostree/src/test/resources/ostree/delta-indexes/Mc/iDXVydLGaHpQCRyFFC0bLYU_9Bap+4G07jB1RRDVI.index
new file mode 100644
index 0000000..455b34c
--- /dev/null
+++ b/jgvariant-ostree/src/test/resources/ostree/delta-indexes/Mc/iDXVydLGaHpQCRyFFC0bLYU_9Bap+4G07jB1RRDVI.index
Binary files differ
diff --git a/jgvariant-ostree/src/test/resources/ostree/delta-indexes/PT/szKdyjiHHymu2hv1hU12xwf6JpdZqJnQmFyRgV_m8.index b/jgvariant-ostree/src/test/resources/ostree/delta-indexes/PT/szKdyjiHHymu2hv1hU12xwf6JpdZqJnQmFyRgV_m8.index
new file mode 100644
index 0000000..4b44198
--- /dev/null
+++ b/jgvariant-ostree/src/test/resources/ostree/delta-indexes/PT/szKdyjiHHymu2hv1hU12xwf6JpdZqJnQmFyRgV_m8.index
Binary files differ
diff --git a/jgvariant-ostree/src/test/resources/ostree/delta-indexes/Zv/8Wf_Nc6H2qyBdEepSQomLudfCV8Bdxam6xqdnrM1A.index b/jgvariant-ostree/src/test/resources/ostree/delta-indexes/Zv/8Wf_Nc6H2qyBdEepSQomLudfCV8Bdxam6xqdnrM1A.index
new file mode 100644
index 0000000..2ff3e34
--- /dev/null
+++ b/jgvariant-ostree/src/test/resources/ostree/delta-indexes/Zv/8Wf_Nc6H2qyBdEepSQomLudfCV8Bdxam6xqdnrM1A.index
Binary files differ
diff --git a/jgvariant-ostree/src/test/resources/ostree/deltas/Mc/iDXVydLGaHpQCRyFFC0bLYU_9Bap+4G07jB1RRDVI-PTszKdyjiHHymu2hv1hU12xwf6JpdZqJnQmFyRgV_m8/0 b/jgvariant-ostree/src/test/resources/ostree/deltas/Mc/iDXVydLGaHpQCRyFFC0bLYU_9Bap+4G07jB1RRDVI-PTszKdyjiHHymu2hv1hU12xwf6JpdZqJnQmFyRgV_m8/0
new file mode 100644
index 0000000..8fbcd13
--- /dev/null
+++ b/jgvariant-ostree/src/test/resources/ostree/deltas/Mc/iDXVydLGaHpQCRyFFC0bLYU_9Bap+4G07jB1RRDVI-PTszKdyjiHHymu2hv1hU12xwf6JpdZqJnQmFyRgV_m8/0
Binary files differ
diff --git a/jgvariant-ostree/src/test/resources/ostree/deltas/Mc/iDXVydLGaHpQCRyFFC0bLYU_9Bap+4G07jB1RRDVI-PTszKdyjiHHymu2hv1hU12xwf6JpdZqJnQmFyRgV_m8/superblock b/jgvariant-ostree/src/test/resources/ostree/deltas/Mc/iDXVydLGaHpQCRyFFC0bLYU_9Bap+4G07jB1RRDVI-PTszKdyjiHHymu2hv1hU12xwf6JpdZqJnQmFyRgV_m8/superblock
new file mode 100644
index 0000000..24b2739
--- /dev/null
+++ b/jgvariant-ostree/src/test/resources/ostree/deltas/Mc/iDXVydLGaHpQCRyFFC0bLYU_9Bap+4G07jB1RRDVI-PTszKdyjiHHymu2hv1hU12xwf6JpdZqJnQmFyRgV_m8/superblock
Binary files differ
diff --git a/jgvariant-ostree/src/test/resources/ostree/deltas/Mc/iDXVydLGaHpQCRyFFC0bLYU_9Bap+4G07jB1RRDVI/0 b/jgvariant-ostree/src/test/resources/ostree/deltas/Mc/iDXVydLGaHpQCRyFFC0bLYU_9Bap+4G07jB1RRDVI/0
new file mode 100644
index 0000000..86a5de4
--- /dev/null
+++ b/jgvariant-ostree/src/test/resources/ostree/deltas/Mc/iDXVydLGaHpQCRyFFC0bLYU_9Bap+4G07jB1RRDVI/0
Binary files differ
diff --git a/jgvariant-ostree/src/test/resources/ostree/deltas/Mc/iDXVydLGaHpQCRyFFC0bLYU_9Bap+4G07jB1RRDVI/superblock b/jgvariant-ostree/src/test/resources/ostree/deltas/Mc/iDXVydLGaHpQCRyFFC0bLYU_9Bap+4G07jB1RRDVI/superblock
new file mode 100644
index 0000000..6a8ea75
--- /dev/null
+++ b/jgvariant-ostree/src/test/resources/ostree/deltas/Mc/iDXVydLGaHpQCRyFFC0bLYU_9Bap+4G07jB1RRDVI/superblock
Binary files differ
diff --git a/jgvariant-ostree/src/test/resources/ostree/deltas/PT/szKdyjiHHymu2hv1hU12xwf6JpdZqJnQmFyRgV_m8-Zv8Wf_Nc6H2qyBdEepSQomLudfCV8Bdxam6xqdnrM1A/0 b/jgvariant-ostree/src/test/resources/ostree/deltas/PT/szKdyjiHHymu2hv1hU12xwf6JpdZqJnQmFyRgV_m8-Zv8Wf_Nc6H2qyBdEepSQomLudfCV8Bdxam6xqdnrM1A/0
new file mode 100644
index 0000000..483db33
--- /dev/null
+++ b/jgvariant-ostree/src/test/resources/ostree/deltas/PT/szKdyjiHHymu2hv1hU12xwf6JpdZqJnQmFyRgV_m8-Zv8Wf_Nc6H2qyBdEepSQomLudfCV8Bdxam6xqdnrM1A/0
Binary files differ
diff --git a/jgvariant-ostree/src/test/resources/ostree/deltas/PT/szKdyjiHHymu2hv1hU12xwf6JpdZqJnQmFyRgV_m8-Zv8Wf_Nc6H2qyBdEepSQomLudfCV8Bdxam6xqdnrM1A/superblock b/jgvariant-ostree/src/test/resources/ostree/deltas/PT/szKdyjiHHymu2hv1hU12xwf6JpdZqJnQmFyRgV_m8-Zv8Wf_Nc6H2qyBdEepSQomLudfCV8Bdxam6xqdnrM1A/superblock
new file mode 100644
index 0000000..219c92a
--- /dev/null
+++ b/jgvariant-ostree/src/test/resources/ostree/deltas/PT/szKdyjiHHymu2hv1hU12xwf6JpdZqJnQmFyRgV_m8-Zv8Wf_Nc6H2qyBdEepSQomLudfCV8Bdxam6xqdnrM1A/superblock
Binary files differ
diff --git a/jgvariant-ostree/src/test/resources/ostree/objects/14/c9b958ac59df4979095a3485b4da5a045fe8737ffdba8cfbfff24988b238f7.dirtree b/jgvariant-ostree/src/test/resources/ostree/objects/14/c9b958ac59df4979095a3485b4da5a045fe8737ffdba8cfbfff24988b238f7.dirtree
new file mode 100644
index 0000000..8f4b074
--- /dev/null
+++ b/jgvariant-ostree/src/test/resources/ostree/objects/14/c9b958ac59df4979095a3485b4da5a045fe8737ffdba8cfbfff24988b238f7.dirtree
Binary files differ
diff --git a/jgvariant-ostree/src/test/resources/ostree/objects/15/87cb76cddd6ecdd5830a0f218d6ba60a95e79643ed8764ad62cf18e62a0b59.file b/jgvariant-ostree/src/test/resources/ostree/objects/15/87cb76cddd6ecdd5830a0f218d6ba60a95e79643ed8764ad62cf18e62a0b59.file
new file mode 100644
index 0000000..3987f9c
--- /dev/null
+++ b/jgvariant-ostree/src/test/resources/ostree/objects/15/87cb76cddd6ecdd5830a0f218d6ba60a95e79643ed8764ad62cf18e62a0b59.file
@@ -0,0 +1 @@
+version 1.1
diff --git a/jgvariant-ostree/src/test/resources/ostree/objects/31/c8835d5c9d2c6687a50091c85142d1b2d853ff416a9fb81b4ee30754510d52.commit b/jgvariant-ostree/src/test/resources/ostree/objects/31/c8835d5c9d2c6687a50091c85142d1b2d853ff416a9fb81b4ee30754510d52.commit
new file mode 100644
index 0000000..bcd2731
--- /dev/null
+++ b/jgvariant-ostree/src/test/resources/ostree/objects/31/c8835d5c9d2c6687a50091c85142d1b2d853ff416a9fb81b4ee30754510d52.commit
Binary files differ
diff --git a/jgvariant-ostree/src/test/resources/ostree/objects/3d/3b3329dca38871f29aeda1bf5854d76c707fa269759a899d0985c91815fe6f.commit b/jgvariant-ostree/src/test/resources/ostree/objects/3d/3b3329dca38871f29aeda1bf5854d76c707fa269759a899d0985c91815fe6f.commit
new file mode 100644
index 0000000..c6ab7dc
--- /dev/null
+++ b/jgvariant-ostree/src/test/resources/ostree/objects/3d/3b3329dca38871f29aeda1bf5854d76c707fa269759a899d0985c91815fe6f.commit
Binary files differ
diff --git a/jgvariant-ostree/src/test/resources/ostree/objects/46/22b8c6cdcb4cbe29b8f79641f0304b30066596194b6b32981e46422d12c282.dirtree b/jgvariant-ostree/src/test/resources/ostree/objects/46/22b8c6cdcb4cbe29b8f79641f0304b30066596194b6b32981e46422d12c282.dirtree
new file mode 100644
index 0000000..196a4bc
--- /dev/null
+++ b/jgvariant-ostree/src/test/resources/ostree/objects/46/22b8c6cdcb4cbe29b8f79641f0304b30066596194b6b32981e46422d12c282.dirtree
Binary files differ
diff --git a/jgvariant-ostree/src/test/resources/ostree/objects/48/cc6a2ecdab284b9d1e5b0e875c905866ff32f65ee1e857df0e691285d6f14c.dirmeta b/jgvariant-ostree/src/test/resources/ostree/objects/48/cc6a2ecdab284b9d1e5b0e875c905866ff32f65ee1e857df0e691285d6f14c.dirmeta
new file mode 100644
index 0000000..4c76b11
--- /dev/null
+++ b/jgvariant-ostree/src/test/resources/ostree/objects/48/cc6a2ecdab284b9d1e5b0e875c905866ff32f65ee1e857df0e691285d6f14c.dirmeta
Binary files differ
diff --git a/jgvariant-ostree/src/test/resources/ostree/objects/66/ff167ff35ce87daac817447a9490a262ee75f095f017716a6eb1a9d9eb3350.commit b/jgvariant-ostree/src/test/resources/ostree/objects/66/ff167ff35ce87daac817447a9490a262ee75f095f017716a6eb1a9d9eb3350.commit
new file mode 100644
index 0000000..45aaf5f
--- /dev/null
+++ b/jgvariant-ostree/src/test/resources/ostree/objects/66/ff167ff35ce87daac817447a9490a262ee75f095f017716a6eb1a9d9eb3350.commit
Binary files differ
diff --git a/jgvariant-ostree/src/test/resources/ostree/objects/73/4ed4332dd46f0ec95395ea6b404f9a19eacfc74de9100e19842cbe9b960d0a.dirtree b/jgvariant-ostree/src/test/resources/ostree/objects/73/4ed4332dd46f0ec95395ea6b404f9a19eacfc74de9100e19842cbe9b960d0a.dirtree
new file mode 100644
index 0000000..d7e9e1f
--- /dev/null
+++ b/jgvariant-ostree/src/test/resources/ostree/objects/73/4ed4332dd46f0ec95395ea6b404f9a19eacfc74de9100e19842cbe9b960d0a.dirtree
Binary files differ
diff --git a/jgvariant-ostree/src/test/resources/ostree/objects/76/766e52e4a737646788570c8c44a3cf70b17ece81ce4c9b44f4f5869f138e8d.dirtree b/jgvariant-ostree/src/test/resources/ostree/objects/76/766e52e4a737646788570c8c44a3cf70b17ece81ce4c9b44f4f5869f138e8d.dirtree
new file mode 100644
index 0000000..7a4ee69
--- /dev/null
+++ b/jgvariant-ostree/src/test/resources/ostree/objects/76/766e52e4a737646788570c8c44a3cf70b17ece81ce4c9b44f4f5869f138e8d.dirtree
Binary files differ
diff --git a/jgvariant-ostree/src/test/resources/ostree/objects/84/2d6670d6c0d116a9723bd4329cacec722079177e886bd833f182500b879bfe.dirtree b/jgvariant-ostree/src/test/resources/ostree/objects/84/2d6670d6c0d116a9723bd4329cacec722079177e886bd833f182500b879bfe.dirtree
new file mode 100644
index 0000000..cf37b48
--- /dev/null
+++ b/jgvariant-ostree/src/test/resources/ostree/objects/84/2d6670d6c0d116a9723bd4329cacec722079177e886bd833f182500b879bfe.dirtree
Binary files differ
diff --git a/jgvariant-ostree/src/test/resources/ostree/objects/88/534f940aa700c0f5d470c86f699179bf11fe486f3a8514f56a9703355d761b.dirtree b/jgvariant-ostree/src/test/resources/ostree/objects/88/534f940aa700c0f5d470c86f699179bf11fe486f3a8514f56a9703355d761b.dirtree
new file mode 100644
index 0000000..1005c58
--- /dev/null
+++ b/jgvariant-ostree/src/test/resources/ostree/objects/88/534f940aa700c0f5d470c86f699179bf11fe486f3a8514f56a9703355d761b.dirtree
Binary files differ
diff --git a/jgvariant-ostree/src/test/resources/ostree/objects/8f/576d91bd42c0d7682000271d40aa27866537d477220b2b97f536ada9da0d7c.file b/jgvariant-ostree/src/test/resources/ostree/objects/8f/576d91bd42c0d7682000271d40aa27866537d477220b2b97f536ada9da0d7c.file
new file mode 100644
index 0000000..26af6a8
--- /dev/null
+++ b/jgvariant-ostree/src/test/resources/ostree/objects/8f/576d91bd42c0d7682000271d40aa27866537d477220b2b97f536ada9da0d7c.file
@@ -0,0 +1 @@
+zero
diff --git a/jgvariant-ostree/src/test/resources/ostree/objects/92/6fd84a0da031392cd97a07948238946894979695a25a3dd9da5fe3f719c98a.file b/jgvariant-ostree/src/test/resources/ostree/objects/92/6fd84a0da031392cd97a07948238946894979695a25a3dd9da5fe3f719c98a.file
new file mode 100644
index 0000000..f719efd
--- /dev/null
+++ b/jgvariant-ostree/src/test/resources/ostree/objects/92/6fd84a0da031392cd97a07948238946894979695a25a3dd9da5fe3f719c98a.file
@@ -0,0 +1 @@
+two
diff --git a/jgvariant-ostree/src/test/resources/ostree/objects/c9/af809d0778808c3b5f38bbac35d88da150cbb4cf3929b2e5ed2f9828080e08.file b/jgvariant-ostree/src/test/resources/ostree/objects/c9/af809d0778808c3b5f38bbac35d88da150cbb4cf3929b2e5ed2f9828080e08.file
new file mode 100644
index 0000000..5626abf
--- /dev/null
+++ b/jgvariant-ostree/src/test/resources/ostree/objects/c9/af809d0778808c3b5f38bbac35d88da150cbb4cf3929b2e5ed2f9828080e08.file
@@ -0,0 +1 @@
+one
diff --git a/jgvariant-ostree/src/test/resources/ostree/objects/cf/ff1525790c356cf268894ce6cabda5c5aa6fabc8b2becb39faf2d195f8ebaa.dirtree b/jgvariant-ostree/src/test/resources/ostree/objects/cf/ff1525790c356cf268894ce6cabda5c5aa6fabc8b2becb39faf2d195f8ebaa.dirtree
new file mode 100644
index 0000000..b568e92
--- /dev/null
+++ b/jgvariant-ostree/src/test/resources/ostree/objects/cf/ff1525790c356cf268894ce6cabda5c5aa6fabc8b2becb39faf2d195f8ebaa.dirtree
Binary files differ
diff --git a/jgvariant-ostree/src/test/resources/ostree/objects/d3/365463d0acd8eda475a874a01dae6b7f20b5ce8864e36fa27d485a7b821d6d.file b/jgvariant-ostree/src/test/resources/ostree/objects/d3/365463d0acd8eda475a874a01dae6b7f20b5ce8864e36fa27d485a7b821d6d.file
new file mode 100644
index 0000000..7c8de03
--- /dev/null
+++ b/jgvariant-ostree/src/test/resources/ostree/objects/d3/365463d0acd8eda475a874a01dae6b7f20b5ce8864e36fa27d485a7b821d6d.file
@@ -0,0 +1 @@
+version 1.0
diff --git a/jgvariant-ostree/src/test/resources/ostree/objects/df/f640414799416e7a874eaec41471905adaf18b694ec537fb99e662626864f3.file b/jgvariant-ostree/src/test/resources/ostree/objects/df/f640414799416e7a874eaec41471905adaf18b694ec537fb99e662626864f3.file
new file mode 100644
index 0000000..ef7df68
--- /dev/null
+++ b/jgvariant-ostree/src/test/resources/ostree/objects/df/f640414799416e7a874eaec41471905adaf18b694ec537fb99e662626864f3.file
@@ -0,0 +1 @@
+version 1.2
diff --git a/jgvariant-ostree/src/test/resources/ostree/objects/e0/83c4299293b067841a51af2c9156dc3b35f95af27773147b0458934ca37d4f.file b/jgvariant-ostree/src/test/resources/ostree/objects/e0/83c4299293b067841a51af2c9156dc3b35f95af27773147b0458934ca37d4f.file
new file mode 100644
index 0000000..2bdf67a
--- /dev/null
+++ b/jgvariant-ostree/src/test/resources/ostree/objects/e0/83c4299293b067841a51af2c9156dc3b35f95af27773147b0458934ca37d4f.file
@@ -0,0 +1 @@
+three
diff --git a/jgvariant-ostree/src/test/resources/ostree/refs/heads/mulkos/1.x/amd64 b/jgvariant-ostree/src/test/resources/ostree/refs/heads/mulkos/1.x/amd64
new file mode 100644
index 0000000..82901ea
--- /dev/null
+++ b/jgvariant-ostree/src/test/resources/ostree/refs/heads/mulkos/1.x/amd64
@@ -0,0 +1 @@
+66ff167ff35ce87daac817447a9490a262ee75f095f017716a6eb1a9d9eb3350
diff --git a/jgvariant-ostree/src/test/resources/ostree/summary b/jgvariant-ostree/src/test/resources/ostree/summary
new file mode 100644
index 0000000..4bd06af
--- /dev/null
+++ b/jgvariant-ostree/src/test/resources/ostree/summary
Binary files differ
diff --git a/jgvariant-parent/pom.xml b/jgvariant-parent/pom.xml
index f91339a..62ce51b 100644
--- a/jgvariant-parent/pom.xml
+++ b/jgvariant-parent/pom.xml
@@ -63,6 +63,7 @@
<apiguardian.version>1.1.2</apiguardian.version>
<errorprone.version>2.10.0</errorprone.version>
<google-java-format.version>1.13.0</google-java-format.version>
+ <inject-resources.version>0.3.0</inject-resources.version>
<jetbrains-annotations.version>22.0.0</jetbrains-annotations.version>
<junit-jupiter.version>5.8.2</junit-jupiter.version>
</properties>
@@ -106,6 +107,12 @@
<version>${junit-jupiter.version}</version>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>io.hosuaby</groupId>
+ <artifactId>inject-resources-junit-jupiter</artifactId>
+ <version>${inject-resources.version}</version>
+ <scope>test</scope>
+ </dependency>
</dependencies>
</dependencyManagement>
diff --git a/pom.xml b/pom.xml
index f891dc9..730648f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -28,6 +28,7 @@
<module>jgvariant-parent</module>
<module>jgvariant-core</module>
+ <module>jgvariant-ostree</module>
<module>jgvariant-bom</module>
</modules>