Picocli: лучший способ указать опцию с необязательным значением, которое печатает текущее значение, когда значение не указано

Я пишу REPL (поэтому я использую picocli для анализа команд, введенных в приложении, а не для анализа аргументов командной строки), и у меня есть команда с опцией, которую я хочу вести следующим образом:

> cmd --myopt
Myopt value = 5
> cmd --myopt 4
> cmd --myopt
Myopt value = 4

То есть, если опция указана без значения, печатается текущее значение параметра, но если оно указано со значением, то значение устанавливается. Я думал сделать это так:

int value = 1; // default
@Option(names = {"-e", "--epsilon"}, arity = "0..1",
        description = "Acceptable values: [0, 1] default: ${DEFAULT-VALUE}")
void setValue(String strValue) {
    if (strValue == "") {
        printValue();
    } else {
        try {
            value = Integer.parseInt(strValue);
            // validate value
        } catch (NumberFormatException e) {
            // print help for this option
        }
    }
}

Это лучший способ? Есть ли другой способ зафиксировать значение по умолчанию в описании, при этом позволяя setValue знать, что значение не указано?

(См. Также https://github.com/remkop/picocli/issues/490).

2 ответа

Я фактически закончил тем, что использовал другой подход; Моему приложению полезно иметь возможность напрямую назначать полю фактического типа (поскольку мы разрабатываем функцию, в которой вы можете "обнаружить" команды, принимающие аргументы различных типов, поэтому наличие поля в качестве фактического типа делает это обратный поиск проще).

Итак, я закончил этим:

static class DoubleConverter implements ITypeConverter<Double> {
    public Double convert(String value) throws Exception {
        if(value.isEmpty()) return Double.NaN; // this is a special value that indicates the option was present without a value
        return Double.valueOf(value);
    }
}

@Option(names = {"-e", "--epsilon"}, arity="0..1", description="Acceptable values: [0, 1] default: 0.1", converter=DoubleConverter.class)
Double epsilon;

По сути, я использую конвертер для хранения специального значения (в данном случае NaN, потому что в итоге мы использовали удвоение), чтобы указать, что опция присутствовала без значения (что отличается от его отсутствия вообще, и в этом случае это было бы ноль).

Затем проверка и другое поведение выполняется в run() метод, как вы предложили:

@Override
public void run() {
    // null indicates the option was not present, so do nothing
    if(epsilon != null) {
        // NaN indicates the option was present but with no value, which means we should print the current value
        if(epsilon.equals(Double.NaN)) {
            // print current value from the application
            printEpsilonValue();
        }
        else {
            // validate value
            if(epsilon < 0.0 || epsilon > 1.0) {
                throw new ParameterException(spec.commandLine(), "Invalid parameter value");
            } else {
                // set the value in the application
                setEpsilonValue(episilon);
            }
        }
    }       
}

Я не смог указать значение по умолчанию в описании, используя переменную, потому что фактическое значение по умолчанию в этом случае должно быть нулевым. Это небольшая жертва, хотя.

Я понимаю, что это необычный случай, но было бы неплохо поддерживать этот тип опции (не булево с арностью 0..n) без необходимости прибегать к специальным значениям. Возможно, возможность указать другое поле, которое будет служить логическим значением, которое указывает, присутствовал ли параметр или нет. Тогда не было бы необходимости в настраиваемом конвертере, и, возможно, по-прежнему можно было бы указать значение по умолчанию (т. Е. Поле Double в этом случае получило бы значение по умолчанию, но если опция не присутствовала, соответствующее булево поле быть ложным, поэтому приложение будет знать, что не следует использовать значение Double).

Это, безусловно, один из способов сделать это. Имейте в виду, что метод установки может вызываться несколько раз: один раз для сброса значений по умолчанию, а затем снова каждый раз, когда параметр соответствует командной строке.

Альтернативой является изменение типа поля на Stringпоставь @Option аннотации на поле, и назовите эту логику (то есть в setValue метод выше) от вашего run или же call метод.

Другие вопросы по тегам