mirror of
https://codeberg.org/PGPainless/sop-java.git
synced 2024-12-22 12:57:57 +01:00
Kotlin conversion: SopCLI
This commit is contained in:
parent
0c2cf5cb19
commit
baa44a6b1a
4 changed files with 114 additions and 138 deletions
|
@ -1,129 +0,0 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
package sop.cli.picocli;
|
|
||||||
|
|
||||||
import picocli.AutoComplete;
|
|
||||||
import picocli.CommandLine;
|
|
||||||
import sop.SOP;
|
|
||||||
import sop.cli.picocli.commands.ArmorCmd;
|
|
||||||
import sop.cli.picocli.commands.ChangeKeyPasswordCmd;
|
|
||||||
import sop.cli.picocli.commands.DearmorCmd;
|
|
||||||
import sop.cli.picocli.commands.DecryptCmd;
|
|
||||||
import sop.cli.picocli.commands.InlineDetachCmd;
|
|
||||||
import sop.cli.picocli.commands.EncryptCmd;
|
|
||||||
import sop.cli.picocli.commands.ExtractCertCmd;
|
|
||||||
import sop.cli.picocli.commands.GenerateKeyCmd;
|
|
||||||
import sop.cli.picocli.commands.InlineSignCmd;
|
|
||||||
import sop.cli.picocli.commands.InlineVerifyCmd;
|
|
||||||
import sop.cli.picocli.commands.ListProfilesCmd;
|
|
||||||
import sop.cli.picocli.commands.RevokeKeyCmd;
|
|
||||||
import sop.cli.picocli.commands.SignCmd;
|
|
||||||
import sop.cli.picocli.commands.VerifyCmd;
|
|
||||||
import sop.cli.picocli.commands.VersionCmd;
|
|
||||||
import sop.exception.SOPGPException;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.ResourceBundle;
|
|
||||||
|
|
||||||
@CommandLine.Command(
|
|
||||||
name = "sop",
|
|
||||||
resourceBundle = "msg_sop",
|
|
||||||
exitCodeOnInvalidInput = SOPGPException.UnsupportedSubcommand.EXIT_CODE,
|
|
||||||
subcommands = {
|
|
||||||
// Meta Subcommands
|
|
||||||
VersionCmd.class,
|
|
||||||
ListProfilesCmd.class,
|
|
||||||
// Key and Certificate Management Subcommands
|
|
||||||
GenerateKeyCmd.class,
|
|
||||||
ChangeKeyPasswordCmd.class,
|
|
||||||
RevokeKeyCmd.class,
|
|
||||||
ExtractCertCmd.class,
|
|
||||||
// Messaging Subcommands
|
|
||||||
SignCmd.class,
|
|
||||||
VerifyCmd.class,
|
|
||||||
EncryptCmd.class,
|
|
||||||
DecryptCmd.class,
|
|
||||||
InlineDetachCmd.class,
|
|
||||||
InlineSignCmd.class,
|
|
||||||
InlineVerifyCmd.class,
|
|
||||||
// Transport Subcommands
|
|
||||||
ArmorCmd.class,
|
|
||||||
DearmorCmd.class,
|
|
||||||
// Miscellaneous Subcommands
|
|
||||||
CommandLine.HelpCommand.class,
|
|
||||||
AutoComplete.GenerateCompletion.class
|
|
||||||
}
|
|
||||||
)
|
|
||||||
public class SopCLI {
|
|
||||||
// Singleton
|
|
||||||
static SOP SOP_INSTANCE;
|
|
||||||
static ResourceBundle cliMsg = ResourceBundle.getBundle("msg_sop");
|
|
||||||
|
|
||||||
public static String EXECUTABLE_NAME = "sop";
|
|
||||||
|
|
||||||
@CommandLine.Option(names = {"--stacktrace"},
|
|
||||||
scope = CommandLine.ScopeType.INHERIT)
|
|
||||||
static boolean stacktrace;
|
|
||||||
|
|
||||||
public static void main(String[] args) {
|
|
||||||
int exitCode = execute(args);
|
|
||||||
if (exitCode != 0) {
|
|
||||||
System.exit(exitCode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int execute(String[] args) {
|
|
||||||
|
|
||||||
// Set locale
|
|
||||||
new CommandLine(new InitLocale()).parseArgs(args);
|
|
||||||
|
|
||||||
// get error message bundle
|
|
||||||
cliMsg = ResourceBundle.getBundle("msg_sop");
|
|
||||||
|
|
||||||
// Prepare CLI
|
|
||||||
CommandLine cmd = new CommandLine(SopCLI.class);
|
|
||||||
|
|
||||||
// explicitly set help command resource bundle
|
|
||||||
cmd.getSubcommands().get("help").setResourceBundle(ResourceBundle.getBundle("msg_help"));
|
|
||||||
|
|
||||||
// Hide generate-completion command
|
|
||||||
cmd.getSubcommands().get("generate-completion").getCommandSpec().usageMessage().hidden(true);
|
|
||||||
|
|
||||||
cmd.setCommandName(EXECUTABLE_NAME)
|
|
||||||
.setExecutionExceptionHandler(new SOPExecutionExceptionHandler())
|
|
||||||
.setExitCodeExceptionMapper(new SOPExceptionExitCodeMapper())
|
|
||||||
.setCaseInsensitiveEnumValuesAllowed(true);
|
|
||||||
|
|
||||||
return cmd.execute(args);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static SOP getSop() {
|
|
||||||
if (SOP_INSTANCE == null) {
|
|
||||||
String errorMsg = cliMsg.getString("sop.error.runtime.no_backend_set");
|
|
||||||
throw new IllegalStateException(errorMsg);
|
|
||||||
}
|
|
||||||
return SOP_INSTANCE;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void setSopInstance(SOP instance) {
|
|
||||||
SOP_INSTANCE = instance;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Control the locale.
|
|
||||||
*
|
|
||||||
* @see <a href="https://picocli.info/#_controlling_the_locale">Picocli Readme</a>
|
|
||||||
*/
|
|
||||||
class InitLocale {
|
|
||||||
@CommandLine.Option(names = { "-l", "--locale" }, descriptionKey = "sop.locale")
|
|
||||||
void setLocale(String locale) {
|
|
||||||
Locale.setDefault(new Locale(locale));
|
|
||||||
}
|
|
||||||
|
|
||||||
@CommandLine.Unmatched
|
|
||||||
List<String> remainder; // ignore any other parameters and options in the first parsing phase
|
|
||||||
}
|
|
|
@ -1,8 +0,0 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implementation of the Stateless OpenPGP Command Line Interface using Picocli.
|
|
||||||
*/
|
|
||||||
package sop.cli.picocli;
|
|
113
sop-java-picocli/src/main/kotlin/sop/cli/picocli/SopCLI.kt
Normal file
113
sop-java-picocli/src/main/kotlin/sop/cli/picocli/SopCLI.kt
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
// SPDX-FileCopyrightText: 2023 Paul Schaub <vanitasvitae@fsfe.org>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package sop.cli.picocli
|
||||||
|
|
||||||
|
import java.util.*
|
||||||
|
import kotlin.system.exitProcess
|
||||||
|
import picocli.AutoComplete.GenerateCompletion
|
||||||
|
import picocli.CommandLine
|
||||||
|
import picocli.CommandLine.*
|
||||||
|
import sop.SOP
|
||||||
|
import sop.cli.picocli.commands.*
|
||||||
|
import sop.exception.SOPGPException
|
||||||
|
|
||||||
|
@Command(
|
||||||
|
name = "sop",
|
||||||
|
resourceBundle = "msg_sop",
|
||||||
|
exitCodeOnInvalidInput = SOPGPException.UnsupportedSubcommand.EXIT_CODE,
|
||||||
|
subcommands =
|
||||||
|
[
|
||||||
|
// Meta subcommands
|
||||||
|
VersionCmd::class,
|
||||||
|
ListProfilesCmd::class,
|
||||||
|
// Key and certificate management
|
||||||
|
GenerateKeyCmd::class,
|
||||||
|
ChangeKeyPasswordCmd::class,
|
||||||
|
RevokeKeyCmd::class,
|
||||||
|
ExtractCertCmd::class,
|
||||||
|
// Messaging subcommands
|
||||||
|
SignCmd::class,
|
||||||
|
VerifyCmd::class,
|
||||||
|
EncryptCmd::class,
|
||||||
|
DecryptCmd::class,
|
||||||
|
InlineDetachCmd::class,
|
||||||
|
InlineSignCmd::class,
|
||||||
|
InlineVerifyCmd::class,
|
||||||
|
// Transport
|
||||||
|
ArmorCmd::class,
|
||||||
|
DearmorCmd::class,
|
||||||
|
// misc
|
||||||
|
HelpCommand::class,
|
||||||
|
GenerateCompletion::class])
|
||||||
|
class SopCLI {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
@JvmStatic private var sopInstance: SOP? = null
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun getSop(): SOP =
|
||||||
|
checkNotNull(sopInstance) { cliMsg.getString("sop.error.runtime.no_backend_set") }
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun setSopInstance(sop: SOP?) {
|
||||||
|
sopInstance = sop
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmField var cliMsg: ResourceBundle = ResourceBundle.getBundle("msg_sop")
|
||||||
|
|
||||||
|
@JvmField var EXECUTABLE_NAME = "sop"
|
||||||
|
|
||||||
|
@JvmField
|
||||||
|
@Option(names = ["--stacktrace"], scope = CommandLine.ScopeType.INHERIT)
|
||||||
|
var stacktrace = false
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun main(vararg args: String) {
|
||||||
|
val exitCode = execute(*args)
|
||||||
|
if (exitCode != 0) {
|
||||||
|
exitProcess(exitCode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun execute(vararg args: String): Int {
|
||||||
|
// Set locale
|
||||||
|
CommandLine(InitLocale()).parseArgs(*args)
|
||||||
|
|
||||||
|
// Re-set bundle with updated locale
|
||||||
|
cliMsg = ResourceBundle.getBundle("msg_sop")
|
||||||
|
|
||||||
|
return CommandLine(SopCLI::class.java)
|
||||||
|
.apply {
|
||||||
|
// explicitly set help command resource bundle
|
||||||
|
subcommands["help"]?.setResourceBundle(ResourceBundle.getBundle("msg_help"))
|
||||||
|
// Hide generate-completion command
|
||||||
|
subcommands["generate-completion"]?.commandSpec?.usageMessage()?.hidden(true)
|
||||||
|
// overwrite executable name
|
||||||
|
commandName = EXECUTABLE_NAME
|
||||||
|
// setup exception handling
|
||||||
|
executionExceptionHandler = SOPExecutionExceptionHandler()
|
||||||
|
exitCodeExceptionMapper = SOPExceptionExitCodeMapper()
|
||||||
|
isCaseInsensitiveEnumValuesAllowed = true
|
||||||
|
}
|
||||||
|
.execute(*args)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Control the locale.
|
||||||
|
*
|
||||||
|
* @see <a href="https://picocli.info/#_controlling_the_locale">Picocli Readme</a>
|
||||||
|
*/
|
||||||
|
@Command
|
||||||
|
class InitLocale {
|
||||||
|
@Option(names = ["-l", "--locale"], descriptionKey = "sop.locale")
|
||||||
|
fun setLocale(locale: String) = Locale.setDefault(Locale(locale))
|
||||||
|
|
||||||
|
@Unmatched
|
||||||
|
var remainder: MutableList<String> =
|
||||||
|
mutableListOf() // ignore any other parameters and options in the first parsing phase
|
||||||
|
}
|
||||||
|
}
|
|
@ -45,7 +45,7 @@ public class SOPTest {
|
||||||
@Test
|
@Test
|
||||||
@ExpectSystemExitWithStatus(1)
|
@ExpectSystemExitWithStatus(1)
|
||||||
public void assertThrowsIfNoSOPBackendSet() {
|
public void assertThrowsIfNoSOPBackendSet() {
|
||||||
SopCLI.SOP_INSTANCE = null;
|
SopCLI.setSopInstance(null);
|
||||||
// At this point, no SOP backend is set, so an InvalidStateException triggers exit(1)
|
// At this point, no SOP backend is set, so an InvalidStateException triggers exit(1)
|
||||||
SopCLI.main(new String[] {"armor"});
|
SopCLI.main(new String[] {"armor"});
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue