001/* 002 * SPDX-License-Identifier: Apache-2.0 003 * 004 * Copyright 2023-2026 The Enola <https://enola.dev> Authors 005 * 006 * Licensed under the Apache License, Version 2.0 (the "License"); 007 * you may not use this file except in compliance with the License. 008 * You may obtain a copy of the License at 009 * 010 * https://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package dev.enola.cli.common; 019 020import com.google.common.annotations.VisibleForTesting; 021 022import picocli.CommandLine; 023 024import java.io.PrintWriter; 025import java.io.StringWriter; 026import java.time.ZoneId; 027import java.util.Arrays; 028import java.util.Locale; 029 030public class CLI { 031 032 private String[] args; 033 private final CommandLine commandLine; 034 private StringWriter out; 035 private StringWriter err; 036 037 private Integer exitCode; 038 039 public CLI(String[] args, Application app) { 040 this( 041 args, 042 new CommandLine(app) 043 .setUsageHelpAutoWidth(true) 044 .setCaseInsensitiveEnumValuesAllowed(true) 045 .registerConverter(Locale.class, new LocaleConverter()) 046 .registerConverter(ZoneId.class, new ZoneIdConverter()) 047 .setExecutionStrategy(LoggingMixin::executionStrategy) 048 .setExitCodeExceptionMapper(new KnownExitCodeExceptionMapper()) 049 .setExecutionExceptionHandler(new QuietExecutionExceptionHandler(app))); 050 } 051 052 public CLI(String[] args, CommandLine commandLine) { 053 this.commandLine = commandLine; 054 this.args = Arrays.copyOf(args, args.length); 055 } 056 057 @VisibleForTesting 058 public void setArgs(String[] args) { 059 this.args = args; 060 } 061 062 public CLI setOut(PrintWriter out) { 063 commandLine.setOut(out); 064 return this; 065 } 066 067 public CLI setErr(PrintWriter err) { 068 commandLine.setErr(err); 069 return this; 070 } 071 072 public void setOutAndErrStrings() { 073 out = new StringWriter(); 074 setOut(new PrintWriter(out)); 075 076 err = new StringWriter(); 077 setErr(new PrintWriter(err)); 078 } 079 080 public String getOutString() { 081 commandLine.getOut().flush(); 082 out.flush(); 083 return out.toString(); 084 } 085 086 public String getErrString() { 087 commandLine.getErr().flush(); 088 err.flush(); 089 return err.toString(); 090 } 091 092 public int execute() { 093 this.exitCode = commandLine.execute(args); 094 commandLine.getOut().flush(); 095 commandLine.getErr().flush(); 096 return exitCode; 097 } 098 099 public int exitCode() { 100 if (exitCode == null) { 101 throw new IllegalStateException("Must execute() first!"); 102 } 103 return exitCode; 104 } 105 106 @Override 107 public String toString() { 108 return "CLI{" + "args=" + Arrays.toString(args) + ", exitCode=" + exitCode + '}'; 109 } 110}