diff options
Diffstat (limited to 'jgvariant-tool/src/main')
9 files changed, 313 insertions, 0 deletions
diff --git a/jgvariant-tool/src/main/java/eu/mulk/jgvariant/tool/Main.java b/jgvariant-tool/src/main/java/eu/mulk/jgvariant/tool/Main.java new file mode 100644 index 0000000..64e375a --- /dev/null +++ b/jgvariant-tool/src/main/java/eu/mulk/jgvariant/tool/Main.java @@ -0,0 +1,28 @@ +// SPDX-FileCopyrightText: © 2023 Matthias Andreas Benkard <code@mail.matthias.benkard.de> +// +// SPDX-License-Identifier: GPL-3.0-or-later + +package eu.mulk.jgvariant.tool; + +import static java.util.logging.Level.WARNING; + +import java.util.logging.Logger; +import picocli.CommandLine; + +/** + * A command line tool to read and manipulate GVariant-formatted files. + * + * <p>Also provides ways to manipulate OSTree repositories. + */ +public final class Main { + static { + Logger.getGlobal().setLevel(WARNING); + } + + public static void main(String[] args) { + int exitCode = new CommandLine(new MainCommand()).execute(args); + System.exit(exitCode); + } + + private Main() {} +} diff --git a/jgvariant-tool/src/main/java/eu/mulk/jgvariant/tool/MainCommand.java b/jgvariant-tool/src/main/java/eu/mulk/jgvariant/tool/MainCommand.java new file mode 100644 index 0000000..7b25dfc --- /dev/null +++ b/jgvariant-tool/src/main/java/eu/mulk/jgvariant/tool/MainCommand.java @@ -0,0 +1,115 @@ +// SPDX-FileCopyrightText: © 2023 Matthias Andreas Benkard <code@mail.matthias.benkard.de> +// +// SPDX-License-Identifier: GPL-3.0-or-later + +package eu.mulk.jgvariant.tool; + +import static java.util.logging.Level.*; + +import eu.mulk.jgvariant.ostree.Summary; +import eu.mulk.jgvariant.tool.jsonb.*; +import jakarta.json.bind.Jsonb; +import jakarta.json.bind.JsonbBuilder; +import jakarta.json.bind.JsonbConfig; +import java.io.File; +import java.io.IOException; +import java.io.PrintWriter; +import java.nio.ByteBuffer; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.util.logging.Logger; +import org.jetbrains.annotations.VisibleForTesting; +import picocli.AutoComplete; +import picocli.CommandLine; +import picocli.CommandLine.*; + +@Command( + name = "jgvariant", + mixinStandardHelpOptions = true, + description = "Manipulate files in GVariant format.", + subcommands = {MainCommand.OstreeCommand.class, AutoComplete.GenerateCompletion.class}) +final class MainCommand { + + private static final Logger LOG = Logger.getLogger("eu.mulk.jgvariant.tool"); + + private static final Jsonb jsonb = + JsonbBuilder.newBuilder() + .withConfig( + new JsonbConfig() + .withFormatting(true) + .withAdapters(ChecksumAdapter.INSTANCE) + .withSerializers( + ByteStringSerializer.INSTANCE, + ByteArraySerializer.INSTANCE, + SignatureSerializer.INSTANCE, + VariantSerializer.INSTANCE)) + .build(); + + @Option( + names = {"-v", "--verbose"}, + description = "Enable verbose logging.", + scope = CommandLine.ScopeType.INHERIT) + void setVerbose(boolean[] verbose) { + Logger.getGlobal() + .setLevel( + switch (verbose.length) { + case 0 -> WARNING; + case 1 -> INFO; + case 2 -> FINE; + default -> ALL; + }); + } + + @Command( + name = "ostree", + mixinStandardHelpOptions = true, + description = "Manipulate OSTree files.", + subcommands = {OstreeCommand.SummaryCommand.class}) + static final class OstreeCommand { + + @Command( + name = "summary", + mixinStandardHelpOptions = true, + description = "Manipulate OSTree summary files.") + static final class SummaryCommand extends BaseCommand { + + @Command(mixinStandardHelpOptions = true) + void read(@Parameters(paramLabel = "<file>") File file) throws IOException { + LOG.fine(() -> "Reading file %s".formatted(file)); + var fileBytes = ByteBuffer.wrap(Files.readAllBytes(fs().getPath(file.getPath()))); + var decoder = Summary.decoder(); + var thing = decoder.decode(fileBytes); + out().println(jsonb.toJson(thing)); + } + + SummaryCommand() {} + } + + OstreeCommand() {} + } + + @Command + abstract static class BaseCommand { + + @Spec CommandLine.Model.CommandSpec spec; + + @VisibleForTesting FileSystem fs = FileSystems.getDefault(); + + protected BaseCommand() {} + + protected PrintWriter out() { + return spec.commandLine().getOut(); + } + + protected PrintWriter err() { + return spec.commandLine().getErr(); + } + + protected FileSystem fs() { + return fs; + } + } + + MainCommand() {} +} diff --git a/jgvariant-tool/src/main/java/eu/mulk/jgvariant/tool/jsonb/ByteArraySerializer.java b/jgvariant-tool/src/main/java/eu/mulk/jgvariant/tool/jsonb/ByteArraySerializer.java new file mode 100644 index 0000000..7f80440 --- /dev/null +++ b/jgvariant-tool/src/main/java/eu/mulk/jgvariant/tool/jsonb/ByteArraySerializer.java @@ -0,0 +1,24 @@ +// SPDX-FileCopyrightText: © 2023 Matthias Andreas Benkard <code@mail.matthias.benkard.de> +// +// SPDX-License-Identifier: GPL-3.0-or-later + +package eu.mulk.jgvariant.tool.jsonb; + +import jakarta.json.bind.serializer.JsonbSerializer; +import jakarta.json.bind.serializer.SerializationContext; +import jakarta.json.stream.JsonGenerator; +import java.util.HexFormat; + +@SuppressWarnings("java:S6548") +public final class ByteArraySerializer implements JsonbSerializer<byte[]> { + + public static final ByteArraySerializer INSTANCE = new ByteArraySerializer(); + + private ByteArraySerializer() {} + + @Override + public void serialize( + byte[] o, JsonGenerator jsonGenerator, SerializationContext serializationContext) { + jsonGenerator.write(HexFormat.of().formatHex(o)); + } +} diff --git a/jgvariant-tool/src/main/java/eu/mulk/jgvariant/tool/jsonb/ByteStringSerializer.java b/jgvariant-tool/src/main/java/eu/mulk/jgvariant/tool/jsonb/ByteStringSerializer.java new file mode 100644 index 0000000..3537c3d --- /dev/null +++ b/jgvariant-tool/src/main/java/eu/mulk/jgvariant/tool/jsonb/ByteStringSerializer.java @@ -0,0 +1,24 @@ +// SPDX-FileCopyrightText: © 2023 Matthias Andreas Benkard <code@mail.matthias.benkard.de> +// +// SPDX-License-Identifier: GPL-3.0-or-later + +package eu.mulk.jgvariant.tool.jsonb; + +import eu.mulk.jgvariant.ostree.ByteString; +import jakarta.json.bind.serializer.JsonbSerializer; +import jakarta.json.bind.serializer.SerializationContext; +import jakarta.json.stream.JsonGenerator; + +@SuppressWarnings("java:S6548") +public final class ByteStringSerializer implements JsonbSerializer<ByteString> { + + public static final ByteStringSerializer INSTANCE = new ByteStringSerializer(); + + private ByteStringSerializer() {} + + @Override + public void serialize( + ByteString o, JsonGenerator jsonGenerator, SerializationContext serializationContext) { + jsonGenerator.write(o.hex()); + } +} diff --git a/jgvariant-tool/src/main/java/eu/mulk/jgvariant/tool/jsonb/ChecksumAdapter.java b/jgvariant-tool/src/main/java/eu/mulk/jgvariant/tool/jsonb/ChecksumAdapter.java new file mode 100644 index 0000000..58a8951 --- /dev/null +++ b/jgvariant-tool/src/main/java/eu/mulk/jgvariant/tool/jsonb/ChecksumAdapter.java @@ -0,0 +1,27 @@ +// SPDX-FileCopyrightText: © 2023 Matthias Andreas Benkard <code@mail.matthias.benkard.de> +// +// SPDX-License-Identifier: GPL-3.0-or-later + +package eu.mulk.jgvariant.tool.jsonb; + +import eu.mulk.jgvariant.ostree.ByteString; +import eu.mulk.jgvariant.ostree.Checksum; +import jakarta.json.bind.adapter.JsonbAdapter; + +@SuppressWarnings("java:S6548") +public final class ChecksumAdapter implements JsonbAdapter<Checksum, ByteString> { + + public static final ChecksumAdapter INSTANCE = new ChecksumAdapter(); + + private ChecksumAdapter() {} + + @Override + public ByteString adaptToJson(Checksum obj) throws Exception { + return obj.byteString(); + } + + @Override + public Checksum adaptFromJson(ByteString obj) throws Exception { + return new Checksum(obj); + } +} diff --git a/jgvariant-tool/src/main/java/eu/mulk/jgvariant/tool/jsonb/SignatureSerializer.java b/jgvariant-tool/src/main/java/eu/mulk/jgvariant/tool/jsonb/SignatureSerializer.java new file mode 100644 index 0000000..dca1044 --- /dev/null +++ b/jgvariant-tool/src/main/java/eu/mulk/jgvariant/tool/jsonb/SignatureSerializer.java @@ -0,0 +1,24 @@ +// SPDX-FileCopyrightText: © 2023 Matthias Andreas Benkard <code@mail.matthias.benkard.de> +// +// SPDX-License-Identifier: GPL-3.0-or-later + +package eu.mulk.jgvariant.tool.jsonb; + +import eu.mulk.jgvariant.core.Signature; +import jakarta.json.bind.serializer.JsonbSerializer; +import jakarta.json.bind.serializer.SerializationContext; +import jakarta.json.stream.JsonGenerator; + +@SuppressWarnings("java:S6548") +public final class SignatureSerializer implements JsonbSerializer<Signature> { + + public static final SignatureSerializer INSTANCE = new SignatureSerializer(); + + private SignatureSerializer() {} + + @Override + public void serialize( + Signature o, JsonGenerator jsonGenerator, SerializationContext serializationContext) { + jsonGenerator.write(o.toString()); + } +} diff --git a/jgvariant-tool/src/main/java/eu/mulk/jgvariant/tool/jsonb/VariantSerializer.java b/jgvariant-tool/src/main/java/eu/mulk/jgvariant/tool/jsonb/VariantSerializer.java new file mode 100644 index 0000000..99ff553 --- /dev/null +++ b/jgvariant-tool/src/main/java/eu/mulk/jgvariant/tool/jsonb/VariantSerializer.java @@ -0,0 +1,43 @@ +// SPDX-FileCopyrightText: © 2023 Matthias Andreas Benkard <code@mail.matthias.benkard.de> +// +// SPDX-License-Identifier: GPL-3.0-or-later + +package eu.mulk.jgvariant.tool.jsonb; + +import com.google.common.primitives.Bytes; +import eu.mulk.jgvariant.core.Signature; +import eu.mulk.jgvariant.core.Variant; +import jakarta.json.bind.serializer.JsonbSerializer; +import jakarta.json.bind.serializer.SerializationContext; +import jakarta.json.stream.JsonGenerator; +import java.text.ParseException; +import java.util.List; + +@SuppressWarnings("java:S6548") +public final class VariantSerializer implements JsonbSerializer<Variant> { + + public static final VariantSerializer INSTANCE = new VariantSerializer(); + + private final ByteArraySerializer byteArraySerializer = ByteArraySerializer.INSTANCE; + + private final Signature byteArraySignature; + + private VariantSerializer() { + try { + byteArraySignature = Signature.parse("ay"); + } catch (ParseException e) { + // impossible + throw new IllegalArgumentException(e); + } + } + + @Override + @SuppressWarnings("unchecked") + public void serialize(Variant obj, JsonGenerator generator, SerializationContext ctx) { + if (obj.signature().equals(byteArraySignature)) { + byteArraySerializer.serialize(Bytes.toArray((List<Byte>) obj.value()), generator, ctx); + } else { + ctx.serialize(obj.value(), generator); + } + } +} diff --git a/jgvariant-tool/src/main/java/eu/mulk/jgvariant/tool/package-info.java b/jgvariant-tool/src/main/java/eu/mulk/jgvariant/tool/package-info.java new file mode 100644 index 0000000..da5c3ba --- /dev/null +++ b/jgvariant-tool/src/main/java/eu/mulk/jgvariant/tool/package-info.java @@ -0,0 +1,8 @@ +// SPDX-FileCopyrightText: © 2023 Matthias Andreas Benkard <code@mail.matthias.benkard.de> +// +// SPDX-License-Identifier: GPL-3.0-or-later + +@API(status = API.Status.INTERNAL) +package eu.mulk.jgvariant.tool; + +import org.apiguardian.api.API; diff --git a/jgvariant-tool/src/main/java/module-info.java b/jgvariant-tool/src/main/java/module-info.java new file mode 100644 index 0000000..e66981c --- /dev/null +++ b/jgvariant-tool/src/main/java/module-info.java @@ -0,0 +1,20 @@ +// SPDX-FileCopyrightText: © 2023 Matthias Andreas Benkard <code@mail.matthias.benkard.de> +// +// SPDX-License-Identifier: GPL-3.0-or-later + +module eu.mulk.jgvariant.tool { + requires transitive eu.mulk.jgvariant.ostree; + requires com.google.common; + requires info.picocli; + requires jakarta.json; + requires jakarta.json.bind; + requires java.logging; + requires static com.google.errorprone.annotations; + requires static org.apiguardian.api; + requires static org.jetbrains.annotations; + + opens eu.mulk.jgvariant.tool to + info.picocli; + + exports eu.mulk.jgvariant.tool; +} |