blob: 897f9683d21b18e5bfd88dda97a67f72081247f4 [file] [log] [blame]
// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
/// Creates a new [Option].
///
/// Since [Option] doesn't have a public constructor, this lets `ArgParser`
/// get to it. This function isn't exported to the public API of the package.
Option newOption(
String name,
String abbr,
String help,
String valueHelp,
Iterable<String> allowed,
Map<String, String> allowedHelp,
defaultsTo,
Function callback,
OptionType type,
{bool negatable,
bool splitCommas,
bool hide = false}) {
return Option._(name, abbr, help, valueHelp, allowed, allowedHelp, defaultsTo,
callback, type,
negatable: negatable, splitCommas: splitCommas, hide: hide);
}
/// A command-line option.
///
/// This represents both boolean flags and options which take a value.
class Option {
/// The name of the option that the user passes as an argument.
final String name;
/// A single-character string that can be used as a shorthand for this option.
///
/// For example, `abbr: "a"` will allow the user to pass `-a value` or
/// `-avalue`.
final String abbr;
@Deprecated('Use abbr instead.')
String get abbreviation => abbr;
/// A description of this option.
final String help;
/// A name for the value this option takes.
final String valueHelp;
/// A list of valid values for this option.
final List<String> allowed;
/// A map from values in [allowed] to documentation for those values.
final Map<String, String> allowedHelp;
/// The value this option will have if the user doesn't explicitly pass it in
final dynamic defaultsTo;
@Deprecated('Use defaultsTo instead.')
dynamic get defaultValue => defaultsTo;
/// Whether this flag's value can be set to `false`.
///
/// For example, if [name] is `flag`, the user can pass `--no-flag` to set its
/// value to `false`.
///
/// This is `null` unless [type] is [OptionType.flag].
final bool negatable;
/// The callback to invoke with the option's value when the option is parsed.
final Function callback;
/// Whether this is a flag, a single value option, or a multi-value option.
final OptionType type;
/// Whether multiple values may be passed by writing `--option a,b` in
/// addition to `--option a --option b`.
final bool splitCommas;
/// Whether this option should be hidden from usage documentation.
final bool hide;
/// Whether the option is boolean-valued flag.
bool get isFlag => type == OptionType.flag;
/// Whether the option takes a single value.
bool get isSingle => type == OptionType.single;
/// Whether the option allows multiple values.
bool get isMultiple => type == OptionType.multiple;
Option._(
this.name,
this.abbr,
this.help,
this.valueHelp,
Iterable<String> allowed,
Map<String, String> allowedHelp,
this.defaultsTo,
this.callback,
OptionType type,
{this.negatable,
bool splitCommas,
this.hide = false})
: allowed = allowed == null ? null : List.unmodifiable(allowed),
allowedHelp =
allowedHelp == null ? null : Map.unmodifiable(allowedHelp),
type = type,
// If the user doesn't specify [splitCommas], it defaults to true for
// multiple options.
splitCommas = splitCommas ?? type == OptionType.multiple {
if (name.isEmpty) {
throw ArgumentError('Name cannot be empty.');
} else if (name.startsWith('-')) {
throw ArgumentError('Name $name cannot start with "-".');
}
// Ensure name does not contain any invalid characters.
if (_invalidChars.hasMatch(name)) {
throw ArgumentError('Name "$name" contains invalid characters.');
}
if (abbr != null) {
if (abbr.length != 1) {
throw ArgumentError('Abbreviation must be null or have length 1.');
} else if (abbr == '-') {
throw ArgumentError('Abbreviation cannot be "-".');
}
if (_invalidChars.hasMatch(abbr)) {
throw ArgumentError('Abbreviation is an invalid character.');
}
}
}
/// Returns [value] if non-`null`, otherwise returns the default value for
/// this option.
///
/// For single-valued options, it will be [defaultsTo] if set or `null`
/// otherwise. For multiple-valued options, it will be an empty list or a
/// list containing [defaultsTo] if set.
dynamic getOrDefault(value) {
if (value != null) return value;
if (isMultiple) return defaultsTo ?? <String>[];
return defaultsTo;
}
static final _invalidChars = RegExp(r'''[ \t\r\n"'\\/]''');
}
/// What kinds of values an option accepts.
class OptionType {
/// An option that can only be `true` or `false`.
///
/// The presence of the option name itself in the argument list means `true`.
static const flag = OptionType._('OptionType.flag');
@Deprecated('Use OptionType.flag instead.')
static const FLAG = flag; // ignore: constant_identifier_names
/// An option that takes a single value.
///
/// Examples:
///
/// --mode debug
/// -mdebug
/// --mode=debug
///
/// If the option is passed more than once, the last one wins.
static const single = OptionType._('OptionType.single');
@Deprecated('Use OptionType.single instead.')
static const SINGLE = single; // ignore: constant_identifier_names
/// An option that allows multiple values.
///
/// Example:
///
/// --output text --output xml
///
/// In the parsed `ArgResults`, a multiple-valued option will always return
/// a list, even if one or no values were passed.
static const multiple = OptionType._('OptionType.multiple');
@Deprecated('Use OptionType.multiple instead.')
static const MULTIPLE = multiple; // ignore: constant_identifier_names
final String name;
const OptionType._(this.name);
}