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 static picocli.CommandLine.ScopeType.INHERIT;
021import static picocli.CommandLine.Spec.Target.MIXEE;
022
023import dev.enola.common.Version;
024import dev.enola.common.logging.JavaUtilLogging;
025
026import picocli.CommandLine;
027import picocli.CommandLine.Model.CommandSpec;
028import picocli.CommandLine.Option;
029import picocli.CommandLine.Spec;
030
031import java.util.logging.Level;
032
033public class LoggingMixin {
034
035    // https://picocli.info/#_use_case_configure_log_level_with_a_global_option
036
037    @Spec(MIXEE)
038    CommandSpec mixee;
039
040    private static Level calcLogLevel(int level) {
041        switch (level) {
042            case 0:
043                return Level.OFF;
044            case 1:
045                return Level.SEVERE; // AKA JUL ERROR
046            case 2:
047                return Level.WARNING;
048            case 3:
049                return Level.CONFIG; // incl. JUL & SLF4j INFO
050            case 4:
051                return Level.FINE; // incl. SLF4j DEBUG
052            case 5:
053                return Level.FINER; // unchanged for SLF4j
054            case 6:
055                return Level.FINEST; // incl. SLF4j TRACE
056        }
057        return Level.ALL;
058    }
059
060    public static int executionStrategy(CommandLine.ParseResult parseResult) {
061        var app = (Application) parseResult.commandSpec().root().userObject();
062        var level = calcLogLevel(app.loggingVerbosity);
063
064        JavaUtilLogging.configure(level);
065        app.loggingIsConfigured();
066        app.log()
067                .error(
068                        """
069                        Hi! \uD83D\uDC4B I'm https://Enola.dev {}. \
070                        \uD83D\uDC7D Resistance \uD83D\uDC7E is futile. We are ONE. \
071                        What's your goal, today? \
072                        """,
073                        Version.get());
074
075        app.start();
076
077        // And now back to and onwards with the default execution strategy
078        return new CommandLine.RunLast().execute(parseResult);
079    }
080
081    @Option(
082            names = {"--verbose", "-v"},
083            scope = INHERIT,
084            description = {
085                "Error verbosity; specify multiple -v options to increase it; e.g. -v -v -v or"
086                        + " -vvv."
087            })
088    public void setVerbose(boolean[] verbosity) {
089        var app = (Application) mixee.root().userObject();
090        app.loggingVerbosity = verbosity.length;
091    }
092
093    // NB Because slf4j-jdk14-*.jar is on the classpath,
094    // we do not need to configure SLF4j, only java.util.logging;
095    // see https://www.slf4j.org/manual.html#swapping.
096}